1*a1a3b679SAndreas Boehler<?php 2*a1a3b679SAndreas Boehler 3*a1a3b679SAndreas Boehlernamespace Sabre\VObject; 4*a1a3b679SAndreas Boehler 5*a1a3b679SAndreas Boehler/** 6*a1a3b679SAndreas Boehler * Property 7*a1a3b679SAndreas Boehler * 8*a1a3b679SAndreas Boehler * A property is always in a KEY:VALUE structure, and may optionally contain 9*a1a3b679SAndreas Boehler * parameters. 10*a1a3b679SAndreas Boehler * 11*a1a3b679SAndreas Boehler * @copyright Copyright (C) 2011-2015 fruux GmbH (https://fruux.com/). 12*a1a3b679SAndreas Boehler * @author Evert Pot (http://evertpot.com/) 13*a1a3b679SAndreas Boehler * @license http://sabre.io/license/ Modified BSD License 14*a1a3b679SAndreas Boehler */ 15*a1a3b679SAndreas Boehlerabstract class Property extends Node { 16*a1a3b679SAndreas Boehler 17*a1a3b679SAndreas Boehler /** 18*a1a3b679SAndreas Boehler * Property name. 19*a1a3b679SAndreas Boehler * 20*a1a3b679SAndreas Boehler * This will contain a string such as DTSTART, SUMMARY, FN. 21*a1a3b679SAndreas Boehler * 22*a1a3b679SAndreas Boehler * @var string 23*a1a3b679SAndreas Boehler */ 24*a1a3b679SAndreas Boehler public $name; 25*a1a3b679SAndreas Boehler 26*a1a3b679SAndreas Boehler /** 27*a1a3b679SAndreas Boehler * Property group. 28*a1a3b679SAndreas Boehler * 29*a1a3b679SAndreas Boehler * This is only used in vcards 30*a1a3b679SAndreas Boehler * 31*a1a3b679SAndreas Boehler * @var string 32*a1a3b679SAndreas Boehler */ 33*a1a3b679SAndreas Boehler public $group; 34*a1a3b679SAndreas Boehler 35*a1a3b679SAndreas Boehler /** 36*a1a3b679SAndreas Boehler * List of parameters 37*a1a3b679SAndreas Boehler * 38*a1a3b679SAndreas Boehler * @var array 39*a1a3b679SAndreas Boehler */ 40*a1a3b679SAndreas Boehler public $parameters = array(); 41*a1a3b679SAndreas Boehler 42*a1a3b679SAndreas Boehler /** 43*a1a3b679SAndreas Boehler * Current value 44*a1a3b679SAndreas Boehler * 45*a1a3b679SAndreas Boehler * @var mixed 46*a1a3b679SAndreas Boehler */ 47*a1a3b679SAndreas Boehler protected $value; 48*a1a3b679SAndreas Boehler 49*a1a3b679SAndreas Boehler /** 50*a1a3b679SAndreas Boehler * In case this is a multi-value property. This string will be used as a 51*a1a3b679SAndreas Boehler * delimiter. 52*a1a3b679SAndreas Boehler * 53*a1a3b679SAndreas Boehler * @var string|null 54*a1a3b679SAndreas Boehler */ 55*a1a3b679SAndreas Boehler public $delimiter = ';'; 56*a1a3b679SAndreas Boehler 57*a1a3b679SAndreas Boehler /** 58*a1a3b679SAndreas Boehler * Creates the generic property. 59*a1a3b679SAndreas Boehler * 60*a1a3b679SAndreas Boehler * Parameters must be specified in key=>value syntax. 61*a1a3b679SAndreas Boehler * 62*a1a3b679SAndreas Boehler * @param Component $root The root document 63*a1a3b679SAndreas Boehler * @param string $name 64*a1a3b679SAndreas Boehler * @param string|array|null $value 65*a1a3b679SAndreas Boehler * @param array $parameters List of parameters 66*a1a3b679SAndreas Boehler * @param string $group The vcard property group 67*a1a3b679SAndreas Boehler * @return void 68*a1a3b679SAndreas Boehler */ 69*a1a3b679SAndreas Boehler function __construct(Component $root, $name, $value = null, array $parameters = array(), $group = null) { 70*a1a3b679SAndreas Boehler 71*a1a3b679SAndreas Boehler $this->name = $name; 72*a1a3b679SAndreas Boehler $this->group = $group; 73*a1a3b679SAndreas Boehler 74*a1a3b679SAndreas Boehler $this->root = $root; 75*a1a3b679SAndreas Boehler 76*a1a3b679SAndreas Boehler foreach($parameters as $k=>$v) { 77*a1a3b679SAndreas Boehler $this->add($k, $v); 78*a1a3b679SAndreas Boehler } 79*a1a3b679SAndreas Boehler 80*a1a3b679SAndreas Boehler if (!is_null($value)) { 81*a1a3b679SAndreas Boehler $this->setValue($value); 82*a1a3b679SAndreas Boehler } 83*a1a3b679SAndreas Boehler 84*a1a3b679SAndreas Boehler } 85*a1a3b679SAndreas Boehler 86*a1a3b679SAndreas Boehler /** 87*a1a3b679SAndreas Boehler * Updates the current value. 88*a1a3b679SAndreas Boehler * 89*a1a3b679SAndreas Boehler * This may be either a single, or multiple strings in an array. 90*a1a3b679SAndreas Boehler * 91*a1a3b679SAndreas Boehler * @param string|array $value 92*a1a3b679SAndreas Boehler * @return void 93*a1a3b679SAndreas Boehler */ 94*a1a3b679SAndreas Boehler function setValue($value) { 95*a1a3b679SAndreas Boehler 96*a1a3b679SAndreas Boehler $this->value = $value; 97*a1a3b679SAndreas Boehler 98*a1a3b679SAndreas Boehler } 99*a1a3b679SAndreas Boehler 100*a1a3b679SAndreas Boehler /** 101*a1a3b679SAndreas Boehler * Returns the current value. 102*a1a3b679SAndreas Boehler * 103*a1a3b679SAndreas Boehler * This method will always return a singular value. If this was a 104*a1a3b679SAndreas Boehler * multi-value object, some decision will be made first on how to represent 105*a1a3b679SAndreas Boehler * it as a string. 106*a1a3b679SAndreas Boehler * 107*a1a3b679SAndreas Boehler * To get the correct multi-value version, use getParts. 108*a1a3b679SAndreas Boehler * 109*a1a3b679SAndreas Boehler * @return string 110*a1a3b679SAndreas Boehler */ 111*a1a3b679SAndreas Boehler function getValue() { 112*a1a3b679SAndreas Boehler 113*a1a3b679SAndreas Boehler if (is_array($this->value)) { 114*a1a3b679SAndreas Boehler if (count($this->value)==0) { 115*a1a3b679SAndreas Boehler return null; 116*a1a3b679SAndreas Boehler } elseif (count($this->value)===1) { 117*a1a3b679SAndreas Boehler return $this->value[0]; 118*a1a3b679SAndreas Boehler } else { 119*a1a3b679SAndreas Boehler return $this->getRawMimeDirValue($this->value); 120*a1a3b679SAndreas Boehler } 121*a1a3b679SAndreas Boehler } else { 122*a1a3b679SAndreas Boehler return $this->value; 123*a1a3b679SAndreas Boehler } 124*a1a3b679SAndreas Boehler 125*a1a3b679SAndreas Boehler } 126*a1a3b679SAndreas Boehler 127*a1a3b679SAndreas Boehler /** 128*a1a3b679SAndreas Boehler * Sets a multi-valued property. 129*a1a3b679SAndreas Boehler * 130*a1a3b679SAndreas Boehler * @param array $parts 131*a1a3b679SAndreas Boehler * @return void 132*a1a3b679SAndreas Boehler */ 133*a1a3b679SAndreas Boehler function setParts(array $parts) { 134*a1a3b679SAndreas Boehler 135*a1a3b679SAndreas Boehler $this->value = $parts; 136*a1a3b679SAndreas Boehler 137*a1a3b679SAndreas Boehler } 138*a1a3b679SAndreas Boehler 139*a1a3b679SAndreas Boehler /** 140*a1a3b679SAndreas Boehler * Returns a multi-valued property. 141*a1a3b679SAndreas Boehler * 142*a1a3b679SAndreas Boehler * This method always returns an array, if there was only a single value, 143*a1a3b679SAndreas Boehler * it will still be wrapped in an array. 144*a1a3b679SAndreas Boehler * 145*a1a3b679SAndreas Boehler * @return array 146*a1a3b679SAndreas Boehler */ 147*a1a3b679SAndreas Boehler function getParts() { 148*a1a3b679SAndreas Boehler 149*a1a3b679SAndreas Boehler if (is_null($this->value)) { 150*a1a3b679SAndreas Boehler return array(); 151*a1a3b679SAndreas Boehler } elseif (is_array($this->value)) { 152*a1a3b679SAndreas Boehler return $this->value; 153*a1a3b679SAndreas Boehler } else { 154*a1a3b679SAndreas Boehler return array($this->value); 155*a1a3b679SAndreas Boehler } 156*a1a3b679SAndreas Boehler 157*a1a3b679SAndreas Boehler } 158*a1a3b679SAndreas Boehler 159*a1a3b679SAndreas Boehler /** 160*a1a3b679SAndreas Boehler * Adds a new parameter, and returns the new item. 161*a1a3b679SAndreas Boehler * 162*a1a3b679SAndreas Boehler * If a parameter with same name already existed, the values will be 163*a1a3b679SAndreas Boehler * combined. 164*a1a3b679SAndreas Boehler * If nameless parameter is added, we try to guess it's name. 165*a1a3b679SAndreas Boehler * 166*a1a3b679SAndreas Boehler * @param string $name 167*a1a3b679SAndreas Boehler * @param string|null|array $value 168*a1a3b679SAndreas Boehler * @return Node 169*a1a3b679SAndreas Boehler */ 170*a1a3b679SAndreas Boehler function add($name, $value = null) { 171*a1a3b679SAndreas Boehler $noName = false; 172*a1a3b679SAndreas Boehler if ($name === null) { 173*a1a3b679SAndreas Boehler $name = Parameter::guessParameterNameByValue($value); 174*a1a3b679SAndreas Boehler $noName = true; 175*a1a3b679SAndreas Boehler } 176*a1a3b679SAndreas Boehler 177*a1a3b679SAndreas Boehler if (isset($this->parameters[strtoupper($name)])) { 178*a1a3b679SAndreas Boehler $this->parameters[strtoupper($name)]->addValue($value); 179*a1a3b679SAndreas Boehler } 180*a1a3b679SAndreas Boehler else { 181*a1a3b679SAndreas Boehler $param = new Parameter($this->root, $name, $value); 182*a1a3b679SAndreas Boehler $param->noName = $noName; 183*a1a3b679SAndreas Boehler $this->parameters[$param->name] = $param; 184*a1a3b679SAndreas Boehler } 185*a1a3b679SAndreas Boehler } 186*a1a3b679SAndreas Boehler 187*a1a3b679SAndreas Boehler /** 188*a1a3b679SAndreas Boehler * Returns an iterable list of children 189*a1a3b679SAndreas Boehler * 190*a1a3b679SAndreas Boehler * @return array 191*a1a3b679SAndreas Boehler */ 192*a1a3b679SAndreas Boehler function parameters() { 193*a1a3b679SAndreas Boehler 194*a1a3b679SAndreas Boehler return $this->parameters; 195*a1a3b679SAndreas Boehler 196*a1a3b679SAndreas Boehler } 197*a1a3b679SAndreas Boehler 198*a1a3b679SAndreas Boehler /** 199*a1a3b679SAndreas Boehler * Returns the type of value. 200*a1a3b679SAndreas Boehler * 201*a1a3b679SAndreas Boehler * This corresponds to the VALUE= parameter. Every property also has a 202*a1a3b679SAndreas Boehler * 'default' valueType. 203*a1a3b679SAndreas Boehler * 204*a1a3b679SAndreas Boehler * @return string 205*a1a3b679SAndreas Boehler */ 206*a1a3b679SAndreas Boehler abstract function getValueType(); 207*a1a3b679SAndreas Boehler 208*a1a3b679SAndreas Boehler /** 209*a1a3b679SAndreas Boehler * Sets a raw value coming from a mimedir (iCalendar/vCard) file. 210*a1a3b679SAndreas Boehler * 211*a1a3b679SAndreas Boehler * This has been 'unfolded', so only 1 line will be passed. Unescaping is 212*a1a3b679SAndreas Boehler * not yet done, but parameters are not included. 213*a1a3b679SAndreas Boehler * 214*a1a3b679SAndreas Boehler * @param string $val 215*a1a3b679SAndreas Boehler * @return void 216*a1a3b679SAndreas Boehler */ 217*a1a3b679SAndreas Boehler abstract function setRawMimeDirValue($val); 218*a1a3b679SAndreas Boehler 219*a1a3b679SAndreas Boehler /** 220*a1a3b679SAndreas Boehler * Returns a raw mime-dir representation of the value. 221*a1a3b679SAndreas Boehler * 222*a1a3b679SAndreas Boehler * @return string 223*a1a3b679SAndreas Boehler */ 224*a1a3b679SAndreas Boehler abstract function getRawMimeDirValue(); 225*a1a3b679SAndreas Boehler 226*a1a3b679SAndreas Boehler /** 227*a1a3b679SAndreas Boehler * Turns the object back into a serialized blob. 228*a1a3b679SAndreas Boehler * 229*a1a3b679SAndreas Boehler * @return string 230*a1a3b679SAndreas Boehler */ 231*a1a3b679SAndreas Boehler function serialize() { 232*a1a3b679SAndreas Boehler 233*a1a3b679SAndreas Boehler $str = $this->name; 234*a1a3b679SAndreas Boehler if ($this->group) $str = $this->group . '.' . $this->name; 235*a1a3b679SAndreas Boehler 236*a1a3b679SAndreas Boehler foreach($this->parameters as $param) { 237*a1a3b679SAndreas Boehler 238*a1a3b679SAndreas Boehler $str.=';' . $param->serialize(); 239*a1a3b679SAndreas Boehler 240*a1a3b679SAndreas Boehler } 241*a1a3b679SAndreas Boehler 242*a1a3b679SAndreas Boehler $str.=':' . $this->getRawMimeDirValue(); 243*a1a3b679SAndreas Boehler 244*a1a3b679SAndreas Boehler $out = ''; 245*a1a3b679SAndreas Boehler while(strlen($str)>0) { 246*a1a3b679SAndreas Boehler if (strlen($str)>75) { 247*a1a3b679SAndreas Boehler $out.= mb_strcut($str,0,75,'utf-8') . "\r\n"; 248*a1a3b679SAndreas Boehler $str = ' ' . mb_strcut($str,75,strlen($str),'utf-8'); 249*a1a3b679SAndreas Boehler } else { 250*a1a3b679SAndreas Boehler $out.=$str . "\r\n"; 251*a1a3b679SAndreas Boehler $str=''; 252*a1a3b679SAndreas Boehler break; 253*a1a3b679SAndreas Boehler } 254*a1a3b679SAndreas Boehler } 255*a1a3b679SAndreas Boehler 256*a1a3b679SAndreas Boehler return $out; 257*a1a3b679SAndreas Boehler 258*a1a3b679SAndreas Boehler } 259*a1a3b679SAndreas Boehler 260*a1a3b679SAndreas Boehler /** 261*a1a3b679SAndreas Boehler * Returns the value, in the format it should be encoded for json. 262*a1a3b679SAndreas Boehler * 263*a1a3b679SAndreas Boehler * This method must always return an array. 264*a1a3b679SAndreas Boehler * 265*a1a3b679SAndreas Boehler * @return array 266*a1a3b679SAndreas Boehler */ 267*a1a3b679SAndreas Boehler function getJsonValue() { 268*a1a3b679SAndreas Boehler 269*a1a3b679SAndreas Boehler return $this->getParts(); 270*a1a3b679SAndreas Boehler 271*a1a3b679SAndreas Boehler } 272*a1a3b679SAndreas Boehler 273*a1a3b679SAndreas Boehler /** 274*a1a3b679SAndreas Boehler * Sets the json value, as it would appear in a jCard or jCal object. 275*a1a3b679SAndreas Boehler * 276*a1a3b679SAndreas Boehler * The value must always be an array. 277*a1a3b679SAndreas Boehler * 278*a1a3b679SAndreas Boehler * @param array $value 279*a1a3b679SAndreas Boehler * @return void 280*a1a3b679SAndreas Boehler */ 281*a1a3b679SAndreas Boehler function setJsonValue(array $value) { 282*a1a3b679SAndreas Boehler 283*a1a3b679SAndreas Boehler if (count($value)===1) { 284*a1a3b679SAndreas Boehler $this->setValue(reset($value)); 285*a1a3b679SAndreas Boehler } else { 286*a1a3b679SAndreas Boehler $this->setValue($value); 287*a1a3b679SAndreas Boehler } 288*a1a3b679SAndreas Boehler 289*a1a3b679SAndreas Boehler } 290*a1a3b679SAndreas Boehler 291*a1a3b679SAndreas Boehler /** 292*a1a3b679SAndreas Boehler * This method returns an array, with the representation as it should be 293*a1a3b679SAndreas Boehler * encoded in json. This is used to create jCard or jCal documents. 294*a1a3b679SAndreas Boehler * 295*a1a3b679SAndreas Boehler * @return array 296*a1a3b679SAndreas Boehler */ 297*a1a3b679SAndreas Boehler function jsonSerialize() { 298*a1a3b679SAndreas Boehler 299*a1a3b679SAndreas Boehler $parameters = array(); 300*a1a3b679SAndreas Boehler 301*a1a3b679SAndreas Boehler foreach($this->parameters as $parameter) { 302*a1a3b679SAndreas Boehler if ($parameter->name === 'VALUE') { 303*a1a3b679SAndreas Boehler continue; 304*a1a3b679SAndreas Boehler } 305*a1a3b679SAndreas Boehler $parameters[strtolower($parameter->name)] = $parameter->jsonSerialize(); 306*a1a3b679SAndreas Boehler } 307*a1a3b679SAndreas Boehler // In jCard, we need to encode the property-group as a separate 'group' 308*a1a3b679SAndreas Boehler // parameter. 309*a1a3b679SAndreas Boehler if ($this->group) { 310*a1a3b679SAndreas Boehler $parameters['group'] = $this->group; 311*a1a3b679SAndreas Boehler } 312*a1a3b679SAndreas Boehler 313*a1a3b679SAndreas Boehler return array_merge( 314*a1a3b679SAndreas Boehler array( 315*a1a3b679SAndreas Boehler strtolower($this->name), 316*a1a3b679SAndreas Boehler (object)$parameters, 317*a1a3b679SAndreas Boehler strtolower($this->getValueType()), 318*a1a3b679SAndreas Boehler ), 319*a1a3b679SAndreas Boehler $this->getJsonValue() 320*a1a3b679SAndreas Boehler ); 321*a1a3b679SAndreas Boehler } 322*a1a3b679SAndreas Boehler 323*a1a3b679SAndreas Boehler 324*a1a3b679SAndreas Boehler /** 325*a1a3b679SAndreas Boehler * Called when this object is being cast to a string. 326*a1a3b679SAndreas Boehler * 327*a1a3b679SAndreas Boehler * If the property only had a single value, you will get just that. In the 328*a1a3b679SAndreas Boehler * case the property had multiple values, the contents will be escaped and 329*a1a3b679SAndreas Boehler * combined with ,. 330*a1a3b679SAndreas Boehler * 331*a1a3b679SAndreas Boehler * @return string 332*a1a3b679SAndreas Boehler */ 333*a1a3b679SAndreas Boehler function __toString() { 334*a1a3b679SAndreas Boehler 335*a1a3b679SAndreas Boehler return (string)$this->getValue(); 336*a1a3b679SAndreas Boehler 337*a1a3b679SAndreas Boehler } 338*a1a3b679SAndreas Boehler 339*a1a3b679SAndreas Boehler /* ArrayAccess interface {{{ */ 340*a1a3b679SAndreas Boehler 341*a1a3b679SAndreas Boehler /** 342*a1a3b679SAndreas Boehler * Checks if an array element exists 343*a1a3b679SAndreas Boehler * 344*a1a3b679SAndreas Boehler * @param mixed $name 345*a1a3b679SAndreas Boehler * @return bool 346*a1a3b679SAndreas Boehler */ 347*a1a3b679SAndreas Boehler function offsetExists($name) { 348*a1a3b679SAndreas Boehler 349*a1a3b679SAndreas Boehler if (is_int($name)) return parent::offsetExists($name); 350*a1a3b679SAndreas Boehler 351*a1a3b679SAndreas Boehler $name = strtoupper($name); 352*a1a3b679SAndreas Boehler 353*a1a3b679SAndreas Boehler foreach($this->parameters as $parameter) { 354*a1a3b679SAndreas Boehler if ($parameter->name == $name) return true; 355*a1a3b679SAndreas Boehler } 356*a1a3b679SAndreas Boehler return false; 357*a1a3b679SAndreas Boehler 358*a1a3b679SAndreas Boehler } 359*a1a3b679SAndreas Boehler 360*a1a3b679SAndreas Boehler /** 361*a1a3b679SAndreas Boehler * Returns a parameter. 362*a1a3b679SAndreas Boehler * 363*a1a3b679SAndreas Boehler * If the parameter does not exist, null is returned. 364*a1a3b679SAndreas Boehler * 365*a1a3b679SAndreas Boehler * @param string $name 366*a1a3b679SAndreas Boehler * @return Node 367*a1a3b679SAndreas Boehler */ 368*a1a3b679SAndreas Boehler function offsetGet($name) { 369*a1a3b679SAndreas Boehler 370*a1a3b679SAndreas Boehler if (is_int($name)) return parent::offsetGet($name); 371*a1a3b679SAndreas Boehler $name = strtoupper($name); 372*a1a3b679SAndreas Boehler 373*a1a3b679SAndreas Boehler if (!isset($this->parameters[$name])) { 374*a1a3b679SAndreas Boehler return null; 375*a1a3b679SAndreas Boehler } 376*a1a3b679SAndreas Boehler 377*a1a3b679SAndreas Boehler return $this->parameters[$name]; 378*a1a3b679SAndreas Boehler 379*a1a3b679SAndreas Boehler } 380*a1a3b679SAndreas Boehler 381*a1a3b679SAndreas Boehler /** 382*a1a3b679SAndreas Boehler * Creates a new parameter 383*a1a3b679SAndreas Boehler * 384*a1a3b679SAndreas Boehler * @param string $name 385*a1a3b679SAndreas Boehler * @param mixed $value 386*a1a3b679SAndreas Boehler * @return void 387*a1a3b679SAndreas Boehler */ 388*a1a3b679SAndreas Boehler function offsetSet($name, $value) { 389*a1a3b679SAndreas Boehler 390*a1a3b679SAndreas Boehler if (is_int($name)) { 391*a1a3b679SAndreas Boehler parent::offsetSet($name, $value); 392*a1a3b679SAndreas Boehler // @codeCoverageIgnoreStart 393*a1a3b679SAndreas Boehler // This will never be reached, because an exception is always 394*a1a3b679SAndreas Boehler // thrown. 395*a1a3b679SAndreas Boehler return; 396*a1a3b679SAndreas Boehler // @codeCoverageIgnoreEnd 397*a1a3b679SAndreas Boehler } 398*a1a3b679SAndreas Boehler 399*a1a3b679SAndreas Boehler $param = new Parameter($this->root, $name, $value); 400*a1a3b679SAndreas Boehler $this->parameters[$param->name] = $param; 401*a1a3b679SAndreas Boehler 402*a1a3b679SAndreas Boehler } 403*a1a3b679SAndreas Boehler 404*a1a3b679SAndreas Boehler /** 405*a1a3b679SAndreas Boehler * Removes one or more parameters with the specified name 406*a1a3b679SAndreas Boehler * 407*a1a3b679SAndreas Boehler * @param string $name 408*a1a3b679SAndreas Boehler * @return void 409*a1a3b679SAndreas Boehler */ 410*a1a3b679SAndreas Boehler function offsetUnset($name) { 411*a1a3b679SAndreas Boehler 412*a1a3b679SAndreas Boehler if (is_int($name)) { 413*a1a3b679SAndreas Boehler parent::offsetUnset($name); 414*a1a3b679SAndreas Boehler // @codeCoverageIgnoreStart 415*a1a3b679SAndreas Boehler // This will never be reached, because an exception is always 416*a1a3b679SAndreas Boehler // thrown. 417*a1a3b679SAndreas Boehler return; 418*a1a3b679SAndreas Boehler // @codeCoverageIgnoreEnd 419*a1a3b679SAndreas Boehler } 420*a1a3b679SAndreas Boehler 421*a1a3b679SAndreas Boehler unset($this->parameters[strtoupper($name)]); 422*a1a3b679SAndreas Boehler 423*a1a3b679SAndreas Boehler } 424*a1a3b679SAndreas Boehler /* }}} */ 425*a1a3b679SAndreas Boehler 426*a1a3b679SAndreas Boehler /** 427*a1a3b679SAndreas Boehler * This method is automatically called when the object is cloned. 428*a1a3b679SAndreas Boehler * Specifically, this will ensure all child elements are also cloned. 429*a1a3b679SAndreas Boehler * 430*a1a3b679SAndreas Boehler * @return void 431*a1a3b679SAndreas Boehler */ 432*a1a3b679SAndreas Boehler function __clone() { 433*a1a3b679SAndreas Boehler 434*a1a3b679SAndreas Boehler foreach($this->parameters as $key=>$child) { 435*a1a3b679SAndreas Boehler $this->parameters[$key] = clone $child; 436*a1a3b679SAndreas Boehler $this->parameters[$key]->parent = $this; 437*a1a3b679SAndreas Boehler } 438*a1a3b679SAndreas Boehler 439*a1a3b679SAndreas Boehler } 440*a1a3b679SAndreas Boehler 441*a1a3b679SAndreas Boehler /** 442*a1a3b679SAndreas Boehler * Validates the node for correctness. 443*a1a3b679SAndreas Boehler * 444*a1a3b679SAndreas Boehler * The following options are supported: 445*a1a3b679SAndreas Boehler * - Node::REPAIR - If something is broken, and automatic repair may 446*a1a3b679SAndreas Boehler * be attempted. 447*a1a3b679SAndreas Boehler * 448*a1a3b679SAndreas Boehler * An array is returned with warnings. 449*a1a3b679SAndreas Boehler * 450*a1a3b679SAndreas Boehler * Every item in the array has the following properties: 451*a1a3b679SAndreas Boehler * * level - (number between 1 and 3 with severity information) 452*a1a3b679SAndreas Boehler * * message - (human readable message) 453*a1a3b679SAndreas Boehler * * node - (reference to the offending node) 454*a1a3b679SAndreas Boehler * 455*a1a3b679SAndreas Boehler * @param int $options 456*a1a3b679SAndreas Boehler * @return array 457*a1a3b679SAndreas Boehler */ 458*a1a3b679SAndreas Boehler function validate($options = 0) { 459*a1a3b679SAndreas Boehler 460*a1a3b679SAndreas Boehler $warnings = array(); 461*a1a3b679SAndreas Boehler 462*a1a3b679SAndreas Boehler // Checking if our value is UTF-8 463*a1a3b679SAndreas Boehler if (!StringUtil::isUTF8($this->getRawMimeDirValue())) { 464*a1a3b679SAndreas Boehler 465*a1a3b679SAndreas Boehler $oldValue = $this->getRawMimeDirValue(); 466*a1a3b679SAndreas Boehler $level = 3; 467*a1a3b679SAndreas Boehler if ($options & self::REPAIR) { 468*a1a3b679SAndreas Boehler $newValue = StringUtil::convertToUTF8($oldValue); 469*a1a3b679SAndreas Boehler if (true || StringUtil::isUTF8($newValue)) { 470*a1a3b679SAndreas Boehler $this->setRawMimeDirValue($newValue); 471*a1a3b679SAndreas Boehler $level = 1; 472*a1a3b679SAndreas Boehler } 473*a1a3b679SAndreas Boehler 474*a1a3b679SAndreas Boehler } 475*a1a3b679SAndreas Boehler 476*a1a3b679SAndreas Boehler 477*a1a3b679SAndreas Boehler if (preg_match('%([\x00-\x08\x0B-\x0C\x0E-\x1F\x7F])%', $oldValue, $matches)) { 478*a1a3b679SAndreas Boehler $message = 'Property contained a control character (0x' . bin2hex($matches[1]) . ')'; 479*a1a3b679SAndreas Boehler } else { 480*a1a3b679SAndreas Boehler $message = 'Property is not valid UTF-8! ' . $oldValue; 481*a1a3b679SAndreas Boehler } 482*a1a3b679SAndreas Boehler 483*a1a3b679SAndreas Boehler $warnings[] = array( 484*a1a3b679SAndreas Boehler 'level' => $level, 485*a1a3b679SAndreas Boehler 'message' => $message, 486*a1a3b679SAndreas Boehler 'node' => $this, 487*a1a3b679SAndreas Boehler ); 488*a1a3b679SAndreas Boehler } 489*a1a3b679SAndreas Boehler 490*a1a3b679SAndreas Boehler // Checking if the propertyname does not contain any invalid bytes. 491*a1a3b679SAndreas Boehler if (!preg_match('/^([A-Z0-9-]+)$/', $this->name)) { 492*a1a3b679SAndreas Boehler $warnings[] = array( 493*a1a3b679SAndreas Boehler 'level' => 1, 494*a1a3b679SAndreas Boehler 'message' => 'The propertyname: ' . $this->name . ' contains invalid characters. Only A-Z, 0-9 and - are allowed', 495*a1a3b679SAndreas Boehler 'node' => $this, 496*a1a3b679SAndreas Boehler ); 497*a1a3b679SAndreas Boehler if ($options & self::REPAIR) { 498*a1a3b679SAndreas Boehler // Uppercasing and converting underscores to dashes. 499*a1a3b679SAndreas Boehler $this->name = strtoupper( 500*a1a3b679SAndreas Boehler str_replace('_', '-', $this->name) 501*a1a3b679SAndreas Boehler ); 502*a1a3b679SAndreas Boehler // Removing every other invalid character 503*a1a3b679SAndreas Boehler $this->name = preg_replace('/([^A-Z0-9-])/u', '', $this->name); 504*a1a3b679SAndreas Boehler 505*a1a3b679SAndreas Boehler } 506*a1a3b679SAndreas Boehler 507*a1a3b679SAndreas Boehler } 508*a1a3b679SAndreas Boehler 509*a1a3b679SAndreas Boehler if ($encoding = $this->offsetGet('ENCODING')) { 510*a1a3b679SAndreas Boehler 511*a1a3b679SAndreas Boehler if ($this->root->getDocumentType()===Document::VCARD40) { 512*a1a3b679SAndreas Boehler $warnings[] = array( 513*a1a3b679SAndreas Boehler 'level' => 1, 514*a1a3b679SAndreas Boehler 'message' => 'ENCODING parameter is not valid in vCard 4.', 515*a1a3b679SAndreas Boehler 'node' => $this 516*a1a3b679SAndreas Boehler ); 517*a1a3b679SAndreas Boehler } else { 518*a1a3b679SAndreas Boehler 519*a1a3b679SAndreas Boehler $encoding = (string)$encoding; 520*a1a3b679SAndreas Boehler 521*a1a3b679SAndreas Boehler $allowedEncoding = array(); 522*a1a3b679SAndreas Boehler 523*a1a3b679SAndreas Boehler switch($this->root->getDocumentType()) { 524*a1a3b679SAndreas Boehler case Document::ICALENDAR20 : 525*a1a3b679SAndreas Boehler $allowedEncoding = array('8BIT', 'BASE64'); 526*a1a3b679SAndreas Boehler break; 527*a1a3b679SAndreas Boehler case Document::VCARD21 : 528*a1a3b679SAndreas Boehler $allowedEncoding = array('QUOTED-PRINTABLE', 'BASE64', '8BIT'); 529*a1a3b679SAndreas Boehler break; 530*a1a3b679SAndreas Boehler case Document::VCARD30 : 531*a1a3b679SAndreas Boehler $allowedEncoding = array('B'); 532*a1a3b679SAndreas Boehler break; 533*a1a3b679SAndreas Boehler 534*a1a3b679SAndreas Boehler } 535*a1a3b679SAndreas Boehler if ($allowedEncoding && !in_array(strtoupper($encoding), $allowedEncoding)) { 536*a1a3b679SAndreas Boehler $warnings[] = array( 537*a1a3b679SAndreas Boehler 'level' => 1, 538*a1a3b679SAndreas Boehler 'message' => 'ENCODING=' . strtoupper($encoding) . ' is not valid for this document type.', 539*a1a3b679SAndreas Boehler 'node' => $this 540*a1a3b679SAndreas Boehler ); 541*a1a3b679SAndreas Boehler } 542*a1a3b679SAndreas Boehler } 543*a1a3b679SAndreas Boehler 544*a1a3b679SAndreas Boehler } 545*a1a3b679SAndreas Boehler 546*a1a3b679SAndreas Boehler // Validating inner parameters 547*a1a3b679SAndreas Boehler foreach($this->parameters as $param) { 548*a1a3b679SAndreas Boehler $warnings = array_merge($warnings, $param->validate($options)); 549*a1a3b679SAndreas Boehler } 550*a1a3b679SAndreas Boehler 551*a1a3b679SAndreas Boehler return $warnings; 552*a1a3b679SAndreas Boehler 553*a1a3b679SAndreas Boehler } 554*a1a3b679SAndreas Boehler 555*a1a3b679SAndreas Boehler} 556