]> CyberLeo.Net >> Repos - Github/sugarcrm.git/blob - Zend/Gdata/App/Base.php
Release 6.5.0
[Github/sugarcrm.git] / Zend / Gdata / App / Base.php
1 <?php
2
3 /**
4  * Zend Framework
5  *
6  * LICENSE
7  *
8  * This source file is subject to the new BSD license that is bundled
9  * with this package in the file LICENSE.txt.
10  * It is also available through the world-wide-web at this URL:
11  * http://framework.zend.com/license/new-bsd
12  * If you did not receive a copy of the license and are unable to
13  * obtain it through the world-wide-web, please send an email
14  * to license@zend.com so we can send you a copy immediately.
15  *
16  * @category   Zend
17  * @package    Zend_Gdata
18  * @subpackage App
19  * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
20  * @license    http://framework.zend.com/license/new-bsd     New BSD License
21
22  */
23
24 /**
25  * @see Zend_Gdata_App_Util
26  */
27 require_once 'Zend/Gdata/App/Util.php';
28
29 /**
30  * Abstract class for all XML elements
31  *
32  * @category   Zend
33  * @package    Zend_Gdata
34  * @subpackage App
35  * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
36  * @license    http://framework.zend.com/license/new-bsd     New BSD License
37  */
38 abstract class Zend_Gdata_App_Base
39 {
40
41     /**
42      * @var string The XML element name, including prefix if desired
43      */
44     protected $_rootElement = null;
45
46     /**
47      * @var string The XML namespace prefix
48      */
49     protected $_rootNamespace = 'atom';
50
51     /**
52      * @var string The XML namespace URI - takes precedence over lookup up the
53      * corresponding URI for $_rootNamespace
54      */
55     protected $_rootNamespaceURI = null;
56
57     /**
58      * @var array Leftover elements which were not handled
59      */
60     protected $_extensionElements = array();
61
62     /**
63      * @var array Leftover attributes which were not handled
64      */
65     protected $_extensionAttributes = array();
66
67     /**
68      * @var string XML child text node content
69      */
70     protected $_text = null;
71
72     /**
73      * @var array Memoized results from calls to lookupNamespace() to avoid
74      *      expensive calls to getGreatestBoundedValue(). The key is in the
75      *      form 'prefix-majorVersion-minorVersion', and the value is the
76      *      output from getGreatestBoundedValue().
77      */
78     protected static $_namespaceLookupCache = array();
79
80     /**
81      * List of namespaces, as a three-dimensional array. The first dimension
82      * represents the namespace prefix, the second dimension represents the
83      * minimum major protocol version, and the third dimension is the minimum
84      * minor protocol version. Null keys are NOT allowed.
85      *
86      * When looking up a namespace for a given prefix, the greatest version
87      * number (both major and minor) which is less than the effective version
88      * should be used.
89      *
90      * @see lookupNamespace()
91      * @see registerNamespace()
92      * @see registerAllNamespaces()
93      * @var array
94      */
95    protected $_namespaces = array(
96         'atom'      => array(
97             1 => array(
98                 0 => 'http://www.w3.org/2005/Atom'
99                 )
100             ),
101         'app'       => array(
102             1 => array(
103                 0 => 'http://purl.org/atom/app#'
104                 ),
105             2 => array(
106                 0 => 'http://www.w3.org/2007/app'
107                 )
108             )
109         );
110
111     public function __construct()
112     {
113     }
114
115     /**
116      * Returns the child text node of this element
117      * This represents any raw text contained within the XML element
118      *
119      * @return string Child text node
120      */
121     public function getText($trim = true)
122     {
123         if ($trim) {
124             return trim($this->_text);
125         } else {
126             return $this->_text;
127         }
128     }
129
130     /**
131      * Sets the child text node of this element
132      * This represents any raw text contained within the XML element
133      *
134      * @param string $value Child text node
135      * @return Zend_Gdata_App_Base Returns an object of the same type as 'this' to provide a fluent interface.
136      */
137     public function setText($value)
138     {
139         $this->_text = $value;
140         return $this;
141     }
142
143     /**
144      * Returns an array of all elements not matched to data model classes
145      * during the parsing of the XML
146      *
147      * @return array All elements not matched to data model classes during parsing
148      */
149     public function getExtensionElements()
150     {
151         return $this->_extensionElements;
152     }
153
154     /**
155      * Sets an array of all elements not matched to data model classes
156      * during the parsing of the XML.  This method can be used to add arbitrary
157      * child XML elements to any data model class.
158      *
159      * @param array $value All extension elements
160      * @return Zend_Gdata_App_Base Returns an object of the same type as 'this' to provide a fluent interface.
161      */
162     public function setExtensionElements($value)
163     {
164         $this->_extensionElements = $value;
165         return $this;
166     }
167
168     /**
169      * Returns an array of all extension attributes not transformed into data
170      * model properties during parsing of the XML.  Each element of the array
171      * is a hashed array of the format:
172      *     array('namespaceUri' => string, 'name' => string, 'value' => string);
173      *
174      * @return array All extension attributes
175      */
176     public function getExtensionAttributes()
177     {
178         return $this->_extensionAttributes;
179     }
180
181     /**
182      * Sets an array of all extension attributes not transformed into data
183      * model properties during parsing of the XML.  Each element of the array
184      * is a hashed array of the format:
185      *     array('namespaceUri' => string, 'name' => string, 'value' => string);
186      * This can be used to add arbitrary attributes to any data model element
187      *
188      * @param array $value All extension attributes
189      * @return Zend_Gdata_App_Base Returns an object of the same type as 'this' to provide a fluent interface.
190      */
191     public function setExtensionAttributes($value)
192     {
193         $this->_extensionAttributes = $value;
194         return $this;
195     }
196
197     /**
198      * Retrieves a DOMElement which corresponds to this element and all
199      * child properties.  This is used to build an entry back into a DOM
200      * and eventually XML text for sending to the server upon updates, or
201      * for application storage/persistence.
202      *
203      * @param DOMDocument $doc The DOMDocument used to construct DOMElements
204      * @return DOMElement The DOMElement representing this element and all
205      * child properties.
206      */
207     public function getDOM($doc = null, $majorVersion = 1, $minorVersion = null)
208     {
209         if ($doc === null) {
210             $doc = new DOMDocument('1.0', 'utf-8');
211         }
212         if ($this->_rootNamespaceURI != null) {
213             $element = $doc->createElementNS($this->_rootNamespaceURI, $this->_rootElement);
214         } elseif ($this->_rootNamespace !== null) {
215             if (strpos($this->_rootElement, ':') === false) {
216                 $elementName = $this->_rootNamespace . ':' . $this->_rootElement;
217             } else {
218                 $elementName = $this->_rootElement;
219             }
220             $element = $doc->createElementNS($this->lookupNamespace($this->_rootNamespace), $elementName);
221         } else {
222             $element = $doc->createElement($this->_rootElement);
223         }
224         if ($this->_text != null) {
225             $element->appendChild($element->ownerDocument->createTextNode($this->_text));
226         }
227         foreach ($this->_extensionElements as $extensionElement) {
228             $element->appendChild($extensionElement->getDOM($element->ownerDocument));
229         }
230         foreach ($this->_extensionAttributes as $attribute) {
231             $element->setAttribute($attribute['name'], $attribute['value']);
232         }
233         return $element;
234     }
235
236     /**
237      * Given a child DOMNode, tries to determine how to map the data into
238      * object instance members.  If no mapping is defined, Extension_Element
239      * objects are created and stored in an array.
240      *
241      * @param DOMNode $child The DOMNode needed to be handled
242      */
243     protected function takeChildFromDOM($child)
244     {
245         if ($child->nodeType == XML_TEXT_NODE) {
246             $this->_text = $child->nodeValue;
247         } else {
248             $extensionElement = new Zend_Gdata_App_Extension_Element();
249             $extensionElement->transferFromDOM($child);
250             $this->_extensionElements[] = $extensionElement;
251         }
252     }
253
254     /**
255      * Given a DOMNode representing an attribute, tries to map the data into
256      * instance members.  If no mapping is defined, the name and value are
257      * stored in an array.
258      *
259      * @param DOMNode $attribute The DOMNode attribute needed to be handled
260      */
261     protected function takeAttributeFromDOM($attribute)
262     {
263         $arrayIndex = ($attribute->namespaceURI != '')?(
264                 $attribute->namespaceURI . ':' . $attribute->name):
265                 $attribute->name;
266         $this->_extensionAttributes[$arrayIndex] =
267                 array('namespaceUri' => $attribute->namespaceURI,
268                       'name' => $attribute->localName,
269                       'value' => $attribute->nodeValue);
270     }
271
272     /**
273      * Transfers each child and attribute into member variables.
274      * This is called when XML is received over the wire and the data
275      * model needs to be built to represent this XML.
276      *
277      * @param DOMNode $node The DOMNode that represents this object's data
278      */
279     public function transferFromDOM($node)
280     {
281         foreach ($node->childNodes as $child) {
282             $this->takeChildFromDOM($child);
283         }
284         foreach ($node->attributes as $attribute) {
285             $this->takeAttributeFromDOM($attribute);
286         }
287     }
288
289     /**
290      * Parses the provided XML text and generates data model classes for
291      * each know element by turning the XML text into a DOM tree and calling
292      * transferFromDOM($element).  The first data model element with the same
293      * name as $this->_rootElement is used and the child elements are
294      * recursively parsed.
295      *
296      * @param string $xml The XML text to parse
297      */
298     public function transferFromXML($xml)
299     {
300         if ($xml) {
301             // Load the feed as an XML DOMDocument object
302             @ini_set('track_errors', 1);
303             $doc = new DOMDocument();
304             $success = @$doc->loadXML($xml);
305             @ini_restore('track_errors');
306             if (!$success) {
307                 require_once 'Zend/Gdata/App/Exception.php';
308                 throw new Zend_Gdata_App_Exception("DOMDocument cannot parse XML: $php_errormsg");
309             }
310             $element = $doc->getElementsByTagName($this->_rootElement)->item(0);
311             if (!$element) {
312                 require_once 'Zend/Gdata/App/Exception.php';
313                 throw new Zend_Gdata_App_Exception('No root <' . $this->_rootElement . '> element');
314             }
315             $this->transferFromDOM($element);
316         } else {
317             require_once 'Zend/Gdata/App/Exception.php';
318             throw new Zend_Gdata_App_Exception('XML passed to transferFromXML cannot be null');
319         }
320     }
321
322     /**
323      * Converts this element and all children into XML text using getDOM()
324      *
325      * @return string XML content
326      */
327     public function saveXML()
328     {
329         $element = $this->getDOM();
330         return $element->ownerDocument->saveXML($element);
331     }
332
333     /**
334      * Alias for saveXML() returns XML content for this element and all
335      * children
336      *
337      * @return string XML content
338      */
339     public function getXML()
340     {
341         return $this->saveXML();
342     }
343
344     /**
345      * Alias for saveXML()
346      *
347      * Can be overridden by children to provide more complex representations
348      * of entries.
349      *
350      * @return string Encoded string content
351      */
352     public function encode()
353     {
354         return $this->saveXML();
355     }
356
357     /**
358      * Get the full version of a namespace prefix
359      *
360      * Looks up a prefix (atom:, etc.) in the list of registered
361      * namespaces and returns the full namespace URI if
362      * available. Returns the prefix, unmodified, if it's not
363      * registered.
364      *
365      * @param string $prefix The namespace prefix to lookup.
366      * @param integer $majorVersion The major protocol version in effect.
367      *        Defaults to '1'.
368      * @param integer $minorVersion The minor protocol version in effect.
369      *        Defaults to null (use latest).
370      * @return string
371      */
372     public function lookupNamespace($prefix,
373                                     $majorVersion = 1,
374                                     $minorVersion = null)
375     {
376         // Check for a memoized result
377         $key = $prefix . ' ' .
378                (is_null($majorVersion) ? 'NULL' : $majorVersion) .
379                ' '. (is_null($minorVersion) ? 'NULL' : $minorVersion);
380         if (array_key_exists($key, self::$_namespaceLookupCache))
381           return self::$_namespaceLookupCache[$key];
382         // If no match, return the prefix by default
383         $result = $prefix;
384
385         // Find tuple of keys that correspond to the namespace we should use
386         if (isset($this->_namespaces[$prefix])) {
387             // Major version search
388             $nsData = $this->_namespaces[$prefix];
389             $foundMajorV = Zend_Gdata_App_Util::findGreatestBoundedValue(
390                     $majorVersion, $nsData);
391             // Minor version search
392             $nsData = $nsData[$foundMajorV];
393             $foundMinorV = Zend_Gdata_App_Util::findGreatestBoundedValue(
394                     $minorVersion, $nsData);
395             // Extract NS
396             $result = $nsData[$foundMinorV];
397         }
398
399         // Memoize result
400         self::$_namespaceLookupCache[$key] = $result;
401
402         return $result;
403     }
404
405     /**
406      * Add a namespace and prefix to the registered list
407      *
408      * Takes a prefix and a full namespace URI and adds them to the
409      * list of registered namespaces for use by
410      * $this->lookupNamespace().
411      *
412      * WARNING: Currently, registering a namespace will NOT invalidate any
413      *          memoized data stored in $_namespaceLookupCache. Under normal
414      *          use, this behavior is acceptable. If you are adding
415      *          contradictory data to the namespace lookup table, you must
416      *          call flushNamespaceLookupCache().
417      *
418      * @param  string $prefix The namespace prefix
419      * @param  string $namespaceUri The full namespace URI
420      * @param integer $majorVersion The major protocol version in effect.
421      *        Defaults to '1'.
422      * @param integer $minorVersion The minor protocol version in effect.
423      *        Defaults to null (use latest).
424      * @return void
425      */
426     public function registerNamespace($prefix,
427                                       $namespaceUri,
428                                       $majorVersion = 1,
429                                       $minorVersion = 0)
430     {
431         $this->_namespaces[$prefix][$majorVersion][$minorVersion] =
432         $namespaceUri;
433     }
434
435     /**
436      * Flush namespace lookup cache.
437      *
438      * Empties the namespace lookup cache. Call this function if you have
439      * added data to the namespace lookup table that contradicts values that
440      * may have been cached during a previous call to lookupNamespace().
441      */
442     public static function flushNamespaceLookupCache()
443     {
444         self::$_namespaceLookupCache = array();
445     }
446
447     /**
448      * Add an array of namespaces to the registered list.
449      *
450      * Takes an array in the format of:
451      * namespace prefix, namespace URI, major protocol version,
452      * minor protocol version and adds them with calls to ->registerNamespace()
453      *
454      * @param array $namespaceArray An array of namespaces.
455      * @return void
456      */
457     public function registerAllNamespaces($namespaceArray)
458     {
459         foreach($namespaceArray as $namespace) {
460                 $this->registerNamespace(
461                     $namespace[0], $namespace[1], $namespace[2], $namespace[3]);
462         }
463     }
464
465
466     /**
467      * Magic getter to allow access like $entry->foo to call $entry->getFoo()
468      * Alternatively, if no getFoo() is defined, but a $_foo protected variable
469      * is defined, this is returned.
470      *
471      * TODO Remove ability to bypass getFoo() methods??
472      *
473      * @param string $name The variable name sought
474      */
475     public function __get($name)
476     {
477         $method = 'get'.ucfirst($name);
478         if (method_exists($this, $method)) {
479             return call_user_func(array(&$this, $method));
480         } else if (property_exists($this, "_${name}")) {
481             return $this->{'_' . $name};
482         } else {
483             require_once 'Zend/Gdata/App/InvalidArgumentException.php';
484             throw new Zend_Gdata_App_InvalidArgumentException(
485                     'Property ' . $name . ' does not exist');
486         }
487     }
488
489     /**
490      * Magic setter to allow acces like $entry->foo='bar' to call
491      * $entry->setFoo('bar') automatically.
492      *
493      * Alternatively, if no setFoo() is defined, but a $_foo protected variable
494      * is defined, this is returned.
495      *
496      * TODO Remove ability to bypass getFoo() methods??
497      *
498      * @param string $name
499      * @param string $value
500      */
501     public function __set($name, $val)
502     {
503         $method = 'set'.ucfirst($name);
504         if (method_exists($this, $method)) {
505             return call_user_func(array(&$this, $method), $val);
506         } else if (isset($this->{'_' . $name}) || ($this->{'_' . $name} === null)) {
507             $this->{'_' . $name} = $val;
508         } else {
509             require_once 'Zend/Gdata/App/InvalidArgumentException.php';
510             throw new Zend_Gdata_App_InvalidArgumentException(
511                     'Property ' . $name . '  does not exist');
512         }
513     }
514
515     /**
516      * Magic __isset method
517      *
518      * @param string $name
519      */
520     public function __isset($name)
521     {
522         $rc = new ReflectionClass(get_class($this));
523         $privName = '_' . $name;
524         if (!($rc->hasProperty($privName))) {
525             require_once 'Zend/Gdata/App/InvalidArgumentException.php';
526             throw new Zend_Gdata_App_InvalidArgumentException(
527                     'Property ' . $name . ' does not exist');
528         } else {
529             if (isset($this->{$privName})) {
530                 if (is_array($this->{$privName})) {
531                     if (count($this->{$privName}) > 0) {
532                         return true;
533                     } else {
534                         return false;
535                     }
536                 } else {
537                     return true;
538                 }
539             } else {
540                 return false;
541             }
542         }
543     }
544
545     /**
546      * Magic __unset method
547      *
548      * @param string $name
549      */
550     public function __unset($name)
551     {
552         if (isset($this->{'_' . $name})) {
553             if (is_array($this->{'_' . $name})) {
554                 $this->{'_' . $name} = array();
555             } else {
556                 $this->{'_' . $name} = null;
557             }
558         }
559     }
560
561     /**
562      * Magic toString method allows using this directly via echo
563      * Works best in PHP >= 4.2.0
564      *
565      * @return string The text representation of this object
566      */
567     public function __toString()
568     {
569         return $this->getText();
570     }
571
572 }