web/enmi/Zend/Gdata/App/Base.php
changeset 19 1c2f13fd785c
parent 0 4eba9c11703f
equal deleted inserted replaced
18:bd595ad770fc 19:1c2f13fd785c
       
     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  * @version    $Id: Base.php 22662 2010-07-24 17:37:36Z mabe $
       
    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                ($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 }