Skip to content

ext/xml: Refactor extension to use FCC instead of zvals for handlers #12340

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 26 commits into from
Oct 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
e1f967f
[skip ci] [W.I.P.] ext/xml: Refactor extension to use FCC instead of …
Girgias Oct 1, 2023
5e7baeb
Fix memory leak of parser->object
nielsdos Oct 1, 2023
c8c5ac2
Clear initialized status after deleting handler
nielsdos Oct 1, 2023
457c9ac
It's not really a libxml function, can also be expat
nielsdos Oct 1, 2023
4dd9a73
fixup
nielsdos Oct 1, 2023
9c710c2
Fix typo?
nielsdos Oct 1, 2023
71e2e3e
Fix start element handler not getting called
nielsdos Oct 1, 2023
6b29565
Add todo
nielsdos Oct 1, 2023
8f4660c
Fix leak
nielsdos Oct 1, 2023
465574b
Add trampoline test for generic XMLParser handler
Girgias Oct 1, 2023
b2f5a74
Deal with error cases
Girgias Oct 1, 2023
4f3520c
TODO Comment
Girgias Oct 1, 2023
e3d834d
Stop special casing false value type
Girgias Oct 3, 2023
13325eb
Use proper param types in gen_stub
Girgias Oct 4, 2023
f61c609
REVIEW
Girgias Oct 6, 2023
7a4175b
Trampo test + fix bug
Girgias Oct 6, 2023
41e31d6
Change parser.object to be a zend_object*
Girgias Oct 7, 2023
69e5b75
[skip ci] Check old FCCs for new set object
Girgias Oct 7, 2023
ce61a80
[skip ci] add ref
Girgias Oct 7, 2023
a5dab5d
UPGRADING
Girgias Oct 9, 2023
04eae36
Fix behaviour that proper callables could change
Girgias Oct 9, 2023
7a68757
Improve error when swapping object
Girgias Oct 9, 2023
edc99d5
Use new F ZPP type check
Girgias Oct 10, 2023
ad61613
Remove things related to unknown encoding handler
nielsdos Oct 12, 2023
654b381
Fix memleak
nielsdos Oct 12, 2023
4ae94e5
Nits
Girgias Oct 14, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions UPGRADING
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,17 @@ PHP 8.4 UPGRADE NOTES
for invalid modes. Previously invalid modes would have been interpreted as
PHP_ROUND_HALF_UP.

- XML:
. The xml_set_*_handler() functions now declare and check for an effective
signature of callable|string|null for the $handler parameters.
Moreover, values of type string that correspond to method names,
of object set with xml_set_object() are now checked to see if the method
exists on the class of the previously passed object.
This means that xml_set_object() must now always be called prior to setting
method names as callables.
Passing an empty string to disable the handler is still allowed,
but not recommended.

- XSL:
. XSLTProcessor::setParameter() will now throw a ValueError when its arguments
contain null bytes. This never actually worked correctly in the first place,
Expand Down
1 change: 1 addition & 0 deletions ext/xml/tests/bug30266.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ class XML_Parser
$p1 = new Xml_Parser();
try {
$p1->parse('<tag1><tag2></tag2></tag1>');
echo "Exception swallowed\n";
} catch (Exception $e) {
echo "OK\n";
}
Expand Down
2 changes: 1 addition & 1 deletion ext/xml/tests/bug32001.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -100,8 +100,8 @@ HERE;

$parser = xml_parser_create(NULL);
xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, 0);
xml_set_element_handler($parser, "start_element", "end_element");
xml_set_object($parser, $this);
xml_set_element_handler($parser, "start_element", "end_element");

