Skip to content

Commit dd722e8

Browse files
committed
Implement Dom\TokenList
Part of RFC: https://wiki.php.net/rfc/dom_additions_84 Closes GH-11688.
1 parent c97885b commit dd722e8

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+2160
-29
lines changed

ext/dom/config.m4

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ if test "$PHP_DOM" != "no"; then
3636
documenttype.c entity.c \
3737
nodelist.c html_collection.c text.c comment.c \
3838
entityreference.c \
39+
token_list.c \
3940
notation.c xpath.c dom_iterators.c \
4041
namednodemap.c xpath_callbacks.c \
4142
$LEXBOR_SOURCES],

ext/dom/config.w32

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ if (PHP_DOM == "yes") {
1414
node.c characterdata.c documenttype.c \
1515
entity.c nodelist.c html_collection.c text.c comment.c \
1616
entityreference.c \
17+
token_list.c \
1718
notation.c xpath.c dom_iterators.c \
1819
namednodemap.c xpath_callbacks.c", null, "-Iext/dom/lexbor");
1920

ext/dom/dom_ce.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ extern PHP_DOM_EXPORT zend_class_entry *dom_modern_entityreference_class_entry;
6262
extern PHP_DOM_EXPORT zend_class_entry *dom_processinginstruction_class_entry;
6363
extern PHP_DOM_EXPORT zend_class_entry *dom_modern_processinginstruction_class_entry;
6464
extern PHP_DOM_EXPORT zend_class_entry *dom_abstract_base_document_class_entry;
65+
extern PHP_DOM_EXPORT zend_class_entry *dom_token_list_class_entry;
6566
#ifdef LIBXML_XPATH_ENABLED
6667
extern PHP_DOM_EXPORT zend_class_entry *dom_xpath_class_entry;
6768
extern PHP_DOM_EXPORT zend_class_entry *dom_modern_xpath_class_entry;

ext/dom/dom_properties.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ zend_result dom_element_class_name_write(dom_object *obj, zval *newval);
8383
zend_result dom_element_id_read(dom_object *obj, zval *retval);
8484
zend_result dom_element_id_write(dom_object *obj, zval *newval);
8585
zend_result dom_element_schema_type_info_read(dom_object *obj, zval *retval);
86+
zend_result dom_element_class_list_read(dom_object *obj, zval *retval);
8687

8788
/* entity properties */
8889
zend_result dom_entity_public_id_read(dom_object *obj, zval *retval);
@@ -146,6 +147,11 @@ zend_result dom_processinginstruction_data_write(dom_object *obj, zval *newval);
146147
/* text properties */
147148
zend_result dom_text_whole_text_read(dom_object *obj, zval *retval);
148149

150+
/* token_list properties */
151+
zend_result dom_token_list_length_read(dom_object *obj, zval *retval);
152+
zend_result dom_token_list_value_read(dom_object *obj, zval *retval);
153+
zend_result dom_token_list_value_write(dom_object *obj, zval *newval);
154+
149155
#ifdef LIBXML_XPATH_ENABLED
150156
/* xpath properties */
151157
zend_result dom_xpath_document_read(dom_object *obj, zval *retval);

ext/dom/element.c

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
#include "namespace_compat.h"
2727
#include "internal_helpers.h"
2828
#include "dom_properties.h"
29+
#include "token_list.h"
2930

3031
/*
3132
* class DOMElement extends DOMNode
@@ -175,6 +176,33 @@ zend_result dom_element_class_name_write(dom_object *obj, zval *newval)
175176
}
176177
/* }}} */
177178

179+
/* {{{ classList TokenList
180+
URL: https://dom.spec.whatwg.org/#dom-element-classlist
181+
*/
182+
zend_result dom_element_class_list_read(dom_object *obj, zval *retval)
183+
{
184+
const uint32_t PROP_INDEX = 20;
185+
186+
#if ZEND_DEBUG
187+
zend_string *class_list_str = ZSTR_INIT_LITERAL("classList", false);
188+
const zend_property_info *prop_info = zend_get_property_info(dom_modern_element_class_entry, class_list_str, 0);
189+
zend_string_release_ex(class_list_str, false);
190+
ZEND_ASSERT(OBJ_PROP_TO_NUM(prop_info->offset) == PROP_INDEX);
191+
#endif
192+
193+
zval *cached_token_list = OBJ_PROP_NUM(&obj->std, PROP_INDEX);
194+
if (Z_ISUNDEF_P(cached_token_list)) {
195+
object_init_ex(cached_token_list, dom_token_list_class_entry);
196+
dom_token_list_object *intern = php_dom_token_list_from_obj(Z_OBJ_P(cached_token_list));
197+
dom_token_list_ctor(intern, obj);
198+
}
199+
200+
ZVAL_OBJ_COPY(retval, Z_OBJ_P(cached_token_list));
201+
202+
return SUCCESS;
203+
}
204+
/* }}} */
205+
178206
/* {{{ id string
179207
URL: https://dom.spec.whatwg.org/#dom-element-id
180208
Since:

ext/dom/php_dom.c

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
#include "internal_helpers.h"
3131
#include "php_dom_arginfo.h"
3232
#include "dom_properties.h"
33+
#include "token_list.h"
3334
#include "zend_interfaces.h"
3435
#include "lexbor/lexbor/core/types.h"
3536
#include "lexbor/lexbor/core/lexbor.h"
@@ -81,6 +82,7 @@ PHP_DOM_EXPORT zend_class_entry *dom_modern_entityreference_class_entry;
8182
PHP_DOM_EXPORT zend_class_entry *dom_processinginstruction_class_entry;
8283
PHP_DOM_EXPORT zend_class_entry *dom_modern_processinginstruction_class_entry;
8384
PHP_DOM_EXPORT zend_class_entry *dom_abstract_base_document_class_entry;
85+
PHP_DOM_EXPORT zend_class_entry *dom_token_list_class_entry;
8486
#ifdef LIBXML_XPATH_ENABLED
8587
PHP_DOM_EXPORT zend_class_entry *dom_xpath_class_entry;
8688
PHP_DOM_EXPORT zend_class_entry *dom_modern_xpath_class_entry;
@@ -97,6 +99,7 @@ static zend_object_handlers dom_modern_nodelist_object_handlers;
9799
static zend_object_handlers dom_html_collection_object_handlers;
98100
static zend_object_handlers dom_object_namespace_node_handlers;
99101
static zend_object_handlers dom_modern_domimplementation_object_handlers;
102+
static zend_object_handlers dom_token_list_object_handlers;
100103
#ifdef LIBXML_XPATH_ENABLED
101104
zend_object_handlers dom_xpath_object_handlers;
102105
#endif
@@ -132,6 +135,7 @@ static HashTable dom_modern_entity_prop_handlers;
132135
static HashTable dom_processinginstruction_prop_handlers;
133136
static HashTable dom_modern_processinginstruction_prop_handlers;
134137
static HashTable dom_namespace_node_prop_handlers;
138+
static HashTable dom_token_list_prop_handlers;
135139
#ifdef LIBXML_XPATH_ENABLED
136140
static HashTable dom_xpath_prop_handlers;
137141
#endif
@@ -633,6 +637,18 @@ static zend_object *dom_object_namespace_node_clone_obj(zend_object *zobject)
633637
return clone;
634638
}
635639

640+
static zend_object *dom_token_list_new(zend_class_entry *class_type)
641+
{
642+
dom_token_list_object *intern = zend_object_alloc(sizeof(*intern), class_type);
643+
644+
intern->dom.prop_handler = &dom_token_list_prop_handlers;
645+
646+
zend_object_std_init(&intern->dom.std, class_type);
647+
object_properties_init(&intern->dom.std, class_type);
648+
649+
return &intern->dom.std;
650+
}
651+
636652
static const zend_module_dep dom_deps[] = {
637653
ZEND_MOD_REQUIRED("libxml")
638654
ZEND_MOD_CONFLICTS("domxml")
@@ -658,7 +674,6 @@ zend_module_entry dom_module_entry = { /* {{{ */
658674
ZEND_GET_MODULE(dom)
659675
#endif
660676

661-
void dom_objects_free_storage(zend_object *object);
662677
void dom_nnodemap_objects_free_storage(zend_object *object);
663678
static zval *dom_nodelist_read_dimension(zend_object *object, zval *offset, int type, zval *rv);
664679
static int dom_nodelist_has_dimension(zend_object *object, zval *member, int check_empty);
@@ -732,6 +747,15 @@ PHP_MINIT_FUNCTION(dom)
732747
dom_object_namespace_node_handlers.free_obj = dom_object_namespace_node_free_storage;
733748
dom_object_namespace_node_handlers.clone_obj = dom_object_namespace_node_clone_obj;
734749

750+
memcpy(&dom_token_list_object_handlers, &dom_object_handlers, sizeof(zend_object_handlers));
751+
dom_token_list_object_handlers.offset = XtOffsetOf(dom_token_list_object, dom.std);
752+
dom_token_list_object_handlers.free_obj = dom_token_list_free_obj;
753+
/* The IDL has the [SameObject] constraint, which is incompatible with cloning because it imposes that there is only
754+
* one instance per parent object. */
755+
dom_token_list_object_handlers.clone_obj = NULL;
756+
dom_token_list_object_handlers.read_dimension = dom_token_list_read_dimension;
757+
dom_token_list_object_handlers.has_dimension = dom_token_list_has_dimension;
758+
735759
zend_hash_init(&classes, 0, NULL, NULL, true);
736760

737761
dom_adjacent_position_class_entry = register_class_Dom_AdjacentPosition();
@@ -1033,6 +1057,7 @@ PHP_MINIT_FUNCTION(dom)
10331057
DOM_REGISTER_PROP_HANDLER(&dom_modern_element_prop_handlers, "tagName", dom_element_tag_name_read, NULL);
10341058
DOM_REGISTER_PROP_HANDLER(&dom_modern_element_prop_handlers, "id", dom_element_id_read, dom_element_id_write);
10351059
DOM_REGISTER_PROP_HANDLER(&dom_modern_element_prop_handlers, "className", dom_element_class_name_read, dom_element_class_name_write);
1060+
DOM_REGISTER_PROP_HANDLER(&dom_modern_element_prop_handlers, "classList", dom_element_class_list_read, NULL);
10361061
DOM_REGISTER_PROP_HANDLER(&dom_modern_element_prop_handlers, "attributes", dom_node_attributes_read, NULL);
10371062
DOM_REGISTER_PROP_HANDLER(&dom_modern_element_prop_handlers, "firstElementChild", dom_parent_node_first_element_child_read, NULL);
10381063
DOM_REGISTER_PROP_HANDLER(&dom_modern_element_prop_handlers, "lastElementChild", dom_parent_node_last_element_child_read, NULL);
@@ -1226,6 +1251,16 @@ PHP_MINIT_FUNCTION(dom)
12261251
zend_hash_add_new_ptr(&classes, dom_modern_xpath_class_entry->name, &dom_xpath_prop_handlers);
12271252
#endif
12281253

1254+
dom_token_list_class_entry = register_class_Dom_TokenList(zend_ce_aggregate, zend_ce_countable);
1255+
dom_token_list_class_entry->create_object = dom_token_list_new;
1256+
dom_token_list_class_entry->default_object_handlers = &dom_token_list_object_handlers;
1257+
dom_token_list_class_entry->get_iterator = dom_token_list_get_iterator;
1258+
1259+
zend_hash_init(&dom_token_list_prop_handlers, 0, NULL, NULL, true);
1260+
DOM_REGISTER_PROP_HANDLER(&dom_token_list_prop_handlers, "length", dom_token_list_length_read, NULL);
1261+
DOM_REGISTER_PROP_HANDLER(&dom_token_list_prop_handlers, "value", dom_token_list_value_read, dom_token_list_value_write);
1262+
zend_hash_add_new_ptr(&classes, dom_token_list_class_entry->name, &dom_token_list_prop_handlers);
1263+
12291264
register_php_dom_symbols(module_number);
12301265

12311266
php_libxml_register_export(dom_node_class_entry, php_dom_export_node);
@@ -1291,6 +1326,7 @@ PHP_MSHUTDOWN_FUNCTION(dom) /* {{{ */
12911326
zend_hash_destroy(&dom_modern_entity_prop_handlers);
12921327
zend_hash_destroy(&dom_processinginstruction_prop_handlers);
12931328
zend_hash_destroy(&dom_modern_processinginstruction_prop_handlers);
1329+
zend_hash_destroy(&dom_token_list_prop_handlers);
12941330
#ifdef LIBXML_XPATH_ENABLED
12951331
zend_hash_destroy(&dom_xpath_prop_handlers);
12961332
#endif

ext/dom/php_dom.h

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,7 @@ static inline dom_object_namespace_node *php_dom_namespace_node_obj_from_obj(zen
122122

123123
#define DOM_HTML_NO_DEFAULT_NS (1U << 31)
124124

125+
void dom_objects_free_storage(zend_object *object);
125126
dom_doc_propsptr dom_get_doc_props(php_libxml_ref_obj *document);
126127
libxml_doc_props const* dom_get_doc_props_read_only(const php_libxml_ref_obj *document);
127128
zend_object *dom_objects_new(zend_class_entry *class_type);
@@ -228,14 +229,8 @@ xmlNodePtr dom_clone_node(php_dom_libxml_ns_mapper *ns_mapper, xmlNodePtr node,
228229

229230
static zend_always_inline bool php_dom_is_cache_tag_stale_from_doc_ptr(const php_libxml_cache_tag *cache_tag, const php_libxml_ref_obj *doc_ptr)
230231
{
231-
ZEND_ASSERT(cache_tag != NULL);
232232
ZEND_ASSERT(doc_ptr != NULL);
233-
/* See overflow comment in php_libxml_invalidate_node_list_cache(). */
234-
#if SIZEOF_SIZE_T == 8
235-
return cache_tag->modification_nr != doc_ptr->cache_tag.modification_nr;
236-
#else
237-
return cache_tag->modification_nr != doc_ptr->cache_tag.modification_nr || UNEXPECTED(doc_ptr->cache_tag.modification_nr == SIZE_MAX);
238-
#endif
233+
return php_libxml_is_cache_tag_stale(cache_tag, &doc_ptr->cache_tag);
239234
}
240235

241236
static zend_always_inline bool php_dom_is_cache_tag_stale_from_node(const php_libxml_cache_tag *cache_tag, const xmlNodePtr node)

ext/dom/php_dom.stub.php

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1304,6 +1304,8 @@ class Element extends Node implements ParentNode, ChildNode
13041304

13051305
public string $id;
13061306
public string $className;
1307+
/** @readonly */
1308+
public TokenList $classList;
13071309

13081310
/** @implementation-alias DOMNode::hasAttributes */
13091311
public function hasAttributes(): bool {}
@@ -1659,6 +1661,31 @@ public function saveXml(?Node $node = null, int $options = 0): string|false {}
16591661
public function saveXmlFile(string $filename, int $options = 0): int|false {}
16601662
}
16611663

1664+
/**
1665+
* @not-serializable
1666+
* @strict-properties
1667+
*/
1668+
final class TokenList implements IteratorAggregate, Countable
1669+
{
1670+
/** @implementation-alias Dom\Node::__construct */
1671+
private function __construct() {}
1672+
1673+
/** @readonly */
1674+
public int $length;
1675+
public function item(int $index): ?string {}
1676+
public function contains(string $token): bool {}
1677+
public function add(string ...$tokens): void {}
1678+
public function remove(string ...$tokens): void {}
1679+
public function toggle(string $token, ?bool $force = null): bool {}
1680+
public function replace(string $token, string $newToken): bool {}
1681+
public function supports(string $token): bool {}
1682+
public string $value;
1683+
1684+
public function count(): int {}
1685+
1686+
public function getIterator(): \Iterator {}
1687+
}
1688+
16621689
#ifdef LIBXML_XPATH_ENABLED
16631690
/** @not-serializable */
16641691
final class XPath

ext/dom/php_dom_arginfo.h

Lines changed: 87 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)