File indexing completed on 2025-03-02 05:29:26

0001 <?php
0002 
0003 /**
0004  * Zend Framework
0005  *
0006  * LICENSE
0007  *
0008  * This source file is subject to the new BSD license that is bundled
0009  * with this package in the file LICENSE.txt.
0010  * It is also available through the world-wide-web at this URL:
0011  * http://framework.zend.com/license/new-bsd
0012  * If you did not receive a copy of the license and are unable to
0013  * obtain it through the world-wide-web, please send an email
0014  * to license@zend.com so we can send you a copy immediately.
0015  *
0016  * @category   Zend
0017  * @package    Zend_Gdata
0018  * @subpackage App
0019  * @copyright  Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
0020  * @license    http://framework.zend.com/license/new-bsd     New BSD License
0021  * @version    $Id$
0022  */
0023 
0024 /**
0025  * @see Zend_Gdata_App_Util
0026  */
0027 // require_once 'Zend/Gdata/App/Util.php';
0028 
0029 /** @see Zend_Xml_Security */
0030 // require_once 'Zend/Xml/Security.php';
0031 
0032 /**
0033  * Abstract class for all XML elements
0034  *
0035  * @category   Zend
0036  * @package    Zend_Gdata
0037  * @subpackage App
0038  * @copyright  Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
0039  * @license    http://framework.zend.com/license/new-bsd     New BSD License
0040  */
0041 abstract class Zend_Gdata_App_Base
0042 {
0043 
0044     /**
0045      * @var string The XML element name, including prefix if desired
0046      */
0047     protected $_rootElement = null;
0048 
0049     /**
0050      * @var string The XML namespace prefix
0051      */
0052     protected $_rootNamespace = 'atom';
0053 
0054     /**
0055      * @var string The XML namespace URI - takes precedence over lookup up the
0056      * corresponding URI for $_rootNamespace
0057      */
0058     protected $_rootNamespaceURI = null;
0059 
0060     /**
0061      * @var array Leftover elements which were not handled
0062      */
0063     protected $_extensionElements = array();
0064 
0065     /**
0066      * @var array Leftover attributes which were not handled
0067      */
0068     protected $_extensionAttributes = array();
0069 
0070     /**
0071      * @var string XML child text node content
0072      */
0073     protected $_text = null;
0074 
0075     /**
0076      * @var array Memoized results from calls to lookupNamespace() to avoid
0077      *      expensive calls to getGreatestBoundedValue(). The key is in the
0078      *      form 'prefix-majorVersion-minorVersion', and the value is the
0079      *      output from getGreatestBoundedValue().
0080      */
0081     protected static $_namespaceLookupCache = array();
0082 
0083     /**
0084      * List of namespaces, as a three-dimensional array. The first dimension
0085      * represents the namespace prefix, the second dimension represents the
0086      * minimum major protocol version, and the third dimension is the minimum
0087      * minor protocol version. Null keys are NOT allowed.
0088      *
0089      * When looking up a namespace for a given prefix, the greatest version
0090      * number (both major and minor) which is less than the effective version
0091      * should be used.
0092      *
0093      * @see lookupNamespace()
0094      * @see registerNamespace()
0095      * @see registerAllNamespaces()
0096      * @var array
0097      */
0098    protected $_namespaces = array(
0099         'atom'      => array(
0100             1 => array(
0101                 0 => 'http://www.w3.org/2005/Atom'
0102                 )
0103             ),
0104         'app'       => array(
0105             1 => array(
0106                 0 => 'http://purl.org/atom/app#'
0107                 ),
0108             2 => array(
0109                 0 => 'http://www.w3.org/2007/app'
0110                 )
0111             )
0112         );
0113 
0114     public function __construct()
0115     {
0116     }
0117 
0118     /**
0119      * Returns the child text node of this element
0120      * This represents any raw text contained within the XML element
0121      *
0122      * @return string Child text node
0123      */
0124     public function getText($trim = true)
0125     {
0126         if ($trim) {
0127             return trim($this->_text);
0128         } else {
0129             return $this->_text;
0130         }
0131     }
0132 
0133     /**
0134      * Sets the child text node of this element
0135      * This represents any raw text contained within the XML element
0136      *
0137      * @param string $value Child text node
0138      * @return Zend_Gdata_App_Base Returns an object of the same type as 'this' to provide a fluent interface.
0139      */
0140     public function setText($value)
0141     {
0142         $this->_text = $value;
0143         return $this;
0144     }
0145 
0146     /**
0147      * Returns an array of all elements not matched to data model classes
0148      * during the parsing of the XML
0149      *
0150      * @return array All elements not matched to data model classes during parsing
0151      */
0152     public function getExtensionElements()
0153     {
0154         return $this->_extensionElements;
0155     }
0156 
0157     /**
0158      * Sets an array of all elements not matched to data model classes
0159      * during the parsing of the XML.  This method can be used to add arbitrary
0160      * child XML elements to any data model class.
0161      *
0162      * @param array $value All extension elements
0163      * @return Zend_Gdata_App_Base Returns an object of the same type as 'this' to provide a fluent interface.
0164      */
0165     public function setExtensionElements($value)
0166     {
0167         $this->_extensionElements = $value;
0168         return $this;
0169     }
0170 
0171     /**
0172      * Returns an array of all extension attributes not transformed into data
0173      * model properties during parsing of the XML.  Each element of the array
0174      * is a hashed array of the format:
0175      *     array('namespaceUri' => string, 'name' => string, 'value' => string);
0176      *
0177      * @return array All extension attributes
0178      */
0179     public function getExtensionAttributes()
0180     {
0181         return $this->_extensionAttributes;
0182     }
0183 
0184     /**
0185      * Sets an array of all extension attributes not transformed into data
0186      * model properties during parsing of the XML.  Each element of the array
0187      * is a hashed array of the format:
0188      *     array('namespaceUri' => string, 'name' => string, 'value' => string);
0189      * This can be used to add arbitrary attributes to any data model element
0190      *
0191      * @param array $value All extension attributes
0192      * @return Zend_Gdata_App_Base Returns an object of the same type as 'this' to provide a fluent interface.
0193      */
0194     public function setExtensionAttributes($value)
0195     {
0196         $this->_extensionAttributes = $value;
0197         return $this;
0198     }
0199 
0200     /**
0201      * Retrieves a DOMElement which corresponds to this element and all
0202      * child properties.  This is used to build an entry back into a DOM
0203      * and eventually XML text for sending to the server upon updates, or
0204      * for application storage/persistence.
0205      *
0206      * @param DOMDocument $doc The DOMDocument used to construct DOMElements
0207      * @return DOMElement The DOMElement representing this element and all
0208      * child properties.
0209      */
0210     public function getDOM($doc = null, $majorVersion = 1, $minorVersion = null)
0211     {
0212         if ($doc === null) {
0213             $doc = new DOMDocument('1.0', 'utf-8');
0214         }
0215         if ($this->_rootNamespaceURI != null) {
0216             $element = $doc->createElementNS($this->_rootNamespaceURI, $this->_rootElement);
0217         } elseif ($this->_rootNamespace !== null) {
0218             if (strpos($this->_rootElement, ':') === false) {
0219                 $elementName = $this->_rootNamespace . ':' . $this->_rootElement;
0220             } else {
0221                 $elementName = $this->_rootElement;
0222             }
0223             $element = $doc->createElementNS($this->lookupNamespace($this->_rootNamespace), $elementName);
0224         } else {
0225             $element = $doc->createElement($this->_rootElement);
0226         }
0227         if ($this->_text != null) {
0228             $element->appendChild($element->ownerDocument->createTextNode($this->_text));
0229         }
0230         foreach ($this->_extensionElements as $extensionElement) {
0231             $element->appendChild($extensionElement->getDOM($element->ownerDocument));
0232         }
0233         foreach ($this->_extensionAttributes as $attribute) {
0234             $element->setAttribute($attribute['name'], $attribute['value']);
0235         }
0236         return $element;
0237     }
0238 
0239     /**
0240      * Given a child DOMNode, tries to determine how to map the data into
0241      * object instance members.  If no mapping is defined, Extension_Element
0242      * objects are created and stored in an array.
0243      *
0244      * @param DOMNode $child The DOMNode needed to be handled
0245      */
0246     protected function takeChildFromDOM($child)
0247     {
0248         if ($child->nodeType == XML_TEXT_NODE) {
0249             $this->_text = $child->nodeValue;
0250         } else {
0251             $extensionElement = new Zend_Gdata_App_Extension_Element();
0252             $extensionElement->transferFromDOM($child);
0253             $this->_extensionElements[] = $extensionElement;
0254         }
0255     }
0256 
0257     /**
0258      * Given a DOMNode representing an attribute, tries to map the data into
0259      * instance members.  If no mapping is defined, the name and value are
0260      * stored in an array.
0261      *
0262      * @param DOMNode $attribute The DOMNode attribute needed to be handled
0263      */
0264     protected function takeAttributeFromDOM($attribute)
0265     {
0266         $arrayIndex = ($attribute->namespaceURI != '')?(
0267                 $attribute->namespaceURI . ':' . $attribute->name):
0268                 $attribute->name;
0269         $this->_extensionAttributes[$arrayIndex] =
0270                 array('namespaceUri' => $attribute->namespaceURI,
0271                       'name' => $attribute->localName,
0272                       'value' => $attribute->nodeValue);
0273     }
0274 
0275     /**
0276      * Transfers each child and attribute into member variables.
0277      * This is called when XML is received over the wire and the data
0278      * model needs to be built to represent this XML.
0279      *
0280      * @param DOMNode $node The DOMNode that represents this object's data
0281      */
0282     public function transferFromDOM($node)
0283     {
0284         foreach ($node->childNodes as $child) {
0285             $this->takeChildFromDOM($child);
0286         }
0287         foreach ($node->attributes as $attribute) {
0288             $this->takeAttributeFromDOM($attribute);
0289         }
0290     }
0291 
0292     /**
0293      * Parses the provided XML text and generates data model classes for
0294      * each know element by turning the XML text into a DOM tree and calling
0295      * transferFromDOM($element).  The first data model element with the same
0296      * name as $this->_rootElement is used and the child elements are
0297      * recursively parsed.
0298      *
0299      * @param string $xml The XML text to parse
0300      */
0301     public function transferFromXML($xml)
0302     {
0303         if ($xml) {
0304             // Load the feed as an XML DOMDocument object
0305             @ini_set('track_errors', 1);
0306             $doc = new DOMDocument();
0307             $doc = @Zend_Xml_Security::scan($xml, $doc);
0308             @ini_restore('track_errors');
0309             if (!$doc) {
0310                 // require_once 'Zend/Gdata/App/Exception.php';
0311                 throw new Zend_Gdata_App_Exception("DOMDocument cannot parse XML: $php_errormsg");
0312             }
0313             $element = $doc->getElementsByTagName($this->_rootElement)->item(0);
0314             if (!$element) {
0315                 // require_once 'Zend/Gdata/App/Exception.php';
0316                 throw new Zend_Gdata_App_Exception('No root <' . $this->_rootElement . '> element');
0317             }
0318             $this->transferFromDOM($element);
0319         } else {
0320             // require_once 'Zend/Gdata/App/Exception.php';
0321             throw new Zend_Gdata_App_Exception('XML passed to transferFromXML cannot be null');
0322         }
0323     }
0324 
0325     /**
0326      * Converts this element and all children into XML text using getDOM()
0327      *
0328      * @return string XML content
0329      */
0330     public function saveXML()
0331     {
0332         $element = $this->getDOM();
0333         return $element->ownerDocument->saveXML($element);
0334     }
0335 
0336     /**
0337      * Alias for saveXML() returns XML content for this element and all
0338      * children
0339      *
0340      * @return string XML content
0341      */
0342     public function getXML()
0343     {
0344         return $this->saveXML();
0345     }
0346 
0347     /**
0348      * Alias for saveXML()
0349      *
0350      * Can be overridden by children to provide more complex representations
0351      * of entries.
0352      *
0353      * @return string Encoded string content
0354      */
0355     public function encode()
0356     {
0357         return $this->saveXML();
0358     }
0359 
0360     /**
0361      * Get the full version of a namespace prefix
0362      *
0363      * Looks up a prefix (atom:, etc.) in the list of registered
0364      * namespaces and returns the full namespace URI if
0365      * available. Returns the prefix, unmodified, if it's not
0366      * registered.
0367      *
0368      * @param string $prefix The namespace prefix to lookup.
0369      * @param integer $majorVersion The major protocol version in effect.
0370      *        Defaults to '1'.
0371      * @param integer $minorVersion The minor protocol version in effect.
0372      *        Defaults to null (use latest).
0373      * @return string
0374      */
0375     public function lookupNamespace($prefix,
0376                                     $majorVersion = 1,
0377                                     $minorVersion = null)
0378     {
0379         // Check for a memoized result
0380         $key = $prefix . ' ' .
0381                ($majorVersion === null ? 'NULL' : $majorVersion) .
0382                ' '. ($minorVersion === null ? 'NULL' : $minorVersion);
0383         if (array_key_exists($key, self::$_namespaceLookupCache))
0384           return self::$_namespaceLookupCache[$key];
0385         // If no match, return the prefix by default
0386         $result = $prefix;
0387 
0388         // Find tuple of keys that correspond to the namespace we should use
0389         if (isset($this->_namespaces[$prefix])) {
0390             // Major version search
0391             $nsData = $this->_namespaces[$prefix];
0392             $foundMajorV = Zend_Gdata_App_Util::findGreatestBoundedValue(
0393                     $majorVersion, $nsData);
0394             // Minor version search
0395             $nsData = $nsData[$foundMajorV];
0396             $foundMinorV = Zend_Gdata_App_Util::findGreatestBoundedValue(
0397                     $minorVersion, $nsData);
0398             // Extract NS
0399             $result = $nsData[$foundMinorV];
0400         }
0401 
0402         // Memoize result
0403         self::$_namespaceLookupCache[$key] = $result;
0404 
0405         return $result;
0406     }
0407 
0408     /**
0409      * Add a namespace and prefix to the registered list
0410      *
0411      * Takes a prefix and a full namespace URI and adds them to the
0412      * list of registered namespaces for use by
0413      * $this->lookupNamespace().
0414      *
0415      * WARNING: Currently, registering a namespace will NOT invalidate any
0416      *          memoized data stored in $_namespaceLookupCache. Under normal
0417      *          use, this behavior is acceptable. If you are adding
0418      *          contradictory data to the namespace lookup table, you must
0419      *          call flushNamespaceLookupCache().
0420      *
0421      * @param  string $prefix The namespace prefix
0422      * @param  string $namespaceUri The full namespace URI
0423      * @param integer $majorVersion The major protocol version in effect.
0424      *        Defaults to '1'.
0425      * @param integer $minorVersion The minor protocol version in effect.
0426      *        Defaults to null (use latest).
0427      * @return void
0428      */
0429     public function registerNamespace($prefix,
0430                                       $namespaceUri,
0431                                       $majorVersion = 1,
0432                                       $minorVersion = 0)
0433     {
0434         $this->_namespaces[$prefix][$majorVersion][$minorVersion] =
0435         $namespaceUri;
0436     }
0437 
0438     /**
0439      * Flush namespace lookup cache.
0440      *
0441      * Empties the namespace lookup cache. Call this function if you have
0442      * added data to the namespace lookup table that contradicts values that
0443      * may have been cached during a previous call to lookupNamespace().
0444      */
0445     public static function flushNamespaceLookupCache()
0446     {
0447         self::$_namespaceLookupCache = array();
0448     }
0449 
0450     /**
0451      * Add an array of namespaces to the registered list.
0452      *
0453      * Takes an array in the format of:
0454      * namespace prefix, namespace URI, major protocol version,
0455      * minor protocol version and adds them with calls to ->registerNamespace()
0456      *
0457      * @param array $namespaceArray An array of namespaces.
0458      * @return void
0459      */
0460     public function registerAllNamespaces($namespaceArray)
0461     {
0462         foreach($namespaceArray as $namespace) {
0463                 $this->registerNamespace(
0464                     $namespace[0], $namespace[1], $namespace[2], $namespace[3]);
0465         }
0466     }
0467 
0468 
0469     /**
0470      * Magic getter to allow access like $entry->foo to call $entry->getFoo()
0471      * Alternatively, if no getFoo() is defined, but a $_foo protected variable
0472      * is defined, this is returned.
0473      *
0474      * TODO Remove ability to bypass getFoo() methods??
0475      *
0476      * @param string $name The variable name sought
0477      */
0478     public function __get($name)
0479     {
0480         $method = 'get'.ucfirst($name);
0481         if (method_exists($this, $method)) {
0482             return call_user_func(array(&$this, $method));
0483         } else if (property_exists($this, "_${name}")) {
0484             return $this->{'_' . $name};
0485         } else {
0486             // require_once 'Zend/Gdata/App/InvalidArgumentException.php';
0487             throw new Zend_Gdata_App_InvalidArgumentException(
0488                     'Property ' . $name . ' does not exist');
0489         }
0490     }
0491 
0492     /**
0493      * Magic setter to allow acces like $entry->foo='bar' to call
0494      * $entry->setFoo('bar') automatically.
0495      *
0496      * Alternatively, if no setFoo() is defined, but a $_foo protected variable
0497      * is defined, this is returned.
0498      *
0499      * TODO Remove ability to bypass getFoo() methods??
0500      *
0501      * @param string $name
0502      * @param string $value
0503      */
0504     public function __set($name, $val)
0505     {
0506         $method = 'set'.ucfirst($name);
0507         if (method_exists($this, $method)) {
0508             return call_user_func(array(&$this, $method), $val);
0509         } else if (isset($this->{'_' . $name}) || ($this->{'_' . $name} === null)) {
0510             $this->{'_' . $name} = $val;
0511         } else {
0512             // require_once 'Zend/Gdata/App/InvalidArgumentException.php';
0513             throw new Zend_Gdata_App_InvalidArgumentException(
0514                     'Property ' . $name . '  does not exist');
0515         }
0516     }
0517 
0518     /**
0519      * Magic __isset method
0520      *
0521      * @param string $name
0522      */
0523     public function __isset($name)
0524     {
0525         $rc = new ReflectionClass(get_class($this));
0526         $privName = '_' . $name;
0527         if (!($rc->hasProperty($privName))) {
0528             // require_once 'Zend/Gdata/App/InvalidArgumentException.php';
0529             throw new Zend_Gdata_App_InvalidArgumentException(
0530                     'Property ' . $name . ' does not exist');
0531         } else {
0532             if (isset($this->{$privName})) {
0533                 if (is_array($this->{$privName})) {
0534                     if (count($this->{$privName}) > 0) {
0535                         return true;
0536                     } else {
0537                         return false;
0538                     }
0539                 } else {
0540                     return true;
0541                 }
0542             } else {
0543                 return false;
0544             }
0545         }
0546     }
0547 
0548     /**
0549      * Magic __unset method
0550      *
0551      * @param string $name
0552      */
0553     public function __unset($name)
0554     {
0555         if (isset($this->{'_' . $name})) {
0556             if (is_array($this->{'_' . $name})) {
0557                 $this->{'_' . $name} = array();
0558             } else {
0559                 $this->{'_' . $name} = null;
0560             }
0561         }
0562     }
0563 
0564     /**
0565      * Magic toString method allows using this directly via echo
0566      * Works best in PHP >= 4.2.0
0567      *
0568      * @return string The text representation of this object
0569      */
0570     public function __toString()
0571     {
0572         return $this->getText();
0573     }
0574 
0575 }