PageRenderTime 58ms CodeModel.GetById 26ms RepoModel.GetById 0ms app.codeStats 0ms

/lib/zend/Zend/Gdata/App/Base.php

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