2424#if defined(HAVE_LIBXML ) && defined(HAVE_DOM )
2525#include "php_dom.h"
2626#include "obj_map.h"
27+ #include "token_list.h"
2728
2829static zend_always_inline void objmap_cache_release_cached_obj (dom_nnodemap_object * objmap )
2930{
@@ -40,6 +41,30 @@ static zend_always_inline void reset_objmap_cache(dom_nnodemap_object *objmap)
4041 objmap -> cached_length = -1 ;
4142}
4243
44+ static bool dom_matches_class_name (const dom_nnodemap_object * map , const xmlNode * nodep )
45+ {
46+ bool ret = false;
47+
48+ if (nodep -> type == XML_ELEMENT_NODE ) {
49+ xmlAttrPtr classes = xmlHasNsProp (nodep , BAD_CAST "class" , NULL );
50+ if (classes != NULL ) {
51+ bool should_free ;
52+ xmlChar * value = php_libxml_attr_value (classes , & should_free );
53+
54+ bool quirks = map -> baseobj -> document -> quirks_mode == PHP_LIBXML_QUIRKS ;
55+ if (dom_ordered_set_all_contained (map -> array , (const char * ) value , quirks )) {
56+ ret = true;
57+ }
58+
59+ if (should_free ) {
60+ xmlFree (value );
61+ }
62+ }
63+ }
64+
65+ return ret ;
66+ }
67+
4368/**************************
4469 * === Length methods === *
4570 **************************/
@@ -106,6 +131,24 @@ static zend_long dom_map_get_by_tag_name_length(dom_nnodemap_object *map)
106131 return count ;
107132}
108133
134+ static zend_long dom_map_get_by_class_name_length (dom_nnodemap_object * map )
135+ {
136+ xmlNodePtr nodep = dom_object_get_node (map -> baseobj );
137+ zend_long count = 0 ;
138+ if (nodep ) {
139+ xmlNodePtr basep = nodep ;
140+ nodep = php_dom_first_child_of_container_node (basep );
141+
142+ while (nodep != NULL ) {
143+ if (dom_matches_class_name (map , nodep )) {
144+ count ++ ;
145+ }
146+ nodep = php_dom_next_in_tree_order (nodep , basep );
147+ }
148+ }
149+ return count ;
150+ }
151+
109152static zend_long dom_map_get_zero_length (dom_nnodemap_object * map )
110153{
111154 return 0 ;
@@ -276,6 +319,10 @@ static void dom_map_collection_named_item_elements_iter(dom_nnodemap_object *map
276319 }
277320}
278321
322+ static void dom_map_collection_named_item_null (dom_nnodemap_object * map , php_dom_obj_map_collection_iter * iter )
323+ {
324+ }
325+
279326static void dom_map_get_by_tag_name_item (dom_nnodemap_object * map , zend_long index , zval * return_value )
280327{
281328 xmlNodePtr nodep = dom_object_get_node (map -> baseobj );
@@ -292,12 +339,54 @@ static void dom_map_get_by_tag_name_item(dom_nnodemap_object *map, zend_long ind
292339 }
293340}
294341
342+ static void dom_map_get_by_class_name_item (dom_nnodemap_object * map , zend_long index , zval * return_value )
343+ {
344+ xmlNodePtr nodep = dom_object_get_node (map -> baseobj );
345+ xmlNodePtr itemnode = NULL ;
346+ if (nodep && index >= 0 ) {
347+ dom_node_idx_pair start_point = dom_obj_map_get_start_point (map , nodep , index );
348+ if (start_point .node ) {
349+ if (start_point .index > 0 ) {
350+ /* Only start iteration at next point if we actually have an index to seek to. */
351+ itemnode = php_dom_next_in_tree_order (start_point .node , nodep );
352+ } else {
353+ itemnode = start_point .node ;
354+ }
355+ } else {
356+ itemnode = php_dom_first_child_of_container_node (nodep );
357+ }
358+
359+ do {
360+ -- start_point .index ;
361+ while (itemnode != NULL && !dom_matches_class_name (map , itemnode )) {
362+ itemnode = php_dom_next_in_tree_order (itemnode , nodep );
363+ }
364+ } while (start_point .index > 0 && itemnode );
365+ }
366+ dom_ret_node_to_zobj (map , itemnode , return_value );
367+ if (itemnode ) {
368+ dom_map_cache_obj (map , itemnode , index , return_value );
369+ }
370+ }
371+
295372static void dom_map_collection_named_item_by_tag_name_iter (dom_nnodemap_object * map , php_dom_obj_map_collection_iter * iter )
296373{
297374 iter -> candidate = dom_get_elements_by_tag_name_ns_raw (iter -> basep , iter -> candidate , map -> ns , map -> local , map -> local_lower , & iter -> cur , iter -> next );
298375 iter -> next = iter -> cur + 1 ;
299376}
300377
378+ static void dom_map_collection_named_item_by_class_name_iter (dom_nnodemap_object * map , php_dom_obj_map_collection_iter * iter )
379+ {
380+ xmlNodePtr basep = iter -> basep ;
381+ xmlNodePtr nodep = iter -> candidate ? php_dom_next_in_tree_order (iter -> candidate , basep ) : php_dom_first_child_of_container_node (basep );
382+
383+ while (nodep != NULL && !dom_matches_class_name (map , nodep )) {
384+ nodep = php_dom_next_in_tree_order (nodep , basep );
385+ }
386+
387+ iter -> candidate = nodep ;
388+ }
389+
301390static void dom_map_get_null_item (dom_nnodemap_object * map , zend_long index , zval * return_value )
302391{
303392 RETURN_NULL ();
@@ -478,6 +567,16 @@ const php_dom_obj_map_handler php_dom_obj_map_by_tag_name = {
478567 .nameless = true,
479568};
480569
570+ const php_dom_obj_map_handler php_dom_obj_map_by_class_name = {
571+ .length = dom_map_get_by_class_name_length ,
572+ .get_item = dom_map_get_by_class_name_item ,
573+ .get_ns_named_item = dom_map_get_ns_named_item_null ,
574+ .has_ns_named_item = dom_map_has_ns_named_item_null ,
575+ .collection_named_item_iter = dom_map_collection_named_item_by_class_name_iter ,
576+ .use_cache = true,
577+ .nameless = true,
578+ };
579+
481580const php_dom_obj_map_handler php_dom_obj_map_child_nodes = {
482581 .length = dom_map_get_nodes_length ,
483582 .get_item = dom_map_get_nodes_item ,
@@ -533,7 +632,7 @@ const php_dom_obj_map_handler php_dom_obj_map_noop = {
533632 .get_item = dom_map_get_null_item ,
534633 .get_ns_named_item = dom_map_get_ns_named_item_null ,
535634 .has_ns_named_item = dom_map_has_ns_named_item_null ,
536- .collection_named_item_iter = NULL ,
635+ .collection_named_item_iter = dom_map_collection_named_item_null ,
537636 .use_cache = false,
538637 .nameless = true,
539638};
0 commit comments