1<?php 2 3namespace Sabre\VObject; 4 5/** 6 * Document. 7 * 8 * A document is just like a component, except that it's also the top level 9 * element. 10 * 11 * Both a VCALENDAR and a VCARD are considered documents. 12 * 13 * This class also provides a registry for document types. 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 */ 19abstract class Document extends Component 20{ 21 /** 22 * Unknown document type. 23 */ 24 const UNKNOWN = 1; 25 26 /** 27 * vCalendar 1.0. 28 */ 29 const VCALENDAR10 = 2; 30 31 /** 32 * iCalendar 2.0. 33 */ 34 const ICALENDAR20 = 3; 35 36 /** 37 * vCard 2.1. 38 */ 39 const VCARD21 = 4; 40 41 /** 42 * vCard 3.0. 43 */ 44 const VCARD30 = 5; 45 46 /** 47 * vCard 4.0. 48 */ 49 const VCARD40 = 6; 50 51 /** 52 * The default name for this component. 53 * 54 * This should be 'VCALENDAR' or 'VCARD'. 55 * 56 * @var string 57 */ 58 public static $defaultName; 59 60 /** 61 * List of properties, and which classes they map to. 62 * 63 * @var array 64 */ 65 public static $propertyMap = []; 66 67 /** 68 * List of components, along with which classes they map to. 69 * 70 * @var array 71 */ 72 public static $componentMap = []; 73 74 /** 75 * List of value-types, and which classes they map to. 76 * 77 * @var array 78 */ 79 public static $valueMap = []; 80 81 /** 82 * Creates a new document. 83 * 84 * We're changing the default behavior slightly here. First, we don't want 85 * to have to specify a name (we already know it), and we want to allow 86 * children to be specified in the first argument. 87 * 88 * But, the default behavior also works. 89 * 90 * So the two sigs: 91 * 92 * new Document(array $children = [], $defaults = true); 93 * new Document(string $name, array $children = [], $defaults = true) 94 */ 95 public function __construct() 96 { 97 $args = func_get_args(); 98 $name = static::$defaultName; 99 if (0 === count($args) || is_array($args[0])) { 100 $children = isset($args[0]) ? $args[0] : []; 101 $defaults = isset($args[1]) ? $args[1] : true; 102 } else { 103 $name = $args[0]; 104 $children = isset($args[1]) ? $args[1] : []; 105 $defaults = isset($args[2]) ? $args[2] : true; 106 } 107 parent::__construct($this, $name, $children, $defaults); 108 } 109 110 /** 111 * Returns the current document type. 112 * 113 * @return int 114 */ 115 public function getDocumentType() 116 { 117 return self::UNKNOWN; 118 } 119 120 /** 121 * Creates a new component or property. 122 * 123 * If it's a known component, we will automatically call createComponent. 124 * otherwise, we'll assume it's a property and call createProperty instead. 125 * 126 * @param string $name 127 * @param string $arg1,... Unlimited number of args 128 * 129 * @return mixed 130 */ 131 public function create($name) 132 { 133 if (isset(static::$componentMap[strtoupper($name)])) { 134 return call_user_func_array([$this, 'createComponent'], func_get_args()); 135 } else { 136 return call_user_func_array([$this, 'createProperty'], func_get_args()); 137 } 138 } 139 140 /** 141 * Creates a new component. 142 * 143 * This method automatically searches for the correct component class, based 144 * on its name. 145 * 146 * You can specify the children either in key=>value syntax, in which case 147 * properties will automatically be created, or you can just pass a list of 148 * Component and Property object. 149 * 150 * By default, a set of sensible values will be added to the component. For 151 * an iCalendar object, this may be something like CALSCALE:GREGORIAN. To 152 * ensure that this does not happen, set $defaults to false. 153 * 154 * @param string $name 155 * @param array $children 156 * @param bool $defaults 157 * 158 * @return Component 159 */ 160 public function createComponent($name, array $children = null, $defaults = true) 161 { 162 $name = strtoupper($name); 163 $class = 'Sabre\\VObject\\Component'; 164 165 if (isset(static::$componentMap[$name])) { 166 $class = static::$componentMap[$name]; 167 } 168 if (is_null($children)) { 169 $children = []; 170 } 171 172 return new $class($this, $name, $children, $defaults); 173 } 174 175 /** 176 * Factory method for creating new properties. 177 * 178 * This method automatically searches for the correct property class, based 179 * on its name. 180 * 181 * You can specify the parameters either in key=>value syntax, in which case 182 * parameters will automatically be created, or you can just pass a list of 183 * Parameter objects. 184 * 185 * @param string $name 186 * @param mixed $value 187 * @param array $parameters 188 * @param string $valueType Force a specific valuetype, such as URI or TEXT 189 * 190 * @return Property 191 */ 192 public function createProperty($name, $value = null, array $parameters = null, $valueType = null) 193 { 194 // If there's a . in the name, it means it's prefixed by a groupname. 195 if (false !== ($i = strpos($name, '.'))) { 196 $group = substr($name, 0, $i); 197 $name = strtoupper(substr($name, $i + 1)); 198 } else { 199 $name = strtoupper($name); 200 $group = null; 201 } 202 203 $class = null; 204 205 if ($valueType) { 206 // The valueType argument comes first to figure out the correct 207 // class. 208 $class = $this->getClassNameForPropertyValue($valueType); 209 } 210 211 if (is_null($class)) { 212 // If a VALUE parameter is supplied, we should use that. 213 if (isset($parameters['VALUE'])) { 214 $class = $this->getClassNameForPropertyValue($parameters['VALUE']); 215 if (is_null($class)) { 216 throw new InvalidDataException('Unsupported VALUE parameter for '.$name.' property. You supplied "'.$parameters['VALUE'].'"'); 217 } 218 } else { 219 $class = $this->getClassNameForPropertyName($name); 220 } 221 } 222 if (is_null($parameters)) { 223 $parameters = []; 224 } 225 226 return new $class($this, $name, $value, $parameters, $group); 227 } 228 229 /** 230 * This method returns a full class-name for a value parameter. 231 * 232 * For instance, DTSTART may have VALUE=DATE. In that case we will look in 233 * our valueMap table and return the appropriate class name. 234 * 235 * This method returns null if we don't have a specialized class. 236 * 237 * @param string $valueParam 238 * 239 * @return string|null 240 */ 241 public function getClassNameForPropertyValue($valueParam) 242 { 243 $valueParam = strtoupper($valueParam); 244 if (isset(static::$valueMap[$valueParam])) { 245 return static::$valueMap[$valueParam]; 246 } 247 } 248 249 /** 250 * Returns the default class for a property name. 251 * 252 * @param string $propertyName 253 * 254 * @return string 255 */ 256 public function getClassNameForPropertyName($propertyName) 257 { 258 if (isset(static::$propertyMap[$propertyName])) { 259 return static::$propertyMap[$propertyName]; 260 } else { 261 return 'Sabre\\VObject\\Property\\Unknown'; 262 } 263 } 264} 265