[ Index ]

PHP Cross Reference of DokuWiki

title

Body

[close]

/vendor/kissifrot/php-ixr/src/Message/ -> Message.php (source)

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