ru-se.com

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs

class-IXR-message.php (8195B)


      1 <?php
      2 
      3 /**
      4  * IXR_MESSAGE
      5  *
      6  * @package IXR
      7  * @since 1.5.0
      8  *
      9  */
     10 class IXR_Message
     11 {
     12     var $message     = false;
     13     var $messageType = false;  // methodCall / methodResponse / fault
     14     var $faultCode   = false;
     15     var $faultString = false;
     16     var $methodName  = '';
     17     var $params      = array();
     18 
     19     // Current variable stacks
     20     var $_arraystructs = array();   // The stack used to keep track of the current array/struct
     21     var $_arraystructstypes = array(); // Stack keeping track of if things are structs or array
     22     var $_currentStructName = array();  // A stack as well
     23     var $_param;
     24     var $_value;
     25     var $_currentTag;
     26     var $_currentTagContents;
     27     // The XML parser
     28     var $_parser;
     29 
     30 	/**
     31 	 * PHP5 constructor.
     32 	 */
     33     function __construct( $message )
     34     {
     35         $this->message =& $message;
     36     }
     37 
     38 	/**
     39 	 * PHP4 constructor.
     40 	 */
     41 	public function IXR_Message( $message ) {
     42 		self::__construct( $message );
     43 	}
     44 
     45     function parse()
     46     {
     47         if ( ! function_exists( 'xml_parser_create' ) ) {
     48             trigger_error( __( "PHP's XML extension is not available. Please contact your hosting provider to enable PHP's XML extension." ) );
     49             return false;
     50         }
     51 
     52         // first remove the XML declaration
     53         // merged from WP #10698 - this method avoids the RAM usage of preg_replace on very large messages
     54         $header = preg_replace( '/<\?xml.*?\?'.'>/s', '', substr( $this->message, 0, 100 ), 1 );
     55         $this->message = trim( substr_replace( $this->message, $header, 0, 100 ) );
     56         if ( '' == $this->message ) {
     57             return false;
     58         }
     59 
     60         // Then remove the DOCTYPE
     61         $header = preg_replace( '/^<!DOCTYPE[^>]*+>/i', '', substr( $this->message, 0, 200 ), 1 );
     62         $this->message = trim( substr_replace( $this->message, $header, 0, 200 ) );
     63         if ( '' == $this->message ) {
     64             return false;
     65         }
     66 
     67         // Check that the root tag is valid
     68         $root_tag = substr( $this->message, 0, strcspn( substr( $this->message, 0, 20 ), "> \t\r\n" ) );
     69         if ( '<!DOCTYPE' === strtoupper( $root_tag ) ) {
     70             return false;
     71         }
     72         if ( ! in_array( $root_tag, array( '<methodCall', '<methodResponse', '<fault' ) ) ) {
     73             return false;
     74         }
     75 
     76         // Bail if there are too many elements to parse
     77         $element_limit = 30000;
     78         if ( function_exists( 'apply_filters' ) ) {
     79             /**
     80              * Filters the number of elements to parse in an XML-RPC response.
     81              *
     82              * @since 4.0.0
     83              *
     84              * @param int $element_limit Default elements limit.
     85              */
     86             $element_limit = apply_filters( 'xmlrpc_element_limit', $element_limit );
     87         }
     88         if ( $element_limit && 2 * $element_limit < substr_count( $this->message, '<' ) ) {
     89             return false;
     90         }
     91 
     92         $this->_parser = xml_parser_create();
     93         // Set XML parser to take the case of tags in to account
     94         xml_parser_set_option($this->_parser, XML_OPTION_CASE_FOLDING, false);
     95         // Set XML parser callback functions
     96         xml_set_object($this->_parser, $this);
     97         xml_set_element_handler($this->_parser, 'tag_open', 'tag_close');
     98         xml_set_character_data_handler($this->_parser, 'cdata');
     99 
    100         // 256Kb, parse in chunks to avoid the RAM usage on very large messages
    101         $chunk_size = 262144;
    102 
    103         /**
    104          * Filters the chunk size that can be used to parse an XML-RPC response message.
    105          *
    106          * @since 4.4.0
    107          *
    108          * @param int $chunk_size Chunk size to parse in bytes.
    109          */
    110         $chunk_size = apply_filters( 'xmlrpc_chunk_parsing_size', $chunk_size );
    111 
    112         $final = false;
    113 
    114         do {
    115             if (strlen($this->message) <= $chunk_size) {
    116                 $final = true;
    117             }
    118 
    119             $part = substr($this->message, 0, $chunk_size);
    120             $this->message = substr($this->message, $chunk_size);
    121 
    122             if (!xml_parse($this->_parser, $part, $final)) {
    123                 xml_parser_free($this->_parser);
    124                 unset($this->_parser);
    125                 return false;
    126             }
    127 
    128             if ($final) {
    129                 break;
    130             }
    131         } while (true);
    132 
    133         xml_parser_free($this->_parser);
    134         unset($this->_parser);
    135 
    136         // Grab the error messages, if any
    137         if ($this->messageType == 'fault') {
    138             $this->faultCode = $this->params[0]['faultCode'];
    139             $this->faultString = $this->params[0]['faultString'];
    140         }
    141         return true;
    142     }
    143 
    144     function tag_open($parser, $tag, $attr)
    145     {
    146         $this->_currentTagContents = '';
    147         $this->currentTag = $tag;
    148         switch($tag) {
    149             case 'methodCall':
    150             case 'methodResponse':
    151             case 'fault':
    152                 $this->messageType = $tag;
    153                 break;
    154                 /* Deal with stacks of arrays and structs */
    155             case 'data':    // data is to all intents and puposes more interesting than array
    156                 $this->_arraystructstypes[] = 'array';
    157                 $this->_arraystructs[] = array();
    158                 break;
    159             case 'struct':
    160                 $this->_arraystructstypes[] = 'struct';
    161                 $this->_arraystructs[] = array();
    162                 break;
    163         }
    164     }
    165 
    166     function cdata($parser, $cdata)
    167     {
    168         $this->_currentTagContents .= $cdata;
    169     }
    170 
    171     function tag_close($parser, $tag)
    172     {
    173         $valueFlag = false;
    174         switch($tag) {
    175             case 'int':
    176             case 'i4':
    177                 $value = (int)trim($this->_currentTagContents);
    178                 $valueFlag = true;
    179                 break;
    180             case 'double':
    181                 $value = (double)trim($this->_currentTagContents);
    182                 $valueFlag = true;
    183                 break;
    184             case 'string':
    185                 $value = (string)trim($this->_currentTagContents);
    186                 $valueFlag = true;
    187                 break;
    188             case 'dateTime.iso8601':
    189                 $value = new IXR_Date(trim($this->_currentTagContents));
    190                 $valueFlag = true;
    191                 break;
    192             case 'value':
    193                 // "If no type is indicated, the type is string."
    194                 if (trim($this->_currentTagContents) != '') {
    195                     $value = (string)$this->_currentTagContents;
    196                     $valueFlag = true;
    197                 }
    198                 break;
    199             case 'boolean':
    200                 $value = (boolean)trim($this->_currentTagContents);
    201                 $valueFlag = true;
    202                 break;
    203             case 'base64':
    204                 $value = base64_decode($this->_currentTagContents);
    205                 $valueFlag = true;
    206                 break;
    207                 /* Deal with stacks of arrays and structs */
    208             case 'data':
    209             case 'struct':
    210                 $value = array_pop($this->_arraystructs);
    211                 array_pop($this->_arraystructstypes);
    212                 $valueFlag = true;
    213                 break;
    214             case 'member':
    215                 array_pop($this->_currentStructName);
    216                 break;
    217             case 'name':
    218                 $this->_currentStructName[] = trim($this->_currentTagContents);
    219                 break;
    220             case 'methodName':
    221                 $this->methodName = trim($this->_currentTagContents);
    222                 break;
    223         }
    224 
    225         if ($valueFlag) {
    226             if (count($this->_arraystructs) > 0) {
    227                 // Add value to struct or array
    228                 if ($this->_arraystructstypes[count($this->_arraystructstypes)-1] == 'struct') {
    229                     // Add to struct
    230                     $this->_arraystructs[count($this->_arraystructs)-1][$this->_currentStructName[count($this->_currentStructName)-1]] = $value;
    231                 } else {
    232                     // Add to array
    233                     $this->_arraystructs[count($this->_arraystructs)-1][] = $value;
    234                 }
    235             } else {
    236                 // Just add as a parameter
    237                 $this->params[] = $value;
    238             }
    239         }
    240         $this->_currentTagContents = '';
    241     }
    242 }