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