php-internal-docs 8.4.8
Unofficial docs for php/php-src
Loading...
Searching...
No Matches
token_list.c
Go to the documentation of this file.
1/*
2 +----------------------------------------------------------------------+
3 | Copyright (c) The PHP Group |
4 +----------------------------------------------------------------------+
5 | This source file is subject to version 3.01 of the PHP license, |
6 | that is bundled with this package in the file LICENSE, and is |
7 | available through the world-wide-web at the following url: |
8 | https://www.php.net/license/3_01.txt |
9 | If you did not receive a copy of the PHP license and are unable to |
10 | obtain it through the world-wide-web, please send a note to |
11 | license@php.net so we can mail you a copy immediately. |
12 +----------------------------------------------------------------------+
13 | Authors: Niels Dossche <nielsdos@php.net> |
14 +----------------------------------------------------------------------+
15*/
16
17#ifdef HAVE_CONFIG_H
18#include <config.h>
19#endif
20
21#include "php.h"
22#if defined(HAVE_LIBXML) && defined(HAVE_DOM)
23#include "php_dom.h"
24#include "token_list.h"
25#include "infra.h"
26#include "zend_interfaces.h"
27
28#define TOKEN_LIST_GET_INTERNAL() php_dom_token_list_from_obj(Z_OBJ_P(ZEND_THIS))
29#define TOKEN_LIST_GET_SET(intern) (&(intern)->token_set)
30#define Z_TOKEN_LIST_P(zv) php_dom_token_list_from_obj(Z_OBJ_P(zv))
31
32typedef struct dom_token_list_it {
34 /* Store the hash position here to allow multiple (e.g. nested) iterations of the same token list. */
36 php_libxml_cache_tag cache_tag;
37} dom_token_list_it;
38
39static zend_always_inline bool dom_contains_ascii_whitespace(const char *data)
40{
42}
43
44static zend_always_inline void dom_add_token(HashTable *ht, zend_string *token)
45{
46 /* Key outlives the value's lifetime because as long as the entry is in the table it is kept alive. */
47 zval zv;
48 ZVAL_STR(&zv, token);
49 zend_hash_add(ht, token, &zv);
50}
51
52/* https://dom.spec.whatwg.org/#concept-ordered-set-parser
53 * and https://infra.spec.whatwg.org/#split-on-ascii-whitespace */
54static void dom_ordered_set_parser(HashTable *token_set, const char *position)
55{
56 /* Adapted steps from "split on ASCII whitespace" such that that loop directly appends to the token set. */
57
58 /* 1. Let position be a position variable for input, initially pointing at the start of input.
59 * => That's the position pointer. */
60 /* 2. Let tokens be a list of strings, initially empty.
61 * => That's the token set. */
62
63 /* 3. Skip ASCII whitespace within input given position. */
64 position += strspn(position, ascii_whitespace);
65
66 /* 4. While position is not past the end of input: */
67 while (*position != '\0') {
68 /* 4.1. Let token be the result of collecting a sequence of code points that are not ASCII whitespace from input */
69 const char *start = position;
70 position += strcspn(position, ascii_whitespace);
71 size_t length = position - start;
72
73 /* 4.2. Append token to tokens. */
74 zend_string *token = zend_string_init(start, length, false);
75 dom_add_token(token_set, token);
76 zend_string_release_ex(token, false);
77
78 /* 4.3. Skip ASCII whitespace within input given position. */
79 position += strspn(position, ascii_whitespace);
80 }
81
82 /* 5. Return tokens.
83 * => That's the token set. */
84}
85
86/* https://dom.spec.whatwg.org/#concept-ordered-set-serializer */
87static char *dom_ordered_set_serializer(HashTable *token_set)
88{
89 size_t length = 0;
90 zend_string *token;
91 ZEND_HASH_MAP_FOREACH_STR_KEY(token_set, token) {
92 size_t needed_size = ZSTR_LEN(token) + 1; /* +1 for the space (or \0 at the end) */
93 if (UNEXPECTED(ZSTR_MAX_LEN - length < needed_size)) {
94 /* Shouldn't really be able to happen in practice. */
95 zend_throw_error(NULL, "Token set too large");
96 return NULL;
97 }
98 length += needed_size;
100
101 if (length == 0) {
102 char *ret = emalloc(1);
103 *ret = '\0';
104 return ret;
105 }
106
107 char *ret = emalloc(length);
108 char *ptr = ret;
109 ZEND_HASH_MAP_FOREACH_STR_KEY(token_set, token) {
110 memcpy(ptr, ZSTR_VAL(token), ZSTR_LEN(token));
111 ptr += ZSTR_LEN(token);
112 *ptr++ = ' ';
114 ptr[-1] = '\0'; /* replace last space with \0 */
115 return ret;
116}
117
118static zend_always_inline xmlNode *dom_token_list_get_element(dom_token_list_object *intern)
119{
120 php_libxml_node_ptr *element_ptr = intern->dom.ptr;
121 return element_ptr->node;
122}
123
124static zend_always_inline const xmlAttr *dom_token_list_get_attr(dom_token_list_object *intern)
125{
126 const xmlNode *element_node = dom_token_list_get_element(intern);
127 return xmlHasNsProp(element_node, BAD_CAST "class", NULL);
128}
129
130/* https://dom.spec.whatwg.org/#concept-dtl-update */
131static void dom_token_list_update(dom_token_list_object *intern)
132{
133 const xmlAttr *attr = dom_token_list_get_attr(intern);
134 HashTable *token_set = TOKEN_LIST_GET_SET(intern);
135
136 php_libxml_invalidate_cache_tag(&intern->cache_tag);
137
138 /* 1. If the associated element does not have an associated attribute and token set is empty, then return. */
139 if (attr == NULL && zend_hash_num_elements(token_set) == 0) {
140 return;
141 }
142
143 /* 2. Set an attribute value for the associated element using associated attribute’s local name and the result of
144 * running the ordered set serializer for token set. */
145 char *value = dom_ordered_set_serializer(token_set);
146 xmlSetNsProp(dom_token_list_get_element(intern), NULL, BAD_CAST "class", BAD_CAST value);
147 efree(intern->cached_string);
148 intern->cached_string = value;
149}
150
151static xmlChar *dom_token_list_get_class_value(const xmlAttr *attr, bool *should_free)
152{
153 if (attr != NULL && attr->children != NULL) {
154 return php_libxml_attr_value(attr, should_free);
155 }
156 *should_free = false;
157 return NULL;
158}
159
160static void dom_token_list_update_set(dom_token_list_object *intern, HashTable *token_set)
161{
162 /* https://dom.spec.whatwg.org/#ref-for-domtokenlist%E2%91%A0%E2%91%A1 */
163 bool should_free;
164 const xmlAttr *attr = dom_token_list_get_attr(intern);
165 /* 1. If the data is null, the token set remains empty. */
166 xmlChar *value = dom_token_list_get_class_value(attr, &should_free);
167 if (value != NULL) {
168 /* 2. Otherwise, parse the token set. */
169 dom_ordered_set_parser(token_set, (const char *) value);
170 intern->cached_string = estrdup((const char *) value);
171 } else {
172 intern->cached_string = NULL;
173 }
174
175 if (should_free) {
176 xmlFree(value);
177 }
178}
179
180static void dom_token_list_ensure_set_up_to_date(dom_token_list_object *intern)
181{
182 bool should_free;
183 const xmlAttr *attr = dom_token_list_get_attr(intern);
184 xmlChar *value = dom_token_list_get_class_value(attr, &should_free);
185
186 /* xmlStrEqual will automatically handle equality rules of NULL vs "" (etc.) correctly. */
187 if (!xmlStrEqual(value, (const xmlChar *) intern->cached_string)) {
188 php_libxml_invalidate_cache_tag(&intern->cache_tag);
189 efree(intern->cached_string);
190 HashTable *token_set = TOKEN_LIST_GET_SET(intern);
191 zend_hash_destroy(token_set);
192 zend_hash_init(token_set, 0, NULL, NULL, false);
193 dom_token_list_update_set(intern, token_set);
194 }
195
196 if (should_free) {
197 xmlFree(value);
198 }
199}
200
201void dom_token_list_ctor(dom_token_list_object *intern, dom_object *element_obj)
202{
203 php_libxml_node_ptr *ptr = element_obj->ptr;
204 ptr->refcount++;
205 intern->dom.ptr = ptr;
206 element_obj->document->refcount++;
207 intern->dom.document = element_obj->document;
208
209 intern->cache_tag.modification_nr = 0;
210
211 HashTable *token_set = TOKEN_LIST_GET_SET(intern);
212 zend_hash_init(token_set, 0, NULL, NULL, false);
213
214 dom_token_list_update_set(intern, token_set);
215}
216
218{
219 dom_token_list_object *intern = php_dom_token_list_from_obj(object);
220
221 zend_object_std_dtor(object);
222
223 if (EXPECTED(intern->dom.ptr != NULL)) { /* Object initialized? */
224 xmlNodePtr node = dom_token_list_get_element(intern);
225 if (php_libxml_decrement_node_ptr_ref(intern->dom.ptr) == 0) {
226 php_libxml_node_free_resource(node);
227 }
228 php_libxml_decrement_doc_ref((php_libxml_node_object *) &intern->dom);
229 HashTable *token_set = TOKEN_LIST_GET_SET(intern);
230 zend_hash_destroy(token_set);
231 efree(intern->cached_string);
232 }
233}
234
235static bool dom_token_list_item_exists(dom_token_list_object *token_list, zend_long index)
236{
237 dom_token_list_ensure_set_up_to_date(token_list);
238
239 HashTable *token_set = TOKEN_LIST_GET_SET(token_list);
240 return index >= 0 && index < zend_hash_num_elements(token_set);
241}
242
243static void dom_token_list_item_read(dom_token_list_object *token_list, zval *retval, zend_long index)
244{
245 dom_token_list_ensure_set_up_to_date(token_list);
246
247 HashTable *token_set = TOKEN_LIST_GET_SET(token_list);
248 if (index >= 0 && index < zend_hash_num_elements(token_set)) {
249 HashPosition position;
250 zend_hash_internal_pointer_reset_ex(token_set, &position);
251 while (index > 0) {
252 zend_hash_move_forward_ex(token_set, &position);
253 index--;
254 }
255 zend_string *str_index;
256 zend_hash_get_current_key_ex(token_set, &str_index, NULL, &position);
257 ZVAL_STR_COPY(retval, str_index);
258 } else {
259 /* Not an out of bounds ValueError, but NULL, as according to spec.
260 * This design choice allows for constructs like `item(x) ?? ...`
261 *
262 * In particular:
263 * https://dom.spec.whatwg.org/#interface-domtokenlist states DOMTokenList implements iterable<DOMString>.
264 * From https://webidl.spec.whatwg.org/#idl-iterable:
265 * If a single type parameter is given,
266 * then the interface has a value iterator and provides values of the specified type.
267 * This applies, and reading the definition of value iterator means we should support indexed properties.
268 * From https://webidl.spec.whatwg.org/#dfn-support-indexed-properties:
269 * An interface that defines an indexed property getter is said to support indexed properties.
270 * And indexed property getter is defined here: https://webidl.spec.whatwg.org/#dfn-indexed-property-getter
271 * Down below in their note they give an example of how an out-of-bounds access evaluates to undefined,
272 * which would map to NULL for us.
273 * This would also be consistent with how out-of-bounds array accesses in PHP result in NULL. */
275 }
276}
277
278/* Adapted from spl_offset_convert_to_long */
279static zend_long dom_token_list_offset_convert_to_long(zval *offset, bool *failed)
280{
281 *failed = false;
282
283 while (true) {
284 switch (Z_TYPE_P(offset)) {
285 case IS_STRING: {
286 zend_ulong index;
287 if (ZEND_HANDLE_NUMERIC(Z_STR_P(offset), index)) {
288 return (zend_long) index;
289 }
291 }
292 default:
293 *failed = true;
294 return 0;
295 case IS_DOUBLE:
296 return zend_dval_to_lval_safe(Z_DVAL_P(offset));
297 case IS_LONG:
298 return Z_LVAL_P(offset);
299 case IS_FALSE:
300 return 0;
301 case IS_TRUE:
302 return 1;
303 case IS_REFERENCE:
305 break;
306 case IS_RESOURCE:
308 return Z_RES_HANDLE_P(offset);
309 }
310 }
311}
312
314{
315 if (!offset) {
316 zend_throw_error(NULL, "Cannot append to Dom\\TokenList");
317 return NULL;
318 }
319
320 bool failed;
321 zend_long index = dom_token_list_offset_convert_to_long(offset, &failed);
322 if (UNEXPECTED(failed)) {
324 return NULL;
325 } else {
326 dom_token_list_item_read(php_dom_token_list_from_obj(object), rv, index);
327 return rv;
328 }
329}
330
331int dom_token_list_has_dimension(zend_object *object, zval *offset, int check_empty)
332{
333 bool failed;
334 zend_long index = dom_token_list_offset_convert_to_long(offset, &failed);
335 if (UNEXPECTED(failed)) {
337 return 0;
338 } else {
339 dom_token_list_object *token_list = php_dom_token_list_from_obj(object);
340 if (check_empty) {
341 /* Need to perform an actual read to have the correct empty() semantics. */
342 zval rv;
343 dom_token_list_item_read(token_list, &rv, index);
344 int is_true = zend_is_true(&rv);
345 zval_ptr_dtor_nogc(&rv);
346 return is_true;
347 } else {
348 return dom_token_list_item_exists(token_list, index);
349 }
350 }
351}
352
353/* https://dom.spec.whatwg.org/#dom-domtokenlist-length */
355{
356 dom_token_list_object *token_list = php_dom_token_list_from_dom_obj(obj);
357 dom_token_list_ensure_set_up_to_date(token_list);
358 ZVAL_LONG(retval, zend_hash_num_elements(TOKEN_LIST_GET_SET(token_list)));
359 return SUCCESS;
360}
361
362/* https://dom.spec.whatwg.org/#dom-domtokenlist-value
363 * and https://dom.spec.whatwg.org/#concept-dtl-serialize */
365{
366 bool should_free;
367 dom_token_list_object *intern = php_dom_token_list_from_dom_obj(obj);
368 const xmlAttr *attr = dom_token_list_get_attr(intern);
369 xmlChar *value = dom_token_list_get_class_value(attr, &should_free);
370 ZVAL_STRING(retval, value ? (const char *) value : "");
371 if (should_free) {
372 xmlFree(value);
373 }
374 return SUCCESS;
375}
376
377/* https://dom.spec.whatwg.org/#dom-domtokenlist-value */
379{
380 dom_token_list_object *intern = php_dom_token_list_from_dom_obj(obj);
381 if (UNEXPECTED(zend_str_has_nul_byte(Z_STR_P(newval)))) {
382 zend_value_error("Value must not contain any null bytes");
383 return FAILURE;
384 }
385 xmlSetNsProp(dom_token_list_get_element(intern), NULL, BAD_CAST "class", BAD_CAST Z_STRVAL_P(newval));
386 /* Note: we don't update the set here, the set is always lazily updated for performance reasons. */
387 return SUCCESS;
388}
389
390/* https://dom.spec.whatwg.org/#dom-domtokenlist-item */
391PHP_METHOD(Dom_TokenList, item)
392{
393 zend_long index;
395 Z_PARAM_LONG(index)
397
398 /* 1. If index is equal to or greater than this’s token set’s size, then return null. */
399 /* 2. Return this’s token set[index]. */
400 dom_token_list_item_read(TOKEN_LIST_GET_INTERNAL(), return_value, index);
401}
402
403/* https://dom.spec.whatwg.org/#dom-domtokenlist-contains */
404PHP_METHOD(Dom_TokenList, contains)
405{
406 zend_string *token;
408 Z_PARAM_PATH_STR(token)
410
411 dom_token_list_object *token_list = TOKEN_LIST_GET_INTERNAL();
412 dom_token_list_ensure_set_up_to_date(token_list);
413 HashTable *token_set = TOKEN_LIST_GET_SET(token_list);
414 RETURN_BOOL(zend_hash_exists(token_set, token));
415}
416
417/* Steps taken from the add, remove, toggle, replace methods. */
418static bool dom_validate_token(const zend_string *str)
419{
420 /* 1. If token is the empty string, then throw a "SyntaxError" DOMException. */
421 if (ZSTR_LEN(str) == 0) {
422 php_dom_throw_error_with_message(SYNTAX_ERR, "The empty string is not a valid token", true);
423 return false;
424 }
425
426 /* 2. If token contains any ASCII whitespace, then throw an "InvalidCharacterError" DOMException. */
427 if (dom_contains_ascii_whitespace(ZSTR_VAL(str))) {
428 php_dom_throw_error_with_message(INVALID_CHARACTER_ERR, "The token must not contain any ASCII whitespace", true);
429 return false;
430 }
431
432 return true;
433}
434
435static bool dom_validate_tokens_varargs(const zval *args, uint32_t argc)
436{
437 for (uint32_t i = 0; i < argc; i++) {
438 if (Z_TYPE(args[i]) != IS_STRING) {
439 zend_argument_type_error(i + 1, "must be of type string, %s given", zend_zval_value_name(&args[i]));
440 return false;
441 }
442
443 if (zend_str_has_nul_byte(Z_STR(args[i]))) {
444 zend_argument_value_error(i + 1, "must not contain any null bytes");
445 return false;
446 }
447
448 if (!dom_validate_token(Z_STR(args[i]))) {
449 return false;
450 }
451 }
452
453 return true;
454}
455
456/* https://dom.spec.whatwg.org/#dom-domtokenlist-add */
457PHP_METHOD(Dom_TokenList, add)
458{
459 zval *args;
460 uint32_t argc;
462 Z_PARAM_VARIADIC('*', args, argc)
464
465 /* 1. For each token in tokens (...) */
466 if (!dom_validate_tokens_varargs(args, argc)) {
468 }
469
470 /* 2. For each token in tokens, append token to this’s token set. */
471 dom_token_list_object *intern = TOKEN_LIST_GET_INTERNAL();
472 dom_token_list_ensure_set_up_to_date(intern);
473 HashTable *token_set = TOKEN_LIST_GET_SET(intern);
474 for (uint32_t i = 0; i < argc; i++) {
475 dom_add_token(token_set, Z_STR(args[i]));
476 }
477
478 /* 3. Run the update steps. */
479 dom_token_list_update(intern);
480}
481
482/* https://dom.spec.whatwg.org/#dom-domtokenlist-remove */
483PHP_METHOD(Dom_TokenList, remove)
484{
485 zval *args;
486 uint32_t argc;
488 Z_PARAM_VARIADIC('*', args, argc)
490
491 /* 1. For each token in tokens (...) */
492 if (!dom_validate_tokens_varargs(args, argc)) {
494 }
495
496 /* 2. For each token in tokens, remove token from this’s token set. */
497 dom_token_list_object *intern = TOKEN_LIST_GET_INTERNAL();
498 dom_token_list_ensure_set_up_to_date(intern);
499 HashTable *token_set = TOKEN_LIST_GET_SET(intern);
500 for (uint32_t i = 0; i < argc; i++) {
501 zend_hash_del(token_set, Z_STR(args[i]));
502 }
503
504 /* 3. Run the update steps. */
505 dom_token_list_update(intern);
506}
507
508/* https://dom.spec.whatwg.org/#dom-domtokenlist-toggle */
509PHP_METHOD(Dom_TokenList, toggle)
510{
511 zend_string *token;
512 bool force, force_not_given = true;
514 Z_PARAM_PATH_STR(token)
516 Z_PARAM_BOOL_OR_NULL(force, force_not_given)
518
519 /* Steps 1 - 2 */
520 if (!dom_validate_token(token)) {
522 }
523
524 /* 3. If this’s token set[token] exists, then: */
525 dom_token_list_object *intern = TOKEN_LIST_GET_INTERNAL();
526 dom_token_list_ensure_set_up_to_date(intern);
527 HashTable *token_set = TOKEN_LIST_GET_SET(intern);
528 zval *found_token = zend_hash_find(token_set, token);
529 if (found_token != NULL) {
530 ZEND_ASSERT(XtOffsetOf(Bucket, val) == 0 && "the cast only works if this is true");
531 Bucket *bucket = (Bucket *) found_token;
532
533 /* 3.1. If force is either not given or is false, then remove token from this’s token set,
534 * run the update steps and return false. */
535 if (force_not_given || !force) {
536 zend_hash_del_bucket(token_set, bucket);
537 dom_token_list_update(intern);
539 }
540
541 /* 3.2. Return true. */
543 }
544 /* 4. Otherwise, if force not given or is true, append token to this’s token set,
545 * run the update steps, and return true. */
546 else if (force_not_given || force) {
547 dom_add_token(token_set, token);
548 dom_token_list_update(intern);
550 }
551
552 /* 5. Return false. */
554}
555
556/* https://dom.spec.whatwg.org/#dom-domtokenlist-replace */
557PHP_METHOD(Dom_TokenList, replace)
558{
559 zend_string *token, *new_token;
561 Z_PARAM_PATH_STR(token)
562 Z_PARAM_PATH_STR(new_token)
564
565 /* Steps 1 - 2 */
566 if (!dom_validate_token(token) || !dom_validate_token(new_token)) {
568 }
569
570 /* 3. If this’s token set does not contain token, then return false. */
571 dom_token_list_object *intern = TOKEN_LIST_GET_INTERNAL();
572 dom_token_list_ensure_set_up_to_date(intern);
573 HashTable *token_set = TOKEN_LIST_GET_SET(intern);
574 zval *found_token = zend_hash_find(token_set, token);
575 if (found_token == NULL) {
577 }
578
579 /* 4. Replace token in this’s token set with newToken. */
580 ZEND_ASSERT(XtOffsetOf(Bucket, val) == 0 && "the cast only works if this is true");
581 Bucket *bucket = (Bucket *) found_token;
582 if (zend_hash_set_bucket_key(token_set, bucket, new_token) == NULL) {
583 /* It already exists, remove token instead. */
584 zend_hash_del_bucket(token_set, bucket);
585 } else {
586 /* Need to use ZVAL_STR instead of Z_STR to reset the type flags. */
587 ZVAL_STR(&bucket->val, new_token);
588 }
589
590 /* 5. Run the update steps. */
591 dom_token_list_update(intern);
592
593 /* 6. Return true. */
595}
596
597/* https://dom.spec.whatwg.org/#concept-domtokenlist-validation */
598PHP_METHOD(Dom_TokenList, supports)
599{
600 zend_string *token;
602 Z_PARAM_PATH_STR(token)
604
605 /* The spec designers have designed the TokenList API with future usages in mind.
606 * But right now, this should just always throw a TypeError because the only user is classList, which
607 * does not define a supported token set. */
608 zend_throw_error(zend_ce_type_error, "Attribute \"class\" does not define any supported tokens");
609}
610
611PHP_METHOD(Dom_TokenList, count)
612{
614 dom_token_list_object *intern = TOKEN_LIST_GET_INTERNAL();
615 dom_token_list_ensure_set_up_to_date(intern);
616 RETURN_LONG(zend_hash_num_elements(TOKEN_LIST_GET_SET(intern)));
617}
618
619PHP_METHOD(Dom_TokenList, getIterator)
620{
623}
624
625static void dom_token_list_it_dtor(zend_object_iterator *iter)
626{
627 zval_ptr_dtor(&iter->data);
628}
629
630static void dom_token_list_it_rewind(zend_object_iterator *iter)
631{
632 dom_token_list_it *iterator = (dom_token_list_it *) iter;
633 dom_token_list_object *object = Z_TOKEN_LIST_P(&iter->data);
634 zend_hash_internal_pointer_reset_ex(TOKEN_LIST_GET_SET(object), &iterator->pos);
635}
636
637static zend_result dom_token_list_it_valid(zend_object_iterator *iter)
638{
639 dom_token_list_it *iterator = (dom_token_list_it *) iter;
640 dom_token_list_object *object = Z_TOKEN_LIST_P(&iter->data);
641 HashTable *token_set = TOKEN_LIST_GET_SET(object);
642
643 dom_token_list_ensure_set_up_to_date(object);
644
645 iterator->pos = zend_hash_get_current_pos_ex(token_set, iterator->pos);
646
647 return iterator->pos >= token_set->nNumUsed ? FAILURE : SUCCESS;
648}
649
650static zval *dom_token_list_it_get_current_data(zend_object_iterator *iter)
651{
652 dom_token_list_it *iterator = (dom_token_list_it *) iter;
653 dom_token_list_object *object = Z_TOKEN_LIST_P(&iter->data);
654 dom_token_list_ensure_set_up_to_date(object);
655 /* Caller manages the refcount of the data. */
656 return zend_hash_get_current_data_ex(TOKEN_LIST_GET_SET(object), &iterator->pos);
657}
658
659static void dom_token_list_it_get_current_key(zend_object_iterator *iter, zval *key)
660{
661 dom_token_list_it *iterator = (dom_token_list_it *) iter;
662 dom_token_list_object *object = Z_TOKEN_LIST_P(&iter->data);
663
664 dom_token_list_ensure_set_up_to_date(object);
665
666 if (UNEXPECTED(php_libxml_is_cache_tag_stale(&object->cache_tag, &iterator->cache_tag))) {
667 iter->index = 0;
669 HashTable *token_set = TOKEN_LIST_GET_SET(object);
671 while (pos != iterator->pos) {
672 iter->index++;
673 zend_hash_move_forward_ex(token_set, &pos);
674 }
675 }
676
677 ZVAL_LONG(key, iter->index);
678}
679
680static void dom_token_list_it_move_forward(zend_object_iterator *iter)
681{
682 dom_token_list_it *iterator = (dom_token_list_it *) iter;
683 dom_token_list_object *object = Z_TOKEN_LIST_P(&iter->data);
684 HashTable *token_set = TOKEN_LIST_GET_SET(object);
685
686 dom_token_list_ensure_set_up_to_date(object);
687
688 HashPosition current = iterator->pos;
689 HashPosition validated = zend_hash_get_current_pos_ex(token_set, iterator->pos);
690
691 /* Check if already moved due to user operations, if so don't move again but reset to the first valid position,
692 * otherwise move one forward. */
693 if (validated != current) {
694 iterator->pos = validated;
695 } else {
696 zend_hash_move_forward_ex(token_set, &iterator->pos);
697 }
698}
699
700static const zend_object_iterator_funcs dom_token_list_it_funcs = {
701 dom_token_list_it_dtor,
702 dom_token_list_it_valid,
703 dom_token_list_it_get_current_data,
704 dom_token_list_it_get_current_key,
705 dom_token_list_it_move_forward,
706 dom_token_list_it_rewind,
707 NULL, /* invalidate_current */
708 NULL, /* get_gc */
709};
710
712{
713 if (by_ref) {
714 zend_throw_error(NULL, "An iterator cannot be used with foreach by reference");
715 return NULL;
716 }
717
718 dom_token_list_object *intern = Z_TOKEN_LIST_P(object);
719 dom_token_list_ensure_set_up_to_date(intern);
720 HashTable *token_set = TOKEN_LIST_GET_SET(intern);
721
722 dom_token_list_it *iterator = emalloc(sizeof(*iterator));
723 zend_iterator_init(&iterator->it);
724 zend_hash_internal_pointer_reset_ex(token_set, &iterator->pos);
725 ZVAL_OBJ_COPY(&iterator->it.data, Z_OBJ_P(object));
726
727 iterator->it.funcs = &dom_token_list_it_funcs;
728 iterator->cache_tag = intern->cache_tag;
729
730 return &iterator->it;
731}
732
733#endif
strspn(string $string, string $characters, int $offset=0, ?int $length=null)
strpbrk(string $string, string $characters)
count(Countable|array $value, int $mode=COUNT_NORMAL)
strcspn(string $string, string $characters, int $offset=0, ?int $length=null)
zend_result dom_token_list_value_write(dom_object *obj, zval *newval)
zend_result dom_token_list_value_read(dom_object *obj, zval *retval)
zend_result dom_token_list_length_read(dom_object *obj, zval *retval)
void php_dom_throw_error_with_message(dom_exception_code error_code, const char *error_message, bool strict_error)
@ INVALID_CHARACTER_ERR
@ SYNTAX_ERR
zend_ffi_type * type
Definition ffi.c:3812
zval * zv
Definition ffi.c:3975
void * ptr
Definition ffi.c:3814
memcpy(ptr1, ptr2, size)
new_type attr
Definition ffi.c:4364
zval * val
Definition ffi.c:4262
HashTable * ht
Definition ffi.c:4838
buf start
Definition ffi.c:4687
zend_long offset
#define NULL
Definition gdcache.h:45
#define SUCCESS
Definition hash_sha3.c:261
foreach($dp as $el) foreach( $dp as $el) if( $pass2< 2) echo ""
const char * ascii_whitespace
#define PHP_METHOD
Definition php.h:365
#define add(i, ts)
unsigned const char * pos
Definition php_ffi.h:52
unsigned char key[REFLECTION_KEY_LEN]
zend_constant * data
zval * current
Definition session.c:1024
zval rv
Definition session.c:1024
zval val
Definition zend_types.h:381
void * ptr
Definition xml_common.h:26
php_libxml_ref_obj * document
Definition xml_common.h:27
uint32_t nNumUsed
Definition zend_types.h:406
php_libxml_cache_tag cache_tag
Definition token_list.h:24
zend_object_iterator * dom_token_list_get_iterator(zend_class_entry *ce, zval *object, int by_ref)
void dom_token_list_free_obj(zend_object *object)
void dom_token_list_ctor(dom_token_list_object *intern, dom_object *element_obj)
int dom_token_list_has_dimension(zend_object *object, zval *offset, int check_empty)
zval * dom_token_list_read_dimension(zend_object *object, zval *offset, int type, zval *rv)
struct _dom_object dom_object
ZEND_API ZEND_COLD void zend_throw_error(zend_class_entry *exception_ce, const char *format,...)
Definition zend.c:1772
ZEND_API ZEND_COLD void zend_value_error(const char *format,...)
Definition zend.c:1849
ZEND_API ZEND_COLD void zend_illegal_container_offset(const zend_string *container, const zval *offset, int type)
Definition zend.c:1802
ZEND_API const char * zend_zval_value_name(const zval *arg)
Definition zend_API.c:148
ZEND_API ZEND_COLD void zend_argument_value_error(uint32_t arg_num, const char *format,...)
Definition zend_API.c:433
ZEND_API ZEND_COLD void zend_argument_type_error(uint32_t arg_num, const char *format,...)
Definition zend_API.c:423
#define Z_PARAM_PATH_STR(dest)
Definition zend_API.h:2041
#define ZEND_PARSE_PARAMETERS_END()
Definition zend_API.h:1641
#define RETURN_FALSE
Definition zend_API.h:1058
#define ZEND_PARSE_PARAMETERS_NONE()
Definition zend_API.h:1623
#define ZVAL_STRING(z, s)
Definition zend_API.h:956
#define Z_PARAM_OPTIONAL
Definition zend_API.h:1667
#define ZEND_PARSE_PARAMETERS_START(min_num_args, max_num_args)
Definition zend_API.h:1620
#define Z_PARAM_BOOL_OR_NULL(dest, is_null)
Definition zend_API.h:1729
#define Z_PARAM_LONG(dest)
Definition zend_API.h:1896
#define Z_PARAM_VARIADIC(spec, dest, dest_num)
Definition zend_API.h:2124
#define RETURN_LONG(l)
Definition zend_API.h:1037
#define RETURN_BOOL(b)
Definition zend_API.h:1035
#define RETURN_THROWS()
Definition zend_API.h:1060
#define ZEND_THIS
Definition zend_API.h:523
#define RETURN_TRUE
Definition zend_API.h:1059
#define efree(ptr)
Definition zend_alloc.h:155
#define estrdup(s)
Definition zend_alloc.h:164
#define emalloc(size)
Definition zend_alloc.h:151
struct _zval_struct zval
zend_string_release_ex(func->internal_function.function_name, 0)
zval * args
#define BP_VAR_IS
ZEND_API zend_class_entry * zend_ce_type_error
ZEND_API ZEND_COLD void ZEND_FASTCALL zend_use_resource_as_offset(const zval *dim)
ZEND_API void ZEND_FASTCALL zend_hash_destroy(HashTable *ht)
Definition zend_hash.c:1727
ZEND_API zval *ZEND_FASTCALL zend_hash_set_bucket_key(HashTable *ht, Bucket *b, zend_string *key)
Definition zend_hash.c:1239
ZEND_API zend_result ZEND_FASTCALL zend_hash_move_forward_ex(HashTable *ht, HashPosition *pos)
Definition zend_hash.c:2773
ZEND_API int ZEND_FASTCALL zend_hash_get_current_key_ex(const HashTable *ht, zend_string **str_index, zend_ulong *num_index, const HashPosition *pos)
Definition zend_hash.c:2846
ZEND_API void ZEND_FASTCALL zend_hash_internal_pointer_reset_ex(HashTable *ht, HashPosition *pos)
Definition zend_hash.c:2733
ZEND_API zval *ZEND_FASTCALL zend_hash_get_current_data_ex(HashTable *ht, HashPosition *pos)
Definition zend_hash.c:2915
ZEND_API void ZEND_FASTCALL zend_hash_del_bucket(HashTable *ht, Bucket *p)
Definition zend_hash.c:1526
ZEND_API HashPosition ZEND_FASTCALL zend_hash_get_current_pos_ex(const HashTable *ht, HashPosition pos)
Definition zend_hash.c:517
ZEND_API zend_result ZEND_FASTCALL zend_hash_del(HashTable *ht, zend_string *key)
Definition zend_hash.c:1534
ZEND_API zval *ZEND_FASTCALL zend_hash_find(const HashTable *ht, zend_string *key)
Definition zend_hash.c:2668
ZEND_API zval *ZEND_FASTCALL zend_hash_add(HashTable *ht, zend_string *key, zval *pData)
Definition zend_hash.c:992
#define zend_hash_init(ht, nSize, pHashFunction, pDestructor, persistent)
Definition zend_hash.h:108
#define ZEND_HANDLE_NUMERIC(key, idx)
Definition zend_hash.h:420
#define ZEND_HASH_MAP_FOREACH_STR_KEY(ht, _key)
Definition zend_hash.h:1346
#define ZEND_HASH_FOREACH_END()
Definition zend_hash.h:1086
ZEND_API zend_result zend_create_internal_iterator_zval(zval *return_value, zval *obj)
ZEND_API void zend_iterator_init(zend_object_iterator *iter)
struct _zend_object_iterator zend_object_iterator
struct _zend_object_iterator_funcs zend_object_iterator_funcs
int32_t zend_long
Definition zend_long.h:42
uint32_t zend_ulong
Definition zend_long.h:43
struct _zend_string zend_string
ZEND_API void zend_object_std_dtor(zend_object *object)
ZEND_API bool ZEND_FASTCALL zend_is_true(const zval *op)
#define ZEND_FALLTHROUGH
#define EXPECTED(condition)
#define zend_always_inline
#define XtOffsetOf(s_type, field)
#define ZEND_ASSERT(c)
#define UNEXPECTED(condition)
struct _zend_class_entry zend_class_entry
struct _zend_object zend_object
#define ZSTR_VAL(zstr)
Definition zend_string.h:68
#define ZSTR_MAX_LEN
#define ZSTR_LEN(zstr)
Definition zend_string.h:69
#define Z_TYPE_P(zval_p)
Definition zend_types.h:660
#define IS_TRUE
Definition zend_types.h:603
#define ZVAL_STR(z, s)
#define Z_REFVAL_P(zval_p)
#define IS_FALSE
Definition zend_types.h:602
#define Z_STRVAL_P(zval_p)
Definition zend_types.h:975
#define ZVAL_NULL(z)
#define ZVAL_LONG(z, l)
#define IS_STRING
Definition zend_types.h:606
#define ZVAL_STR_COPY(z, s)
struct _zend_array HashTable
Definition zend_types.h:386
#define IS_RESOURCE
Definition zend_types.h:609
#define Z_OBJ_P(zval_p)
Definition zend_types.h:990
#define IS_DOUBLE
Definition zend_types.h:605
#define Z_STR_P(zval_p)
Definition zend_types.h:972
#define Z_STR(zval)
Definition zend_types.h:971
@ FAILURE
Definition zend_types.h:61
#define IS_LONG
Definition zend_types.h:604
#define IS_REFERENCE
Definition zend_types.h:610
#define Z_RES_HANDLE_P(zval_p)
uint32_t HashPosition
Definition zend_types.h:548
struct _Bucket Bucket
#define ZVAL_OBJ_COPY(z, o)
ZEND_RESULT_CODE zend_result
Definition zend_types.h:64
#define Z_TYPE(zval)
Definition zend_types.h:659
#define Z_DVAL_P(zval_p)
Definition zend_types.h:969
#define Z_LVAL_P(zval_p)
Definition zend_types.h:966
ZEND_API void zval_ptr_dtor(zval *zval_ptr)
zval retval
zval * return_value
object
zval * ret
value