Message.php 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199
  1. <?php
  2. namespace IXR;
  3. /**
  4. * IXR消息
  5. *
  6. * @package IXR
  7. */
  8. class Message
  9. {
  10. /**
  11. * @var string
  12. */
  13. public $message;
  14. /**
  15. * @var string
  16. */
  17. public $messageType; // methodCall / methodResponse / fault
  18. public $faultCode;
  19. public $faultString;
  20. /**
  21. * @var string
  22. */
  23. public $methodName;
  24. /**
  25. * @var array
  26. */
  27. public $params = [];
  28. // Current variable stacks
  29. private $arrayStructs = []; // The stack used to keep track of the current array/struct
  30. private $arrayStructsTypes = []; // Stack keeping track of if things are structs or array
  31. private $currentStructName = []; // A stack as well
  32. private $currentTagContents;
  33. /**
  34. * @param string $message
  35. */
  36. public function __construct(string $message)
  37. {
  38. $this->message = $message;
  39. }
  40. /**
  41. * @return bool
  42. */
  43. public function parse(): bool
  44. {
  45. // first remove the XML declaration
  46. $this->message = preg_replace('/<\?xml(.*)?\?' . '>/', '', $this->message);
  47. if (trim($this->message) == '') {
  48. return false;
  49. }
  50. $parser = xml_parser_create();
  51. // Set XML parser to take the case of tags in to account
  52. xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, false);
  53. // Set XML parser callback functions
  54. xml_set_object($parser, $this);
  55. xml_set_element_handler($parser, [$this, 'tagOpen'], [$this, 'tagClose']);
  56. xml_set_character_data_handler($parser, [$this, 'cdata']);
  57. if (!xml_parse($parser, $this->message)) {
  58. /* die(sprintf('XML error: %s at line %d',
  59. xml_error_string(xml_get_error_code($this->parser)),
  60. xml_get_current_line_number($this->parser))); */
  61. return false;
  62. }
  63. xml_parser_free($parser);
  64. // Grab the error messages, if any
  65. if ($this->messageType == 'fault') {
  66. $this->faultCode = $this->params[0]['faultCode'];
  67. $this->faultString = $this->params[0]['faultString'];
  68. }
  69. return true;
  70. }
  71. /**
  72. * @param $parser
  73. * @param $tag
  74. * @param $attr
  75. */
  76. private function tagOpen($parser, string $tag, $attr)
  77. {
  78. switch ($tag) {
  79. case 'methodCall':
  80. case 'methodResponse':
  81. case 'fault':
  82. $this->messageType = $tag;
  83. break;
  84. /* Deal with stacks of arrays and structs */
  85. case 'data': // data is to all intents and puposes more interesting than array
  86. $this->arrayStructsTypes[] = 'array';
  87. $this->arrayStructs[] = [];
  88. break;
  89. case 'struct':
  90. $this->arrayStructsTypes[] = 'struct';
  91. $this->arrayStructs[] = [];
  92. break;
  93. }
  94. }
  95. /**
  96. * @param $parser
  97. * @param string $cdata
  98. */
  99. private function cdata($parser, string $cdata)
  100. {
  101. $this->currentTagContents .= $cdata;
  102. }
  103. /**
  104. * @param $parser
  105. * @param string $tag
  106. */
  107. private function tagClose($parser, string $tag)
  108. {
  109. switch ($tag) {
  110. case 'int':
  111. case 'i4':
  112. $value = (int) trim($this->currentTagContents);
  113. $this->currentTagContents = '';
  114. break;
  115. case 'double':
  116. $value = (double) trim($this->currentTagContents);
  117. $this->currentTagContents = '';
  118. break;
  119. case 'string':
  120. $value = (string)trim($this->currentTagContents);
  121. $this->currentTagContents = '';
  122. break;
  123. case 'dateTime.iso8601':
  124. $value = new Date(trim($this->currentTagContents));
  125. // $value = $iso->getTimestamp();
  126. $this->currentTagContents = '';
  127. break;
  128. case 'value':
  129. // "If no type is indicated, the type is string."
  130. if (trim($this->currentTagContents) != '') {
  131. $value = (string) $this->currentTagContents;
  132. $this->currentTagContents = '';
  133. }
  134. break;
  135. case 'boolean':
  136. $value = (bool) trim($this->currentTagContents);
  137. $this->currentTagContents = '';
  138. break;
  139. case 'base64':
  140. $value = base64_decode($this->currentTagContents);
  141. $this->currentTagContents = '';
  142. break;
  143. /* Deal with stacks of arrays and structs */
  144. case 'data':
  145. case 'struct':
  146. $value = array_pop($this->arrayStructs);
  147. array_pop($this->arrayStructsTypes);
  148. break;
  149. case 'member':
  150. array_pop($this->currentStructName);
  151. break;
  152. case 'name':
  153. $this->currentStructName[] = trim($this->currentTagContents);
  154. $this->currentTagContents = '';
  155. break;
  156. case 'methodName':
  157. $this->methodName = trim($this->currentTagContents);
  158. $this->currentTagContents = '';
  159. break;
  160. }
  161. if (isset($value)) {
  162. /*
  163. if (!is_array($value) && !is_object($value)) {
  164. $value = trim($value);
  165. }
  166. */
  167. if (count($this->arrayStructs) > 0) {
  168. // Add value to struct or array
  169. if ($this->arrayStructsTypes[count($this->arrayStructsTypes) - 1] == 'struct') {
  170. // Add to struct
  171. $this->arrayStructs[count($this->arrayStructs) - 1]
  172. [$this->currentStructName[count($this->currentStructName) - 1]] = $value;
  173. } else {
  174. // Add to array
  175. $this->arrayStructs[count($this->arrayStructs) - 1][] = $value;
  176. }
  177. } else {
  178. // Just add as a paramater
  179. $this->params[] = $value;
  180. }
  181. }
  182. }
  183. }