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 static $defaultName; 59 60 /** 61 * List of properties, and which classes they map to. 62 * 63 * @var array 64 */ 65 static $propertyMap = []; 66 67 /** 68 * List of components, along with which classes they map to. 69 * 70 * @var array 71 */ 72 static $componentMap = []; 73 74 /** 75 * List of value-types, and which classes they map to. 76 * 77 * @var array 78 */ 79 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 * @return void 96 */ 97 function __construct() { 98 99 $args = func_get_args(); 100 if (count($args) === 0 || is_array($args[0])) { 101 array_unshift($args, $this, static::$defaultName); 102 call_user_func_array(['parent', '__construct'], $args); 103 } else { 104 array_unshift($args, $this); 105 call_user_func_array(['parent', '__construct'], $args); 106 } 107 108 } 109 110 /** 111 * Returns the current document type. 112 * 113 * @return int 114 */ 115 function getDocumentType() { 116 117 return self::UNKNOWN; 118 119 } 120 121 /** 122 * Creates a new component or property. 123 * 124 * If it's a known component, we will automatically call createComponent. 125 * otherwise, we'll assume it's a property and call createProperty instead. 126 * 127 * @param string $name 128 * @param string $arg1,... Unlimited number of args 129 * 130 * @return mixed 131 */ 132 function create($name) { 133 134 if (isset(static::$componentMap[strtoupper($name)])) { 135 136 return call_user_func_array([$this, 'createComponent'], func_get_args()); 137 138 } else { 139 140 return call_user_func_array([$this, 'createProperty'], func_get_args()); 141 142 } 143 144 } 145 146 /** 147 * Creates a new component. 148 * 149 * This method automatically searches for the correct component class, based 150 * on its name. 151 * 152 * You can specify the children either in key=>value syntax, in which case 153 * properties will automatically be created, or you can just pass a list of 154 * Component and Property object. 155 * 156 * By default, a set of sensible values will be added to the component. For 157 * an iCalendar object, this may be something like CALSCALE:GREGORIAN. To 158 * ensure that this does not happen, set $defaults to false. 159 * 160 * @param string $name 161 * @param array $children 162 * @param bool $defaults 163 * 164 * @return Component 165 */ 166 function createComponent($name, array $children = null, $defaults = true) { 167 168 $name = strtoupper($name); 169 $class = 'Sabre\\VObject\\Component'; 170 171 if (isset(static::$componentMap[$name])) { 172 $class = static::$componentMap[$name]; 173 } 174 if (is_null($children)) $children = []; 175 return new $class($this, $name, $children, $defaults); 176 177 } 178 179 /** 180 * Factory method for creating new properties. 181 * 182 * This method automatically searches for the correct property class, based 183 * on its name. 184 * 185 * You can specify the parameters either in key=>value syntax, in which case 186 * parameters will automatically be created, or you can just pass a list of 187 * Parameter objects. 188 * 189 * @param string $name 190 * @param mixed $value 191 * @param array $parameters 192 * @param string $valueType Force a specific valuetype, such as URI or TEXT 193 * 194 * @return Property 195 */ 196 function createProperty($name, $value = null, array $parameters = null, $valueType = null) { 197 198 // If there's a . in the name, it means it's prefixed by a groupname. 199 if (($i = strpos($name, '.')) !== false) { 200 $group = substr($name, 0, $i); 201 $name = strtoupper(substr($name, $i + 1)); 202 } else { 203 $name = strtoupper($name); 204 $group = null; 205 } 206 207 $class = null; 208 209 if ($valueType) { 210 // The valueType argument comes first to figure out the correct 211 // class. 212 $class = $this->getClassNameForPropertyValue($valueType); 213 } 214 215 if (is_null($class)) { 216 // If a VALUE parameter is supplied, we should use that. 217 if (isset($parameters['VALUE'])) { 218 $class = $this->getClassNameForPropertyValue($parameters['VALUE']); 219 if (is_null($class)) { 220 throw new InvalidDataException('Unsupported VALUE parameter for ' . $name . ' property. You supplied "' . $parameters['VALUE'] . '"'); 221 } 222 } 223 else { 224 $class = $this->getClassNameForPropertyName($name); 225 } 226 } 227 if (is_null($parameters)) $parameters = []; 228 229 return new $class($this, $name, $value, $parameters, $group); 230 231 } 232 233 /** 234 * This method returns a full class-name for a value parameter. 235 * 236 * For instance, DTSTART may have VALUE=DATE. In that case we will look in 237 * our valueMap table and return the appropriate class name. 238 * 239 * This method returns null if we don't have a specialized class. 240 * 241 * @param string $valueParam 242 * @return string|null 243 */ 244 function getClassNameForPropertyValue($valueParam) { 245 246 $valueParam = strtoupper($valueParam); 247 if (isset(static::$valueMap[$valueParam])) { 248 return static::$valueMap[$valueParam]; 249 } 250 251 } 252 253 /** 254 * Returns the default class for a property name. 255 * 256 * @param string $propertyName 257 * 258 * @return string 259 */ 260 function getClassNameForPropertyName($propertyName) { 261 262 if (isset(static::$propertyMap[$propertyName])) { 263 return static::$propertyMap[$propertyName]; 264 } else { 265 return 'Sabre\\VObject\\Property\\Unknown'; 266 } 267 268 } 269 270} 271