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.
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
25 * @see Zend_Gdata_App_Util
27 require_once 'Zend/Gdata/App/Util.php';
30 * Abstract class for all XML elements
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
38 abstract class Zend_Gdata_App_Base
42 * @var string The XML element name, including prefix if desired
44 protected $_rootElement = null;
47 * @var string The XML namespace prefix
49 protected $_rootNamespace = 'atom';
52 * @var string The XML namespace URI - takes precedence over lookup up the
53 * corresponding URI for $_rootNamespace
55 protected $_rootNamespaceURI = null;
58 * @var array Leftover elements which were not handled
60 protected $_extensionElements = array();
63 * @var array Leftover attributes which were not handled
65 protected $_extensionAttributes = array();
68 * @var string XML child text node content
70 protected $_text = null;
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().
78 protected static $_namespaceLookupCache = array();
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.
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
90 * @see lookupNamespace()
91 * @see registerNamespace()
92 * @see registerAllNamespaces()
95 protected $_namespaces = array(
98 0 => 'http://www.w3.org/2005/Atom'
103 0 => 'http://purl.org/atom/app#'
106 0 => 'http://www.w3.org/2007/app'
111 public function __construct()
116 * Returns the child text node of this element
117 * This represents any raw text contained within the XML element
119 * @return string Child text node
121 public function getText($trim = true)
124 return trim($this->_text);
131 * Sets the child text node of this element
132 * This represents any raw text contained within the XML element
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.
137 public function setText($value)
139 $this->_text = $value;
144 * Returns an array of all elements not matched to data model classes
145 * during the parsing of the XML
147 * @return array All elements not matched to data model classes during parsing
149 public function getExtensionElements()
151 return $this->_extensionElements;
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.
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.
162 public function setExtensionElements($value)
164 $this->_extensionElements = $value;
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);
174 * @return array All extension attributes
176 public function getExtensionAttributes()
178 return $this->_extensionAttributes;
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
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.
191 public function setExtensionAttributes($value)
193 $this->_extensionAttributes = $value;
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.
203 * @param DOMDocument $doc The DOMDocument used to construct DOMElements
204 * @return DOMElement The DOMElement representing this element and all
207 public function getDOM($doc = null, $majorVersion = 1, $minorVersion = null)
210 $doc = new DOMDocument('1.0', 'utf-8');
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;
218 $elementName = $this->_rootElement;
220 $element = $doc->createElementNS($this->lookupNamespace($this->_rootNamespace), $elementName);
222 $element = $doc->createElement($this->_rootElement);
224 if ($this->_text != null) {
225 $element->appendChild($element->ownerDocument->createTextNode($this->_text));
227 foreach ($this->_extensionElements as $extensionElement) {
228 $element->appendChild($extensionElement->getDOM($element->ownerDocument));
230 foreach ($this->_extensionAttributes as $attribute) {
231 $element->setAttribute($attribute['name'], $attribute['value']);
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.
241 * @param DOMNode $child The DOMNode needed to be handled
243 protected function takeChildFromDOM($child)
245 if ($child->nodeType == XML_TEXT_NODE) {
246 $this->_text = $child->nodeValue;
248 $extensionElement = new Zend_Gdata_App_Extension_Element();
249 $extensionElement->transferFromDOM($child);
250 $this->_extensionElements[] = $extensionElement;
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.
259 * @param DOMNode $attribute The DOMNode attribute needed to be handled
261 protected function takeAttributeFromDOM($attribute)
263 $arrayIndex = ($attribute->namespaceURI != '')?(
264 $attribute->namespaceURI . ':' . $attribute->name):
266 $this->_extensionAttributes[$arrayIndex] =
267 array('namespaceUri' => $attribute->namespaceURI,
268 'name' => $attribute->localName,
269 'value' => $attribute->nodeValue);
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.
277 * @param DOMNode $node The DOMNode that represents this object's data
279 public function transferFromDOM($node)
281 foreach ($node->childNodes as $child) {
282 $this->takeChildFromDOM($child);
284 foreach ($node->attributes as $attribute) {
285 $this->takeAttributeFromDOM($attribute);
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.
296 * @param string $xml The XML text to parse
298 public function transferFromXML($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');
307 require_once 'Zend/Gdata/App/Exception.php';
308 throw new Zend_Gdata_App_Exception("DOMDocument cannot parse XML: $php_errormsg");
310 $element = $doc->getElementsByTagName($this->_rootElement)->item(0);
312 require_once 'Zend/Gdata/App/Exception.php';
313 throw new Zend_Gdata_App_Exception('No root <' . $this->_rootElement . '> element');
315 $this->transferFromDOM($element);
317 require_once 'Zend/Gdata/App/Exception.php';
318 throw new Zend_Gdata_App_Exception('XML passed to transferFromXML cannot be null');
323 * Converts this element and all children into XML text using getDOM()
325 * @return string XML content
327 public function saveXML()
329 $element = $this->getDOM();
330 return $element->ownerDocument->saveXML($element);
334 * Alias for saveXML() returns XML content for this element and all
337 * @return string XML content
339 public function getXML()
341 return $this->saveXML();
345 * Alias for saveXML()
347 * Can be overridden by children to provide more complex representations
350 * @return string Encoded string content
352 public function encode()
354 return $this->saveXML();
358 * Get the full version of a namespace prefix
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
365 * @param string $prefix The namespace prefix to lookup.
366 * @param integer $majorVersion The major protocol version in effect.
368 * @param integer $minorVersion The minor protocol version in effect.
369 * Defaults to null (use latest).
372 public function lookupNamespace($prefix,
374 $minorVersion = null)
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
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);
396 $result = $nsData[$foundMinorV];
400 self::$_namespaceLookupCache[$key] = $result;
406 * Add a namespace and prefix to the registered list
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().
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().
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.
422 * @param integer $minorVersion The minor protocol version in effect.
423 * Defaults to null (use latest).
426 public function registerNamespace($prefix,
431 $this->_namespaces[$prefix][$majorVersion][$minorVersion] =
436 * Flush namespace lookup cache.
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().
442 public static function flushNamespaceLookupCache()
444 self::$_namespaceLookupCache = array();
448 * Add an array of namespaces to the registered list.
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()
454 * @param array $namespaceArray An array of namespaces.
457 public function registerAllNamespaces($namespaceArray)
459 foreach($namespaceArray as $namespace) {
460 $this->registerNamespace(
461 $namespace[0], $namespace[1], $namespace[2], $namespace[3]);
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.
471 * TODO Remove ability to bypass getFoo() methods??
473 * @param string $name The variable name sought
475 public function __get($name)
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};
483 require_once 'Zend/Gdata/App/InvalidArgumentException.php';
484 throw new Zend_Gdata_App_InvalidArgumentException(
485 'Property ' . $name . ' does not exist');
490 * Magic setter to allow acces like $entry->foo='bar' to call
491 * $entry->setFoo('bar') automatically.
493 * Alternatively, if no setFoo() is defined, but a $_foo protected variable
494 * is defined, this is returned.
496 * TODO Remove ability to bypass getFoo() methods??
498 * @param string $name
499 * @param string $value
501 public function __set($name, $val)
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;
509 require_once 'Zend/Gdata/App/InvalidArgumentException.php';
510 throw new Zend_Gdata_App_InvalidArgumentException(
511 'Property ' . $name . ' does not exist');
516 * Magic __isset method
518 * @param string $name
520 public function __isset($name)
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');
529 if (isset($this->{$privName})) {
530 if (is_array($this->{$privName})) {
531 if (count($this->{$privName}) > 0) {
546 * Magic __unset method
548 * @param string $name
550 public function __unset($name)
552 if (isset($this->{'_' . $name})) {
553 if (is_array($this->{'_' . $name})) {
554 $this->{'_' . $name} = array();
556 $this->{'_' . $name} = null;
562 * Magic toString method allows using this directly via echo
563 * Works best in PHP >= 4.2.0
565 * @return string The text representation of this object
567 public function __toString()
569 return $this->getText();