Several PHP functions that have not been doing anything since PHP 8.0/8.1, specifically: * `finfo_close()` since the `ext/fileinfo` migration in PHP 8.1 * `xml_parser_free()` since the `ext/xml` migration in PHP 8.0 * `curl_close()` since the `ext/curl` migration in PHP 8.0 * `curl_share_close()` since the `ext/curl` migration in PHP 8.0 * `imagedestroy()` since the `ext/gd` migration in PHP 8.0 will be deprecated in PHP 8.5 and will thus be throwing warnings. This commit adds conditional checks to only call these functions on the relevant PHP versions. Reference: [https://wiki.php.net/rfc/deprecations_php_8_5#deprecate_no-op_functions_from_the_resource_to_object_conversion PHP RFC: Deprecations for PHP 8.5: Deprecate no-op functions from the resource to object conversion]. Props TobiasBg, SergeyBiryukov. See #63061. Built from https://develop.svn.wordpress.org/trunk@60703 git-svn-id: http://core.svn.wordpress.org/trunk@60039 1a063a9b-81f0-0310-95a4-ce76da25c4cd
248 lines
8.2 KiB
PHP
248 lines
8.2 KiB
PHP
<?php
|
|
|
|
/**
|
|
* IXR_MESSAGE
|
|
*
|
|
* @package IXR
|
|
* @since 1.5.0
|
|
*
|
|
*/
|
|
class IXR_Message
|
|
{
|
|
var $message = false;
|
|
var $messageType = false; // methodCall / methodResponse / fault
|
|
var $faultCode = false;
|
|
var $faultString = false;
|
|
var $methodName = '';
|
|
var $params = array();
|
|
|
|
// Current variable stacks
|
|
var $_arraystructs = array(); // The stack used to keep track of the current array/struct
|
|
var $_arraystructstypes = array(); // Stack keeping track of if things are structs or array
|
|
var $_currentStructName = array(); // A stack as well
|
|
var $_param;
|
|
var $_value;
|
|
var $_currentTag;
|
|
var $_currentTagContents;
|
|
// The XML parser
|
|
var $_parser;
|
|
|
|
/**
|
|
* PHP5 constructor.
|
|
*/
|
|
function __construct( $message )
|
|
{
|
|
$this->message =& $message;
|
|
}
|
|
|
|
/**
|
|
* PHP4 constructor.
|
|
*/
|
|
public function IXR_Message( $message ) {
|
|
self::__construct( $message );
|
|
}
|
|
|
|
function parse()
|
|
{
|
|
if ( ! function_exists( 'xml_parser_create' ) ) {
|
|
trigger_error( __( "PHP's XML extension is not available. Please contact your hosting provider to enable PHP's XML extension." ) );
|
|
return false;
|
|
}
|
|
|
|
// first remove the XML declaration
|
|
// merged from WP #10698 - this method avoids the RAM usage of preg_replace on very large messages
|
|
$header = preg_replace( '/<\?xml.*?\?'.'>/s', '', substr( $this->message, 0, 100 ), 1 );
|
|
$this->message = trim( substr_replace( $this->message, $header, 0, 100 ) );
|
|
if ( '' == $this->message ) {
|
|
return false;
|
|
}
|
|
|
|
// Then remove the DOCTYPE
|
|
$header = preg_replace( '/^<!DOCTYPE[^>]*+>/i', '', substr( $this->message, 0, 200 ), 1 );
|
|
$this->message = trim( substr_replace( $this->message, $header, 0, 200 ) );
|
|
if ( '' == $this->message ) {
|
|
return false;
|
|
}
|
|
|
|
// Check that the root tag is valid
|
|
$root_tag = substr( $this->message, 0, strcspn( substr( $this->message, 0, 20 ), "> \t\r\n" ) );
|
|
if ( '<!DOCTYPE' === strtoupper( $root_tag ) ) {
|
|
return false;
|
|
}
|
|
if ( ! in_array( $root_tag, array( '<methodCall', '<methodResponse', '<fault' ) ) ) {
|
|
return false;
|
|
}
|
|
|
|
// Bail if there are too many elements to parse
|
|
$element_limit = 30000;
|
|
if ( function_exists( 'apply_filters' ) ) {
|
|
/**
|
|
* Filters the number of elements to parse in an XML-RPC response.
|
|
*
|
|
* @since 4.0.0
|
|
*
|
|
* @param int $element_limit Default elements limit.
|
|
*/
|
|
$element_limit = apply_filters( 'xmlrpc_element_limit', $element_limit );
|
|
}
|
|
if ( $element_limit && 2 * $element_limit < substr_count( $this->message, '<' ) ) {
|
|
return false;
|
|
}
|
|
|
|
$this->_parser = xml_parser_create();
|
|
// Set XML parser to take the case of tags in to account
|
|
xml_parser_set_option($this->_parser, XML_OPTION_CASE_FOLDING, false);
|
|
// Set XML parser callback functions
|
|
xml_set_element_handler($this->_parser, array($this, 'tag_open'), array($this, 'tag_close'));
|
|
xml_set_character_data_handler($this->_parser, array($this, 'cdata'));
|
|
|
|
// 256Kb, parse in chunks to avoid the RAM usage on very large messages
|
|
$chunk_size = 262144;
|
|
|
|
/**
|
|
* Filters the chunk size that can be used to parse an XML-RPC response message.
|
|
*
|
|
* @since 4.4.0
|
|
*
|
|
* @param int $chunk_size Chunk size to parse in bytes.
|
|
*/
|
|
$chunk_size = apply_filters( 'xmlrpc_chunk_parsing_size', $chunk_size );
|
|
|
|
$final = false;
|
|
|
|
do {
|
|
if (strlen($this->message) <= $chunk_size) {
|
|
$final = true;
|
|
}
|
|
|
|
$part = substr($this->message, 0, $chunk_size);
|
|
$this->message = substr($this->message, $chunk_size);
|
|
|
|
if (!xml_parse($this->_parser, $part, $final)) {
|
|
if (PHP_VERSION_ID < 80000) { // xml_parser_free() has no effect as of PHP 8.0.
|
|
xml_parser_free($this->_parser);
|
|
}
|
|
|
|
unset($this->_parser);
|
|
return false;
|
|
}
|
|
|
|
if ($final) {
|
|
break;
|
|
}
|
|
} while (true);
|
|
|
|
if (PHP_VERSION_ID < 80000) { // xml_parser_free() has no effect as of PHP 8.0.
|
|
xml_parser_free($this->_parser);
|
|
}
|
|
|
|
unset($this->_parser);
|
|
|
|
// Grab the error messages, if any
|
|
if ($this->messageType == 'fault') {
|
|
$this->faultCode = $this->params[0]['faultCode'];
|
|
$this->faultString = $this->params[0]['faultString'];
|
|
}
|
|
return true;
|
|
}
|
|
|
|
function tag_open($parser, $tag, $attr)
|
|
{
|
|
$this->_currentTagContents = '';
|
|
$this->_currentTag = $tag;
|
|
switch($tag) {
|
|
case 'methodCall':
|
|
case 'methodResponse':
|
|
case 'fault':
|
|
$this->messageType = $tag;
|
|
break;
|
|
/* Deal with stacks of arrays and structs */
|
|
case 'data': // data is to all intents and puposes more interesting than array
|
|
$this->_arraystructstypes[] = 'array';
|
|
$this->_arraystructs[] = array();
|
|
break;
|
|
case 'struct':
|
|
$this->_arraystructstypes[] = 'struct';
|
|
$this->_arraystructs[] = array();
|
|
break;
|
|
}
|
|
}
|
|
|
|
function cdata($parser, $cdata)
|
|
{
|
|
$this->_currentTagContents .= $cdata;
|
|
}
|
|
|
|
function tag_close($parser, $tag)
|
|
{
|
|
$valueFlag = false;
|
|
switch($tag) {
|
|
case 'int':
|
|
case 'i4':
|
|
$value = (int)trim($this->_currentTagContents);
|
|
$valueFlag = true;
|
|
break;
|
|
case 'double':
|
|
$value = (float)trim($this->_currentTagContents);
|
|
$valueFlag = true;
|
|
break;
|
|
case 'string':
|
|
$value = (string)trim($this->_currentTagContents);
|
|
$valueFlag = true;
|
|
break;
|
|
case 'dateTime.iso8601':
|
|
$value = new IXR_Date(trim($this->_currentTagContents));
|
|
$valueFlag = true;
|
|
break;
|
|
case 'value':
|
|
// "If no type is indicated, the type is string."
|
|
if (trim($this->_currentTagContents) != '') {
|
|
$value = (string)$this->_currentTagContents;
|
|
$valueFlag = true;
|
|
}
|
|
break;
|
|
case 'boolean':
|
|
$value = (bool)trim($this->_currentTagContents);
|
|
$valueFlag = true;
|
|
break;
|
|
case 'base64':
|
|
$value = base64_decode($this->_currentTagContents);
|
|
$valueFlag = true;
|
|
break;
|
|
/* Deal with stacks of arrays and structs */
|
|
case 'data':
|
|
case 'struct':
|
|
$value = array_pop($this->_arraystructs);
|
|
array_pop($this->_arraystructstypes);
|
|
$valueFlag = true;
|
|
break;
|
|
case 'member':
|
|
array_pop($this->_currentStructName);
|
|
break;
|
|
case 'name':
|
|
$this->_currentStructName[] = trim($this->_currentTagContents);
|
|
break;
|
|
case 'methodName':
|
|
$this->methodName = trim($this->_currentTagContents);
|
|
break;
|
|
}
|
|
|
|
if ($valueFlag) {
|
|
if (count($this->_arraystructs) > 0) {
|
|
// Add value to struct or array
|
|
if ($this->_arraystructstypes[count($this->_arraystructstypes)-1] == 'struct') {
|
|
// Add to struct
|
|
$this->_arraystructs[count($this->_arraystructs)-1][$this->_currentStructName[count($this->_currentStructName)-1]] = $value;
|
|
} else {
|
|
// Add to array
|
|
$this->_arraystructs[count($this->_arraystructs)-1][] = $value;
|
|
}
|
|
} else {
|
|
// Just add as a parameter
|
|
$this->params[] = $value;
|
|
}
|
|
}
|
|
$this->_currentTagContents = '';
|
|
}
|
|
}
|