home *** CD-ROM | disk | FTP | other *** search
/ HTML Examples / WP.iso / wordpress / wp-includes / IXR / class-IXR-message.php < prev    next >
Encoding:
PHP Script  |  2016-10-29  |  7.8 KB  |  235 lines

  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;
  13.     var $messageType;  // methodCall / methodResponse / fault
  14.     var $faultCode;
  15.     var $faultString;
  16.     var $methodName;
  17.     var $params;
  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 reponse 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.         do {
  114.             if (strlen($this->message) <= $chunk_size) {
  115.                 $final = true;
  116.             }
  117.             $part = substr($this->message, 0, $chunk_size);
  118.             $this->message = substr($this->message, $chunk_size);
  119.             if (!xml_parse($this->_parser, $part, $final)) {
  120.                 return false;
  121.             }
  122.             if ($final) {
  123.                 break;
  124.             }
  125.         } while (true);
  126.         xml_parser_free($this->_parser);
  127.  
  128.         // Grab the error messages, if any
  129.         if ($this->messageType == 'fault') {
  130.             $this->faultCode = $this->params[0]['faultCode'];
  131.             $this->faultString = $this->params[0]['faultString'];
  132.         }
  133.         return true;
  134.     }
  135.  
  136.     function tag_open($parser, $tag, $attr)
  137.     {
  138.         $this->_currentTagContents = '';
  139.         $this->currentTag = $tag;
  140.         switch($tag) {
  141.             case 'methodCall':
  142.             case 'methodResponse':
  143.             case 'fault':
  144.                 $this->messageType = $tag;
  145.                 break;
  146.                 /* Deal with stacks of arrays and structs */
  147.             case 'data':    // data is to all intents and puposes more interesting than array
  148.                 $this->_arraystructstypes[] = 'array';
  149.                 $this->_arraystructs[] = array();
  150.                 break;
  151.             case 'struct':
  152.                 $this->_arraystructstypes[] = 'struct';
  153.                 $this->_arraystructs[] = array();
  154.                 break;
  155.         }
  156.     }
  157.  
  158.     function cdata($parser, $cdata)
  159.     {
  160.         $this->_currentTagContents .= $cdata;
  161.     }
  162.  
  163.     function tag_close($parser, $tag)
  164.     {
  165.         $valueFlag = false;
  166.         switch($tag) {
  167.             case 'int':
  168.             case 'i4':
  169.                 $value = (int)trim($this->_currentTagContents);
  170.                 $valueFlag = true;
  171.                 break;
  172.             case 'double':
  173.                 $value = (double)trim($this->_currentTagContents);
  174.                 $valueFlag = true;
  175.                 break;
  176.             case 'string':
  177.                 $value = (string)trim($this->_currentTagContents);
  178.                 $valueFlag = true;
  179.                 break;
  180.             case 'dateTime.iso8601':
  181.                 $value = new IXR_Date(trim($this->_currentTagContents));
  182.                 $valueFlag = true;
  183.                 break;
  184.             case 'value':
  185.                 // "If no type is indicated, the type is string."
  186.                 if (trim($this->_currentTagContents) != '') {
  187.                     $value = (string)$this->_currentTagContents;
  188.                     $valueFlag = true;
  189.                 }
  190.                 break;
  191.             case 'boolean':
  192.                 $value = (boolean)trim($this->_currentTagContents);
  193.                 $valueFlag = true;
  194.                 break;
  195.             case 'base64':
  196.                 $value = base64_decode($this->_currentTagContents);
  197.                 $valueFlag = true;
  198.                 break;
  199.                 /* Deal with stacks of arrays and structs */
  200.             case 'data':
  201.             case 'struct':
  202.                 $value = array_pop($this->_arraystructs);
  203.                 array_pop($this->_arraystructstypes);
  204.                 $valueFlag = true;
  205.                 break;
  206.             case 'member':
  207.                 array_pop($this->_currentStructName);
  208.                 break;
  209.             case 'name':
  210.                 $this->_currentStructName[] = trim($this->_currentTagContents);
  211.                 break;
  212.             case 'methodName':
  213.                 $this->methodName = trim($this->_currentTagContents);
  214.                 break;
  215.         }
  216.  
  217.         if ($valueFlag) {
  218.             if (count($this->_arraystructs) > 0) {
  219.                 // Add value to struct or array
  220.                 if ($this->_arraystructstypes[count($this->_arraystructstypes)-1] == 'struct') {
  221.                     // Add to struct
  222.                     $this->_arraystructs[count($this->_arraystructs)-1][$this->_currentStructName[count($this->_currentStructName)-1]] = $value;
  223.                 } else {
  224.                     // Add to array
  225.                     $this->_arraystructs[count($this->_arraystructs)-1][] = $value;
  226.                 }
  227.             } else {
  228.                 // Just add as a parameter
  229.                 $this->params[] = $value;
  230.             }
  231.         }
  232.         $this->_currentTagContents = '';
  233.     }
  234. }
  235.