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