web/lib/Zend/XmlRpc/Value.php
changeset 64 162c1de6545a
parent 19 1c2f13fd785c
child 68 ecaf28ffe26e
equal deleted inserted replaced
63:5b37998e522e 64:162c1de6545a
       
     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_XmlRpc
       
    17  * @subpackage Value
       
    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: Value.php 22024 2010-04-27 18:08:24Z matthew $
       
    21  */
       
    22 
       
    23 /**
       
    24  * Represent a native XML-RPC value entity, used as parameters for the methods
       
    25  * called by the Zend_XmlRpc_Client object and as the return value for those calls.
       
    26  *
       
    27  * This object as a very important static function Zend_XmlRpc_Value::getXmlRpcValue, this
       
    28  * function acts likes a factory for the Zend_XmlRpc_Value objects
       
    29  *
       
    30  * Using this function, users/Zend_XmlRpc_Client object can create the Zend_XmlRpc_Value objects
       
    31  * from PHP variables, XML string or by specifing the exact XML-RPC natvie type
       
    32  *
       
    33  * @package    Zend_XmlRpc
       
    34  * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
       
    35  * @license    http://framework.zend.com/license/new-bsd     New BSD License
       
    36  */
       
    37 abstract class Zend_XmlRpc_Value
       
    38 {
       
    39     /**
       
    40      * The native XML-RPC representation of this object's value
       
    41      *
       
    42      * If the native type of this object is array or struct, this will be an array
       
    43      * of Zend_XmlRpc_Value objects
       
    44      */
       
    45     protected $_value;
       
    46 
       
    47     /**
       
    48      * The native XML-RPC type of this object
       
    49      * One of the XMLRPC_TYPE_* constants
       
    50      */
       
    51     protected $_type;
       
    52 
       
    53     /**
       
    54      * XML code representation of this object (will be calculated only once)
       
    55      */
       
    56     protected $_xml;
       
    57 
       
    58     /**
       
    59      * @var Zend_XmlRpc_Generator_GeneratorAbstract
       
    60      */
       
    61     protected static $_generator;
       
    62 
       
    63     /**
       
    64      * Specify that the XML-RPC native type will be auto detected from a PHP variable type
       
    65      */
       
    66     const AUTO_DETECT_TYPE = 'auto_detect';
       
    67 
       
    68     /**
       
    69      * Specify that the XML-RPC value will be parsed out from a given XML code
       
    70      */
       
    71     const XML_STRING = 'xml';
       
    72 
       
    73     /**
       
    74      * All the XML-RPC native types
       
    75      */
       
    76     const XMLRPC_TYPE_I4        = 'i4';
       
    77     const XMLRPC_TYPE_INTEGER   = 'int';
       
    78     const XMLRPC_TYPE_I8        = 'i8';
       
    79     const XMLRPC_TYPE_APACHEI8  = 'ex:i8';
       
    80     const XMLRPC_TYPE_DOUBLE    = 'double';
       
    81     const XMLRPC_TYPE_BOOLEAN   = 'boolean';
       
    82     const XMLRPC_TYPE_STRING    = 'string';
       
    83     const XMLRPC_TYPE_DATETIME  = 'dateTime.iso8601';
       
    84     const XMLRPC_TYPE_BASE64    = 'base64';
       
    85     const XMLRPC_TYPE_ARRAY     = 'array';
       
    86     const XMLRPC_TYPE_STRUCT    = 'struct';
       
    87     const XMLRPC_TYPE_NIL       = 'nil';
       
    88     const XMLRPC_TYPE_APACHENIL = 'ex:nil';
       
    89 
       
    90     /**
       
    91      * Get the native XML-RPC type (the type is one of the Zend_XmlRpc_Value::XMLRPC_TYPE_* constants)
       
    92      *
       
    93      * @return string
       
    94      */
       
    95     public function getType()
       
    96     {
       
    97         return $this->_type;
       
    98     }
       
    99 
       
   100     /**
       
   101      * Get XML generator instance
       
   102      *
       
   103      * @return Zend_XmlRpc_Generator_GeneratorAbstract
       
   104      */
       
   105     public static function getGenerator()
       
   106     {
       
   107         if (!self::$_generator) {
       
   108             if (extension_loaded('xmlwriter')) {
       
   109                 require_once 'Zend/XmlRpc/Generator/XmlWriter.php';
       
   110                 self::$_generator = new Zend_XmlRpc_Generator_XmlWriter();
       
   111             } else {
       
   112                 require_once 'Zend/XmlRpc/Generator/DomDocument.php';
       
   113                 self::$_generator = new Zend_XmlRpc_Generator_DomDocument();
       
   114             }
       
   115         }
       
   116 
       
   117         return self::$_generator;
       
   118     }
       
   119 
       
   120     /**
       
   121      * Sets XML generator instance
       
   122      *
       
   123      * @param Zend_XmlRpc_Generator_GeneratorAbstract $generator
       
   124      * @return void
       
   125      */
       
   126     public static function setGenerator(Zend_XmlRpc_Generator_GeneratorAbstract $generator)
       
   127     {
       
   128         self::$_generator = $generator;
       
   129     }
       
   130 
       
   131     /**
       
   132      * Changes the encoding of the generator
       
   133      *
       
   134      * @param string $encoding
       
   135      * @return void
       
   136      */
       
   137     public static function setEncoding($encoding)
       
   138     {
       
   139         $generator = self::getGenerator();
       
   140         $newGenerator = new $generator($encoding);
       
   141         self::setGenerator($newGenerator);
       
   142     }
       
   143 
       
   144     /**
       
   145      * Return the value of this object, convert the XML-RPC native value into a PHP variable
       
   146      *
       
   147      * @return mixed
       
   148      */
       
   149     abstract public function getValue();
       
   150 
       
   151 
       
   152     /**
       
   153      * Return the XML code that represent a native MXL-RPC value
       
   154      *
       
   155      * @return string
       
   156      */
       
   157     public function saveXml()
       
   158     {
       
   159         if (!$this->_xml) {
       
   160             $this->generateXml();
       
   161             $this->_xml = (string) $this->getGenerator();
       
   162         }
       
   163         return $this->_xml;
       
   164     }
       
   165 
       
   166     /**
       
   167      * Generate XML code that represent a native XML/RPC value
       
   168      *
       
   169      * @return void
       
   170      */
       
   171     public function generateXml()
       
   172     {
       
   173         $this->_generateXml();
       
   174     }
       
   175 
       
   176     /**
       
   177      * Creates a Zend_XmlRpc_Value* object, representing a native XML-RPC value
       
   178      * A XmlRpcValue object can be created in 3 ways:
       
   179      * 1. Autodetecting the native type out of a PHP variable
       
   180      *    (if $type is not set or equal to Zend_XmlRpc_Value::AUTO_DETECT_TYPE)
       
   181      * 2. By specifing the native type ($type is one of the Zend_XmlRpc_Value::XMLRPC_TYPE_* constants)
       
   182      * 3. From a XML string ($type is set to Zend_XmlRpc_Value::XML_STRING)
       
   183      *
       
   184      * By default the value type is autodetected according to it's PHP type
       
   185      *
       
   186      * @param mixed $value
       
   187      * @param Zend_XmlRpc_Value::constant $type
       
   188      *
       
   189      * @return Zend_XmlRpc_Value
       
   190      * @static
       
   191      */
       
   192     public static function getXmlRpcValue($value, $type = self::AUTO_DETECT_TYPE)
       
   193     {
       
   194         switch ($type) {
       
   195             case self::AUTO_DETECT_TYPE:
       
   196                 // Auto detect the XML-RPC native type from the PHP type of $value
       
   197                 return self::_phpVarToNativeXmlRpc($value);
       
   198 
       
   199             case self::XML_STRING:
       
   200                 // Parse the XML string given in $value and get the XML-RPC value in it
       
   201                 return self::_xmlStringToNativeXmlRpc($value);
       
   202 
       
   203             case self::XMLRPC_TYPE_I4:
       
   204                 // fall through to the next case
       
   205             case self::XMLRPC_TYPE_INTEGER:
       
   206                 require_once 'Zend/XmlRpc/Value/Integer.php';
       
   207                 return new Zend_XmlRpc_Value_Integer($value);
       
   208 
       
   209             case self::XMLRPC_TYPE_I8:
       
   210                 // fall through to the next case
       
   211             case self::XMLRPC_TYPE_APACHEI8:
       
   212                 require_once 'Zend/XmlRpc/Value/BigInteger.php';
       
   213                 return new Zend_XmlRpc_Value_BigInteger($value);
       
   214 
       
   215             case self::XMLRPC_TYPE_DOUBLE:
       
   216                 require_once 'Zend/XmlRpc/Value/Double.php';
       
   217                 return new Zend_XmlRpc_Value_Double($value);
       
   218 
       
   219             case self::XMLRPC_TYPE_BOOLEAN:
       
   220                 require_once 'Zend/XmlRpc/Value/Boolean.php';
       
   221                 return new Zend_XmlRpc_Value_Boolean($value);
       
   222 
       
   223             case self::XMLRPC_TYPE_STRING:
       
   224                 require_once 'Zend/XmlRpc/Value/String.php';
       
   225                 return new Zend_XmlRpc_Value_String($value);
       
   226 
       
   227             case self::XMLRPC_TYPE_BASE64:
       
   228                 require_once 'Zend/XmlRpc/Value/Base64.php';
       
   229                 return new Zend_XmlRpc_Value_Base64($value);
       
   230 
       
   231             case self::XMLRPC_TYPE_NIL:
       
   232                 // fall through to the next case
       
   233             case self::XMLRPC_TYPE_APACHENIL:
       
   234                 require_once 'Zend/XmlRpc/Value/Nil.php';
       
   235                 return new Zend_XmlRpc_Value_Nil();
       
   236 
       
   237             case self::XMLRPC_TYPE_DATETIME:
       
   238                 require_once 'Zend/XmlRpc/Value/DateTime.php';
       
   239                 return new Zend_XmlRpc_Value_DateTime($value);
       
   240 
       
   241             case self::XMLRPC_TYPE_ARRAY:
       
   242                 require_once 'Zend/XmlRpc/Value/Array.php';
       
   243                 return new Zend_XmlRpc_Value_Array($value);
       
   244 
       
   245             case self::XMLRPC_TYPE_STRUCT:
       
   246                 require_once 'Zend/XmlRpc/Value/Struct.php';
       
   247                 return new Zend_XmlRpc_Value_Struct($value);
       
   248 
       
   249             default:
       
   250                 require_once 'Zend/XmlRpc/Value/Exception.php';
       
   251                 throw new Zend_XmlRpc_Value_Exception('Given type is not a '. __CLASS__ .' constant');
       
   252         }
       
   253     }
       
   254 
       
   255 
       
   256     /**
       
   257      * Transform a PHP native variable into a XML-RPC native value
       
   258      *
       
   259      * @param mixed $value The PHP variable for convertion
       
   260      *
       
   261      * @return Zend_XmlRpc_Value
       
   262      * @static
       
   263      */
       
   264     protected static function _phpVarToNativeXmlRpc($value)
       
   265     {
       
   266         switch (gettype($value)) {
       
   267             case 'object':
       
   268                 // Check to see if it's an XmlRpc value
       
   269                 if ($value instanceof Zend_XmlRpc_Value) {
       
   270                     return $value;
       
   271                 }
       
   272 
       
   273                 if ($value instanceof Zend_Crypt_Math_BigInteger) {
       
   274                     require_once 'Zend/XmlRpc/Value/BigInteger.php';
       
   275                     return new Zend_XmlRpc_Value_BigInteger($value);
       
   276                 }
       
   277 
       
   278                 if ($value instanceof Zend_Date or $value instanceof DateTime) {
       
   279                     require_once 'Zend/XmlRpc/Value/DateTime.php';
       
   280                     return new Zend_XmlRpc_Value_DateTime($value);
       
   281                 }
       
   282 
       
   283                 // Otherwise, we convert the object into a struct
       
   284                 $value = get_object_vars($value);
       
   285                 // Break intentionally omitted
       
   286             case 'array':
       
   287                 // Default native type for a PHP array (a simple numeric array) is 'array'
       
   288                 require_once 'Zend/XmlRpc/Value/Array.php';
       
   289                 $obj = 'Zend_XmlRpc_Value_Array';
       
   290 
       
   291                 // Determine if this is an associative array
       
   292                 if (!empty($value) && is_array($value) && (array_keys($value) !== range(0, count($value) - 1))) {
       
   293                     require_once 'Zend/XmlRpc/Value/Struct.php';
       
   294                     $obj = 'Zend_XmlRpc_Value_Struct';
       
   295                 }
       
   296                 return new $obj($value);
       
   297 
       
   298             case 'integer':
       
   299                 require_once 'Zend/XmlRpc/Value/Integer.php';
       
   300                 return new Zend_XmlRpc_Value_Integer($value);
       
   301 
       
   302             case 'double':
       
   303                 require_once 'Zend/XmlRpc/Value/Double.php';
       
   304                 return new Zend_XmlRpc_Value_Double($value);
       
   305 
       
   306             case 'boolean':
       
   307                 require_once 'Zend/XmlRpc/Value/Boolean.php';
       
   308                 return new Zend_XmlRpc_Value_Boolean($value);
       
   309 
       
   310             case 'NULL':
       
   311             case 'null':
       
   312                 require_once 'Zend/XmlRpc/Value/Nil.php';
       
   313                 return new Zend_XmlRpc_Value_Nil();
       
   314 
       
   315             case 'string':
       
   316                 // Fall through to the next case
       
   317             default:
       
   318                 // If type isn't identified (or identified as string), it treated as string
       
   319                 require_once 'Zend/XmlRpc/Value/String.php';
       
   320                 return new Zend_XmlRpc_Value_String($value);
       
   321         }
       
   322     }
       
   323 
       
   324 
       
   325     /**
       
   326      * Transform an XML string into a XML-RPC native value
       
   327      *
       
   328      * @param string|SimpleXMLElement $xml A SimpleXMLElement object represent the XML string
       
   329      *                                            It can be also a valid XML string for convertion
       
   330      *
       
   331      * @return Zend_XmlRpc_Value
       
   332      * @static
       
   333      */
       
   334     protected static function _xmlStringToNativeXmlRpc($xml)
       
   335     {
       
   336         self::_createSimpleXMLElement($xml);
       
   337 
       
   338         self::_extractTypeAndValue($xml, $type, $value);
       
   339 
       
   340         switch ($type) {
       
   341             // All valid and known XML-RPC native values
       
   342             case self::XMLRPC_TYPE_I4:
       
   343                 // Fall through to the next case
       
   344             case self::XMLRPC_TYPE_INTEGER:
       
   345                 require_once 'Zend/XmlRpc/Value/Integer.php';
       
   346                 $xmlrpcValue = new Zend_XmlRpc_Value_Integer($value);
       
   347                 break;
       
   348             case self::XMLRPC_TYPE_APACHEI8:
       
   349                 // Fall through to the next case
       
   350             case self::XMLRPC_TYPE_I8:
       
   351                 require_once 'Zend/XmlRpc/Value/BigInteger.php';
       
   352                 $xmlrpcValue = new Zend_XmlRpc_Value_BigInteger($value);
       
   353                 break;
       
   354             case self::XMLRPC_TYPE_DOUBLE:
       
   355                 require_once 'Zend/XmlRpc/Value/Double.php';
       
   356                 $xmlrpcValue = new Zend_XmlRpc_Value_Double($value);
       
   357                 break;
       
   358             case self::XMLRPC_TYPE_BOOLEAN:
       
   359                 require_once 'Zend/XmlRpc/Value/Boolean.php';
       
   360                 $xmlrpcValue = new Zend_XmlRpc_Value_Boolean($value);
       
   361                 break;
       
   362             case self::XMLRPC_TYPE_STRING:
       
   363                 require_once 'Zend/XmlRpc/Value/String.php';
       
   364                 $xmlrpcValue = new Zend_XmlRpc_Value_String($value);
       
   365                 break;
       
   366             case self::XMLRPC_TYPE_DATETIME:  // The value should already be in a iso8601 format
       
   367                 require_once 'Zend/XmlRpc/Value/DateTime.php';
       
   368                 $xmlrpcValue = new Zend_XmlRpc_Value_DateTime($value);
       
   369                 break;
       
   370             case self::XMLRPC_TYPE_BASE64:    // The value should already be base64 encoded
       
   371                 require_once 'Zend/XmlRpc/Value/Base64.php';
       
   372                 $xmlrpcValue = new Zend_XmlRpc_Value_Base64($value, true);
       
   373                 break;
       
   374             case self::XMLRPC_TYPE_NIL:
       
   375                 // Fall through to the next case
       
   376             case self::XMLRPC_TYPE_APACHENIL:
       
   377                 // The value should always be NULL
       
   378                 require_once 'Zend/XmlRpc/Value/Nil.php';
       
   379                 $xmlrpcValue = new Zend_XmlRpc_Value_Nil();
       
   380                 break;
       
   381             case self::XMLRPC_TYPE_ARRAY:
       
   382                 // PHP 5.2.4 introduced a regression in how empty($xml->value)
       
   383                 // returns; need to look for the item specifically
       
   384                 $data = null;
       
   385                 foreach ($value->children() as $key => $value) {
       
   386                     if ('data' == $key) {
       
   387                         $data = $value;
       
   388                         break;
       
   389                     }
       
   390                 }
       
   391 
       
   392                 if (null === $data) {
       
   393                     require_once 'Zend/XmlRpc/Value/Exception.php';
       
   394                     throw new Zend_XmlRpc_Value_Exception('Invalid XML for XML-RPC native '. self::XMLRPC_TYPE_ARRAY .' type: ARRAY tag must contain DATA tag');
       
   395                 }
       
   396                 $values = array();
       
   397                 // Parse all the elements of the array from the XML string
       
   398                 // (simple xml element) to Zend_XmlRpc_Value objects
       
   399                 foreach ($data->value as $element) {
       
   400                     $values[] = self::_xmlStringToNativeXmlRpc($element);
       
   401                 }
       
   402                 require_once 'Zend/XmlRpc/Value/Array.php';
       
   403                 $xmlrpcValue = new Zend_XmlRpc_Value_Array($values);
       
   404                 break;
       
   405             case self::XMLRPC_TYPE_STRUCT:
       
   406                 $values = array();
       
   407                 // Parse all the memebers of the struct from the XML string
       
   408                 // (simple xml element) to Zend_XmlRpc_Value objects
       
   409                 foreach ($value->member as $member) {
       
   410                     // @todo? If a member doesn't have a <value> tag, we don't add it to the struct
       
   411                     // Maybe we want to throw an exception here ?
       
   412                     if (!isset($member->value) or !isset($member->name)) {
       
   413                         continue;
       
   414                         //throw new Zend_XmlRpc_Value_Exception('Member of the '. self::XMLRPC_TYPE_STRUCT .' XML-RPC native type must contain a VALUE tag');
       
   415                     }
       
   416                     $values[(string)$member->name] = self::_xmlStringToNativeXmlRpc($member->value);
       
   417                 }
       
   418                 require_once 'Zend/XmlRpc/Value/Struct.php';
       
   419                 $xmlrpcValue = new Zend_XmlRpc_Value_Struct($values);
       
   420                 break;
       
   421             default:
       
   422                 require_once 'Zend/XmlRpc/Value/Exception.php';
       
   423                 throw new Zend_XmlRpc_Value_Exception('Value type \''. $type .'\' parsed from the XML string is not a known XML-RPC native type');
       
   424                 break;
       
   425         }
       
   426         $xmlrpcValue->_setXML($xml->asXML());
       
   427 
       
   428         return $xmlrpcValue;
       
   429     }
       
   430 
       
   431     protected static function _createSimpleXMLElement(&$xml)
       
   432     {
       
   433         if ($xml instanceof SimpleXMLElement) {
       
   434             return;
       
   435         }
       
   436 
       
   437         try {
       
   438             $xml = new SimpleXMLElement($xml);
       
   439         } catch (Exception $e) {
       
   440             // The given string is not a valid XML
       
   441             require_once 'Zend/XmlRpc/Value/Exception.php';
       
   442             throw new Zend_XmlRpc_Value_Exception('Failed to create XML-RPC value from XML string: ' . $e->getMessage(), $e->getCode(), $e);
       
   443         }
       
   444     }
       
   445 
       
   446     /**
       
   447      * Extract XML/RPC type and value from SimpleXMLElement object
       
   448      *
       
   449      * @param SimpleXMLElement $xml
       
   450      * @param string &$type Type bind variable
       
   451      * @param string &$value Value bind variable
       
   452      * @return void
       
   453      */
       
   454     protected static function _extractTypeAndValue(SimpleXMLElement $xml, &$type, &$value)
       
   455     {
       
   456         list($type, $value) = each($xml);
       
   457 
       
   458         if (!$type and $value === null) {
       
   459             $namespaces = array('ex' => 'http://ws.apache.org/xmlrpc/namespaces/extensions');
       
   460             foreach ($namespaces as $namespaceName => $namespaceUri) {
       
   461                 $namespaceXml = $xml->children($namespaceUri);
       
   462                 list($type, $value) = each($namespaceXml);
       
   463                 if ($type !== null) {
       
   464                     $type = $namespaceName . ':' . $type;
       
   465                     break;
       
   466                 }
       
   467             }
       
   468         }
       
   469 
       
   470         // If no type was specified, the default is string
       
   471         if (!$type) {
       
   472             $type = self::XMLRPC_TYPE_STRING;
       
   473         }
       
   474     }
       
   475 
       
   476     /**
       
   477      * @param $xml
       
   478      * @return void
       
   479      */
       
   480     protected function _setXML($xml)
       
   481     {
       
   482         $this->_xml = $this->getGenerator()->stripDeclaration($xml);
       
   483     }
       
   484 }