1
0
mirror of https://github.com/php/php-src.git synced 2026-03-24 00:02:20 +01:00

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 <7771979+nielsdos@users.noreply.github.com>
This commit is contained in:
George Peter Banyard
2023-10-20 13:14:55 +01:00
committed by GitHub
parent 6d10a69898
commit 0e5d654409
15 changed files with 826 additions and 348 deletions

View File

@@ -49,6 +49,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,

View File

@@ -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";
}

View File

@@ -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);

View File

@@ -1,16 +0,0 @@
--TEST--
Bug #72085 (SEGV on unknown address zif_xml_parse)
--EXTENSIONS--
xml
--FILE--
<?php
$var1 = xml_parser_create_ns();
xml_set_element_handler($var1, new Exception(""), 4096);
try {
xml_parse($var1, str_repeat("<a>", 10));
} catch (Error $e) {
echo $e->getMessage(), "\n";
}
?>
--EXPECT--
Invalid callback Exception::__invoke, no array or string given

View File

@@ -6,18 +6,20 @@ xml
edgarsandi - <edgar.r.sandi@gmail.com>
--FILE--
<?php
function start_elem($parser, $xml) {
xml_parse($parser, $xml);
}
function start_elem($parser, $xml) {
xml_parse($parser, $xml);
}
$xml = <<<HERE
<a xmlns="ahihi">
<bar foo="ahihi"/>
</a>
function dummy() {}
$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--

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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"

View File

@@ -0,0 +1,95 @@
--TEST--
Error conditions when setting invalid handler callables for xml_set_element_handler()
--EXTENSIONS--
xml
--FILE--
<?php
declare(strict_types=1);
function dummy() {}
echo 'Invalid $parser:', PHP_EOL;
$obj = new stdClass();
try {
xml_set_element_handler($obj, null, null);
} catch (\Throwable $e) {
echo $e::class, ': ', $e->getMessage(), PHP_EOL;
}
/* Create valid parser */
$parser = xml_parser_create();
echo 'Invalid start callable type true:', PHP_EOL;
try {
xml_set_element_handler($parser, true, null);
} catch (\Throwable $e) {
echo $e::class, ': ', $e->getMessage(), PHP_EOL;
}
echo 'Invalid end callable type true:', PHP_EOL;
try {
xml_set_element_handler($parser, null, true);
} catch (\Throwable $e) {
echo $e::class, ': ', $e->getMessage(), PHP_EOL;
}
echo 'Invalid start callable type int:', PHP_EOL;
try {
xml_set_element_handler($parser, 10, null);
} catch (\Throwable $e) {
echo $e::class, ': ', $e->getMessage(), PHP_EOL;
}
echo 'Invalid end callable type int:', PHP_EOL;
try {
xml_set_element_handler($parser, null, 10);
} catch (\Throwable $e) {
echo $e::class, ': ', $e->getMessage(), PHP_EOL;
}
echo 'Invalid start callable, no object set and string not callable:', PHP_EOL;
try {
xml_set_element_handler($parser, "nonexistent_method", null);
} catch (\Throwable $e) {
echo $e::class, ': ', $e->getMessage(), PHP_EOL;
}
echo 'Invalid end callable, no object set and string not callable:', PHP_EOL;
try {
xml_set_element_handler($parser, null, "nonexistent_method");
} catch (\Throwable $e) {
echo $e::class, ': ', $e->getMessage(), PHP_EOL;
}
echo 'Invalid start callable, string non existent method on set object:', PHP_EOL;
xml_set_object($parser, $obj);
try {
xml_set_element_handler($parser, "nonexistent_method", null);
} catch (\Throwable $e) {
echo $e::class, ': ', $e->getMessage(), PHP_EOL;
}
echo 'Invalid end callable, string non existent method on set object:', PHP_EOL;
xml_set_object($parser, $obj);
try {
xml_set_element_handler($parser, null, "nonexistent_method");
} catch (\Throwable $e) {
echo $e::class, ': ', $e->getMessage(), PHP_EOL;
}
?>
--EXPECT--
Invalid $parser:
TypeError: xml_set_element_handler(): Argument #1 ($parser) must be of type XMLParser, stdClass given
Invalid start callable type true:
TypeError: xml_set_element_handler(): Argument #2 ($start_handler) must be of type callable|string|null
Invalid end callable type true:
TypeError: xml_set_element_handler(): Argument #3 ($end_handler) must be of type callable|string|null
Invalid start callable type int:
TypeError: xml_set_element_handler(): Argument #2 ($start_handler) must be of type callable|string|null
Invalid end callable type int:
TypeError: xml_set_element_handler(): Argument #3 ($end_handler) must be of type callable|string|null
Invalid start callable, no object set and string not callable:
ValueError: xml_set_element_handler(): Argument #2 ($start_handler) an object must be set via xml_set_object() to be able to lookup method
Invalid end callable, no object set and string not callable:
ValueError: xml_set_element_handler(): Argument #3 ($end_handler) an object must be set via xml_set_object() to be able to lookup method
Invalid start callable, string non existent method on set object:
ValueError: xml_set_element_handler(): Argument #2 ($start_handler) method stdClass::nonexistent_method() does not exist
Invalid end callable, string non existent method on set object:
ValueError: xml_set_element_handler(): Argument #3 ($end_handler) method stdClass::nonexistent_method() does not exist

View File

@@ -0,0 +1,56 @@
--TEST--
Swap underlying object to call methods with xml_set_object()
--EXTENSIONS--
xml
--FILE--
<?php
function end_handler(XMLParser $parser, string $tag) {
echo "end_handler($tag)\n";
}
class A {
public function start_element($parser, $name, $attributes) {
global $b;
xml_set_object($parser, $b);
echo "A::start_element($name)\n";
}
public function PIHandler($parser, $target, $data) {
echo "A::PIHandler($target)\n";
}
}
class B {
public function start_element($parser, $name) {
echo "B::start_element($name)\n";
}
public function end_element($parser, $name) {
echo "B::end_element($name)\n";
}
public function PIHandler($parser, $target, $data) {
echo "B::PIHandler($target)\n";
}
}
$a = new A;
$b = new B;
$parser = xml_parser_create();
xml_set_object($parser, $a);
xml_set_element_handler($parser, "start_element", "end_handler");
xml_set_processing_instruction_handler($parser, [$a, "PIHandler"]);
xml_parse($parser, <<<XML
<?xml version="1.0"?>
<container>
<child/>
</container>
<?pi-test data ?>
XML);
?>
--EXPECT--
A::start_element(CONTAINER)
B::start_element(CHILD)
end_handler(CHILD)
end_handler(CONTAINER)
A::PIHandler(pi-test)

