<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[59056] trunk: Code Modernization: Remove xml_set_object() in IXR_Message::parse().</title>
</head>
<body>

<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt;  }
#msg dl a { font-weight: bold}
#msg dl a:link    { color:#fc3; }
#msg dl a:active  { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { white-space: pre-line; overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff  {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta" style="font-size: 105%">
<dt style="float: left; width: 6em; font-weight: bold">Revision</dt> <dd><a style="font-weight: bold" href="https://core.trac.wordpress.org/changeset/59056">59056</a><script type="application/ld+json">{"@context":"http://schema.org","@type":"EmailMessage","description":"Review this Commit","action":{"@type":"ViewAction","url":"https://core.trac.wordpress.org/changeset/59056","name":"Review Commit"}}</script></dd>
<dt style="float: left; width: 6em; font-weight: bold">Author</dt> <dd>hellofromTonya</dd>
<dt style="float: left; width: 6em; font-weight: bold">Date</dt> <dd>2024-09-18 18:02:43 +0000 (Wed, 18 Sep 2024)</dd>
</dl>

<pre style='padding-left: 1em; margin: 2em 0; border-left: 2px solid #ccc; line-height: 1.25; font-size: 105%; font-family: sans-serif'>Code Modernization: Remove xml_set_object() in IXR_Message::parse().

The XML Parser extension still supports a quite dated mechanism for method based callbacks, where the object is first set via `xml_set_object()` and the callbacks are then set by passing only the name of the method to the relevant parameters on any of the `xml_set_*_handler()` functions.

{{{
xml_set_object( $parser, $my_obj );
xml_set_character_data_handler( $parser, 'method_name_on_my_obj' );
}}}

Passing proper callables to the `xml_set_*_handler()` functions has been supported for the longest time and is cross-version compatible. So the above code is 100% equivalent to:

{{{
xml_set_character_data_handler( $parser, [$my_obj, 'method_name_on_my_obj'] );
}}}

The mechanism of setting the callbacks with `xml_set_object()` has now been deprecated as of PHP 8.4, in favour of passing proper callables to the `xml_set_*_handler()` functions. This is also means that calling the `xml_set_object()` function is deprecated as well.

This commit fixes this deprecation for the `IXR_Message::parse()` method.

This change is safeguarded via the new`Tests_XMLRPC_Message::test_parse_sets_handlers()` test method.

Note: Though this is "officially" an external library, this package is no longer externally maintained. The code style of the fix in the source file is in line with the existing code style for the file.

Refs:
* https://wiki.php.net/rfc/deprecations_php_8_4#xml_set_object_and_xml_set_handler_with_string_method_names
* https://www.php.net/manual/en/function.xml-set-object.php
* https://www.php.net/manual/en/ref.xml.php

Follow-up to <a href="https://core.trac.wordpress.org/changeset/15612">[15612]</a>, <a href="https://core.trac.wordpress.org/changeset/1346">[1346]</a>.

Props jrf, hellofromTonya.
See <a href="https://core.trac.wordpress.org/ticket/62061">#62061</a>.</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunksrcwpincludesIXRclassIXRmessagephp">trunk/src/wp-includes/IXR/class-IXR-message.php</a></li>
<li><a href="#trunktestsphpunittestsxmlrpcmessagephp">trunk/tests/phpunit/tests/xmlrpc/message.php</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunksrcwpincludesIXRclassIXRmessagephp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/src/wp-includes/IXR/class-IXR-message.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-includes/IXR/class-IXR-message.php   2024-09-18 17:24:30 UTC (rev 59055)
+++ trunk/src/wp-includes/IXR/class-IXR-message.php     2024-09-18 18:02:43 UTC (rev 59056)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -93,9 +93,8 @@
</span><span class="cx" style="display: block; padding: 0 10px">         // Set XML parser to take the case of tags in to account
</span><span class="cx" style="display: block; padding: 0 10px">         xml_parser_set_option($this->_parser, XML_OPTION_CASE_FOLDING, false);
</span><span class="cx" style="display: block; padding: 0 10px">         // Set XML parser callback functions
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        xml_set_object($this->_parser, $this);
-        xml_set_element_handler($this->_parser, 'tag_open', 'tag_close');
-        xml_set_character_data_handler($this->_parser, 'cdata');
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+        xml_set_element_handler($this->_parser, array($this, 'tag_open'), array($this, 'tag_close'));
+        xml_set_character_data_handler($this->_parser, array($this, 'cdata'));
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">         // 256Kb, parse in chunks to avoid the RAM usage on very large messages
</span><span class="cx" style="display: block; padding: 0 10px">         $chunk_size = 262144;
</span></span></pre></div>
<a id="trunktestsphpunittestsxmlrpcmessagephp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/tests/phpunit/tests/xmlrpc/message.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/tests/phpunit/tests/xmlrpc/message.php      2024-09-18 17:24:30 UTC (rev 59055)
+++ trunk/tests/phpunit/tests/xmlrpc/message.php        2024-09-18 18:02:43 UTC (rev 59056)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -30,4 +30,37 @@
</span><span class="cx" style="display: block; padding: 0 10px">                $this->assertSame( 'methodResponse', $message->messageType ); // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
</span><span class="cx" style="display: block; padding: 0 10px">                $this->assertSame( array( '1' ), $message->params );
</span><span class="cx" style="display: block; padding: 0 10px">        }
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
+       /**
+        * Test that the `IXR_Message::parse()` method correctly sets callback functions to handle certain parts of the XML.
+        *
+        * Safeguards handling of the PHP 8.4 deprecation of `xml_set_object()`.
+        *
+        * @covers IXR_Message::parse
+        */
+       public function test_parse_sets_handlers() {
+               $xml     = '<methodResponse><params><param><value>1</value></param></params></methodResponse>';
+               $message = new class( $xml ) extends IXR_Message {
+                       public $tag_open_call_counter  = 0;
+                       public $tag_close_call_counter = 0;
+                       public $cdata_call_counter     = 0;
+
+                       public function tag_open( $parser, $tag, $attr ) {
+                               ++$this->tag_open_call_counter;
+                       }
+                       public function cdata( $parser, $cdata ) {
+                               ++$this->cdata_call_counter;
+                       }
+                       public function tag_close( $parser, $tag ) {
+                               ++$this->tag_close_call_counter;
+                       }
+               };
+
+               $this->assertTrue( $message->parse(), 'XML parsing failed' );
+
+               $msg = '%s() handler did not get called expected nr of times';
+               $this->assertSame( 4, $message->tag_open_call_counter, sprintf( $msg, 'tag_open' ) );
+               $this->assertSame( 4, $message->tag_close_call_counter, sprintf( $msg, 'tag_close' ) );
+               $this->assertSame( 1, $message->cdata_call_counter, sprintf( $msg, 'cdata' ) );
+       }
</ins><span class="cx" style="display: block; padding: 0 10px"> }
</span></span></pre>
</div>
</div>

</body>
</html>