MDL-67850 lib: add new plist library
[moodle.git] / lib / plist / classes / CFPropertyList / CFType.php
1 <?php
2 /**
3  * Data-Types for CFPropertyList as defined by Apple.
4  * {@link http://developer.apple.com/documentation/Darwin/Reference/ManPages/man5/plist.5.html Property Lists}
5  * @author Rodney Rehm <rodney.rehm@medialize.de>
6  * @author Christian Kruse <cjk@wwwtech.de>
7  * @package plist
8  * @subpackage plist.types
9  * @version $Id$
10  */
11 namespace CFPropertyList;
12 use \DOMDocument, \Iterator, \ArrayAccess;
14 /**
15  * Base-Class of all CFTypes used by CFPropertyList
16  * @author Rodney Rehm <rodney.rehm@medialize.de>
17  * @author Christian Kruse <cjk@wwwtech.de>
18  * @package plist
19  * @subpackage plist.types
20  * @version $Id$
21  * @example example-create-01.php Using the CFPropertyList API
22  * @example example-create-02.php Using CFPropertyList::guess()
23  * @example example-create-03.php Using CFPropertyList::guess() with {@link CFDate} and {@link CFData}
24  */
25 abstract class CFType {
26   /**
27    * CFType nodes
28    * @var array
29    */
30   protected $value = null;
32   /**
33    * Create new CFType.
34    * @param mixed $value Value of CFType
35    */
36   public function __construct($value=null) {
37     $this->setValue($value);
38   }
40   /************************************************************************************************
41    *    M A G I C   P R O P E R T I E S
42    ************************************************************************************************/
44   /**
45    * Get the CFType's value
46    * @return mixed CFType's value
47    */
48   public function getValue() {
49     return $this->value;
50   }
52   /**
53    * Set the CFType's value
54    * @return void
55    */
56   public function setValue($value) {
57     $this->value = $value;
58   }
60   /************************************************************************************************
61    *    S E R I A L I Z I N G
62    ************************************************************************************************/
64   /**
65    * Get XML-Node.
66    * @param DOMDocument $doc DOMDocument to create DOMNode in
67    * @param string $nodeName Name of element to create
68    * @return DOMNode Node created based on CType
69    * @uses $value as nodeValue
70    */
71   public function toXML(DOMDocument $doc, $nodeName) {
72     $node = $doc->createElement($nodeName);
73     $text = $doc->createTextNode($this->value);
74     $node->appendChild($text);
75     return $node;
76   }
78   /**
79    * convert value to binary representation
80    * @param CFBinaryPropertyList The binary property list object
81    * @return The offset in the object table
82    */
83   public abstract function toBinary(CFBinaryPropertyList &$bplist);
85   /**
86    * Get CFType's value.
87    * @return mixed primitive value
88    * @uses $value for retrieving primitive of CFType
89    */
90   public function toArray() {
91     return $this->getValue();
92   }
94 }
96 /**
97  * String Type of CFPropertyList
98  * @author Rodney Rehm <rodney.rehm@medialize.de>
99  * @author Christian Kruse <cjk@wwwtech.de>
100  * @package plist
101  * @subpackage plist.types
102  */
103 class CFString extends CFType {
104   /**
105    * Get XML-Node.
106    * @param DOMDocument $doc DOMDocument to create DOMNode in
107    * @param string $nodeName For compatibility reasons; just ignore it
108    * @return DOMNode &lt;string&gt;-Element
109    */
110   public function toXML(DOMDocument $doc,$nodeName="") {
111     return parent::toXML($doc, 'string');
112   }
114   /**
115    * convert value to binary representation
116    * @param CFBinaryPropertyList The binary property list object
117    * @return The offset in the object table
118    */
119   public function toBinary(CFBinaryPropertyList &$bplist) {
120     return $bplist->stringToBinary($this->value);
121   }
124 class CFUid extends CFType {
125   public
126   function toXML(DOMDocument $doc,$nodeName="") {
127     $obj = new CFDictionary(array('CF$UID' => new CFNumber($this->value)));
128     return $obj->toXml($doc);
129   }
131   public
132   function toBinary(CFBinaryPropertyList &$bplist) {
133     return $bplist->uidToBinary($this->value);
134   }
137 /**
138  * Number Type of CFPropertyList
139  * @author Rodney Rehm <rodney.rehm@medialize.de>
140  * @author Christian Kruse <cjk@wwwtech.de>
141  * @package plist
142  * @subpackage plist.types
143  */
144 class CFNumber extends CFType {
145   /**
146    * Get XML-Node.
147    * Returns &lt;real&gt; if $value is a float, &lt;integer&gt; if $value is an integer.
148    * @param DOMDocument $doc DOMDocument to create DOMNode in
149    * @param string $nodeName For compatibility reasons; just ignore it
150    * @return DOMNode &lt;real&gt; or &lt;integer&gt;-Element
151    */
152   public function toXML(DOMDocument $doc,$nodeName="") {
153     $ret = 'real';
154     if(intval($this->value) == $this->value && !is_float($this->value) && strpos($this->value,'.') === false) {
155       $this->value = intval($this->value);
156       $ret = 'integer';
157     }
158     return parent::toXML($doc, $ret);
159   }
161   /**
162    * convert value to binary representation
163    * @param CFBinaryPropertyList The binary property list object
164    * @return The offset in the object table
165    */
166   public function toBinary(CFBinaryPropertyList &$bplist) {
167     return $bplist->numToBinary($this->value);
168   }
171 /**
172  * Date Type of CFPropertyList
173  * Note: CFDate uses Unix timestamp (epoch) to store dates internally
174  * @author Rodney Rehm <rodney.rehm@medialize.de>
175  * @author Christian Kruse <cjk@wwwtech.de>
176  * @package plist
177  * @subpackage plist.types
178  */
179 class CFDate extends CFType {
180   const TIMESTAMP_APPLE = 0;
181   const TIMESTAMP_UNIX  = 1;
182   const DATE_DIFF_APPLE_UNIX = 978307200;
184   /**
185    * Create new Date CFType.
186    * @param integer $value timestamp to set
187    * @param integer $format format the timestamp is specified in, use {@link TIMESTAMP_APPLE} or {@link TIMESTAMP_UNIX}, defaults to {@link TIMESTAMP_APPLE}
188    * @uses setValue() to convert the timestamp
189    */
190   function __construct($value,$format=CFDate::TIMESTAMP_UNIX) {
191     $this->setValue($value,$format);
192   }
194   /**
195    * Set the Date CFType's value.
196    * @param integer $value timestamp to set
197    * @param integer $format format the timestamp is specified in, use {@link TIMESTAMP_APPLE} or {@link TIMESTAMP_UNIX}, defaults to {@link TIMESTAMP_UNIX}
198    * @return void
199    * @uses TIMESTAMP_APPLE to determine timestamp type
200    * @uses TIMESTAMP_UNIX to determine timestamp type
201    * @uses DATE_DIFF_APPLE_UNIX to convert Apple-timestamp to Unix-timestamp
202    */
203   function setValue($value,$format=CFDate::TIMESTAMP_UNIX) {
204     if($format == CFDate::TIMESTAMP_UNIX) $this->value = $value;
205     else $this->value = $value + CFDate::DATE_DIFF_APPLE_UNIX;
206   }
208   /**
209    * Get the Date CFType's value.
210    * @param integer $format format the timestamp is specified in, use {@link TIMESTAMP_APPLE} or {@link TIMESTAMP_UNIX}, defaults to {@link TIMESTAMP_UNIX}
211    * @return integer Unix timestamp
212    * @uses TIMESTAMP_APPLE to determine timestamp type
213    * @uses TIMESTAMP_UNIX to determine timestamp type
214    * @uses DATE_DIFF_APPLE_UNIX to convert Unix-timestamp to Apple-timestamp
215    */
216   function getValue($format=CFDate::TIMESTAMP_UNIX) {
217     if($format == CFDate::TIMESTAMP_UNIX) return $this->value;
218     else return $this->value - CFDate::DATE_DIFF_APPLE_UNIX;
219   }
221   /**
222    * Get XML-Node.
223    * @param DOMDocument $doc DOMDocument to create DOMNode in
224    * @param string $nodeName For compatibility reasons; just ignore it
225    * @return DOMNode &lt;date&gt;-Element
226    */
227   public function toXML(DOMDocument $doc,$nodeName="") {
228     $text = $doc->createTextNode(gmdate("Y-m-d\TH:i:s\Z",$this->getValue()));
229     $node = $doc->createElement("date");
230     $node->appendChild($text);
231     return $node;
232   }
234   /**
235    * convert value to binary representation
236    * @param CFBinaryPropertyList The binary property list object
237    * @return The offset in the object table
238    */
239   public function toBinary(CFBinaryPropertyList &$bplist) {
240     return $bplist->dateToBinary($this->value);
241   }
243   /**
244    * Create a UNIX timestamp from a PList date string
245    * @param string $val The date string (e.g. "2009-05-13T20:23:43Z")
246    * @return integer The UNIX timestamp
247    * @throws PListException when encountering an unknown date string format
248    */
249   public static function dateValue($val) {
250     //2009-05-13T20:23:43Z
251     if(!preg_match('/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})Z/',$val,$matches)) throw new PListException("Unknown date format: $val");
252     return gmmktime($matches[4],$matches[5],$matches[6],$matches[2],$matches[3],$matches[1]);
253   }
256 /**
257  * Boolean Type of CFPropertyList
258  * @author Rodney Rehm <rodney.rehm@medialize.de>
259  * @author Christian Kruse <cjk@wwwtech.de>
260  * @package plist
261  * @subpackage plist.types
262  */
263 class CFBoolean extends CFType {
264   /**
265    * Get XML-Node.
266    * Returns &lt;true&gt; if $value is a true, &lt;false&gt; if $value is false.
267    * @param DOMDocument $doc DOMDocument to create DOMNode in
268    * @param string $nodeName For compatibility reasons; just ignore it
269    * @return DOMNode &lt;true&gt; or &lt;false&gt;-Element
270    */
271   public function toXML(DOMDocument $doc,$nodeName="") {
272     return $doc->createElement($this->value ? 'true' : 'false');
273   }
275   /**
276    * convert value to binary representation
277    * @param CFBinaryPropertyList The binary property list object
278    * @return The offset in the object table
279    */
280   public function toBinary(CFBinaryPropertyList &$bplist) {
281     return $bplist->boolToBinary($this->value);
282   }
286 /**
287  * Data Type of CFPropertyList
288  * Note: Binary data is base64-encoded.
289  * @author Rodney Rehm <rodney.rehm@medialize.de>
290  * @author Christian Kruse <cjk@wwwtech.de>
291  * @package plist
292  * @subpackage plist.types
293  */
294 class CFData extends CFType {
295   /**
296    * Create new Data CFType
297    * @param string $value data to be contained by new object
298    * @param boolean $already_coded if true $value will not be base64-encoded, defaults to false
299    */
300   public function __construct($value=null,$already_coded=false) {
301     if($already_coded) $this->value = $value;
302     else $this->setValue($value);
303   }
305   /**
306    * Set the CFType's value and base64-encode it.
307    * <b>Note:</b> looks like base64_encode has troubles with UTF-8 encoded strings
308    * @return void
309    */
310   public function setValue($value) {
311     //if(function_exists('mb_check_encoding') && mb_check_encoding($value, 'UTF-8')) $value = utf8_decode($value);
312     $this->value = base64_encode($value);
313   }
315   /**
316    * Get base64 encoded data
317    * @return string The base64 encoded data value
318    */
319   public function getCodedValue() {
320     return $this->value;
321   }
323   /**
324    * Get the base64-decoded CFType's value.
325    * @return mixed CFType's value
326    */
327   public function getValue() {
328     return base64_decode($this->value);
329   }
331   /**
332    * Get XML-Node.
333    * @param DOMDocument $doc DOMDocument to create DOMNode in
334    * @param string $nodeName For compatibility reasons; just ignore it
335    * @return DOMNode &lt;data&gt;-Element
336    */
337   public function toXML(DOMDocument $doc,$nodeName="") {
338     return parent::toXML($doc, 'data');
339   }
341   /**
342    * convert value to binary representation
343    * @param CFBinaryPropertyList The binary property list object
344    * @return The offset in the object table
345    */
346   public function toBinary(CFBinaryPropertyList &$bplist) {
347     return $bplist->dataToBinary($this->getValue());
348   }
351 /**
352  * Array Type of CFPropertyList
353  * @author Rodney Rehm <rodney.rehm@medialize.de>
354  * @author Christian Kruse <cjk@wwwtech.de>
355  * @package plist
356  * @subpackage plist.types
357  */
358 class CFArray extends CFType implements Iterator, ArrayAccess {
359   /**
360    * Position of iterator {@link http://php.net/manual/en/class.iterator.php}
361    * @var integer
362    */
363   protected $iteratorPosition = 0;
366   /**
367    * Create new CFType.
368    * @param array $value Value of CFType
369    */
370   public function __construct($value=array()) {
371     $this->value = $value;
372   }
374   /**
375    * Set the CFType's value
376    * <b>Note:</b> this dummy does nothing
377    * @return void
378    */
379   public function setValue($value) {
380   }
382   /**
383    * Add CFType to collection.
384    * @param CFType $value CFType to add to collection, defaults to null which results in an empty {@link CFString}
385    * @return void
386    * @uses $value for adding $value
387    */
388   public function add(CFType $value=null) {
389     // anything but CFType is null, null is an empty string - sad but true
390     if( !$value )
391       $value = new CFString();
393     $this->value[] = $value;
394   }
396   /**
397    * Get CFType from collection.
398    * @param integer $key Key of CFType to retrieve from collection
399    * @return CFType CFType found at $key, null else
400    * @uses $value for retrieving CFType of $key
401    */
402   public function get($key) {
403     if(isset($this->value[$key])) return $this->value[$key];
404     return null;
405   }
407   /**
408    * Remove CFType from collection.
409    * @param integer $key Key of CFType to removes from collection
410    * @return CFType removed CFType, null else
411    * @uses $value for removing CFType of $key
412    */
413   public function del($key) {
414     if(isset($this->value[$key])) unset($this->value[$key]);
415   }
418   /************************************************************************************************
419    *    S E R I A L I Z I N G
420    ************************************************************************************************/
422   /**
423    * Get XML-Node.
424    * @param DOMDocument $doc DOMDocument to create DOMNode in
425    * @param string $nodeName For compatibility reasons; just ignore it
426    * @return DOMNode &lt;array&gt;-Element
427    */
428   public function toXML(DOMDocument $doc,$nodeName="") {
429     $node = $doc->createElement('array');
431     foreach($this->value as $value) $node->appendChild($value->toXML($doc));
432     return $node;
433   }
435   /**
436    * convert value to binary representation
437    * @param CFBinaryPropertyList The binary property list object
438    * @return The offset in the object table
439    */
440   public function toBinary(CFBinaryPropertyList &$bplist) {
441     return $bplist->arrayToBinary($this);
442   }
444   /**
445    * Get CFType's value.
446    * @return array primitive value
447    * @uses $value for retrieving primitive of CFType
448    */
449   public function toArray() {
450     $a = array();
451     foreach($this->value as $value) $a[] = $value->toArray();
452     return $a;
453   }
456   /************************************************************************************************
457    *    I T E R A T O R   I N T E R F A C E
458    ************************************************************************************************/
460   /**
461    * Rewind {@link $iteratorPosition} to first position (being 0)
462    * @link http://php.net/manual/en/iterator.rewind.php
463    * @return void
464    * @uses $iteratorPosition set to 0
465    */
466   public function rewind() {
467     $this->iteratorPosition = 0;
468   }
470   /**
471    * Get Iterator's current {@link CFType} identified by {@link $iteratorPosition}
472    * @link http://php.net/manual/en/iterator.current.php
473    * @return CFType current Item
474    * @uses $iteratorPosition identify current key
475    */
476   public function current() {
477     return $this->value[$this->iteratorPosition];
478   }
480   /**
481    * Get Iterator's current key identified by {@link $iteratorPosition}
482    * @link http://php.net/manual/en/iterator.key.php
483    * @return string key of the current Item
484    * @uses $iteratorPosition identify current key
485    */
486   public function key() {
487     return $this->iteratorPosition;
488   }
490   /**
491    * Increment {@link $iteratorPosition} to address next {@see CFType}
492    * @link http://php.net/manual/en/iterator.next.php
493    * @return void
494    * @uses $iteratorPosition increment by 1
495    */
496   public function next() {
497     $this->iteratorPosition++;
498   }
500   /**
501    * Test if {@link $iteratorPosition} addresses a valid element of {@link $value}
502    * @link http://php.net/manual/en/iterator.valid.php
503    * @return boolean true if current position is valid, false else
504    * @uses $iteratorPosition test if within {@link $iteratorKeys}
505    * @uses $iteratorPosition test if within {@link $value}
506    */
507   public function valid() {
508     return isset($this->value[$this->iteratorPosition]);
509   }
511   /************************************************************************************************
512    *    ArrayAccess   I N T E R F A C E
513    ************************************************************************************************/
515   /**
516    * Determine if the array's key exists
517    * @param string $key the key to check
518    * @return bool true if the offset exists, false if not
519    * @link http://php.net/manual/en/arrayaccess.offsetexists.php
520    * @uses $value to check if $key exists
521    * @author Sean Coates <sean@php.net>
522    */
523   public function offsetExists($key) {
524     return isset($this->value[$key]);
525   }
527   /**
528    * Fetch a specific key from the CFArray
529    * @param string $key the key to check
530    * @return mixed the value associated with the key; null if the key is not found
531    * @link http://php.net/manual/en/arrayaccess.offsetget.php
532    * @uses get() to get the key's value
533    * @author Sean Coates <sean@php.net>
534    */
535   public function offsetGet($key) {
536     return $this->get($key);
537   }
539   /**
540    * Set a value in the array
541    * @param string $key the key to set
542    * @param string $value the value to set
543    * @return void
544    * @link http://php.net/manual/en/arrayaccess.offsetset.php
545    * @uses setValue() to set the key's new value
546    * @author Sean Coates <sean@php.net>
547    */
548   public function offsetSet($key, $value) {
549     return $this->setValue($value);
550   }
552   /**
553    * Unsets a value in the array
554    * <b>Note:</b> this dummy does nothing
555    * @param string $key the key to set
556    * @return void
557    * @link http://php.net/manual/en/arrayaccess.offsetunset.php
558    * @author Sean Coates <sean@php.net>
559    */
560   public function offsetUnset($key) {
562   }
567 /**
568  * Array Type of CFPropertyList
569  * @author Rodney Rehm <rodney.rehm@medialize.de>
570  * @author Christian Kruse <cjk@wwwtech.de>
571  * @package plist
572  * @subpackage plist.types
573  */
574 class CFDictionary extends CFType implements Iterator {
575   /**
576    * Position of iterator {@link http://php.net/manual/en/class.iterator.php}
577    * @var integer
578    */
579   protected $iteratorPosition = 0;
581   /**
582    * List of Keys for numerical iterator access {@link http://php.net/manual/en/class.iterator.php}
583    * @var array
584    */
585   protected $iteratorKeys = null;
588   /**
589    * Create new CFType.
590    * @param array $value Value of CFType
591    */
592   public function __construct($value=array()) {
593     $this->value = $value;
594   }
596   /**
597    * Set the CFType's value
598    * <b>Note:</b> this dummy does nothing
599    * @return void
600    */
601   public function setValue($value) {
602   }
604   /**
605    * Add CFType to collection.
606    * @param string $key Key to add to collection
607    * @param CFType $value CFType to add to collection, defaults to null which results in an empty {@link CFString}
608    * @return void
609    * @uses $value for adding $key $value pair
610    */
611   public function add($key, CFType $value=null) {
612     // anything but CFType is null, null is an empty string - sad but true
613     if( !$value )
614       $value = new CFString();
616     $this->value[$key] = $value;
617   }
619   /**
620    * Get CFType from collection.
621    * @param string $key Key of CFType to retrieve from collection
622    * @return CFType CFType found at $key, null else
623    * @uses $value for retrieving CFType of $key
624    */
625   public function get($key) {
626     if(isset($this->value[$key])) return $this->value[$key];
627     return null;
628   }
630   /**
631    * Generic getter (magic)
632    * @param integer $key Key of CFType to retrieve from collection
633    * @return CFType CFType found at $key, null else
634    * @link http://php.net/oop5.overloading
635    * @uses get() to retrieve the key's value
636    * @author Sean Coates <sean@php.net>
637    */
638   public function __get($key) {
639     return $this->get($key);
640   }
642   /**
643    * Remove CFType from collection.
644    * @param string $key Key of CFType to removes from collection
645    * @return CFType removed CFType, null else
646    * @uses $value for removing CFType of $key
647    */
648   public function del($key) {
649     if(isset($this->value[$key])) unset($this->value[$key]);
650   }
653   /************************************************************************************************
654    *    S E R I A L I Z I N G
655    ************************************************************************************************/
657   /**
658    * Get XML-Node.
659    * @param DOMDocument $doc DOMDocument to create DOMNode in
660    * @param string $nodeName For compatibility reasons; just ignore it
661    * @return DOMNode &lt;dict&gt;-Element
662    */
663   public function toXML(DOMDocument $doc,$nodeName="") {
664     $node = $doc->createElement('dict');
666     foreach($this->value as $key => $value) {
667       $node->appendChild($doc->createElement('key', $key));
668       $node->appendChild($value->toXML($doc));
669     }
671     return $node;
672   }
674   /**
675    * convert value to binary representation
676    * @param CFBinaryPropertyList The binary property list object
677    * @return The offset in the object table
678    */
679   public function toBinary(CFBinaryPropertyList &$bplist) {
680     return $bplist->dictToBinary($this);
681   }
683   /**
684    * Get CFType's value.
685    * @return array primitive value
686    * @uses $value for retrieving primitive of CFType
687    */
688   public function toArray() {
689     $a = array();
691     foreach($this->value as $key => $value) $a[$key] = $value->toArray();
692     return $a;
693   }
696   /************************************************************************************************
697    *    I T E R A T O R   I N T E R F A C E
698    ************************************************************************************************/
700   /**
701    * Rewind {@link $iteratorPosition} to first position (being 0)
702    * @link http://php.net/manual/en/iterator.rewind.php
703    * @return void
704    * @uses $iteratorPosition set to 0
705    * @uses $iteratorKeys store keys of {@link $value}
706    */
707   public function rewind() {
708     $this->iteratorPosition = 0;
709     $this->iteratorKeys = array_keys($this->value);
710   }
712   /**
713    * Get Iterator's current {@link CFType} identified by {@link $iteratorPosition}
714    * @link http://php.net/manual/en/iterator.current.php
715    * @return CFType current Item
716    * @uses $iteratorPosition identify current key
717    * @uses $iteratorKeys identify current value
718    */
719   public function current() {
720     return $this->value[$this->iteratorKeys[$this->iteratorPosition]];
721   }
723   /**
724    * Get Iterator's current key identified by {@link $iteratorPosition}
725    * @link http://php.net/manual/en/iterator.key.php
726    * @return string key of the current Item
727    * @uses $iteratorPosition identify current key
728    * @uses $iteratorKeys identify current value
729    */
730   public function key() {
731     return $this->iteratorKeys[$this->iteratorPosition];
732   }
734   /**
735    * Increment {@link $iteratorPosition} to address next {@see CFType}
736    * @link http://php.net/manual/en/iterator.next.php
737    * @return void
738    * @uses $iteratorPosition increment by 1
739    */
740   public function next() {
741     $this->iteratorPosition++;
742   }
744   /**
745    * Test if {@link $iteratorPosition} addresses a valid element of {@link $value}
746    * @link http://php.net/manual/en/iterator.valid.php
747    * @return boolean true if current position is valid, false else
748    * @uses $iteratorPosition test if within {@link $iteratorKeys}
749    * @uses $iteratorPosition test if within {@link $value}
750    */
751   public function valid() {
752     return isset($this->iteratorKeys[$this->iteratorPosition]) && isset($this->value[$this->iteratorKeys[$this->iteratorPosition]]);
753   }
757 # eof