View File

@@ -0,0 +1,46 @@
--TEST--
Swap underlying object to call methods with xml_set_object() new object has missing methods
--EXTENSIONS--
xml
--FILE--
<?php
class A {
public function start_element($parser, $name, $attributes) {
global $b;
echo "A::start_element($name)\n";
try {
xml_set_object($parser, $b);
} catch (\Throwable $e) {
echo $e::class, ': ', $e->getMessage(), PHP_EOL;
exit();
}
}
public function end_element($parser, $name) {
echo "B::end_element($name)\n";
}
}
class B {
public function start_element($parser, $name) {
echo "B::start_element($name)\n";
}
}
$a = new A;
$b = new B;
$parser = xml_parser_create();
xml_set_object($parser, $a);
xml_set_element_handler($parser, "start_element", "end_element");
xml_parse($parser, <<<XML
<?xml version="1.0"?>
<container>
<child/>
</container>
XML);
?>
--EXPECT--
A::start_element(CONTAINER)
ValueError: xml_set_object(): Argument #2 ($object) cannot safely swap to object of class B as method "end_element" does not exist, which was set via xml_set_element_handler()

View File

@@ -69,34 +69,17 @@ typedef struct {
* It is not owned, do not release it. */
zval index;
/* We return a pointer to these zvals in get_gc(), so it's
* important that a) they are adjacent b) object is the first
* and c) the number of zvals is kept up to date. */
#define XML_PARSER_NUM_ZVALS 12
zval object;
zval startElementHandler;
zval endElementHandler;
zval characterDataHandler;
zval processingInstructionHandler;
zval defaultHandler;
zval unparsedEntityDeclHandler;
zval notationDeclHandler;
zval externalEntityRefHandler;
zval unknownEncodingHandler;
zval startNamespaceDeclHandler;
zval endNamespaceDeclHandler;
zend_function *startElementPtr;
zend_function *endElementPtr;
zend_function *characterDataPtr;
zend_function *processingInstructionPtr;
zend_function *defaultPtr;
zend_function *unparsedEntityDeclPtr;
zend_function *notationDeclPtr;
zend_function *externalEntityRefPtr;
zend_function *unknownEncodingPtr;
zend_function *startNamespaceDeclPtr;
zend_function *endNamespaceDeclPtr;
zend_object *object;
zend_fcall_info_cache startElementHandler;
zend_fcall_info_cache endElementHandler;
zend_fcall_info_cache characterDataHandler;
zend_fcall_info_cache processingInstructionHandler;
zend_fcall_info_cache defaultHandler;
zend_fcall_info_cache unparsedEntityDeclHandler;
zend_fcall_info_cache notationDeclHandler;
zend_fcall_info_cache externalEntityRefHandler;
zend_fcall_info_cache startNamespaceDeclHandler;
zend_fcall_info_cache endNamespaceDeclHandler;
zval data;
zval info;
@@ -148,12 +131,10 @@ static HashTable *xml_parser_get_gc(zend_object *object, zval **table, int *n);
static zend_function *xml_parser_get_constructor(zend_object *object);
static zend_string *xml_utf8_decode(const XML_Char *, size_t, const XML_Char *);
static void xml_set_handler(zval *, zval *);
inline static unsigned short xml_encode_iso_8859_1(unsigned char);
inline static char xml_decode_iso_8859_1(unsigned short);
inline static unsigned short xml_encode_us_ascii(unsigned char);
inline static char xml_decode_us_ascii(unsigned short);
static void xml_call_handler(xml_parser *, zval *, zend_function *, int, zval *, zval *);
static void _xml_xmlchar_zval(const XML_Char *, int, const XML_Char *, zval *);
static int _xml_xmlcharlen(const XML_Char *);
static void _xml_add_to_info(xml_parser *parser, const char *name);
@@ -330,44 +311,51 @@ static void xml_parser_free_obj(zend_object *object)
XML_ParserFree(parser->parser);
}
xml_parser_free_ltags(parser);
if (!Z_ISUNDEF(parser->startElementHandler)) {
zval_ptr_dtor(&parser->startElementHandler);
if (ZEND_FCC_INITIALIZED(parser->startElementHandler)) {
zend_fcc_dtor(&parser->startElementHandler);
parser->startElementHandler.function_handler = NULL;
}
if (!Z_ISUNDEF(parser->endElementHandler)) {
zval_ptr_dtor(&parser->endElementHandler);
if (ZEND_FCC_INITIALIZED(parser->endElementHandler)) {
zend_fcc_dtor(&parser->endElementHandler);
parser->endElementHandler.function_handler = NULL;
}
if (!Z_ISUNDEF(parser->characterDataHandler)) {
zval_ptr_dtor(&parser->characterDataHandler);
if (ZEND_FCC_INITIALIZED(parser->characterDataHandler)) {
zend_fcc_dtor(&parser->characterDataHandler);
parser->characterDataHandler.function_handler = NULL;
}
if (!Z_ISUNDEF(parser->processingInstructionHandler)) {
zval_ptr_dtor(&parser->processingInstructionHandler);
if (ZEND_FCC_INITIALIZED(parser->processingInstructionHandler)) {
zend_fcc_dtor(&parser->processingInstructionHandler);
parser->processingInstructionHandler.function_handler = NULL;
}
if (!Z_ISUNDEF(parser->defaultHandler)) {
zval_ptr_dtor(&parser->defaultHandler);
if (ZEND_FCC_INITIALIZED(parser->defaultHandler)) {
zend_fcc_dtor(&parser->defaultHandler);
parser->defaultHandler.function_handler = NULL;
}
if (!Z_ISUNDEF(parser->unparsedEntityDeclHandler)) {
zval_ptr_dtor(&parser->unparsedEntityDeclHandler);
if (ZEND_FCC_INITIALIZED(parser->unparsedEntityDeclHandler)) {
zend_fcc_dtor(&parser->unparsedEntityDeclHandler);
parser->unparsedEntityDeclHandler.function_handler = NULL;
}
if (!Z_ISUNDEF(parser->notationDeclHandler)) {
zval_ptr_dtor(&parser->notationDeclHandler);
if (ZEND_FCC_INITIALIZED(parser->notationDeclHandler)) {
zend_fcc_dtor(&parser->notationDeclHandler);
parser->notationDeclHandler.function_handler = NULL;
}
if (!Z_ISUNDEF(parser->externalEntityRefHandler)) {
zval_ptr_dtor(&parser->externalEntityRefHandler);
if (ZEND_FCC_INITIALIZED(parser->externalEntityRefHandler)) {
zend_fcc_dtor(&parser->externalEntityRefHandler);
parser->externalEntityRefHandler.function_handler = NULL;
}
if (!Z_ISUNDEF(parser->unknownEncodingHandler)) {
zval_ptr_dtor(&parser->unknownEncodingHandler);
if (ZEND_FCC_INITIALIZED(parser->startNamespaceDeclHandler)) {
zend_fcc_dtor(&parser->startNamespaceDeclHandler);
parser->startNamespaceDeclHandler.function_handler = NULL;
}
if (!Z_ISUNDEF(parser->startNamespaceDeclHandler)) {
zval_ptr_dtor(&parser->startNamespaceDeclHandler);
}
if (!Z_ISUNDEF(parser->endNamespaceDeclHandler)) {
zval_ptr_dtor(&parser->endNamespaceDeclHandler);
if (ZEND_FCC_INITIALIZED(parser->endNamespaceDeclHandler)) {
zend_fcc_dtor(&parser->endNamespaceDeclHandler);
parser->endNamespaceDeclHandler.function_handler = NULL;
}
if (parser->baseURI) {
efree(parser->baseURI);
}
if (!Z_ISUNDEF(parser->object)) {
zval_ptr_dtor(&parser->object);
if (parser->object) {
OBJ_RELEASE(parser->object);
}
zend_object_std_dtor(&parser->std);
@@ -376,8 +364,44 @@ static void xml_parser_free_obj(zend_object *object)
static HashTable *xml_parser_get_gc(zend_object *object, zval **table, int *n)
{
xml_parser *parser = xml_parser_from_obj(object);
*table = &parser->object;
*n = XML_PARSER_NUM_ZVALS;
zend_get_gc_buffer *gc_buffer = zend_get_gc_buffer_create();
if (parser->object) {
zend_get_gc_buffer_add_obj(gc_buffer, parser->object);
}
if (ZEND_FCC_INITIALIZED(parser->startElementHandler)) {
zend_get_gc_buffer_add_fcc(gc_buffer, &parser->startElementHandler);
}
if (ZEND_FCC_INITIALIZED(parser->endElementHandler)) {
zend_get_gc_buffer_add_fcc(gc_buffer, &parser->endElementHandler);
}
if (ZEND_FCC_INITIALIZED(parser->characterDataHandler)) {
zend_get_gc_buffer_add_fcc(gc_buffer, &parser->characterDataHandler);
}
if (ZEND_FCC_INITIALIZED(parser->processingInstructionHandler)) {
zend_get_gc_buffer_add_fcc(gc_buffer, &parser->processingInstructionHandler);
}
if (ZEND_FCC_INITIALIZED(parser->defaultHandler)) {
zend_get_gc_buffer_add_fcc(gc_buffer, &parser->defaultHandler);
}
if (ZEND_FCC_INITIALIZED(parser->unparsedEntityDeclHandler)) {
zend_get_gc_buffer_add_fcc(gc_buffer, &parser->unparsedEntityDeclHandler);
}
if (ZEND_FCC_INITIALIZED(parser->notationDeclHandler)) {
zend_get_gc_buffer_add_fcc(gc_buffer, &parser->notationDeclHandler);
}
if (ZEND_FCC_INITIALIZED(parser->externalEntityRefHandler)) {
zend_get_gc_buffer_add_fcc(gc_buffer, &parser->externalEntityRefHandler);
}
if (ZEND_FCC_INITIALIZED(parser->startNamespaceDeclHandler)) {
zend_get_gc_buffer_add_fcc(gc_buffer, &parser->startNamespaceDeclHandler);
}
if (ZEND_FCC_INITIALIZED(parser->endNamespaceDeclHandler)) {
zend_get_gc_buffer_add_fcc(gc_buffer, &parser->endNamespaceDeclHandler);
}
zend_get_gc_buffer_use(gc_buffer, table, n);
return zend_std_get_properties(object);
}
@@ -386,67 +410,19 @@ static zend_function *xml_parser_get_constructor(zend_object *object) {
return NULL;
}
/* {{{ xml_set_handler() */
static void xml_set_handler(zval *handler, zval *data)
/* This is always called to simplify the mess to deal with BC breaks, but only set a new handler if it is initialized */
static void xml_set_handler(zend_fcall_info_cache *const parser_handler, const zend_fcall_info_cache *const fn)
{
/* If we have already a handler, release it */
if (handler) {
zval_ptr_dtor(handler);
if (ZEND_FCC_INITIALIZED(*parser_handler)) {
zend_fcc_dtor(parser_handler);
parser_handler->function_handler = NULL;
}
/* IS_ARRAY might indicate that we're using array($obj, 'method') syntax */
if (Z_TYPE_P(data) != IS_ARRAY && Z_TYPE_P(data) != IS_OBJECT) {
convert_to_string(data);
if (Z_STRLEN_P(data) == 0) {
ZVAL_UNDEF(handler);
return;
}
}
ZVAL_COPY(handler, data);
}
/* }}} */
/* {{{ xml_call_handler() */
static void xml_call_handler(xml_parser *parser, zval *handler, zend_function *function_ptr, int argc, zval *argv, zval *retval)
{
int i;
ZVAL_UNDEF(retval);
if (parser && handler && !EG(exception)) {
int result;
zend_fcall_info fci;
fci.size = sizeof(fci);
ZVAL_COPY_VALUE(&fci.function_name, handler);
fci.object = Z_OBJ(parser->object);
fci.retval = retval;
fci.param_count = argc;
fci.params = argv;
fci.named_params = NULL;
result = zend_call_function(&fci, NULL);
if (result == FAILURE) {
zval *method;
zval *obj;
if (Z_TYPE_P(handler) == IS_STRING) {
php_error_docref(NULL, E_WARNING, "Unable to call handler %s()", Z_STRVAL_P(handler));
} else if (Z_TYPE_P(handler) == IS_ARRAY &&
(obj = zend_hash_index_find(Z_ARRVAL_P(handler), 0)) != NULL &&
(method = zend_hash_index_find(Z_ARRVAL_P(handler), 1)) != NULL &&
Z_TYPE_P(obj) == IS_OBJECT &&
Z_TYPE_P(method) == IS_STRING) {
php_error_docref(NULL, E_WARNING, "Unable to call handler %s::%s()", ZSTR_VAL(Z_OBJCE_P(obj)->name), Z_STRVAL_P(method));
} else
php_error_docref(NULL, E_WARNING, "Unable to call handler");
}
}
for (i = 0; i < argc; i++) {
zval_ptr_dtor(&argv[i]);
if (ZEND_FCC_INITIALIZED(*fn)) {
zend_fcc_dup(parser_handler, fn);
}
}
/* }}} */
/* {{{ xml_encode_iso_8859_1() */
inline static unsigned short xml_encode_iso_8859_1(unsigned char c)
@@ -589,7 +565,6 @@ void _xml_startElementHandler(void *userData, const XML_Char *name, const XML_Ch
xml_parser *parser = (xml_parser *)userData;
const char **attrs = (const char **) attributes;
zend_string *att, *tag_name, *val;
zval retval, args[3];
if (!parser) {
return;
@@ -599,7 +574,8 @@ void _xml_startElementHandler(void *userData, const XML_Char *name, const XML_Ch
tag_name = _xml_decode_tag(parser, name);
if (!Z_ISUNDEF(parser->startElementHandler)) {
if (ZEND_FCC_INITIALIZED(parser->startElementHandler)) {
zval args[3];
ZVAL_COPY(&args[0], &parser->index);
ZVAL_STRING(&args[1], SKIP_TAGSTART(ZSTR_VAL(tag_name)));
array_init(&args[2]);
@@ -618,8 +594,10 @@ void _xml_startElementHandler(void *userData, const XML_Char *name, const XML_Ch
zend_string_release_ex(att, 0);
}
xml_call_handler(parser, &parser->startElementHandler, parser->startElementPtr, 3, args, &retval);
zval_ptr_dtor(&retval);
zend_call_known_fcc(&parser->startElementHandler, /* retval */ NULL, /* param_count */ 3, args, /* named_params */ NULL);
zval_ptr_dtor(&args[0]);
zval_ptr_dtor(&args[1]);
zval_ptr_dtor(&args[2]);
}
if (!Z_ISUNDEF(parser->data)) {
@@ -681,16 +659,16 @@ void _xml_endElementHandler(void *userData, const XML_Char *name)
return;
}
zval retval, args[2];
zend_string *tag_name = _xml_decode_tag(parser, name);
if (!Z_ISUNDEF(parser->endElementHandler)) {
if (ZEND_FCC_INITIALIZED(parser->endElementHandler)) {
zval args[2];
ZVAL_COPY(&args[0], &parser->index);
ZVAL_STRING(&args[1], SKIP_TAGSTART(ZSTR_VAL(tag_name)));
xml_call_handler(parser, &parser->endElementHandler, parser->endElementPtr, 2, args, &retval);
zval_ptr_dtor(&retval);
zend_call_known_fcc(&parser->endElementHandler, /* retval */ NULL, /* param_count */ 2, args, /* named_params */ NULL);
zval_ptr_dtor(&args[0]);
zval_ptr_dtor(&args[1]);
}
if (!Z_ISUNDEF(parser->data)) {
@@ -732,13 +710,14 @@ void _xml_characterDataHandler(void *userData, const XML_Char *s, int len)
return;
}
zval retval, args[2];
if (!Z_ISUNDEF(parser->characterDataHandler)) {
if (ZEND_FCC_INITIALIZED(parser->characterDataHandler)) {
zval args[2];
ZVAL_COPY(&args[0], &parser->index);
_xml_xmlchar_zval(s, len, parser->target_encoding, &args[1]);
xml_call_handler(parser, &parser->characterDataHandler, parser->characterDataPtr, 2, args, &retval);
zval_ptr_dtor(&retval);
zend_call_known_fcc(&parser->characterDataHandler, /* retval */ NULL, /* param_count */ 2, args, /* named_params */ NULL);
zval_ptr_dtor(&args[0]);
zval_ptr_dtor(&args[1]);
}
if (Z_ISUNDEF(parser->data)) {
@@ -820,17 +799,20 @@ void _xml_processingInstructionHandler(void *userData, const XML_Char *target, c
{
xml_parser *parser = (xml_parser *)userData;
if (!parser || Z_ISUNDEF(parser->processingInstructionHandler)) {
if (!parser || !ZEND_FCC_INITIALIZED(parser->processingInstructionHandler)) {
return;
}
zval retval, args[3];
zval args[3];
ZVAL_COPY(&args[0], &parser->index);
_xml_xmlchar_zval(target, 0, parser->target_encoding, &args[1]);
_xml_xmlchar_zval(data, 0, parser->target_encoding, &args[2]);
xml_call_handler(parser, &parser->processingInstructionHandler, parser->processingInstructionPtr, 3, args, &retval);
zval_ptr_dtor(&retval);
zend_call_known_fcc(&parser->processingInstructionHandler, /* retval */ NULL, /* param_count */ 3, args, /* named_params */ NULL);
zval_ptr_dtor(&args[0]);
zval_ptr_dtor(&args[1]);
zval_ptr_dtor(&args[2]);
}
/* }}} */
@@ -839,16 +821,18 @@ void _xml_defaultHandler(void *userData, const XML_Char *s, int len)
{
xml_parser *parser = (xml_parser *)userData;
if (!parser || Z_ISUNDEF(parser->defaultHandler)) {
if (!parser || !ZEND_FCC_INITIALIZED(parser->defaultHandler)) {
return;
}
zval retval, args[2];
zval args[2];
ZVAL_COPY(&args[0], &parser->index);
_xml_xmlchar_zval(s, len, parser->target_encoding, &args[1]);
xml_call_handler(parser, &parser->defaultHandler, parser->defaultPtr, 2, args, &retval);
zval_ptr_dtor(&retval);
zend_call_known_fcc(&parser->defaultHandler, /* retval */ NULL, /* param_count */ 2, args, /* named_params */ NULL);
zval_ptr_dtor(&args[0]);
zval_ptr_dtor(&args[1]);
}
/* }}} */
@@ -859,11 +843,11 @@ void _xml_unparsedEntityDeclHandler(void *userData,
{
xml_parser *parser = (xml_parser *)userData;
if (!parser || Z_ISUNDEF(parser->unparsedEntityDeclHandler)) {
if (!parser || !ZEND_FCC_INITIALIZED(parser->unparsedEntityDeclHandler)) {
return;
}
zval retval, args[6];
zval args[6];
ZVAL_COPY(&args[0], &parser->index);
_xml_xmlchar_zval(entityName, 0, parser->target_encoding, &args[1]);
@@ -871,8 +855,14 @@ void _xml_unparsedEntityDeclHandler(void *userData,
_xml_xmlchar_zval(systemId, 0, parser->target_encoding, &args[3]);
_xml_xmlchar_zval(publicId, 0, parser->target_encoding, &args[4]);
_xml_xmlchar_zval(notationName, 0, parser->target_encoding, &args[5]);
xml_call_handler(parser, &parser->unparsedEntityDeclHandler, parser->unparsedEntityDeclPtr, 6, args, &retval);
zval_ptr_dtor(&retval);
zend_call_known_fcc(&parser->unparsedEntityDeclHandler, /* retval */ NULL, /* param_count */ 6, args, /* named_params */ NULL);
zval_ptr_dtor(&args[0]);
zval_ptr_dtor(&args[1]);
zval_ptr_dtor(&args[2]);
zval_ptr_dtor(&args[3]);
zval_ptr_dtor(&args[4]);
zval_ptr_dtor(&args[5]);
}
/* }}} */
@@ -882,19 +872,24 @@ void _xml_notationDeclHandler(void *userData, const XML_Char *notationName,
{
xml_parser *parser = (xml_parser *)userData;
if (!parser || Z_ISUNDEF(parser->notationDeclHandler)) {
if (!parser || !ZEND_FCC_INITIALIZED(parser->notationDeclHandler)) {
return;
}
zval retval, args[5];
zval args[5];
ZVAL_COPY(&args[0], &parser->index);
_xml_xmlchar_zval(notationName, 0, parser->target_encoding, &args[1]);
_xml_xmlchar_zval(base, 0, parser->target_encoding, &args[2]);
_xml_xmlchar_zval(systemId, 0, parser->target_encoding, &args[3]);
_xml_xmlchar_zval(publicId, 0, parser->target_encoding, &args[4]);
xml_call_handler(parser, &parser->notationDeclHandler, parser->notationDeclPtr, 5, args, &retval);
zval_ptr_dtor(&retval);
zend_call_known_fcc(&parser->notationDeclHandler, /* retval */ NULL, /* param_count */ 5, args, /* named_params */ NULL);
zval_ptr_dtor(&args[0]);
zval_ptr_dtor(&args[1]);
zval_ptr_dtor(&args[2]);
zval_ptr_dtor(&args[3]);
zval_ptr_dtor(&args[4]);
}
/* }}} */
@@ -904,26 +899,34 @@ int _xml_externalEntityRefHandler(XML_Parser parserPtr, const XML_Char *openEnti
{
xml_parser *parser = XML_GetUserData(parserPtr);
if (!parser || Z_ISUNDEF(parser->externalEntityRefHandler)) {
if (!parser || !ZEND_FCC_INITIALIZED(parser->externalEntityRefHandler)) {
return 0;
}
int ret = 0; /* abort if no handler is set (should be configurable?) */
zval retval, args[5];
zval args[5];
zval retval;
ZVAL_COPY(&args[0], &parser->index);
_xml_xmlchar_zval(openEntityNames, 0, parser->target_encoding, &args[1]);
_xml_xmlchar_zval(base, 0, parser->target_encoding, &args[2]);
_xml_xmlchar_zval(systemId, 0, parser->target_encoding, &args[3]);
_xml_xmlchar_zval(publicId, 0, parser->target_encoding, &args[4]);
xml_call_handler(parser, &parser->externalEntityRefHandler, parser->externalEntityRefPtr, 5, args, &retval);
zend_call_known_fcc(&parser->externalEntityRefHandler, /* retval */ &retval, /* param_count */ 5, args, /* named_params */ NULL);
zval_ptr_dtor(&args[0]);
zval_ptr_dtor(&args[1]);
zval_ptr_dtor(&args[2]);
zval_ptr_dtor(&args[3]);
zval_ptr_dtor(&args[4]);
/* TODO Better handling from callable return value */
if (!Z_ISUNDEF(retval)) {
convert_to_long(&retval);
ret = Z_LVAL(retval);
} else {
ret = 0;
}
return ret;
}
/* }}} */
@@ -933,17 +936,20 @@ void _xml_startNamespaceDeclHandler(void *userData,const XML_Char *prefix, const
{
xml_parser *parser = (xml_parser *)userData;
if (!parser || Z_ISUNDEF(parser->startNamespaceDeclHandler)) {
if (!parser || !ZEND_FCC_INITIALIZED(parser->startNamespaceDeclHandler)) {
return;
}
zval retval, args[3];
zval args[3];
ZVAL_COPY(&args[0], &parser->index);
_xml_xmlchar_zval(prefix, 0, parser->target_encoding, &args[1]);
_xml_xmlchar_zval(uri, 0, parser->target_encoding, &args[2]);
xml_call_handler(parser, &parser->startNamespaceDeclHandler, parser->startNamespaceDeclPtr, 3, args, &retval);
zval_ptr_dtor(&retval);
zend_call_known_fcc(&parser->startNamespaceDeclHandler, /* retval */ NULL, /* param_count */ 3, args, /* named_params */ NULL);
zval_ptr_dtor(&args[0]);
zval_ptr_dtor(&args[1]);
zval_ptr_dtor(&args[2]);
}
/* }}} */
@@ -952,16 +958,18 @@ void _xml_endNamespaceDeclHandler(void *userData, const XML_Char *prefix)
{
xml_parser *parser = (xml_parser *)userData;
if (!parser || Z_ISUNDEF(parser->endNamespaceDeclHandler)) {
if (!parser || !ZEND_FCC_INITIALIZED(parser->endNamespaceDeclHandler)) {
return;
}
zval retval, args[2];
zval args[2];
ZVAL_COPY(&args[0], &parser->index);
_xml_xmlchar_zval(prefix, 0, parser->target_encoding, &args[1]);
xml_call_handler(parser, &parser->endNamespaceDeclHandler, parser->endNamespaceDeclPtr, 2, args, &retval);
zval_ptr_dtor(&retval);
zend_call_known_fcc(&parser->endNamespaceDeclHandler, /* retval */ NULL, /* param_count */ 2, args, /* named_params */ NULL);
zval_ptr_dtor(&args[0]);
zval_ptr_dtor(&args[1]);
}
/* }}} */
@@ -1036,20 +1044,95 @@ PHP_FUNCTION(xml_parser_create_ns)
}
/* }}} */
static bool php_xml_check_string_method_arg(
unsigned int arg_num,
zend_object *object,
zend_string *method_name,
zend_fcall_info_cache *const parser_handler_fcc
) {
if (ZSTR_LEN(method_name) == 0) {
ZEND_ASSERT(arg_num != 0);
/* Unset handler */
return true;
}
if (!object) {
ZEND_ASSERT(arg_num != 0);
zend_argument_value_error(arg_num, "an object must be set via xml_set_object() to be able to lookup method");
return false;
}
zend_class_entry *ce = object->ce;
zend_string *lc_name = zend_string_tolower(method_name);
zend_function *method_ptr = zend_hash_find_ptr(&ce->function_table, lc_name);
zend_string_release_ex(lc_name, 0);
if (!method_ptr) {
if (arg_num) {
zend_argument_value_error(arg_num, "method %s::%s() does not exist", ZSTR_VAL(ce->name), ZSTR_VAL(method_name));
}
return false;
}
parser_handler_fcc->function_handler = method_ptr;
/* We set the calling scope to NULL to be able to differentiate a "method" set from a proper callable */
parser_handler_fcc->calling_scope = NULL;
parser_handler_fcc->called_scope = ce;
parser_handler_fcc->object = object;
return true;
}
#define PHP_XML_CHECK_NEW_THIS_METHODS(parser_to_check, new_this_obj, fcc_field, handler_set_method) \
if ( \
ZEND_FCC_INITIALIZED(parser_to_check->fcc_field) \
&& parser_to_check->fcc_field.object == parser_to_check->object \
&& parser_to_check->fcc_field.calling_scope == NULL \
) { \
zend_string *method_name = zend_string_copy(parser_to_check->fcc_field.function_handler->common.function_name); \
zend_fcc_dtor(&parser_to_check->fcc_field); \
bool status = php_xml_check_string_method_arg(0, new_this_obj, method_name, &parser_to_check->fcc_field); \
if (status == false) { \
zend_argument_value_error(2, "cannot safely swap to object of class %s as method \"%s\" does not exist, which was set via " handler_set_method, \
ZSTR_VAL(new_this_obj->ce->name), ZSTR_VAL(method_name)); \
zend_string_release(method_name); \
RETURN_THROWS(); \
} \
zend_string_release(method_name); \
zend_fcc_addref(&parser_to_check->fcc_field); \
}
/* {{{ Set up object which should be used for callbacks */
PHP_FUNCTION(xml_set_object)
{
xml_parser *parser;
zval *pind, *mythis;
zend_object *new_this;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "Oo", &pind, xml_parser_ce, &mythis) == FAILURE) {
RETURN_THROWS();
}
parser = Z_XMLPARSER_P(pind);
new_this = Z_OBJ_P(mythis);
zval_ptr_dtor(&parser->object);
ZVAL_OBJ_COPY(&parser->object, Z_OBJ_P(mythis));
if (parser->object) {
PHP_XML_CHECK_NEW_THIS_METHODS(parser, new_this, startElementHandler, "xml_set_element_handler()");
PHP_XML_CHECK_NEW_THIS_METHODS(parser, new_this, endElementHandler, "xml_set_element_handler()");
PHP_XML_CHECK_NEW_THIS_METHODS(parser, new_this, characterDataHandler, "xml_set_character_data_handler()");
PHP_XML_CHECK_NEW_THIS_METHODS(parser, new_this, processingInstructionHandler, "xml_set_processing_instruction_handler()");
PHP_XML_CHECK_NEW_THIS_METHODS(parser, new_this, defaultHandler, "xml_set_default_handler()");
PHP_XML_CHECK_NEW_THIS_METHODS(parser, new_this, unparsedEntityDeclHandler, "xml_set_unparsed_entity_decl_handler()");
PHP_XML_CHECK_NEW_THIS_METHODS(parser, new_this, notationDeclHandler, "xml_set_notation_decl_handler()");
PHP_XML_CHECK_NEW_THIS_METHODS(parser, new_this, externalEntityRefHandler, "xml_set_external_entity_ref_handler()");
PHP_XML_CHECK_NEW_THIS_METHODS(parser, new_this, startNamespaceDeclHandler, "xml_set_start_namespace_decl_handler()");
PHP_XML_CHECK_NEW_THIS_METHODS(parser, new_this, endNamespaceDeclHandler, "xml_set_end_namespace_decl_handler()");
OBJ_RELEASE(parser->object);
}
parser->object = new_this;
GC_ADDREF(parser->object);
RETURN_TRUE;
}
@@ -1059,164 +1142,139 @@ PHP_FUNCTION(xml_set_object)
PHP_FUNCTION(xml_set_element_handler)
{
xml_parser *parser;
zval *pind, *shdl, *ehdl;
zval *pind;
zend_fcall_info start_fci = {0};
zend_fcall_info_cache start_fcc = {0};
zend_fcall_info end_fci = {0};
zend_fcall_info_cache end_fcc = {0};
zend_string *start_method_name = NULL;
zend_string *end_method_name = NULL;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "Ozz", &pind, xml_parser_ce, &shdl, &ehdl) == FAILURE) {
RETURN_THROWS();
if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS(), "OF!F!", &pind, xml_parser_ce, &start_fci, &start_fcc, &end_fci, &end_fcc) == SUCCESS) {
parser = Z_XMLPARSER_P(pind);
goto set_handlers;
}
zend_release_fcall_info_cache(&start_fcc);
zend_release_fcall_info_cache(&end_fcc);
if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS(), "OF!S", &pind, xml_parser_ce, &start_fci, &start_fcc, &end_method_name) == SUCCESS) {
parser = Z_XMLPARSER_P(pind);
bool status = php_xml_check_string_method_arg(3, parser->object, end_method_name, &end_fcc);
if (status == false) {
zend_release_fcall_info_cache(&start_fcc);
zend_release_fcall_info_cache(&end_fcc);
RETURN_THROWS();
}
} else if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS(), "OSF!", &pind, xml_parser_ce, &start_method_name, &end_fci, &end_fcc) == SUCCESS) {
parser = Z_XMLPARSER_P(pind);
bool status = php_xml_check_string_method_arg(2, parser->object, start_method_name, &start_fcc);
if (status == false) {
zend_release_fcall_info_cache(&start_fcc);
zend_release_fcall_info_cache(&end_fcc);
RETURN_THROWS();
}
} else if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS(), "OSS", &pind, xml_parser_ce, &start_method_name, &end_method_name) == SUCCESS) {
zend_release_fcall_info_cache(&start_fcc);
zend_release_fcall_info_cache(&end_fcc);
parser = Z_XMLPARSER_P(pind);
bool status = php_xml_check_string_method_arg(2, parser->object, start_method_name, &start_fcc);
if (status == false) {
RETURN_THROWS();
}
status = php_xml_check_string_method_arg(3, parser->object, end_method_name, &end_fcc);
if (status == false) {
RETURN_THROWS();
}
} else {
zval *dummy_start;
zval *dummy_end;
zend_release_fcall_info_cache(&start_fcc);
zend_release_fcall_info_cache(&end_fcc);
if (zend_parse_parameters(ZEND_NUM_ARGS(), "Ozz", &pind, xml_parser_ce, &dummy_start, &dummy_end) == FAILURE) {
RETURN_THROWS();
} else {
switch (Z_TYPE_P(dummy_start)) {
case IS_NULL:
case IS_STRING:
break;
default:
zend_argument_type_error(2, "must be of type callable|string|null");
RETURN_THROWS();
}
zend_argument_type_error(3, "must be of type callable|string|null");
RETURN_THROWS();
}
}
parser = Z_XMLPARSER_P(pind);
xml_set_handler(&parser->startElementHandler, shdl);
xml_set_handler(&parser->endElementHandler, ehdl);
set_handlers:
xml_set_handler(&parser->startElementHandler, &start_fcc);
xml_set_handler(&parser->endElementHandler, &end_fcc);
XML_SetElementHandler(parser->parser, _xml_startElementHandler, _xml_endElementHandler);
RETURN_TRUE;
}
/* }}} */
/* {{{ Set up character data handler */
PHP_FUNCTION(xml_set_character_data_handler)
{
xml_parser *parser;
zval *pind, *hdl;
static void php_xml_set_handler_parse_callable(
INTERNAL_FUNCTION_PARAMETERS,
xml_parser **const parser,
zend_fcall_info_cache *const parser_handler_fcc
) {
zval *pind;
zend_fcall_info handler_fci = {0};
zend_fcall_info_cache handler_fcc = {0};
zend_string *method_name = NULL;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "Oz", &pind, xml_parser_ce, &hdl) == FAILURE) {
if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS(), "OF!", &pind, xml_parser_ce, &handler_fci, &handler_fcc) == SUCCESS) {
*parser = Z_XMLPARSER_P(pind);
if (!ZEND_FCI_INITIALIZED(handler_fci)) {
/* Free handler, so just return and a uninitialized FCC communicates this */
return;
}
memcpy(parser_handler_fcc, &handler_fcc, sizeof(zend_fcall_info_cache));
} else if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS(), "OS", &pind, xml_parser_ce, &method_name) == SUCCESS) {
*parser = Z_XMLPARSER_P(pind);
bool status = php_xml_check_string_method_arg(2, (*parser)->object, method_name, parser_handler_fcc);
if (status == false) {
RETURN_THROWS();
}
} else {
zval *dummy;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "Oz", &pind, xml_parser_ce, &dummy) == FAILURE) {
RETURN_THROWS();
}
zend_argument_type_error(2, "must be of type callable|string|null");
RETURN_THROWS();
}
parser = Z_XMLPARSER_P(pind);
xml_set_handler(&parser->characterDataHandler, hdl);
XML_SetCharacterDataHandler(parser->parser, _xml_characterDataHandler);
RETURN_TRUE;
}
/* }}} */
/* {{{ Set up processing instruction (PI) handler */
PHP_FUNCTION(xml_set_processing_instruction_handler)
{
xml_parser *parser;
zval *pind, *hdl;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "Oz", &pind, xml_parser_ce, &hdl) == FAILURE) {
RETURN_THROWS();
#define XML_SET_HANDLER_PHP_FUNCTION(function_name, parser_handler_name, parse_function, c_function) \
PHP_FUNCTION(function_name) \
{ \
xml_parser *parser = NULL; \
zend_fcall_info_cache handler_fcc = {0}; \
php_xml_set_handler_parse_callable(INTERNAL_FUNCTION_PARAM_PASSTHRU, &parser, &handler_fcc); \
if (EG(exception)) { return; } \
ZEND_ASSERT(parser); \
xml_set_handler(&parser->parser_handler_name, &handler_fcc); \
parse_function(parser->parser, c_function); \
RETURN_TRUE; \
}
parser = Z_XMLPARSER_P(pind);
xml_set_handler(&parser->processingInstructionHandler, hdl);
XML_SetProcessingInstructionHandler(parser->parser, _xml_processingInstructionHandler);
RETURN_TRUE;
}
/* }}} */
/* {{{ Set up default handler */
PHP_FUNCTION(xml_set_default_handler)
{
xml_parser *parser;
zval *pind, *hdl;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "Oz", &pind, xml_parser_ce, &hdl) == FAILURE) {
RETURN_THROWS();
}
parser = Z_XMLPARSER_P(pind);
xml_set_handler(&parser->defaultHandler, hdl);
XML_SetDefaultHandler(parser->parser, _xml_defaultHandler);
RETURN_TRUE;
}
/* }}} */
/* {{{ Set up unparsed entity declaration handler */
PHP_FUNCTION(xml_set_unparsed_entity_decl_handler)
{
xml_parser *parser;
zval *pind, *hdl;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "Oz", &pind, xml_parser_ce, &hdl) == FAILURE) {
RETURN_THROWS();
}
parser = Z_XMLPARSER_P(pind);
xml_set_handler(&parser->unparsedEntityDeclHandler, hdl);
XML_SetUnparsedEntityDeclHandler(parser->parser, _xml_unparsedEntityDeclHandler);
RETURN_TRUE;
}
/* }}} */
/* {{{ Set up notation declaration handler */
PHP_FUNCTION(xml_set_notation_decl_handler)
{
xml_parser *parser;
zval *pind, *hdl;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "Oz", &pind, xml_parser_ce, &hdl) == FAILURE) {
RETURN_THROWS();
}
parser = Z_XMLPARSER_P(pind);
xml_set_handler(&parser->notationDeclHandler, hdl);
XML_SetNotationDeclHandler(parser->parser, _xml_notationDeclHandler);
RETURN_TRUE;
}
/* }}} */
/* {{{ Set up external entity reference handler */
PHP_FUNCTION(xml_set_external_entity_ref_handler)
{
xml_parser *parser;
zval *pind, *hdl;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "Oz", &pind, xml_parser_ce, &hdl) == FAILURE) {
RETURN_THROWS();
}
parser = Z_XMLPARSER_P(pind);
xml_set_handler(&parser->externalEntityRefHandler, hdl);
XML_SetExternalEntityRefHandler(parser->parser, (void *) _xml_externalEntityRefHandler);
RETURN_TRUE;
}
/* }}} */
/* {{{ Set up character data handler */
PHP_FUNCTION(xml_set_start_namespace_decl_handler)
{
xml_parser *parser;
zval *pind, *hdl;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "Oz", &pind, xml_parser_ce, &hdl) == FAILURE) {
RETURN_THROWS();
}
parser = Z_XMLPARSER_P(pind);
xml_set_handler(&parser->startNamespaceDeclHandler, hdl);
XML_SetStartNamespaceDeclHandler(parser->parser, _xml_startNamespaceDeclHandler);
RETURN_TRUE;
}
/* }}} */
/* {{{ Set up character data handler */
PHP_FUNCTION(xml_set_end_namespace_decl_handler)
{
xml_parser *parser;
zval *pind, *hdl;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "Oz", &pind, xml_parser_ce, &hdl) == FAILURE) {
RETURN_THROWS();
}
parser = Z_XMLPARSER_P(pind);
xml_set_handler(&parser->endNamespaceDeclHandler, hdl);
XML_SetEndNamespaceDeclHandler(parser->parser, _xml_endNamespaceDeclHandler);
RETURN_TRUE;
}
/* }}} */
XML_SET_HANDLER_PHP_FUNCTION(xml_set_character_data_handler, characterDataHandler, XML_SetCharacterDataHandler, _xml_characterDataHandler);
XML_SET_HANDLER_PHP_FUNCTION(xml_set_processing_instruction_handler, processingInstructionHandler, XML_SetProcessingInstructionHandler, _xml_processingInstructionHandler);
XML_SET_HANDLER_PHP_FUNCTION(xml_set_default_handler, defaultHandler, XML_SetDefaultHandler, _xml_defaultHandler);
XML_SET_HANDLER_PHP_FUNCTION(xml_set_unparsed_entity_decl_handler, unparsedEntityDeclHandler, XML_SetUnparsedEntityDeclHandler, _xml_unparsedEntityDeclHandler);
XML_SET_HANDLER_PHP_FUNCTION(xml_set_notation_decl_handler, notationDeclHandler, XML_SetNotationDeclHandler, _xml_notationDeclHandler);
XML_SET_HANDLER_PHP_FUNCTION(xml_set_external_entity_ref_handler, externalEntityRefHandler, XML_SetExternalEntityRefHandler, (void *) _xml_externalEntityRefHandler);
XML_SET_HANDLER_PHP_FUNCTION(xml_set_start_namespace_decl_handler, startNamespaceDeclHandler, XML_SetStartNamespaceDeclHandler, _xml_startNamespaceDeclHandler);
XML_SET_HANDLER_PHP_FUNCTION(xml_set_end_namespace_decl_handler, endNamespaceDeclHandler, XML_SetEndNamespaceDeclHandler, _xml_endNamespaceDeclHandler);
/* {{{ Start parsing an XML document */
PHP_FUNCTION(xml_parse)

View File

@@ -146,35 +146,23 @@ function xml_parser_create_ns(?string $encoding = null, string $separator = ":")
function xml_set_object(XMLParser $parser, object $object): true {}
/**
* @param callable $start_handler
* @param callable $end_handler
*/
function xml_set_element_handler(XMLParser $parser, $start_handler, $end_handler): true {}
function xml_set_element_handler(XMLParser $parser, callable|string|null $start_handler, callable|string|null $end_handler): true {}
/** @param callable $handler */
function xml_set_character_data_handler(XMLParser $parser, $handler): true {}
function xml_set_character_data_handler(XMLParser $parser, callable|string|null $handler): true {}
/** @param callable $handler */
function xml_set_processing_instruction_handler(XMLParser $parser, $handler): true {}
function xml_set_processing_instruction_handler(XMLParser $parser, callable|string|null $handler): true {}
/** @param callable $handler */
function xml_set_default_handler(XMLParser $parser, $handler): true {}
function xml_set_default_handler(XMLParser $parser, callable|string|null $handler): true {}
/** @param callable $handler */
function xml_set_unparsed_entity_decl_handler(XMLParser $parser, $handler): true {}
function xml_set_unparsed_entity_decl_handler(XMLParser $parser, callable|string|null $handler): true {}
/** @param callable $handler */
function xml_set_notation_decl_handler(XMLParser $parser, $handler): true {}
function xml_set_notation_decl_handler(XMLParser $parser, callable|string|null $handler): true {}
/** @param callable $handler */
function xml_set_external_entity_ref_handler(XMLParser $parser, $handler): true {}
function xml_set_external_entity_ref_handler(XMLParser $parser, callable|string|null $handler): true {}
/** @param callable $handler */
function xml_set_start_namespace_decl_handler(XMLParser $parser, $handler): true {}
function xml_set_start_namespace_decl_handler(XMLParser $parser, callable|string|null $handler): true {}
/** @param callable $handler */
function xml_set_end_namespace_decl_handler(XMLParser $parser, $handler): true {}
function xml_set_end_namespace_decl_handler(XMLParser $parser, callable|string|null $handler): true {}
function xml_parse(XMLParser $parser, string $data, bool $is_final = false): int {}

8
ext/xml/xml_arginfo.h generated
View File

@@ -1,5 +1,5 @@
/* This is a generated file, edit the .stub.php file instead.
* Stub hash: f87e295b35cd43db72a936ee5745297a45730090 */
* Stub hash: eb168a134e8acf6f19f0cc2c9ddeae95da61045d */
ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_xml_parser_create, 0, 0, XMLParser, 0)
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, encoding, IS_STRING, 1, "null")
@@ -17,13 +17,13 @@ ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_xml_set_element_handler, 0, 3, IS_TRUE, 0)
ZEND_ARG_OBJ_INFO(0, parser, XMLParser, 0)
ZEND_ARG_INFO(0, start_handler)
ZEND_ARG_INFO(0, end_handler)
ZEND_ARG_TYPE_MASK(0, start_handler, MAY_BE_CALLABLE|MAY_BE_STRING|MAY_BE_NULL, NULL)
ZEND_ARG_TYPE_MASK(0, end_handler, MAY_BE_CALLABLE|MAY_BE_STRING|MAY_BE_NULL, NULL)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_xml_set_character_data_handler, 0, 2, IS_TRUE, 0)
ZEND_ARG_OBJ_INFO(0, parser, XMLParser, 0)
ZEND_ARG_INFO(0, handler)
ZEND_ARG_TYPE_MASK(0, handler, MAY_BE_CALLABLE|MAY_BE_STRING|MAY_BE_NULL, NULL)
ZEND_END_ARG_INFO()
#define arginfo_xml_set_processing_instruction_handler arginfo_xml_set_character_data_handler