xref: /plugin/davcal/vendor/sabre/vobject/lib/Property.php (revision a1a3b6794e0e143a4a8b51d3185ce2d339be61ab)
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