From 674210aca3a4c34ff4c2904e4993e4325dcdac3e Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Wed, 4 Mar 2020 10:53:47 +0100 Subject: [PATCH] Make SimpleXMLElement a RecursiveIterator Context: https://externals.io/message/108789 This essentially moves the functionality of SimpleXMLIterator into SimpleXMLElement, and makes SimpleXMLIterator a no-op extension. Ideally SimpleXMLElement would be an IteratorAggregate, whose getIterator() method returns SimpleXMLIterator. However, because SimpleXMLIterator extends SimpleXMLElement (and code depends on this in non-trivial ways), this is not possible. The only way to not keep SimpleXMLElement as a magic Traversable (that implements neither Iterator nor IteratorAggregate) is to move the SimpleXMLIterator functionality into it. --- ext/simplexml/config.m4 | 2 +- ext/simplexml/config.w32 | 2 +- ext/simplexml/php_simplexml.h | 3 + ext/simplexml/simplexml.c | 155 ++++++++++++++++++++++-- ext/simplexml/simplexml.stub.php | 27 ++++- ext/simplexml/simplexml_arginfo.h | 33 ++++++ ext/simplexml/sxe.c | 190 ------------------------------ ext/simplexml/sxe.h | 27 ----- ext/simplexml/sxe.stub.php | 27 ----- ext/simplexml/sxe_arginfo.h | 37 ------ 10 files changed, 211 insertions(+), 292 deletions(-) delete mode 100644 ext/simplexml/sxe.c delete mode 100644 ext/simplexml/sxe.h delete mode 100644 ext/simplexml/sxe.stub.php delete mode 100644 ext/simplexml/sxe_arginfo.h diff --git a/ext/simplexml/config.m4 b/ext/simplexml/config.m4 index 1acd1898c1cc..8b8a6f814a1e 100644 --- a/ext/simplexml/config.m4 +++ b/ext/simplexml/config.m4 @@ -12,7 +12,7 @@ if test "$PHP_SIMPLEXML" != "no"; then PHP_SETUP_LIBXML(SIMPLEXML_SHARED_LIBADD, [ AC_DEFINE(HAVE_SIMPLEXML,1,[ ]) - PHP_NEW_EXTENSION(simplexml, simplexml.c sxe.c, $ext_shared) + PHP_NEW_EXTENSION(simplexml, simplexml.c, $ext_shared) PHP_INSTALL_HEADERS([ext/simplexml/php_simplexml.h ext/simplexml/php_simplexml_exports.h]) PHP_SUBST(SIMPLEXML_SHARED_LIBADD) ]) diff --git a/ext/simplexml/config.w32 b/ext/simplexml/config.w32 index a95be657384d..a6011fedc20c 100644 --- a/ext/simplexml/config.w32 +++ b/ext/simplexml/config.w32 @@ -7,7 +7,7 @@ if (PHP_SIMPLEXML == "yes") { ADD_EXTENSION_DEP('simplexml', 'libxml') && CHECK_HEADER_ADD_INCLUDE("libxml/tree.h", "CFLAGS_SIMPLEXML", PHP_PHP_BUILD + "\\include\\libxml2") ) { - EXTENSION("simplexml", "simplexml.c sxe.c"); + EXTENSION("simplexml", "simplexml.c"); AC_DEFINE("HAVE_SIMPLEXML", 1, "Simple XML support"); if (!PHP_SIMPLEXML_SHARED) { ADD_FLAG("CFLAGS_SIMPLEXML", "/D LIBXML_STATIC"); diff --git a/ext/simplexml/php_simplexml.h b/ext/simplexml/php_simplexml.h index 164525d7b63b..45ebff627978 100644 --- a/ext/simplexml/php_simplexml.h +++ b/ext/simplexml/php_simplexml.h @@ -77,6 +77,9 @@ typedef struct { # define PHP_SXE_API ZEND_API #endif +extern PHP_SXE_API zend_class_entry *ce_SimpleXMLIterator; +extern PHP_SXE_API zend_class_entry *ce_SimpleXMLElement; + PHP_SXE_API zend_class_entry *sxe_get_element_class_entry(); #endif diff --git a/ext/simplexml/simplexml.c b/ext/simplexml/simplexml.c index 58a18d3c0dd8..cdb900db6264 100644 --- a/ext/simplexml/simplexml.c +++ b/ext/simplexml/simplexml.c @@ -31,9 +31,11 @@ #include "simplexml_arginfo.h" #include "zend_exceptions.h" #include "zend_interfaces.h" -#include "sxe.h" +#include "ext/spl/spl_iterators.h" zend_class_entry *sxe_class_entry = NULL; +PHP_SXE_API zend_class_entry *ce_SimpleXMLIterator; +PHP_SXE_API zend_class_entry *ce_SimpleXMLElement; PHP_SXE_API zend_class_entry *sxe_get_element_class_entry() /* {{{ */ { @@ -2024,6 +2026,138 @@ SXE_METHOD(count) } /* }}} */ + +/* {{{ proto void SimpleXMLElement::rewind() + Rewind to first element */ +SXE_METHOD(rewind) +{ + if (zend_parse_parameters_none() == FAILURE) { + RETURN_THROWS(); + } + + php_sxe_rewind_iterator(Z_SXEOBJ_P(ZEND_THIS)); +} +/* }}} */ + +/* {{{ proto bool SimpleXMLElement::valid() + Check whether iteration is valid */ +SXE_METHOD(valid) +{ + php_sxe_object *sxe = Z_SXEOBJ_P(ZEND_THIS); + + if (zend_parse_parameters_none() == FAILURE) { + RETURN_THROWS(); + } + + RETURN_BOOL(!Z_ISUNDEF(sxe->iter.data)); +} +/* }}} */ + +/* {{{ proto SimpleXMLElement SimpleXMLElement::current() + Get current element */ +SXE_METHOD(current) +{ + php_sxe_object *sxe = Z_SXEOBJ_P(ZEND_THIS); + zval *data; + + if (zend_parse_parameters_none() == FAILURE) { + RETURN_THROWS(); + } + + if (Z_ISUNDEF(sxe->iter.data)) { + return; /* return NULL */ + } + + data = &sxe->iter.data; + ZVAL_COPY_DEREF(return_value, data); +} +/* }}} */ + +/* {{{ proto string SimpleXMLElement::key() + Get name of current child element */ +SXE_METHOD(key) +{ + xmlNodePtr curnode; + php_sxe_object *intern; + php_sxe_object *sxe = Z_SXEOBJ_P(ZEND_THIS); + + if (zend_parse_parameters_none() == FAILURE) { + RETURN_THROWS(); + } + + if (Z_ISUNDEF(sxe->iter.data)) { + RETURN_FALSE; + } + + intern = Z_SXEOBJ_P(&sxe->iter.data); + if (intern != NULL && intern->node != NULL) { + curnode = (xmlNodePtr)((php_libxml_node_ptr *)intern->node)->node; + RETURN_STRINGL((char*)curnode->name, xmlStrlen(curnode->name)); + } + + RETURN_FALSE; +} +/* }}} */ + +/* {{{ proto void SimpleXMLElement::next() + Move to next element */ +SXE_METHOD(next) +{ + if (zend_parse_parameters_none() == FAILURE) { + RETURN_THROWS(); + } + + php_sxe_move_forward_iterator(Z_SXEOBJ_P(ZEND_THIS)); +} +/* }}} */ + +/* {{{ proto bool SimpleXMLElement::hasChildren() + Check whether element has children (elements) */ +SXE_METHOD(hasChildren) +{ + php_sxe_object *sxe = Z_SXEOBJ_P(ZEND_THIS); + php_sxe_object *child; + xmlNodePtr node; + + if (zend_parse_parameters_none() == FAILURE) { + RETURN_THROWS(); + } + + if (Z_ISUNDEF(sxe->iter.data) || sxe->iter.type == SXE_ITER_ATTRLIST) { + RETURN_FALSE; + } + child = Z_SXEOBJ_P(&sxe->iter.data); + + GET_NODE(child, node); + if (node) { + node = node->children; + } + while (node && node->type != XML_ELEMENT_NODE) { + node = node->next; + } + RETURN_BOOL(node ? 1 : 0); +} +/* }}} */ + +/* {{{ proto SimpleXMLElement SimpleXMLElement::getChildren() + Get child element iterator */ +SXE_METHOD(getChildren) +{ + php_sxe_object *sxe = Z_SXEOBJ_P(ZEND_THIS); + zval *data; + + if (zend_parse_parameters_none() == FAILURE) { + RETURN_THROWS(); + } + + if (Z_ISUNDEF(sxe->iter.data) || sxe->iter.type == SXE_ITER_ATTRLIST) { + return; /* return NULL */ + } + + data = &sxe->iter.data; + ZVAL_COPY_DEREF(return_value, data); +} + static zend_object_handlers sxe_object_handlers; /* {{{ sxe_object_clone() @@ -2606,13 +2740,14 @@ ZEND_GET_MODULE(simplexml) */ PHP_MINIT_FUNCTION(simplexml) { - zend_class_entry sxe; + zend_class_entry ce; - INIT_CLASS_ENTRY(sxe, "SimpleXMLElement", class_SimpleXMLElement_methods); - sxe.create_object = sxe_object_new; - sxe_class_entry = zend_register_internal_class(&sxe); + INIT_CLASS_ENTRY(ce, "SimpleXMLElement", class_SimpleXMLElement_methods); + sxe_class_entry = zend_register_internal_class(&ce); + sxe_class_entry->create_object = sxe_object_new; sxe_class_entry->get_iterator = php_sxe_get_iterator; - zend_class_implements(sxe_class_entry, 3, zend_ce_traversable, zend_ce_countable, zend_ce_stringable); + zend_class_implements(sxe_class_entry, 3, + zend_ce_countable, zend_ce_stringable, spl_ce_RecursiveIterator); memcpy(&sxe_object_handlers, &std_object_handlers, sizeof(zend_object_handlers)); sxe_object_handlers.offset = XtOffsetOf(php_sxe_object, zo); @@ -2639,9 +2774,13 @@ PHP_MINIT_FUNCTION(simplexml) sxe_class_entry->serialize = zend_class_serialize_deny; sxe_class_entry->unserialize = zend_class_unserialize_deny; - php_libxml_register_export(sxe_class_entry, simplexml_export_node); + /* TODO: Why do we have two variables for this? */ + ce_SimpleXMLElement = sxe_class_entry; - PHP_MINIT(sxe)(INIT_FUNC_ARGS_PASSTHRU); + INIT_CLASS_ENTRY(ce, "SimpleXMLIterator", NULL); + ce_SimpleXMLIterator = zend_register_internal_class_ex(&ce, ce_SimpleXMLElement); + + php_libxml_register_export(sxe_class_entry, simplexml_export_node); return SUCCESS; } diff --git a/ext/simplexml/simplexml.stub.php b/ext/simplexml/simplexml.stub.php index 68eaf2be6e25..2922adce4bc3 100644 --- a/ext/simplexml/simplexml.stub.php +++ b/ext/simplexml/simplexml.stub.php @@ -8,7 +8,7 @@ function simplexml_load_string(string $data, ?string $class_name = SimpleXMLElem function simplexml_import_dom(DOMNode $node, ?string $class_name = SimpleXMLElement::class): ?SimpleXMLElement {} -class SimpleXMLElement implements Stringable +class SimpleXMLElement implements Stringable, Countable, RecursiveIterator { /** @return array|false */ public function xpath(string $path) {} @@ -52,4 +52,29 @@ public function __toString(): string {} /** @return int */ public function count() {} + + /** @return void */ + public function rewind() {} + + /** @return bool */ + public function valid() {} + + /** @return ?SimpleXMLElement */ + public function current() {} + + /** @return string|false */ + public function key() {} + + /** @return void */ + public function next() {} + + /** @return bool */ + public function hasChildren() {} + + /** @return ?SimpleXMLElement */ + public function getChildren() {} +} + +class SimpleXMLIterator extends SimpleXMLElement +{ } diff --git a/ext/simplexml/simplexml_arginfo.h b/ext/simplexml/simplexml_arginfo.h index 1a3845076495..dd9914ccfd00 100644 --- a/ext/simplexml/simplexml_arginfo.h +++ b/ext/simplexml/simplexml_arginfo.h @@ -76,6 +76,20 @@ ZEND_END_ARG_INFO() #define arginfo_class_SimpleXMLElement_count arginfo_class_SimpleXMLElement_getName +#define arginfo_class_SimpleXMLElement_rewind arginfo_class_SimpleXMLElement_getName + +#define arginfo_class_SimpleXMLElement_valid arginfo_class_SimpleXMLElement_getName + +#define arginfo_class_SimpleXMLElement_current arginfo_class_SimpleXMLElement_getName + +#define arginfo_class_SimpleXMLElement_key arginfo_class_SimpleXMLElement_getName + +#define arginfo_class_SimpleXMLElement_next arginfo_class_SimpleXMLElement_getName + +#define arginfo_class_SimpleXMLElement_hasChildren arginfo_class_SimpleXMLElement_getName + +#define arginfo_class_SimpleXMLElement_getChildren arginfo_class_SimpleXMLElement_getName + ZEND_FUNCTION(simplexml_load_file); ZEND_FUNCTION(simplexml_load_string); @@ -93,6 +107,13 @@ ZEND_METHOD(SimpleXMLElement, addAttribute); ZEND_METHOD(SimpleXMLElement, getName); ZEND_METHOD(SimpleXMLElement, __toString); ZEND_METHOD(SimpleXMLElement, count); +ZEND_METHOD(SimpleXMLElement, rewind); +ZEND_METHOD(SimpleXMLElement, valid); +ZEND_METHOD(SimpleXMLElement, current); +ZEND_METHOD(SimpleXMLElement, key); +ZEND_METHOD(SimpleXMLElement, next); +ZEND_METHOD(SimpleXMLElement, hasChildren); +ZEND_METHOD(SimpleXMLElement, getChildren); static const zend_function_entry ext_functions[] = { @@ -118,5 +139,17 @@ static const zend_function_entry class_SimpleXMLElement_methods[] = { ZEND_ME(SimpleXMLElement, getName, arginfo_class_SimpleXMLElement_getName, ZEND_ACC_PUBLIC) ZEND_ME(SimpleXMLElement, __toString, arginfo_class_SimpleXMLElement___toString, ZEND_ACC_PUBLIC) ZEND_ME(SimpleXMLElement, count, arginfo_class_SimpleXMLElement_count, ZEND_ACC_PUBLIC) + ZEND_ME(SimpleXMLElement, rewind, arginfo_class_SimpleXMLElement_rewind, ZEND_ACC_PUBLIC) + ZEND_ME(SimpleXMLElement, valid, arginfo_class_SimpleXMLElement_valid, ZEND_ACC_PUBLIC) + ZEND_ME(SimpleXMLElement, current, arginfo_class_SimpleXMLElement_current, ZEND_ACC_PUBLIC) + ZEND_ME(SimpleXMLElement, key, arginfo_class_SimpleXMLElement_key, ZEND_ACC_PUBLIC) + ZEND_ME(SimpleXMLElement, next, arginfo_class_SimpleXMLElement_next, ZEND_ACC_PUBLIC) + ZEND_ME(SimpleXMLElement, hasChildren, arginfo_class_SimpleXMLElement_hasChildren, ZEND_ACC_PUBLIC) + ZEND_ME(SimpleXMLElement, getChildren, arginfo_class_SimpleXMLElement_getChildren, ZEND_ACC_PUBLIC) + ZEND_FE_END +}; + + +static const zend_function_entry class_SimpleXMLIterator_methods[] = { ZEND_FE_END }; diff --git a/ext/simplexml/sxe.c b/ext/simplexml/sxe.c deleted file mode 100644 index 66ff567249fc..000000000000 --- a/ext/simplexml/sxe.c +++ /dev/null @@ -1,190 +0,0 @@ -/* - +----------------------------------------------------------------------+ - | Copyright (c) The PHP Group | - +----------------------------------------------------------------------+ - | This source file is subject to version 3.01 of the PHP license, | - | that is bundled with this package in the file LICENSE, and is | - | available through the world-wide-web at the following url: | - | http://www.php.net/license/3_01.txt | - | If you did not receive a copy of the PHP license and are unable to | - | obtain it through the world-wide-web, please send a note to | - | license@php.net so we can mail you a copy immediately. | - +----------------------------------------------------------------------+ - | Authors: Marcus Boerger | - +----------------------------------------------------------------------+ - */ - -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif - -#include "php.h" -#include "php_ini.h" -#include "ext/standard/info.h" -#include "zend_interfaces.h" - -#include "php_simplexml.h" -#include "ext/spl/php_spl.h" -#include "ext/spl/spl_iterators.h" -#include "sxe.h" -#include "sxe_arginfo.h" - -PHP_SXE_API zend_class_entry *ce_SimpleXMLIterator = NULL; -PHP_SXE_API zend_class_entry *ce_SimpleXMLElement; - -#include "php_simplexml_exports.h" - -/* {{{ proto void SimpleXMLIterator::rewind() - Rewind to first element */ -PHP_METHOD(SimpleXMLIterator, rewind) -{ - if (zend_parse_parameters_none() == FAILURE) { - RETURN_THROWS(); - } - - php_sxe_rewind_iterator(Z_SXEOBJ_P(ZEND_THIS)); -} -/* }}} */ - -/* {{{ proto bool SimpleXMLIterator::valid() - Check whether iteration is valid */ -PHP_METHOD(SimpleXMLIterator, valid) -{ - php_sxe_object *sxe = Z_SXEOBJ_P(ZEND_THIS); - - if (zend_parse_parameters_none() == FAILURE) { - RETURN_THROWS(); - } - - RETURN_BOOL(!Z_ISUNDEF(sxe->iter.data)); -} -/* }}} */ - -/* {{{ proto SimpleXMLIterator SimpleXMLIterator::current() - Get current element */ -PHP_METHOD(SimpleXMLIterator, current) -{ - php_sxe_object *sxe = Z_SXEOBJ_P(ZEND_THIS); - zval *data; - - if (zend_parse_parameters_none() == FAILURE) { - RETURN_THROWS(); - } - - if (Z_ISUNDEF(sxe->iter.data)) { - return; /* return NULL */ - } - - data = &sxe->iter.data; - ZVAL_COPY_DEREF(return_value, data); -} -/* }}} */ - -/* {{{ proto string SimpleXMLIterator::key() - Get name of current child element */ -PHP_METHOD(SimpleXMLIterator, key) -{ - xmlNodePtr curnode; - php_sxe_object *intern; - php_sxe_object *sxe = Z_SXEOBJ_P(ZEND_THIS); - - if (zend_parse_parameters_none() == FAILURE) { - RETURN_THROWS(); - } - - if (Z_ISUNDEF(sxe->iter.data)) { - RETURN_FALSE; - } - - intern = Z_SXEOBJ_P(&sxe->iter.data); - if (intern != NULL && intern->node != NULL) { - curnode = (xmlNodePtr)((php_libxml_node_ptr *)intern->node)->node; - RETURN_STRINGL((char*)curnode->name, xmlStrlen(curnode->name)); - } - - RETURN_FALSE; -} -/* }}} */ - -/* {{{ proto void SimpleXMLIterator::next() - Move to next element */ -PHP_METHOD(SimpleXMLIterator, next) -{ - if (zend_parse_parameters_none() == FAILURE) { - RETURN_THROWS(); - } - - php_sxe_move_forward_iterator(Z_SXEOBJ_P(ZEND_THIS)); -} -/* }}} */ - -/* {{{ proto bool SimpleXMLIterator::hasChildren() - Check whether element has children (elements) */ -PHP_METHOD(SimpleXMLIterator, hasChildren) -{ - php_sxe_object *sxe = Z_SXEOBJ_P(ZEND_THIS); - php_sxe_object *child; - xmlNodePtr node; - - if (zend_parse_parameters_none() == FAILURE) { - RETURN_THROWS(); - } - - if (Z_ISUNDEF(sxe->iter.data) || sxe->iter.type == SXE_ITER_ATTRLIST) { - RETURN_FALSE; - } - child = Z_SXEOBJ_P(&sxe->iter.data); - - GET_NODE(child, node); - if (node) { - node = node->children; - } - while (node && node->type != XML_ELEMENT_NODE) { - node = node->next; - } - RETURN_BOOL(node ? 1 : 0); -} -/* }}} */ - -/* {{{ proto SimpleXMLIterator SimpleXMLIterator::getChildren() - Get child element iterator */ -PHP_METHOD(SimpleXMLIterator, getChildren) -{ - php_sxe_object *sxe = Z_SXEOBJ_P(ZEND_THIS); - zval *data; - - if (zend_parse_parameters_none() == FAILURE) { - RETURN_THROWS(); - } - - if (Z_ISUNDEF(sxe->iter.data) || sxe->iter.type == SXE_ITER_ATTRLIST) { - return; /* return NULL */ - } - - data = &sxe->iter.data; - ZVAL_COPY_DEREF(return_value, data); -} - -PHP_MINIT_FUNCTION(sxe) /* {{{ */ -{ - zend_class_entry *pce; - zend_class_entry sxi; - - if ((pce = zend_hash_str_find_ptr(CG(class_table), "simplexmlelement", sizeof("SimpleXMLElement") - 1)) == NULL) { - ce_SimpleXMLElement = NULL; - ce_SimpleXMLIterator = NULL; - return SUCCESS; /* SimpleXML must be initialized before */ - } - - ce_SimpleXMLElement = pce; - - INIT_CLASS_ENTRY_EX(sxi, "SimpleXMLIterator", sizeof("SimpleXMLIterator") - 1, class_SimpleXMLIterator_methods); - ce_SimpleXMLIterator = zend_register_internal_class_ex(&sxi, ce_SimpleXMLElement); - ce_SimpleXMLIterator->create_object = ce_SimpleXMLElement->create_object; - - zend_class_implements(ce_SimpleXMLIterator, 1, spl_ce_RecursiveIterator); - zend_class_implements(ce_SimpleXMLIterator, 1, zend_ce_countable); - - return SUCCESS; -} -/* }}} */ diff --git a/ext/simplexml/sxe.h b/ext/simplexml/sxe.h deleted file mode 100644 index 771ddea4bb87..000000000000 --- a/ext/simplexml/sxe.h +++ /dev/null @@ -1,27 +0,0 @@ -/* - +----------------------------------------------------------------------+ - | Copyright (c) The PHP Group | - +----------------------------------------------------------------------+ - | This source file is subject to version 3.01 of the PHP license, | - | that is bundled with this package in the file LICENSE, and is | - | available through the world-wide-web at the following url: | - | http://www.php.net/license/3_01.txt | - | If you did not receive a copy of the PHP license and are unable to | - | obtain it through the world-wide-web, please send a note to | - | license@php.net so we can mail you a copy immediately. | - +----------------------------------------------------------------------+ - | Authors: Marcus Boerger | - +----------------------------------------------------------------------+ - */ - -#ifndef SXE_H -#define SXE_H - -#include "php.h" - -extern PHP_SXE_API zend_class_entry *ce_SimpleXMLIterator; -extern PHP_SXE_API zend_class_entry *ce_SimpleXMLElement; - -PHP_MINIT_FUNCTION(sxe); - -#endif /* SXE_H */ diff --git a/ext/simplexml/sxe.stub.php b/ext/simplexml/sxe.stub.php deleted file mode 100644 index a6e24dbc5cfd..000000000000 --- a/ext/simplexml/sxe.stub.php +++ /dev/null @@ -1,27 +0,0 @@ -