/zf/library/Zend/Gdata/App/Base.php
PHP | 572 lines | 258 code | 40 blank | 274 comment | 40 complexity | 4c8d4f7bfce550a0b45d8040ad59e7ca MD5 | raw file
Possible License(s): MIT, BSD-3-Clause, Apache-2.0, LGPL-2.1, LGPL-3.0, BSD-2-Clause
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-2011 Zend Technologies USA Inc. (http://www.zend.com) 20 * @license http://framework.zend.com/license/new-bsd New BSD License 21 * @version $Id: Base.php 23775 2011-03-01 17:25:24Z ralph $ 22 */ 23 24/** 25 * @see Zend_Gdata_App_Util 26 */ 27require_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-2011 Zend Technologies USA Inc. (http://www.zend.com) 36 * @license http://framework.zend.com/license/new-bsd New BSD License 37 */ 38abstract 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 ($majorVersion === null ? 'NULL' : $majorVersion) . 379 ' '. ($minorVersion === null ? '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}