[ Index ]

PHP Cross Reference of WordPress Trunk (Updated Daily)

Search

title

Body

[close]

/wp-includes/IXR/ -> class-IXR-message.php (source)

   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_element_handler($this->_parser, array($this, 'tag_open'), array($this, 'tag_close'));
  97          xml_set_character_data_handler($this->_parser, array($this, 'cdata'));
  98  
  99          // 256Kb, parse in chunks to avoid the RAM usage on very large messages
 100          $chunk_size = 262144;
 101  
 102          /**
 103           * Filters the chunk size that can be used to parse an XML-RPC response message.
 104           *
 105           * @since 4.4.0
 106           *
 107           * @param int $chunk_size Chunk size to parse in bytes.
 108           */
 109          $chunk_size = apply_filters( 'xmlrpc_chunk_parsing_size', $chunk_size );
 110  
 111          $final = false;
 112  
 113          do {
 114              if (strlen($this->message) <= $chunk_size) {
 115                  $final = true;
 116              }
 117  
 118              $part = substr($this->message, 0, $chunk_size);
 119              $this->message = substr($this->message, $chunk_size);
 120  
 121              if (!xml_parse($this->_parser, $part, $final)) {
 122                  if (PHP_VERSION_ID < 80000) { // xml_parser_free() has no effect as of PHP 8.0.
 123                      xml_parser_free($this->_parser);
 124                  }
 125  
 126                  unset($this->_parser);
 127                  return false;
 128              }
 129  
 130              if ($final) {
 131                  break;
 132              }
 133          } while (true);
 134  
 135          if (PHP_VERSION_ID < 80000) { // xml_parser_free() has no effect as of PHP 8.0.
 136              xml_parser_free($this->_parser);
 137          }
 138  
 139          unset($this->_parser);
 140  
 141          // Grab the error messages, if any
 142          if ($this->messageType == 'fault') {
 143              $this->faultCode = $this->params[0]['faultCode'];
 144              $this->faultString = $this->params[0]['faultString'];
 145          }
 146          return true;
 147      }
 148  
 149      function tag_open($parser, $tag, $attr)
 150      {
 151          $this->_currentTagContents = '';
 152          $this->_currentTag = $tag;
 153          switch($tag) {
 154              case 'methodCall':
 155              case 'methodResponse':
 156              case 'fault':
 157                  $this->messageType = $tag;
 158                  break;
 159                  /* Deal with stacks of arrays and structs */
 160              case 'data':    // data is to all intents and puposes more interesting than array
 161                  $this->_arraystructstypes[] = 'array';
 162                  $this->_arraystructs[] = array();
 163                  break;
 164              case 'struct':
 165                  $this->_arraystructstypes[] = 'struct';
 166                  $this->_arraystructs[] = array();
 167                  break;
 168          }
 169      }
 170  
 171      function cdata($parser, $cdata)
 172      {
 173          $this->_currentTagContents .= $cdata;
 174      }
 175  
 176      function tag_close($parser, $tag)
 177      {
 178          $valueFlag = false;
 179          switch($tag) {
 180              case 'int':
 181              case 'i4':
 182                  $value = (int)trim($this->_currentTagContents);
 183                  $valueFlag = true;
 184                  break;
 185              case 'double':
 186                  $value = (float)trim($this->_currentTagContents);
 187                  $valueFlag = true;
 188                  break;
 189              case 'string':
 190                  $value = (string)trim($this->_currentTagContents);
 191                  $valueFlag = true;
 192                  break;
 193              case 'dateTime.iso8601':
 194                  $value = new IXR_Date(trim($this->_currentTagContents));
 195                  $valueFlag = true;
 196                  break;
 197              case 'value':
 198                  // "If no type is indicated, the type is string."
 199                  if (trim($this->_currentTagContents) != '') {
 200                      $value = (string)$this->_currentTagContents;
 201                      $valueFlag = true;
 202                  }
 203                  break;
 204              case 'boolean':
 205                  $value = (bool)trim($this->_currentTagContents);
 206                  $valueFlag = true;
 207                  break;
 208              case 'base64':
 209                  $value = base64_decode($this->_currentTagContents);
 210                  $valueFlag = true;
 211                  break;
 212                  /* Deal with stacks of arrays and structs */
 213              case 'data':
 214              case 'struct':
 215                  $value = array_pop($this->_arraystructs);
 216                  array_pop($this->_arraystructstypes);
 217                  $valueFlag = true;
 218                  break;
 219              case 'member':
 220                  array_pop($this->_currentStructName);
 221                  break;
 222              case 'name':
 223                  $this->_currentStructName[] = trim($this->_currentTagContents);
 224                  break;
 225              case 'methodName':
 226                  $this->methodName = trim($this->_currentTagContents);
 227                  break;
 228          }
 229  
 230          if ($valueFlag) {
 231              if (count($this->_arraystructs) > 0) {
 232                  // Add value to struct or array
 233                  if ($this->_arraystructstypes[count($this->_arraystructstypes)-1] == 'struct') {
 234                      // Add to struct
 235                      $this->_arraystructs[count($this->_arraystructs)-1][$this->_currentStructName[count($this->_currentStructName)-1]] = $value;
 236                  } else {
 237                      // Add to array
 238                      $this->_arraystructs[count($this->_arraystructs)-1][] = $value;
 239                  }
 240              } else {
 241                  // Just add as a parameter
 242                  $this->params[] = $value;
 243              }
 244          }
 245          $this->_currentTagContents = '';
 246      }
 247  }


Generated : Tue Sep 9 08:20:04 2025 Cross-referenced by PHPXref