Skip to content

Commit 9e34c6d

Browse files
committed
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.
1 parent c05a9c3 commit 9e34c6d

File tree

10 files changed

+199
-281
lines changed

10 files changed

+199
-281
lines changed

ext/simplexml/config.m4

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ if test "$PHP_SIMPLEXML" != "no"; then
1212

1313
PHP_SETUP_LIBXML(SIMPLEXML_SHARED_LIBADD, [
1414
AC_DEFINE(HAVE_SIMPLEXML,1,[ ])
15-
PHP_NEW_EXTENSION(simplexml, simplexml.c sxe.c, $ext_shared)
15+
PHP_NEW_EXTENSION(simplexml, simplexml.c, $ext_shared)
1616
PHP_INSTALL_HEADERS([ext/simplexml/php_simplexml.h ext/simplexml/php_simplexml_exports.h])
1717
PHP_SUBST(SIMPLEXML_SHARED_LIBADD)
1818
])

ext/simplexml/config.w32

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ if (PHP_SIMPLEXML == "yes") {
77
ADD_EXTENSION_DEP('simplexml', 'libxml') &&
88
CHECK_HEADER_ADD_INCLUDE("libxml/tree.h", "CFLAGS_SIMPLEXML", PHP_PHP_BUILD + "\\include\\libxml2")
99
) {
10-
EXTENSION("simplexml", "simplexml.c sxe.c");
10+
EXTENSION("simplexml", "simplexml.c");
1111
AC_DEFINE("HAVE_SIMPLEXML", 1, "Simple XML support");
1212
if (!PHP_SIMPLEXML_SHARED) {
1313
ADD_FLAG("CFLAGS_SIMPLEXML", "/D LIBXML_STATIC");

ext/simplexml/php_simplexml.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,9 @@ typedef struct {
7777
# define PHP_SXE_API ZEND_API
7878
#endif
7979

80+
extern PHP_SXE_API zend_class_entry *ce_SimpleXMLIterator;
81+
extern PHP_SXE_API zend_class_entry *ce_SimpleXMLElement;
82+
8083
PHP_SXE_API zend_class_entry *sxe_get_element_class_entry();
8184

8285
#endif

ext/simplexml/simplexml.c

Lines changed: 154 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,11 @@
3131
#include "simplexml_arginfo.h"
3232
#include "zend_exceptions.h"
3333
#include "zend_interfaces.h"
34-
#include "sxe.h"
34+
#include "ext/spl/spl_iterators.h"
3535

3636
zend_class_entry *sxe_class_entry = NULL;
37+
PHP_SXE_API zend_class_entry *ce_SimpleXMLIterator;
38+
PHP_SXE_API zend_class_entry *ce_SimpleXMLElement;
3739

3840
PHP_SXE_API zend_class_entry *sxe_get_element_class_entry() /* {{{ */
3941
{
@@ -2016,6 +2018,138 @@ SXE_METHOD(count)
20162018
}
20172019
/* }}} */
20182020

2021+
2022+
/* {{{ proto void SimpleXMLElement::rewind()
2023+
Rewind to first element */
2024+
SXE_METHOD(rewind)
2025+
{
2026+
if (zend_parse_parameters_none() == FAILURE) {
2027+
RETURN_THROWS();
2028+
}
2029+
2030+
php_sxe_rewind_iterator(Z_SXEOBJ_P(ZEND_THIS));
2031+
}
2032+
/* }}} */
2033+
2034+
/* {{{ proto bool SimpleXMLElement::valid()
2035+
Check whether iteration is valid */
2036+
SXE_METHOD(valid)
2037+
{
2038+
php_sxe_object *sxe = Z_SXEOBJ_P(ZEND_THIS);
2039+
2040+
if (zend_parse_parameters_none() == FAILURE) {
2041+
RETURN_THROWS();
2042+
}
2043+
2044+
RETURN_BOOL(!Z_ISUNDEF(sxe->iter.data));
2045+
}
2046+
/* }}} */
2047+
2048+
/* {{{ proto SimpleXMLElement SimpleXMLElement::current()
2049+
Get current element */
2050+
SXE_METHOD(current)
2051+
{
2052+
php_sxe_object *sxe = Z_SXEOBJ_P(ZEND_THIS);
2053+
zval *data;
2054+
2055+
if (zend_parse_parameters_none() == FAILURE) {
2056+
RETURN_THROWS();
2057+
}
2058+
2059+
if (Z_ISUNDEF(sxe->iter.data)) {
2060+
return; /* return NULL */
2061+
}
2062+
2063+
data = &sxe->iter.data;
2064+
ZVAL_COPY_DEREF(return_value, data);
2065+
}
2066+
/* }}} */
2067+
2068+
/* {{{ proto string SimpleXMLElement::key()
2069+
Get name of current child element */
2070+
SXE_METHOD(key)
2071+
{
2072+
xmlNodePtr curnode;
2073+
php_sxe_object *intern;
2074+
php_sxe_object *sxe = Z_SXEOBJ_P(ZEND_THIS);
2075+
2076+
if (zend_parse_parameters_none() == FAILURE) {
2077+
RETURN_THROWS();
2078+
}
2079+
2080+
if (Z_ISUNDEF(sxe->iter.data)) {
2081+
RETURN_FALSE;
2082+
}
2083+
2084+
intern = Z_SXEOBJ_P(&sxe->iter.data);
2085+
if (intern != NULL && intern->node != NULL) {
2086+
curnode = (xmlNodePtr)((php_libxml_node_ptr *)intern->node)->node;
2087+
RETURN_STRINGL((char*)curnode->name, xmlStrlen(curnode->name));
2088+
}
2089+
2090+
RETURN_FALSE;
2091+
}
2092+
/* }}} */
2093+
2094+
/* {{{ proto void SimpleXMLElement::next()
2095+
Move to next element */
2096+
SXE_METHOD(next)
2097+
{
2098+
if (zend_parse_parameters_none() == FAILURE) {
2099+
RETURN_THROWS();
2100+
}
2101+
2102+
php_sxe_move_forward_iterator(Z_SXEOBJ_P(ZEND_THIS));
2103+
}
2104+
/* }}} */
2105+
2106+
/* {{{ proto bool SimpleXMLElement::hasChildren()
2107+
Check whether element has children (elements) */
2108+
SXE_METHOD(hasChildren)
2109+
{
2110+
php_sxe_object *sxe = Z_SXEOBJ_P(ZEND_THIS);
2111+
php_sxe_object *child;
2112+
xmlNodePtr node;
2113+
2114+
if (zend_parse_parameters_none() == FAILURE) {
2115+
RETURN_THROWS();
2116+
}
2117+
2118+
if (Z_ISUNDEF(sxe->iter.data) || sxe->iter.type == SXE_ITER_ATTRLIST) {
2119+
RETURN_FALSE;
2120+
}
2121+
child = Z_SXEOBJ_P(&sxe->iter.data);
2122+
2123+
GET_NODE(child, node);
2124+
if (node) {
2125+
node = node->children;
2126+
}
2127+
while (node && node->type != XML_ELEMENT_NODE) {
2128+
node = node->next;
2129+
}
2130+
RETURN_BOOL(node ? 1 : 0);
2131+
}
2132+
/* }}} */
2133+
2134+
/* {{{ proto SimpleXMLElement SimpleXMLElement::getChildren()
2135+
Get child element iterator */
2136+
SXE_METHOD(getChildren)
2137+
{
2138+
php_sxe_object *sxe = Z_SXEOBJ_P(ZEND_THIS);
2139+
zval *data;
2140+
2141+
if (zend_parse_parameters_none() == FAILURE) {
2142+
RETURN_THROWS();
2143+
}
2144+
2145+
if (Z_ISUNDEF(sxe->iter.data) || sxe->iter.type == SXE_ITER_ATTRLIST) {
2146+
return; /* return NULL */
2147+
}
2148+
2149+
data = &sxe->iter.data;
2150+
ZVAL_COPY_DEREF(return_value, data);
2151+
}
2152+
20192153
static zend_object_handlers sxe_object_handlers;
20202154

20212155
/* {{{ sxe_object_clone()
@@ -2619,6 +2753,13 @@ static const zend_function_entry sxe_functions[] = { /* {{{ */
26192753
SXE_ME(addAttribute, arginfo_class_SimpleXMLElement_addAttribute, ZEND_ACC_PUBLIC)
26202754
SXE_ME(__toString, arginfo_class_SimpleXMLElement___toString, ZEND_ACC_PUBLIC)
26212755
SXE_ME(count, arginfo_class_SimpleXMLElement_count, ZEND_ACC_PUBLIC)
2756+
SXE_ME(rewind, arginfo_class_SimpleXMLElement_rewind, ZEND_ACC_PUBLIC)
2757+
SXE_ME(valid, arginfo_class_SimpleXMLElement_valid, ZEND_ACC_PUBLIC)
2758+
SXE_ME(current, arginfo_class_SimpleXMLElement_current, ZEND_ACC_PUBLIC)
2759+
SXE_ME(key, arginfo_class_SimpleXMLElement_key, ZEND_ACC_PUBLIC)
2760+
SXE_ME(next, arginfo_class_SimpleXMLElement_next, ZEND_ACC_PUBLIC)
2761+
SXE_ME(hasChildren, arginfo_class_SimpleXMLElement_hasChildren, ZEND_ACC_PUBLIC)
2762+
SXE_ME(getChildren, arginfo_class_SimpleXMLElement_getChildren, ZEND_ACC_PUBLIC)
26222763
PHP_FE_END
26232764
};
26242765
/* }}} */
@@ -2627,13 +2768,14 @@ static const zend_function_entry sxe_functions[] = { /* {{{ */
26272768
*/
26282769
PHP_MINIT_FUNCTION(simplexml)
26292770
{
2630-
zend_class_entry sxe;
2771+
zend_class_entry ce;
26312772

2632-
INIT_CLASS_ENTRY(sxe, "SimpleXMLElement", sxe_functions);
2633-
sxe.create_object = sxe_object_new;
2634-
sxe_class_entry = zend_register_internal_class(&sxe);
2773+
INIT_CLASS_ENTRY(ce, "SimpleXMLElement", sxe_functions);
2774+
sxe_class_entry = zend_register_internal_class(&ce);
2775+
sxe_class_entry->create_object = sxe_object_new;
26352776
sxe_class_entry->get_iterator = php_sxe_get_iterator;
2636-
zend_class_implements(sxe_class_entry, 3, zend_ce_traversable, zend_ce_countable, zend_ce_stringable);
2777+
zend_class_implements(sxe_class_entry, 3,
2778+
zend_ce_countable, zend_ce_stringable, spl_ce_RecursiveIterator);
26372779

26382780
memcpy(&sxe_object_handlers, &std_object_handlers, sizeof(zend_object_handlers));
26392781
sxe_object_handlers.offset = XtOffsetOf(php_sxe_object, zo);
@@ -2660,9 +2802,13 @@ PHP_MINIT_FUNCTION(simplexml)
26602802
sxe_class_entry->serialize = zend_class_serialize_deny;
26612803
sxe_class_entry->unserialize = zend_class_unserialize_deny;
26622804

2663-
php_libxml_register_export(sxe_class_entry, simplexml_export_node);
2805+
/* TODO: Why do we have two variables for this? */
2806+
ce_SimpleXMLElement = sxe_class_entry;
26642807

2665-
PHP_MINIT(sxe)(INIT_FUNC_ARGS_PASSTHRU);
2808+
INIT_CLASS_ENTRY(ce, "SimpleXMLIterator", NULL);
2809+
ce_SimpleXMLIterator = zend_register_internal_class_ex(&ce, ce_SimpleXMLElement);
2810+
2811+
php_libxml_register_export(sxe_class_entry, simplexml_export_node);
26662812

26672813
return SUCCESS;
26682814
}

ext/simplexml/simplexml.stub.php

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ function simplexml_load_string(string $data, ?string $class_name = SimpleXMLElem
66

77
function simplexml_import_dom(DOMNode $node, ?string $class_name = SimpleXMLElement::class): ?SimpleXMLElement {}
88

9-
class SimpleXMLElement implements Stringable
9+
class SimpleXMLElement implements Stringable, Countable, RecursiveIterator
1010
{
1111
/** @return array|false */
1212
public function xpath(string $path) {}
@@ -47,4 +47,29 @@ public function __toString(): string {}
4747

4848
/** @return int */
4949
public function count() {}
50+
51+
/** @return void */
52+
public function rewind() {}
53+
54+
/** @return bool */
55+
public function valid() {}
56+
57+
/** @return ?SimpleXMLElement */
58+
public function current() {}
59+
60+
/** @return string|false */
61+
public function key() {}
62+
63+
/** @return void */
64+
public function next() {}
65+
66+
/** @return bool */
67+
public function hasChildren() {}
68+
69+
/** @return ?SimpleXMLElement */
70+
public function getChildren() {}
71+
}
72+
73+
class SimpleXMLIterator extends SimpleXMLElement
74+
{
5075
}

ext/simplexml/simplexml_arginfo.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,3 +75,17 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_SimpleXMLElement___toStrin
7575
ZEND_END_ARG_INFO()
7676

7777
#define arginfo_class_SimpleXMLElement_count arginfo_class_SimpleXMLElement_getName
78+
79+
#define arginfo_class_SimpleXMLElement_rewind arginfo_class_SimpleXMLElement_getName
80+
81+
#define arginfo_class_SimpleXMLElement_valid arginfo_class_SimpleXMLElement_getName
82+
83+
#define arginfo_class_SimpleXMLElement_current arginfo_class_SimpleXMLElement_getName
84+
85+
#define arginfo_class_SimpleXMLElement_key arginfo_class_SimpleXMLElement_getName
86+
87+
#define arginfo_class_SimpleXMLElement_next arginfo_class_SimpleXMLElement_getName
88+
89+
#define arginfo_class_SimpleXMLElement_hasChildren arginfo_class_SimpleXMLElement_getName
90+
91+
#define arginfo_class_SimpleXMLElement_getChildren arginfo_class_SimpleXMLElement_getName

0 commit comments

Comments
 (0)