if ($this->chunk_size == 0) {
$success = @xml_parse($parser, $data, true);
Expand Down
16 changes: 0 additions & 16 deletions ext/xml/tests/bug72085.phpt

This file was deleted.

18 changes: 10 additions & 8 deletions ext/xml/tests/bug73135.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,20 @@ xml
edgarsandi - <[email protected]>
--FILE--
<?php
function start_elem($parser, $xml) {
xml_parse($parser, $xml);
}
function start_elem($parser, $xml) {
xml_parse($parser, $xml);
}

function dummy() {}

$xml = <<<HERE
<a xmlns="ahihi">
<bar foo="ahihi"/>
</a>
$xml = <<<HERE
<a xmlns="ahihi">
<bar foo="ahihi"/>
</a>
HERE;

$parser = xml_parser_create_ns();
xml_set_element_handler($parser, 'start_elem', 'ahihi');
xml_set_element_handler($parser, 'start_elem', 'dummy');
xml_parse($parser, $xml);
?>
--EXPECTF--
Expand Down
97 changes: 97 additions & 0 deletions ext/xml/tests/set_element_handler_trampoline.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
--TEST--
Test xml_set_element_handler handlers as trampoline callback
--EXTENSIONS--
xml
--FILE--
<?php

class CustomXmlParser
{
public function startHandler($XmlParser, $tag, $attr)
{
echo 'Method start handler: ', $tag, PHP_EOL;
}

public function endHandler($XmlParser, $tag)
{
echo 'Method end handler: ', $tag, PHP_EOL;
}
}

$customParser = new CustomXmlParser;

class TrampolineTest {
public function __call(string $name, array $arguments) {
echo 'Trampoline for ', $name, PHP_EOL;
echo 'Tag: ', $arguments[1], PHP_EOL;
}
}

$o = new TrampolineTest();
$startCallback = [$o, 'start_handler'];
$endCallback = [$o, 'end_handler'];

$xml = <<<HERE
<a>
<b/>
<c>Text</c>
</a>
HERE;

echo "Both handlers are trampolines:\n";
$parser = xml_parser_create();
xml_set_element_handler($parser, $startCallback, $endCallback);
xml_parse($parser, $xml, true);
xml_parser_free($parser);

echo "\nStart handler is trampoline, end handler method string:\n";
$parser = xml_parser_create();
xml_set_object($parser, $customParser);
xml_set_element_handler($parser, $startCallback, 'endHandler');
xml_parse($parser, $xml, true);
xml_parser_free($parser);

echo "\nEnd handler is trampoline, start handler method string:\n";
$parser = xml_parser_create();
xml_set_object($parser, $customParser);
xml_set_element_handler($parser, 'startHandler', $endCallback);
xml_parse($parser, $xml, true);
xml_parser_free($parser);

?>
--EXPECT--
Both handlers are trampolines:
Trampoline for start_handler
Tag: A
Trampoline for start_handler
Tag: B
Trampoline for end_handler
Tag: B
Trampoline for start_handler
Tag: C
Trampoline for end_handler
Tag: C
Trampoline for end_handler
Tag: A

Start handler is trampoline, end handler method string:
Trampoline for start_handler
Tag: A
Trampoline for start_handler
Tag: B
Method end handler: B
Trampoline for start_handler
Tag: C
Method end handler: C
Method end handler: A

End handler is trampoline, start handler method string:
Method start handler: A
Method start handler: B
Trampoline for end_handler
Tag: B
Method start handler: C
Trampoline for end_handler
Tag: C
Trampoline for end_handler
Tag: A
46 changes: 46 additions & 0 deletions ext/xml/tests/set_element_handler_trampoline_errors.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
--TEST--
Test xml_set_element_handler handlers as trampoline callback
--EXTENSIONS--
xml
--FILE--
<?php

class TrampolineTest {
public function __call(string $name, array $arguments) {
echo 'Trampoline for ', $name, PHP_EOL;
echo 'Tag: ', $arguments[1], PHP_EOL;
}
}

$o = new TrampolineTest();
$startCallback = [$o, 'start_handler'];
$endCallback = [$o, 'end_handler'];

$xml = <<<HERE
<a>
<b/>
<c>Text</c>
</a>
HERE;

$parser = xml_parser_create();
echo "2nd arg is rubbish:\n";
try {
xml_set_element_handler($parser, [], $endCallback);
} catch (\Throwable $e) {
echo $e::class, ': ', $e->getMessage(), PHP_EOL;
}
echo "3rd arg is rubbish:\n";
try {
xml_set_element_handler($parser, $startCallback, []);
} catch (\Throwable $e) {
echo $e::class, ': ', $e->getMessage(), PHP_EOL;
}
xml_parser_free($parser);

?>
--EXPECT--
2nd arg is rubbish:
TypeError: xml_set_element_handler(): Argument #2 ($start_handler) must be of type callable|string|null
3rd arg is rubbish:
TypeError: xml_set_element_handler(): Argument #2 ($start_handler) must be of type callable|string|null
61 changes: 61 additions & 0 deletions ext/xml/tests/set_handler_errors.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
--TEST--
Error conditions when setting invalid handler callables
--EXTENSIONS--
xml
--FILE--
<?php
declare(strict_types=1);

/* Use xml_set_processing_instruction_handler() for generic implementation */
echo 'Invalid $parser:', PHP_EOL;
$obj = new stdClass();
try {
xml_set_processing_instruction_handler($obj, null);
} catch (\Throwable $e) {
echo $e::class, ': ', $e->getMessage(), PHP_EOL;
}

/* Create valid parser */
$parser = xml_parser_create();

echo 'Invalid callable type true:', PHP_EOL;
try {
xml_set_processing_instruction_handler($parser, true);
} catch (\Throwable $e) {
echo $e::class, ': ', $e->getMessage(), PHP_EOL;
}

echo 'Invalid callable type int:', PHP_EOL;
try {
xml_set_processing_instruction_handler($parser, 10);
} catch (\Throwable $e) {
echo $e::class, ': ', $e->getMessage(), PHP_EOL;
}

echo 'String not callable and no object set:', PHP_EOL;
try {
xml_set_processing_instruction_handler($parser, "nonexistent_method");
} catch (\Throwable $e) {
echo $e::class, ': ', $e->getMessage(), PHP_EOL;
}

echo 'String non existent method on set object:', PHP_EOL;
xml_set_object($parser, $obj);
try {
xml_set_processing_instruction_handler($parser, "nonexistent_method");
} catch (\Throwable $e) {
echo $e::class, ': ', $e->getMessage(), PHP_EOL;
}

?>
--EXPECT--
Invalid $parser:
TypeError: xml_set_processing_instruction_handler(): Argument #1 ($parser) must be of type XMLParser, stdClass given
Invalid callable type true:
TypeError: xml_set_processing_instruction_handler(): Argument #2 ($handler) must be of type callable|string|null
Invalid callable type int:
TypeError: xml_set_processing_instruction_handler(): Argument #2 ($handler) must be of type callable|string|null
String not callable and no object set:
ValueError: xml_set_processing_instruction_handler(): Argument #2 ($handler) an object must be set via xml_set_object() to be able to lookup method
String non existent method on set object:
ValueError: xml_set_processing_instruction_handler(): Argument #2 ($handler) method stdClass::nonexistent_method() does not exist
33 changes: 33 additions & 0 deletions ext/xml/tests/set_handler_trampoline.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
--TEST--
Test XMLParser generic handlers as trampoline callback
--EXTENSIONS--
xml
--FILE--
<?php
class TrampolineTest {
public function __call(string $name, array $arguments) {
echo 'Trampoline for ', $name, PHP_EOL;
echo 'Target: ', $arguments[1], PHP_EOL;
echo 'Data: ', $arguments[2], PHP_EOL;
}
}

$o = new TrampolineTest();
$callback = [$o, 'pi_handler'];

$xml = <<<HERE
<?xml version="1.0" encoding="ISO-8859-1"?>
<?xml-stylesheet href="default.xsl" type="text/xml"?>
HERE;

/* Use xml_set_processing_instruction_handler() for generic implementation */
$parser = xml_parser_create();
xml_set_processing_instruction_handler($parser, $callback);
xml_parse($parser, $xml, true);
xml_parser_free($parser);

?>
--EXPECT--
Trampoline for pi_handler
Target: xml-stylesheet
Data: href="default.xsl" type="text/xml"
Loading