454 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			PHP
		
	
	
	
			
		
		
	
	
			454 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			PHP
		
	
	
	
| <?php
 | |
| 
 | |
| namespace Sabre\VObject;
 | |
| 
 | |
| /**
 | |
|  * VObject Property
 | |
|  *
 | |
|  * A property in VObject is usually in the form PARAMNAME:paramValue.
 | |
|  * An example is : SUMMARY:Weekly meeting
 | |
|  *
 | |
|  * Properties can also have parameters:
 | |
|  * SUMMARY;LANG=en:Weekly meeting.
 | |
|  *
 | |
|  * Parameters can be accessed using the ArrayAccess interface.
 | |
|  *
 | |
|  * @copyright Copyright (C) 2011-2015 fruux GmbH (https://fruux.com/).
 | |
|  * @author Evert Pot (http://evertpot.com/)
 | |
|  * @license http://sabre.io/license/ Modified BSD License
 | |
|  */
 | |
| class Property extends Node {
 | |
| 
 | |
|     /**
 | |
|      * Propertyname
 | |
|      *
 | |
|      * @var string
 | |
|      */
 | |
|     public $name;
 | |
| 
 | |
|     /**
 | |
|      * Group name
 | |
|      *
 | |
|      * This may be something like 'HOME' for vcards.
 | |
|      *
 | |
|      * @var string
 | |
|      */
 | |
|     public $group;
 | |
| 
 | |
|     /**
 | |
|      * Property parameters
 | |
|      *
 | |
|      * @var array
 | |
|      */
 | |
|     public $parameters = array();
 | |
| 
 | |
|     /**
 | |
|      * Property value
 | |
|      *
 | |
|      * @var string
 | |
|      */
 | |
|     public $value;
 | |
| 
 | |
|     /**
 | |
|      * If properties are added to this map, they will be automatically mapped
 | |
|      * to their respective classes, if parsed by the reader or constructed with
 | |
|      * the 'create' method.
 | |
|      *
 | |
|      * @var array
 | |
|      */
 | |
|     static public $classMap = array(
 | |
|         'COMPLETED'     => 'Sabre\\VObject\\Property\\DateTime',
 | |
|         'CREATED'       => 'Sabre\\VObject\\Property\\DateTime',
 | |
|         'DTEND'         => 'Sabre\\VObject\\Property\\DateTime',
 | |
|         'DTSTAMP'       => 'Sabre\\VObject\\Property\\DateTime',
 | |
|         'DTSTART'       => 'Sabre\\VObject\\Property\\DateTime',
 | |
|         'DUE'           => 'Sabre\\VObject\\Property\\DateTime',
 | |
|         'EXDATE'        => 'Sabre\\VObject\\Property\\MultiDateTime',
 | |
|         'LAST-MODIFIED' => 'Sabre\\VObject\\Property\\DateTime',
 | |
|         'RECURRENCE-ID' => 'Sabre\\VObject\\Property\\DateTime',
 | |
|         'TRIGGER'       => 'Sabre\\VObject\\Property\\DateTime',
 | |
|         'N'             => 'Sabre\\VObject\\Property\\Compound',
 | |
|         'ORG'           => 'Sabre\\VObject\\Property\\Compound',
 | |
|         'ADR'           => 'Sabre\\VObject\\Property\\Compound',
 | |
|         'CATEGORIES'    => 'Sabre\\VObject\\Property\\Compound',
 | |
|     );
 | |
| 
 | |
|     /**
 | |
|      * Creates the new property by name, but in addition will also see if
 | |
|      * there's a class mapped to the property name.
 | |
|      *
 | |
|      * Parameters can be specified with the optional third argument. Parameters
 | |
|      * must be a key->value map of the parameter name, and value. If the value
 | |
|      * is specified as an array, it is assumed that multiple parameters with
 | |
|      * the same name should be added.
 | |
|      *
 | |
|      * @param string $name
 | |
|      * @param string $value
 | |
|      * @param array $parameters
 | |
|      * @return Property
 | |
|      */
 | |
|     static public function create($name, $value = null, array $parameters = array()) {
 | |
| 
 | |
|         $name = strtoupper($name);
 | |
|         $shortName = $name;
 | |
|         $group = null;
 | |
|         if (strpos($shortName,'.')!==false) {
 | |
|             list($group, $shortName) = explode('.', $shortName);
 | |
|         }
 | |
| 
 | |
|         if (isset(self::$classMap[$shortName])) {
 | |
|             return new self::$classMap[$shortName]($name, $value, $parameters);
 | |
|         } else {
 | |
|             return new self($name, $value, $parameters);
 | |
|         }
 | |
| 
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Creates a new property object
 | |
|      *
 | |
|      * Parameters can be specified with the optional third argument. Parameters
 | |
|      * must be a key->value map of the parameter name, and value. If the value
 | |
|      * is specified as an array, it is assumed that multiple parameters with
 | |
|      * the same name should be added.
 | |
|      *
 | |
|      * @param string $name
 | |
|      * @param string $value
 | |
|      * @param array $parameters
 | |
|      */
 | |
|     public function __construct($name, $value = null, array $parameters = array()) {
 | |
| 
 | |
|         if (!is_scalar($value) && !is_null($value)) {
 | |
|             throw new \InvalidArgumentException('The value argument must be scalar or null');
 | |
|         }
 | |
| 
 | |
|         $name = strtoupper($name);
 | |
|         $group = null;
 | |
|         if (strpos($name,'.')!==false) {
 | |
|             list($group, $name) = explode('.', $name);
 | |
|         }
 | |
|         $this->name = $name;
 | |
|         $this->group = $group;
 | |
|         $this->setValue($value);
 | |
| 
 | |
|         foreach($parameters as $paramName => $paramValues) {
 | |
| 
 | |
|             if (!is_array($paramValues)) {
 | |
|                 $paramValues = array($paramValues);
 | |
|             }
 | |
| 
 | |
|             foreach($paramValues as $paramValue) {
 | |
|                 $this->add($paramName, $paramValue);
 | |
|             }
 | |
| 
 | |
|         }
 | |
| 
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Updates the internal value
 | |
|      *
 | |
|      * @param string $value
 | |
|      * @return void
 | |
|      */
 | |
|     public function setValue($value) {
 | |
| 
 | |
|         $this->value = $value;
 | |
| 
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Returns the internal value
 | |
|      *
 | |
|      * @param string $value
 | |
|      * @return string
 | |
|      */
 | |
|     public function getValue() {
 | |
| 
 | |
|         return $this->value;
 | |
| 
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Turns the object back into a serialized blob.
 | |
|      *
 | |
|      * @return string
 | |
|      */
 | |
|     public function serialize() {
 | |
| 
 | |
|         $str = $this->name;
 | |
|         if ($this->group) $str = $this->group . '.' . $this->name;
 | |
| 
 | |
|         foreach($this->parameters as $param) {
 | |
| 
 | |
|             $str.=';' . $param->serialize();
 | |
| 
 | |
|         }
 | |
| 
 | |
|         $src = array(
 | |
|             '\\',
 | |
|             "\n",
 | |
|             "\r",
 | |
|         );
 | |
|         $out = array(
 | |
|             '\\\\',
 | |
|             '\n',
 | |
|             '',
 | |
|         );
 | |
| 
 | |
|         // avoid double-escaping of \, and \; from Compound properties
 | |
|         if (method_exists($this, 'setParts')) {
 | |
|             $src[] = '\\\\,';
 | |
|             $out[] = '\\,';
 | |
|             $src[] = '\\\\;';
 | |
|             $out[] = '\\;';
 | |
|         }
 | |
| 
 | |
|         $str.=':' . str_replace($src, $out, $this->value);
 | |
| 
 | |
|         $out = '';
 | |
|         while(strlen($str)>0) {
 | |
|             if (strlen($str)>75) {
 | |
|                 $out.= mb_strcut($str,0,75,'utf-8') . "\r\n";
 | |
|                 $str = ' ' . mb_strcut($str,75,strlen($str),'utf-8');
 | |
|             } else {
 | |
|                 $out.=$str . "\r\n";
 | |
|                 $str='';
 | |
|                 break;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         return $out;
 | |
| 
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Adds a new componenten or element
 | |
|      *
 | |
|      * You can call this method with the following syntaxes:
 | |
|      *
 | |
|      * add(Parameter $element)
 | |
|      * add(string $name, $value)
 | |
|      *
 | |
|      * The first version adds an Parameter
 | |
|      * The second adds a property as a string.
 | |
|      *
 | |
|      * @param mixed $item
 | |
|      * @param mixed $itemValue
 | |
|      * @return void
 | |
|      */
 | |
|     public function add($item, $itemValue = null) {
 | |
| 
 | |
|         if ($item instanceof Parameter) {
 | |
|             if (!is_null($itemValue)) {
 | |
|                 throw new \InvalidArgumentException('The second argument must not be specified, when passing a VObject');
 | |
|             }
 | |
|             $item->parent = $this;
 | |
|             $this->parameters[] = $item;
 | |
|         } elseif(is_string($item)) {
 | |
| 
 | |
|             $parameter = new Parameter($item,$itemValue);
 | |
|             $parameter->parent = $this;
 | |
|             $this->parameters[] = $parameter;
 | |
| 
 | |
|         } else {
 | |
| 
 | |
|             throw new \InvalidArgumentException('The first argument must either be a Node a string');
 | |
| 
 | |
|         }
 | |
| 
 | |
|     }
 | |
| 
 | |
|     /* ArrayAccess interface {{{ */
 | |
| 
 | |
|     /**
 | |
|      * Checks if an array element exists
 | |
|      *
 | |
|      * @param mixed $name
 | |
|      * @return bool
 | |
|      */
 | |
|     public function offsetExists($name) {
 | |
| 
 | |
|         if (is_int($name)) return parent::offsetExists($name);
 | |
| 
 | |
|         $name = strtoupper($name);
 | |
| 
 | |
|         foreach($this->parameters as $parameter) {
 | |
|             if ($parameter->name == $name) return true;
 | |
|         }
 | |
|         return false;
 | |
| 
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Returns a parameter, or parameter list.
 | |
|      *
 | |
|      * @param string $name
 | |
|      * @return Node
 | |
|      */
 | |
|     public function offsetGet($name) {
 | |
| 
 | |
|         if (is_int($name)) return parent::offsetGet($name);
 | |
|         $name = strtoupper($name);
 | |
| 
 | |
|         $result = array();
 | |
|         foreach($this->parameters as $parameter) {
 | |
|             if ($parameter->name == $name)
 | |
|                 $result[] = $parameter;
 | |
|         }
 | |
| 
 | |
|         if (count($result)===0) {
 | |
|             return null;
 | |
|         } elseif (count($result)===1) {
 | |
|             return $result[0];
 | |
|         } else {
 | |
|             $result[0]->setIterator(new ElementList($result));
 | |
|             return $result[0];
 | |
|         }
 | |
| 
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Creates a new parameter
 | |
|      *
 | |
|      * @param string $name
 | |
|      * @param mixed $value
 | |
|      * @return void
 | |
|      */
 | |
|     public function offsetSet($name, $value) {
 | |
| 
 | |
|         if (is_int($name)) parent::offsetSet($name, $value);
 | |
| 
 | |
|         if (is_scalar($value)) {
 | |
|             if (!is_string($name))
 | |
|                 throw new \InvalidArgumentException('A parameter name must be specified. This means you cannot use the $array[]="string" to add parameters.');
 | |
| 
 | |
|             $this->offsetUnset($name);
 | |
|             $parameter = new Parameter($name, $value);
 | |
|             $parameter->parent = $this;
 | |
|             $this->parameters[] = $parameter;
 | |
| 
 | |
|         } elseif ($value instanceof Parameter) {
 | |
|             if (!is_null($name))
 | |
|                 throw new \InvalidArgumentException('Don\'t specify a parameter name if you\'re passing a \\Sabre\\VObject\\Parameter. Add using $array[]=$parameterObject.');
 | |
| 
 | |
|             $value->parent = $this;
 | |
|             $this->parameters[] = $value;
 | |
|         } else {
 | |
|             throw new \InvalidArgumentException('You can only add parameters to the property object');
 | |
|         }
 | |
| 
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Removes one or more parameters with the specified name
 | |
|      *
 | |
|      * @param string $name
 | |
|      * @return void
 | |
|      */
 | |
|     public function offsetUnset($name) {
 | |
| 
 | |
|         if (is_int($name)) parent::offsetUnset($name);
 | |
|         $name = strtoupper($name);
 | |
| 
 | |
|         foreach($this->parameters as $key=>$parameter) {
 | |
|             if ($parameter->name == $name) {
 | |
|                 $parameter->parent = null;
 | |
|                 unset($this->parameters[$key]);
 | |
|             }
 | |
| 
 | |
|         }
 | |
| 
 | |
|     }
 | |
| 
 | |
|     /* }}} */
 | |
| 
 | |
|     /**
 | |
|      * Called when this object is being cast to a string
 | |
|      *
 | |
|      * @return string
 | |
|      */
 | |
|     public function __toString() {
 | |
| 
 | |
|         return (string)$this->value;
 | |
| 
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * This method is automatically called when the object is cloned.
 | |
|      * Specifically, this will ensure all child elements are also cloned.
 | |
|      *
 | |
|      * @return void
 | |
|      */
 | |
|     public function __clone() {
 | |
| 
 | |
|         foreach($this->parameters as $key=>$child) {
 | |
|             $this->parameters[$key] = clone $child;
 | |
|             $this->parameters[$key]->parent = $this;
 | |
|         }
 | |
| 
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Validates the node for correctness.
 | |
|      *
 | |
|      * The following options are supported:
 | |
|      *   - Node::REPAIR - If something is broken, and automatic repair may
 | |
|      *                    be attempted.
 | |
|      *
 | |
|      * An array is returned with warnings.
 | |
|      *
 | |
|      * Every item in the array has the following properties:
 | |
|      *    * level - (number between 1 and 3 with severity information)
 | |
|      *    * message - (human readable message)
 | |
|      *    * node - (reference to the offending node)
 | |
|      *
 | |
|      * @param int $options
 | |
|      * @return array
 | |
|      */
 | |
|     public function validate($options = 0) {
 | |
| 
 | |
|         $warnings = array();
 | |
| 
 | |
|         // Checking if our value is UTF-8
 | |
|         if (!StringUtil::isUTF8($this->value)) {
 | |
|             $warnings[] = array(
 | |
|                 'level' => 1,
 | |
|                 'message' => 'Property is not valid UTF-8!',
 | |
|                 'node' => $this,
 | |
|             );
 | |
|             if ($options & self::REPAIR) {
 | |
|                 $this->value = StringUtil::convertToUTF8($this->value);
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         // Checking if the propertyname does not contain any invalid bytes.
 | |
|         if (!preg_match('/^([A-Z0-9-]+)$/', $this->name)) {
 | |
|             $warnings[] = array(
 | |
|                 'level' => 1,
 | |
|                 'message' => 'The propertyname: ' . $this->name . ' contains invalid characters. Only A-Z, 0-9 and - are allowed',
 | |
|                 'node' => $this,
 | |
|             );
 | |
|             if ($options & self::REPAIR) {
 | |
|                 // Uppercasing and converting underscores to dashes.
 | |
|                 $this->name = strtoupper(
 | |
|                     str_replace('_', '-', $this->name)
 | |
|                 );
 | |
|                 // Removing every other invalid character
 | |
|                 $this->name = preg_replace('/([^A-Z0-9-])/u', '', $this->name);
 | |
| 
 | |
|             }
 | |
| 
 | |
|         }
 | |
| 
 | |
|         // Validating inner parameters
 | |
|         foreach($this->parameters as $param) {
 | |
|             $warnings = array_merge($warnings, $param->validate($options));
 | |
|         }
 | |
| 
 | |
|         return $warnings;
 | |
| 
 | |
|     }
 | |
| 
 | |
| }
 |