Json.php 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190
  1. <?php
  2. namespace Sabre\VObject\Parser;
  3. use Sabre\VObject\Component\VCalendar;
  4. use Sabre\VObject\Component\VCard;
  5. use Sabre\VObject\Document;
  6. use Sabre\VObject\EofException;
  7. use Sabre\VObject\ParseException;
  8. use Sabre\VObject\Property\FlatText;
  9. use Sabre\VObject\Property\Text;
  10. /**
  11. * Json Parser.
  12. *
  13. * This parser parses both the jCal and jCard formats.
  14. *
  15. * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
  16. * @author Evert Pot (http://evertpot.com/)
  17. * @license http://sabre.io/license/ Modified BSD License
  18. */
  19. class Json extends Parser
  20. {
  21. /**
  22. * The input data.
  23. *
  24. * @var array
  25. */
  26. protected $input;
  27. /**
  28. * Root component.
  29. *
  30. * @var Document
  31. */
  32. protected $root;
  33. /**
  34. * This method starts the parsing process.
  35. *
  36. * If the input was not supplied during construction, it's possible to pass
  37. * it here instead.
  38. *
  39. * If either input or options are not supplied, the defaults will be used.
  40. *
  41. * @param resource|string|array|null $input
  42. * @param int $options
  43. *
  44. * @return \Sabre\VObject\Document
  45. */
  46. public function parse($input = null, $options = 0)
  47. {
  48. if (!is_null($input)) {
  49. $this->setInput($input);
  50. }
  51. if (is_null($this->input)) {
  52. throw new EofException('End of input stream, or no input supplied');
  53. }
  54. if (0 !== $options) {
  55. $this->options = $options;
  56. }
  57. switch ($this->input[0]) {
  58. case 'vcalendar':
  59. $this->root = new VCalendar([], false);
  60. break;
  61. case 'vcard':
  62. $this->root = new VCard([], false);
  63. break;
  64. default:
  65. throw new ParseException('The root component must either be a vcalendar, or a vcard');
  66. }
  67. foreach ($this->input[1] as $prop) {
  68. $this->root->add($this->parseProperty($prop));
  69. }
  70. if (isset($this->input[2])) {
  71. foreach ($this->input[2] as $comp) {
  72. $this->root->add($this->parseComponent($comp));
  73. }
  74. }
  75. // Resetting the input so we can throw an feof exception the next time.
  76. $this->input = null;
  77. return $this->root;
  78. }
  79. /**
  80. * Parses a component.
  81. *
  82. * @return \Sabre\VObject\Component
  83. */
  84. public function parseComponent(array $jComp)
  85. {
  86. // We can remove $self from PHP 5.4 onward.
  87. $self = $this;
  88. $properties = array_map(
  89. function ($jProp) use ($self) {
  90. return $self->parseProperty($jProp);
  91. },
  92. $jComp[1]
  93. );
  94. if (isset($jComp[2])) {
  95. $components = array_map(
  96. function ($jComp) use ($self) {
  97. return $self->parseComponent($jComp);
  98. },
  99. $jComp[2]
  100. );
  101. } else {
  102. $components = [];
  103. }
  104. return $this->root->createComponent(
  105. $jComp[0],
  106. array_merge($properties, $components),
  107. $defaults = false
  108. );
  109. }
  110. /**
  111. * Parses properties.
  112. *
  113. * @return \Sabre\VObject\Property
  114. */
  115. public function parseProperty(array $jProp)
  116. {
  117. list(
  118. $propertyName,
  119. $parameters,
  120. $valueType
  121. ) = $jProp;
  122. $propertyName = strtoupper($propertyName);
  123. // This is the default class we would be using if we didn't know the
  124. // value type. We're using this value later in this function.
  125. $defaultPropertyClass = $this->root->getClassNameForPropertyName($propertyName);
  126. $parameters = (array) $parameters;
  127. $value = array_slice($jProp, 3);
  128. $valueType = strtoupper($valueType);
  129. if (isset($parameters['group'])) {
  130. $propertyName = $parameters['group'].'.'.$propertyName;
  131. unset($parameters['group']);
  132. }
  133. $prop = $this->root->createProperty($propertyName, null, $parameters, $valueType);
  134. $prop->setJsonValue($value);
  135. // We have to do something awkward here. FlatText as well as Text
  136. // represents TEXT values. We have to normalize these here. In the
  137. // future we can get rid of FlatText once we're allowed to break BC
  138. // again.
  139. if (FlatText::class === $defaultPropertyClass) {
  140. $defaultPropertyClass = Text::class;
  141. }
  142. // If the value type we received (e.g.: TEXT) was not the default value
  143. // type for the given property (e.g.: BDAY), we need to add a VALUE=
  144. // parameter.
  145. if ($defaultPropertyClass !== get_class($prop)) {
  146. $prop['VALUE'] = $valueType;
  147. }
  148. return $prop;
  149. }
  150. /**
  151. * Sets the input data.
  152. *
  153. * @param resource|string|array $input
  154. */
  155. public function setInput($input)
  156. {
  157. if (is_resource($input)) {
  158. $input = stream_get_contents($input);
  159. }
  160. if (is_string($input)) {
  161. $input = json_decode($input);
  162. }
  163. $this->input = $input;
  164. }
  165. }