1<?php 2 3namespace SabreForRainLoop\VObject\Parser; 4 5use 6 SabreForRainLoop\VObject\Component\VCalendar, 7 SabreForRainLoop\VObject\Component\VCard, 8 SabreForRainLoop\VObject\ParseException, 9 SabreForRainLoop\VObject\EofException; 10 11/** 12 * Json Parser. 13 * 14 * This parser parses both the jCal and jCard formats. 15 * 16 * @copyright Copyright (C) 2007-2013 fruux GmbH. All rights reserved. 17 * @author Evert Pot (http://evertpot.com/) 18 * @license http://code.google.com/p/sabredav/wiki/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 \SabreForRainLoop\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(function($jProp) use ($self) { 98 return $self->parseProperty($jProp); 99 }, $jComp[1]); 100 101 if (isset($jComp[2])) { 102 103 $components = array_map(function($jComp) use ($self) { 104 return $self->parseComponent($jComp); 105 }, $jComp[2]); 106 107 } else $components = array(); 108 109 return $this->root->createComponent( 110 $jComp[0], 111 array_merge( $properties, $components), 112 $defaults = false 113 ); 114 115 } 116 117 /** 118 * Parses properties. 119 * 120 * @param array $jProp 121 * @return \SabreForRainLoop\VObject\Property 122 */ 123 public function parseProperty(array $jProp) { 124 125 list( 126 $propertyName, 127 $parameters, 128 $valueType 129 ) = $jProp; 130 131 $propertyName = strtoupper($propertyName); 132 133 // This is the default class we would be using if we didn't know the 134 // value type. We're using this value later in this function. 135 $defaultPropertyClass = $this->root->getClassNameForPropertyName($propertyName); 136 137 $parameters = (array)$parameters; 138 139 $value = array_slice($jProp, 3); 140 141 $valueType = strtoupper($valueType); 142 143 if (isset($parameters['group'])) { 144 $propertyName = $parameters['group'] . '.' . $propertyName; 145 unset($parameters['group']); 146 } 147 148 $prop = $this->root->createProperty($propertyName, null, $parameters, $valueType); 149 $prop->setJsonValue($value); 150 151 // We have to do something awkward here. FlatText as well as Text 152 // represents TEXT values. We have to normalize these here. In the 153 // future we can get rid of FlatText once we're allowed to break BC 154 // again. 155 if ($defaultPropertyClass === 'SabreForRainLoop\VObject\Property\FlatText') { 156 $defaultPropertyClass = 'SabreForRainLoop\VObject\Property\Text'; 157 } 158 159 // If the value type we received (e.g.: TEXT) was not the default value 160 // type for the given property (e.g.: BDAY), we need to add a VALUE= 161 // parameter. 162 if ($defaultPropertyClass !== get_class($prop)) { 163 $prop["VALUE"] = $valueType; 164 } 165 166 return $prop; 167 168 } 169 170 /** 171 * Sets the input data 172 * 173 * @param resource|string|array $input 174 * @return void 175 */ 176 public function setInput($input) { 177 178 if (is_resource($input)) { 179 $input = stream_get_contents($input); 180 } 181 if (is_string($input)) { 182 $input = json_decode($input); 183 } 184 $this->input = $input; 185 186 } 187 188} 189