Skip to content

Commit 0e5d654

Browse files
Girgiasnielsdos
andauthored
ext/xml: Refactor extension to use FCC instead of zvals for handlers (#12340)
To get proper errors and sensible behaviour, as the current behaviour is somewhat insane and part of it should be axed ASAP. The behaviour is mostly intact with some minor BC breaks which are mentioned in UPGRADING. Co-authored-by: Niels Dossche <[email protected]>
1 parent 6d10a69 commit 0e5d654

15 files changed

+821
-343
lines changed

UPGRADING

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,17 @@ PHP 8.4 UPGRADE NOTES
4949
for invalid modes. Previously invalid modes would have been interpreted as
5050
PHP_ROUND_HALF_UP.
5151

52+
- XML:
53+
. The xml_set_*_handler() functions now declare and check for an effective
54+
signature of callable|string|null for the $handler parameters.
55+
Moreover, values of type string that correspond to method names,
56+
of object set with xml_set_object() are now checked to see if the method
57+
exists on the class of the previously passed object.
58+
This means that xml_set_object() must now always be called prior to setting
59+
method names as callables.
60+
Passing an empty string to disable the handler is still allowed,
61+
but not recommended.
62+
5263
- XSL:
5364
. XSLTProcessor::setParameter() will now throw a ValueError when its arguments
5465
contain null bytes. This never actually worked correctly in the first place,

ext/xml/tests/bug30266.phpt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ class XML_Parser
4242
$p1 = new Xml_Parser();
4343
try {
4444
$p1->parse('<tag1><tag2></tag2></tag1>');
45+
echo "Exception swallowed\n";
4546
} catch (Exception $e) {
4647
echo "OK\n";
4748
}

ext/xml/tests/bug32001.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,8 +100,8 @@ HERE;
100100

101101
$parser = xml_parser_create(NULL);
102102
xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, 0);
103-
xml_set_element_handler($parser, "start_element", "end_element");
104103
xml_set_object($parser, $this);
104+
xml_set_element_handler($parser, "start_element", "end_element");
105105

106106
if ($this->chunk_size == 0) {
107107
$success = @xml_parse($parser, $data, true);

ext/xml/tests/bug72085.phpt

Lines changed: 0 additions & 16 deletions
This file was deleted.

ext/xml/tests/bug73135.phpt

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,20 @@ xml
66
edgarsandi - <[email protected]>
77
--FILE--
88
<?php
9-
function start_elem($parser, $xml) {
10-
xml_parse($parser, $xml);
11-
}
9+
function start_elem($parser, $xml) {
10+
xml_parse($parser, $xml);
11+
}
12+
13+
function dummy() {}
1214

13-
$xml = <<<HERE
14-
<a xmlns="ahihi">
15-
<bar foo="ahihi"/>
16-
</a>
15+
$xml = <<<HERE
16+
<a xmlns="ahihi">
17+
<bar foo="ahihi"/>
18+
</a>
1719
HERE;
1820

1921
$parser = xml_parser_create_ns();
20-
xml_set_element_handler($parser, 'start_elem', 'ahihi');
22+
xml_set_element_handler($parser, 'start_elem', 'dummy');
2123
xml_parse($parser, $xml);
2224
?>
2325
--EXPECTF--
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
--TEST--
2+
Test xml_set_element_handler handlers as trampoline callback
3+
--EXTENSIONS--
4+
xml
5+
--FILE--
6+
<?php
7+
8+
class CustomXmlParser
9+
{
10+
public function startHandler($XmlParser, $tag, $attr)
11+
{
12+
echo 'Method start handler: ', $tag, PHP_EOL;
13+
}
14+
15+
public function endHandler($XmlParser, $tag)
16+
{
17+
echo 'Method end handler: ', $tag, PHP_EOL;
18+
}
19+
}
20+
21+
$customParser = new CustomXmlParser;
22+
23+
class TrampolineTest {
24+
public function __call(string $name, array $arguments) {
25+
echo 'Trampoline for ', $name, PHP_EOL;
26+
echo 'Tag: ', $arguments[1], PHP_EOL;
27+
}
28+
}
29+
30+
$o = new TrampolineTest();
31+
$startCallback = [$o, 'start_handler'];
32+
$endCallback = [$o, 'end_handler'];
33+
34+
$xml = <<<HERE
35+
<a>
36+
<b/>
37+
<c>Text</c>
38+
</a>
39+
HERE;
40+
41+
echo "Both handlers are trampolines:\n";
42+
$parser = xml_parser_create();
43+
xml_set_element_handler($parser, $startCallback, $endCallback);
44+
xml_parse($parser, $xml, true);
45+
xml_parser_free($parser);
46+
47+
echo "\nStart handler is trampoline, end handler method string:\n";
48+
$parser = xml_parser_create();
49+
xml_set_object($parser, $customParser);
50+
xml_set_element_handler($parser, $startCallback, 'endHandler');
51+
xml_parse($parser, $xml, true);
52+
xml_parser_free($parser);
53+
54+
echo "\nEnd handler is trampoline, start handler method string:\n";
55+
$parser = xml_parser_create();
56+
xml_set_object($parser, $customParser);
57+
xml_set_element_handler($parser, 'startHandler', $endCallback);
58+
xml_parse($parser, $xml, true);
59+
xml_parser_free($parser);
60+
61+
?>
62+
--EXPECT--
63+
Both handlers are trampolines:
64+
Trampoline for start_handler
65+
Tag: A
66+
Trampoline for start_handler
67+
Tag: B
68+
Trampoline for end_handler
69+
Tag: B
70+
Trampoline for start_handler
71+
Tag: C
72+
Trampoline for end_handler
73+
Tag: C
74+
Trampoline for end_handler
75+
Tag: A
76+
77+
Start handler is trampoline, end handler method string:
78+
Trampoline for start_handler
79+
Tag: A
80+
Trampoline for start_handler
81+
Tag: B
82+
Method end handler: B
83+
Trampoline for start_handler
84+
Tag: C
85+
Method end handler: C
86+
Method end handler: A
87+
88+
End handler is trampoline, start handler method string:
89+
Method start handler: A
90+
Method start handler: B
91+
Trampoline for end_handler
92+
Tag: B
93+
Method start handler: C
94+
Trampoline for end_handler
95+
Tag: C
96+
Trampoline for end_handler
97+
Tag: A
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
--TEST--
2+
Test xml_set_element_handler handlers as trampoline callback
3+
--EXTENSIONS--
4+
xml
5+
--FILE--
6+
<?php
7+
8+
class TrampolineTest {
9+
public function __call(string $name, array $arguments) {
10+
echo 'Trampoline for ', $name, PHP_EOL;
11+
echo 'Tag: ', $arguments[1], PHP_EOL;
12+
}
13+
}
14+
15+
$o = new TrampolineTest();
16+
$startCallback = [$o, 'start_handler'];
17+
$endCallback = [$o, 'end_handler'];
18+
19+
$xml = <<<HERE
20+
<a>
21+
<b/>
22+
<c>Text</c>
23+
</a>
24+
HERE;
25+
26+
$parser = xml_parser_create();
27+
echo "2nd arg is rubbish:\n";
28+
try {
29+
xml_set_element_handler($parser, [], $endCallback);
30+
} catch (\Throwable $e) {
31+
echo $e::class, ': ', $e->getMessage(), PHP_EOL;
32+
}
33+
echo "3rd arg is rubbish:\n";
34+
try {
35+
xml_set_element_handler($parser, $startCallback, []);
36+
} catch (\Throwable $e) {
37+
echo $e::class, ': ', $e->getMessage(), PHP_EOL;
38+
}
39+
xml_parser_free($parser);
40+
41+
?>
42+
--EXPECT--
43+
2nd arg is rubbish:
44+
TypeError: xml_set_element_handler(): Argument #2 ($start_handler) must be of type callable|string|null
45+
3rd arg is rubbish:
46+
TypeError: xml_set_element_handler(): Argument #2 ($start_handler) must be of type callable|string|null

ext/xml/tests/set_handler_errors.phpt

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
--TEST--
2+
Error conditions when setting invalid handler callables
3+
--EXTENSIONS--
4+
xml
5+
--FILE--
6+
<?php
7+
declare(strict_types=1);
8+
9+
/* Use xml_set_processing_instruction_handler() for generic implementation */
10+
echo 'Invalid $parser:', PHP_EOL;
11+
$obj = new stdClass();
12+
try {
13+
xml_set_processing_instruction_handler($obj, null);
14+
} catch (\Throwable $e) {
15+
echo $e::class, ': ', $e->getMessage(), PHP_EOL;
16+
}
17+
18+
/* Create valid parser */
19+
$parser = xml_parser_create();
20+
21+
echo 'Invalid callable type true:', PHP_EOL;
22+
try {
23+
xml_set_processing_instruction_handler($parser, true);
24+
} catch (\Throwable $e) {
25+
echo $e::class, ': ', $e->getMessage(), PHP_EOL;
26+
}
27+
28+
echo 'Invalid callable type int:', PHP_EOL;
29+
try {
30+
xml_set_processing_instruction_handler($parser, 10);
31+
} catch (\Throwable $e) {
32+
echo $e::class, ': ', $e->getMessage(), PHP_EOL;
33+
}
34+
35+
echo 'String not callable and no object set:', PHP_EOL;
36+
try {
37+
xml_set_processing_instruction_handler($parser, "nonexistent_method");
38+
} catch (\Throwable $e) {
39+
echo $e::class, ': ', $e->getMessage(), PHP_EOL;
40+
}
41+
42+
echo 'String non existent method on set object:', PHP_EOL;
43+
xml_set_object($parser, $obj);
44+
try {
45+
xml_set_processing_instruction_handler($parser, "nonexistent_method");
46+
} catch (\Throwable $e) {
47+
echo $e::class, ': ', $e->getMessage(), PHP_EOL;
48+
}
49+
50+
?>
51+
--EXPECT--
52+
Invalid $parser:
53+
TypeError: xml_set_processing_instruction_handler(): Argument #1 ($parser) must be of type XMLParser, stdClass given
54+
Invalid callable type true:
55+
TypeError: xml_set_processing_instruction_handler(): Argument #2 ($handler) must be of type callable|string|null
56+
Invalid callable type int:
57+
TypeError: xml_set_processing_instruction_handler(): Argument #2 ($handler) must be of type callable|string|null
58+
String not callable and no object set:
59+
ValueError: xml_set_processing_instruction_handler(): Argument #2 ($handler) an object must be set via xml_set_object() to be able to lookup method
60+
String non existent method on set object:
61+
ValueError: xml_set_processing_instruction_handler(): Argument #2 ($handler) method stdClass::nonexistent_method() does not exist
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
--TEST--
2+
Test XMLParser generic handlers as trampoline callback
3+
--EXTENSIONS--
4+
xml
5+
--FILE--
6+
<?php
7+
class TrampolineTest {
8+
public function __call(string $name, array $arguments) {
9+
echo 'Trampoline for ', $name, PHP_EOL;
10+
echo 'Target: ', $arguments[1], PHP_EOL;
11+
echo 'Data: ', $arguments[2], PHP_EOL;
12+
}
13+
}
14+
15+
$o = new TrampolineTest();
16+
$callback = [$o, 'pi_handler'];
17+
18+
$xml = <<<HERE
19+
<?xml version="1.0" encoding="ISO-8859-1"?>
20+
<?xml-stylesheet href="default.xsl" type="text/xml"?>
21+
HERE;
22+
23+
/* Use xml_set_processing_instruction_handler() for generic implementation */
24+
$parser = xml_parser_create();
25+
xml_set_processing_instruction_handler($parser, $callback);
26+
xml_parse($parser, $xml, true);
27+
xml_parser_free($parser);
28+
29+
?>
30+
--EXPECT--
31+
Trampoline for pi_handler
32+
Target: xml-stylesheet
33+
Data: href="default.xsl" type="text/xml"

0 commit comments

Comments
 (0)