mirror of
https://github.com/php/php-src.git
synced 2024-11-26 19:33:55 +08:00
Implement CSS selectors
This commit is contained in:
parent
7defc23532
commit
88da914910
@ -247,6 +247,7 @@ PHP 8.4 INTERNALS UPGRADE NOTES
|
||||
- Removed the "properties" HashTable field from php_libxml_node_object.
|
||||
- Added a way to attached private data to a php_libxml_ref_obj.
|
||||
- Added a way to fix a class type onto php_libxml_ref_obj.
|
||||
- Added a way to record quirks mode in php_libxml_ref_obj.
|
||||
- Added php_libxml_uses_internal_errors().
|
||||
- Added a way to override document handlers (e.g. serialization) with
|
||||
php_libxml_document_handlers.
|
||||
|
12
codecov.yml
12
codecov.yml
@ -1,3 +1,11 @@
|
||||
ignore:
|
||||
- "ext/dom/lexbor/lexbor" # bundled library
|
||||
- "ext/pcre/pcre2lib" # bundled library
|
||||
# bundled libraries
|
||||
- "ext/dom/lexbor/lexbor/core"
|
||||
- "ext/dom/lexbor/lexbor/css"
|
||||
- "ext/dom/lexbor/lexbor/dom"
|
||||
- "ext/dom/lexbor/lexbor/encoding"
|
||||
- "ext/dom/lexbor/lexbor/html"
|
||||
- "ext/dom/lexbor/lexbor/ns"
|
||||
- "ext/dom/lexbor/lexbor/ports"
|
||||
- "ext/dom/lexbor/lexbor/tag"
|
||||
- "ext/pcre/pcre2lib"
|
||||
|
@ -22,12 +22,14 @@ if test "$PHP_DOM" != "no"; then
|
||||
$LEXBOR_DIR/encoding/big5.c $LEXBOR_DIR/encoding/decode.c $LEXBOR_DIR/encoding/encode.c $LEXBOR_DIR/encoding/encoding.c $LEXBOR_DIR/encoding/euc_kr.c $LEXBOR_DIR/encoding/gb18030.c $LEXBOR_DIR/encoding/iso_2022_jp_katakana.c $LEXBOR_DIR/encoding/jis0208.c $LEXBOR_DIR/encoding/jis0212.c $LEXBOR_DIR/encoding/range.c $LEXBOR_DIR/encoding/res.c $LEXBOR_DIR/encoding/single.c \
|
||||
$LEXBOR_DIR/html/encoding.c $LEXBOR_DIR/html/interface.c $LEXBOR_DIR/html/parser.c $LEXBOR_DIR/html/token.c $LEXBOR_DIR/html/token_attr.c $LEXBOR_DIR/html/tokenizer.c $LEXBOR_DIR/html/tree.c \
|
||||
$LEXBOR_DIR/html/interfaces/anchor_element.c $LEXBOR_DIR/html/interfaces/area_element.c $LEXBOR_DIR/html/interfaces/audio_element.c $LEXBOR_DIR/html/interfaces/base_element.c $LEXBOR_DIR/html/interfaces/body_element.c $LEXBOR_DIR/html/interfaces/br_element.c $LEXBOR_DIR/html/interfaces/button_element.c $LEXBOR_DIR/html/interfaces/canvas_element.c $LEXBOR_DIR/html/interfaces/data_element.c $LEXBOR_DIR/html/interfaces/data_list_element.c $LEXBOR_DIR/html/interfaces/details_element.c $LEXBOR_DIR/html/interfaces/dialog_element.c $LEXBOR_DIR/html/interfaces/directory_element.c $LEXBOR_DIR/html/interfaces/div_element.c $LEXBOR_DIR/html/interfaces/d_list_element.c $LEXBOR_DIR/html/interfaces/document.c $LEXBOR_DIR/html/interfaces/element.c $LEXBOR_DIR/html/interfaces/embed_element.c $LEXBOR_DIR/html/interfaces/field_set_element.c $LEXBOR_DIR/html/interfaces/font_element.c $LEXBOR_DIR/html/interfaces/form_element.c $LEXBOR_DIR/html/interfaces/frame_element.c $LEXBOR_DIR/html/interfaces/frame_set_element.c $LEXBOR_DIR/html/interfaces/head_element.c $LEXBOR_DIR/html/interfaces/heading_element.c $LEXBOR_DIR/html/interfaces/hr_element.c $LEXBOR_DIR/html/interfaces/html_element.c $LEXBOR_DIR/html/interfaces/iframe_element.c $LEXBOR_DIR/html/interfaces/image_element.c $LEXBOR_DIR/html/interfaces/input_element.c $LEXBOR_DIR/html/interfaces/label_element.c $LEXBOR_DIR/html/interfaces/legend_element.c $LEXBOR_DIR/html/interfaces/li_element.c $LEXBOR_DIR/html/interfaces/link_element.c $LEXBOR_DIR/html/interfaces/map_element.c $LEXBOR_DIR/html/interfaces/marquee_element.c $LEXBOR_DIR/html/interfaces/media_element.c $LEXBOR_DIR/html/interfaces/menu_element.c $LEXBOR_DIR/html/interfaces/meta_element.c $LEXBOR_DIR/html/interfaces/meter_element.c $LEXBOR_DIR/html/interfaces/mod_element.c $LEXBOR_DIR/html/interfaces/object_element.c $LEXBOR_DIR/html/interfaces/o_list_element.c $LEXBOR_DIR/html/interfaces/opt_group_element.c $LEXBOR_DIR/html/interfaces/option_element.c $LEXBOR_DIR/html/interfaces/output_element.c $LEXBOR_DIR/html/interfaces/paragraph_element.c $LEXBOR_DIR/html/interfaces/param_element.c $LEXBOR_DIR/html/interfaces/picture_element.c $LEXBOR_DIR/html/interfaces/pre_element.c $LEXBOR_DIR/html/interfaces/progress_element.c $LEXBOR_DIR/html/interfaces/quote_element.c $LEXBOR_DIR/html/interfaces/script_element.c $LEXBOR_DIR/html/interfaces/select_element.c $LEXBOR_DIR/html/interfaces/slot_element.c $LEXBOR_DIR/html/interfaces/source_element.c $LEXBOR_DIR/html/interfaces/span_element.c $LEXBOR_DIR/html/interfaces/style_element.c $LEXBOR_DIR/html/interfaces/table_caption_element.c $LEXBOR_DIR/html/interfaces/table_cell_element.c $LEXBOR_DIR/html/interfaces/table_col_element.c $LEXBOR_DIR/html/interfaces/table_element.c $LEXBOR_DIR/html/interfaces/table_row_element.c $LEXBOR_DIR/html/interfaces/table_section_element.c $LEXBOR_DIR/html/interfaces/template_element.c $LEXBOR_DIR/html/interfaces/text_area_element.c $LEXBOR_DIR/html/interfaces/time_element.c $LEXBOR_DIR/html/interfaces/title_element.c $LEXBOR_DIR/html/interfaces/track_element.c $LEXBOR_DIR/html/interfaces/u_list_element.c $LEXBOR_DIR/html/interfaces/unknown_element.c $LEXBOR_DIR/html/interfaces/video_element.c $LEXBOR_DIR/html/interfaces/window.c \
|
||||
$LEXBOR_DIR/selectors/selectors.c \
|
||||
$LEXBOR_DIR/css/state.c $LEXBOR_DIR/css/log.c $LEXBOR_DIR/css/parser.c $LEXBOR_DIR/css/selectors/state.c $LEXBOR_DIR/css/selectors/selectors.c $LEXBOR_DIR/css/selectors/selector.c $LEXBOR_DIR/css/selectors/pseudo_state.c $LEXBOR_DIR/css/selectors/pseudo.c $LEXBOR_DIR/css/syntax/tokenizer/error.c $LEXBOR_DIR/css/syntax/state.c $LEXBOR_DIR/css/syntax/parser.c $LEXBOR_DIR/css/syntax/syntax.c $LEXBOR_DIR/css/syntax/anb.c $LEXBOR_DIR/css/syntax/tokenizer.c $LEXBOR_DIR/css/syntax/token.c $LEXBOR_DIR/css/css.c \
|
||||
$LEXBOR_DIR/selectors-adapted/selectors.c \
|
||||
$LEXBOR_DIR/ns/ns.c \
|
||||
$LEXBOR_DIR/tag/tag.c"
|
||||
PHP_NEW_EXTENSION(dom, [php_dom.c attr.c document.c infra.c \
|
||||
xml_document.c html_document.c xml_serializer.c html5_serializer.c html5_parser.c namespace_compat.c \
|
||||
domexception.c parentnode.c \
|
||||
domexception.c \
|
||||
parentnode/tree.c parentnode/css_selectors.c \
|
||||
processinginstruction.c cdatasection.c \
|
||||
documentfragment.c domimplementation.c \
|
||||
element.c node.c characterdata.c \
|
||||
@ -38,6 +40,7 @@ if test "$PHP_DOM" != "no"; then
|
||||
namednodemap.c xpath_callbacks.c \
|
||||
$LEXBOR_SOURCES],
|
||||
$ext_shared,,$PHP_LEXBOR_CFLAGS)
|
||||
PHP_ADD_BUILD_DIR($ext_builddir/parentnode)
|
||||
PHP_ADD_BUILD_DIR($ext_builddir/$LEXBOR_DIR/ports/posix/lexbor/core)
|
||||
PHP_ADD_BUILD_DIR($ext_builddir/$LEXBOR_DIR/core)
|
||||
PHP_ADD_BUILD_DIR($ext_builddir/$LEXBOR_DIR/dom/interfaces)
|
||||
@ -45,7 +48,10 @@ if test "$PHP_DOM" != "no"; then
|
||||
PHP_ADD_BUILD_DIR($ext_builddir/$LEXBOR_DIR/html/tokenizer)
|
||||
PHP_ADD_BUILD_DIR($ext_builddir/$LEXBOR_DIR/html/interfaces)
|
||||
PHP_ADD_BUILD_DIR($ext_builddir/$LEXBOR_DIR/encoding)
|
||||
PHP_ADD_BUILD_DIR($ext_builddir/$LEXBOR_DIR/selectors)
|
||||
PHP_ADD_BUILD_DIR($ext_builddir/$LEXBOR_DIR/css/selectors)
|
||||
PHP_ADD_BUILD_DIR($ext_builddir/$LEXBOR_DIR/css/tokenizer)
|
||||
PHP_ADD_BUILD_DIR($ext_builddir/$LEXBOR_DIR/css/syntax/tokenizer)
|
||||
PHP_ADD_BUILD_DIR($ext_builddir/$LEXBOR_DIR/selectors-adapted)
|
||||
PHP_ADD_BUILD_DIR($ext_builddir/$LEXBOR_DIR/ns)
|
||||
PHP_ADD_BUILD_DIR($ext_builddir/$LEXBOR_DIR/tag)
|
||||
PHP_SUBST(DOM_SHARED_LIBADD)
|
||||
|
@ -9,7 +9,7 @@ if (PHP_DOM == "yes") {
|
||||
) {
|
||||
EXTENSION("dom", "php_dom.c attr.c document.c infra.c \
|
||||
xml_document.c html_document.c xml_serializer.c html5_serializer.c html5_parser.c namespace_compat.c \
|
||||
domexception.c parentnode.c processinginstruction.c \
|
||||
domexception.c processinginstruction.c \
|
||||
cdatasection.c documentfragment.c domimplementation.c element.c \
|
||||
node.c characterdata.c documenttype.c \
|
||||
entity.c nodelist.c html_collection.c text.c comment.c \
|
||||
@ -17,6 +17,7 @@ if (PHP_DOM == "yes") {
|
||||
notation.c xpath.c dom_iterators.c \
|
||||
namednodemap.c xpath_callbacks.c", null, "-Iext/dom/lexbor");
|
||||
|
||||
ADD_SOURCES("ext/dom/parentnode", "tree.c css_selectors.c", "dom");
|
||||
ADD_SOURCES("ext/dom/lexbor/lexbor/ports/windows_nt/lexbor/core", "memory.c", "dom");
|
||||
ADD_SOURCES("ext/dom/lexbor/lexbor/core", "array_obj.c array.c avl.c bst.c diyfp.c conv.c dobject.c dtoa.c hash.c mem.c mraw.c print.c serialize.c shs.c str.c strtod.c", "dom");
|
||||
ADD_SOURCES("ext/dom/lexbor/lexbor/dom", "interface.c", "dom");
|
||||
@ -27,7 +28,11 @@ if (PHP_DOM == "yes") {
|
||||
ADD_SOURCES("ext/dom/lexbor/lexbor/html", "encoding.c interface.c parser.c token.c token_attr.c tokenizer.c tree.c", "dom");
|
||||
ADD_SOURCES("ext/dom/lexbor/lexbor/encoding", "big5.c decode.c encode.c encoding.c euc_kr.c gb18030.c iso_2022_jp_katakana.c jis0208.c jis0212.c range.c res.c single.c", "dom");
|
||||
ADD_SOURCES("ext/dom/lexbor/lexbor/html/interfaces", "anchor_element.c area_element.c audio_element.c base_element.c body_element.c br_element.c button_element.c canvas_element.c data_element.c data_list_element.c details_element.c dialog_element.c directory_element.c div_element.c d_list_element.c document.c element.c embed_element.c field_set_element.c font_element.c form_element.c frame_element.c frame_set_element.c head_element.c heading_element.c hr_element.c html_element.c iframe_element.c image_element.c input_element.c label_element.c legend_element.c li_element.c link_element.c map_element.c marquee_element.c media_element.c menu_element.c meta_element.c meter_element.c mod_element.c object_element.c o_list_element.c opt_group_element.c option_element.c output_element.c paragraph_element.c param_element.c picture_element.c pre_element.c progress_element.c quote_element.c script_element.c select_element.c slot_element.c source_element.c span_element.c style_element.c table_caption_element.c table_cell_element.c table_col_element.c table_element.c table_row_element.c table_section_element.c template_element.c text_area_element.c time_element.c title_element.c track_element.c u_list_element.c unknown_element.c video_element.c window.c", "dom");
|
||||
ADD_SOURCES("ext/dom/lexbor/lexbor/selectors", "selectors.c", "dom");
|
||||
ADD_SOURCES("ext/dom/lexbor/lexbor/selectors-adapted", "selectors.c", "dom");
|
||||
ADD_SOURCES("ext/dom/lexbor/lexbor/css", "state.c log.c parser.c css.c", "dom");
|
||||
ADD_SOURCES("ext/dom/lexbor/lexbor/css/selectors", "state.c selectors.c selector.c pseudo_state.c pseudo.c", "dom");
|
||||
ADD_SOURCES("ext/dom/lexbor/lexbor/css/syntax", "state.c parser.c syntax.c anb.c tokenizer.c token.c", "dom");
|
||||
ADD_SOURCES("ext/dom/lexbor/lexbor/css/syntax/tokenizer", "error.c", "dom");
|
||||
ADD_SOURCES("ext/dom/lexbor/lexbor/ns", "ns.c", "dom");
|
||||
ADD_SOURCES("ext/dom/lexbor/lexbor/tag", "tag.c", "dom");
|
||||
ADD_FLAG("CFLAGS_DOM", "/D LEXBOR_STATIC ");
|
||||
|
@ -46,4 +46,7 @@ typedef enum {
|
||||
VALIDATION_ERR = 16
|
||||
} dom_exception_code;
|
||||
|
||||
void php_dom_throw_error(dom_exception_code error_code, bool strict_error);
|
||||
void php_dom_throw_error_with_message(dom_exception_code error_code, const char *error_message, bool strict_error);
|
||||
|
||||
#endif /* DOM_EXCEPTION_H */
|
||||
|
@ -1752,4 +1752,66 @@ out:
|
||||
}
|
||||
/* }}} end DOMElement::prepend */
|
||||
|
||||
static void php_dom_dispatch_query_selector(INTERNAL_FUNCTION_PARAMETERS, bool all)
|
||||
{
|
||||
zend_string *selectors_str;
|
||||
|
||||
ZEND_PARSE_PARAMETERS_START(1, 1)
|
||||
Z_PARAM_STR(selectors_str)
|
||||
ZEND_PARSE_PARAMETERS_END();
|
||||
|
||||
xmlNodePtr thisp;
|
||||
dom_object *intern;
|
||||
zval *id;
|
||||
DOM_GET_THIS_OBJ(thisp, id, xmlNodePtr, intern);
|
||||
|
||||
if (all) {
|
||||
dom_parent_node_query_selector_all(thisp, intern, return_value, selectors_str);
|
||||
} else {
|
||||
dom_parent_node_query_selector(thisp, intern, return_value, selectors_str);
|
||||
}
|
||||
}
|
||||
|
||||
PHP_METHOD(Dom_Element, querySelector)
|
||||
{
|
||||
php_dom_dispatch_query_selector(INTERNAL_FUNCTION_PARAM_PASSTHRU, false);
|
||||
}
|
||||
|
||||
PHP_METHOD(Dom_Element, querySelectorAll)
|
||||
{
|
||||
php_dom_dispatch_query_selector(INTERNAL_FUNCTION_PARAM_PASSTHRU, true);
|
||||
}
|
||||
|
||||
PHP_METHOD(Dom_Element, matches)
|
||||
{
|
||||
zend_string *selectors_str;
|
||||
|
||||
ZEND_PARSE_PARAMETERS_START(1, 1)
|
||||
Z_PARAM_STR(selectors_str)
|
||||
ZEND_PARSE_PARAMETERS_END();
|
||||
|
||||
xmlNodePtr thisp;
|
||||
dom_object *intern;
|
||||
zval *id;
|
||||
DOM_GET_THIS_OBJ(thisp, id, xmlNodePtr, intern);
|
||||
|
||||
dom_element_matches(thisp, intern, return_value, selectors_str);
|
||||
}
|
||||
|
||||
PHP_METHOD(Dom_Element, closest)
|
||||
{
|
||||
zend_string *selectors_str;
|
||||
|
||||
ZEND_PARSE_PARAMETERS_START(1, 1)
|
||||
Z_PARAM_STR(selectors_str)
|
||||
ZEND_PARSE_PARAMETERS_END();
|
||||
|
||||
xmlNodePtr thisp;
|
||||
dom_object *intern;
|
||||
zval *id;
|
||||
DOM_GET_THIS_OBJ(thisp, id, xmlNodePtr, intern);
|
||||
|
||||
dom_element_closest(thisp, intern, return_value, selectors_str);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -24,6 +24,7 @@
|
||||
#include "html5_parser.h"
|
||||
#include <lexbor/html/parser.h>
|
||||
#include <lexbor/html/interfaces/element.h>
|
||||
#include <lexbor/dom/dom.h>
|
||||
#include <libxml/parserInternals.h>
|
||||
#include <libxml/HTMLtree.h>
|
||||
#include <Zend/zend.h>
|
||||
@ -380,6 +381,7 @@ void lexbor_libxml2_bridge_copy_observations(lxb_html_tree_t *tree, lexbor_libxm
|
||||
observations->has_explicit_html_tag = tree->has_explicit_html_tag;
|
||||
observations->has_explicit_head_tag = tree->has_explicit_head_tag;
|
||||
observations->has_explicit_body_tag = tree->has_explicit_body_tag;
|
||||
observations->quirks_mode = lxb_dom_interface_document(tree->document)->compat_mode == LXB_DOM_DOCUMENT_CMODE_QUIRKS;
|
||||
}
|
||||
|
||||
#endif /* HAVE_LIBXML && HAVE_DOM */
|
||||
|
@ -47,6 +47,7 @@ typedef struct _lexbor_libxml2_bridge_extracted_observations {
|
||||
bool has_explicit_html_tag;
|
||||
bool has_explicit_head_tag;
|
||||
bool has_explicit_body_tag;
|
||||
bool quirks_mode;
|
||||
} lexbor_libxml2_bridge_extracted_observations;
|
||||
|
||||
typedef struct _lexbor_libxml2_bridge_parse_context {
|
||||
|
@ -917,6 +917,7 @@ PHP_METHOD(Dom_HTMLDocument, createFromString)
|
||||
NULL
|
||||
);
|
||||
dom_set_xml_class(intern->document);
|
||||
intern->document->quirks_mode = ctx.observations.quirks_mode;
|
||||
intern->document->private_data = php_dom_libxml_ns_mapper_header(ns_mapper);
|
||||
return;
|
||||
|
||||
@ -1137,6 +1138,7 @@ PHP_METHOD(Dom_HTMLDocument, createFromFile)
|
||||
NULL
|
||||
);
|
||||
dom_set_xml_class(intern->document);
|
||||
intern->document->quirks_mode = ctx.observations.quirks_mode;
|
||||
intern->document->private_data = php_dom_libxml_ns_mapper_header(ns_mapper);
|
||||
return;
|
||||
|
||||
|
1934
ext/dom/lexbor/lexbor/selectors-adapted/selectors.c
Normal file
1934
ext/dom/lexbor/lexbor/selectors-adapted/selectors.c
Normal file
File diff suppressed because it is too large
Load Diff
254
ext/dom/lexbor/lexbor/selectors-adapted/selectors.h
Normal file
254
ext/dom/lexbor/lexbor/selectors-adapted/selectors.h
Normal file
@ -0,0 +1,254 @@
|
||||
/*
|
||||
* Copyright (C) 2021-2024 Alexander Borisov
|
||||
*
|
||||
* Author: Alexander Borisov <borisov@lexbor.com>
|
||||
* Adapted for PHP libxml2 by: Niels Dossche <nielsdos@php.net>
|
||||
*/
|
||||
|
||||
|
||||
#ifndef LEXBOR_SELECTORS_H
|
||||
#define LEXBOR_SELECTORS_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "lexbor/selectors/base.h"
|
||||
#include "lexbor/dom/dom.h"
|
||||
#include "lexbor/css/selectors/selectors.h"
|
||||
#include "lexbor/core/array_obj.h"
|
||||
#include <libxml/tree.h>
|
||||
|
||||
|
||||
typedef enum {
|
||||
LXB_SELECTORS_OPT_DEFAULT = 0x00,
|
||||
|
||||
/*
|
||||
* Includes the passed (root) node in the search.
|
||||
*
|
||||
* By default, the root node does not participate in selector searches,
|
||||
* only its children.
|
||||
*
|
||||
* This behavior is logical, if you have found a node and then you want to
|
||||
* search for other nodes in it, you don't need to check it again.
|
||||
*
|
||||
* But there are cases when it is necessary for root node to participate
|
||||
* in the search. That's what this option is for.
|
||||
*/
|
||||
LXB_SELECTORS_OPT_MATCH_ROOT = 1 << 1,
|
||||
|
||||
/*
|
||||
* Stop searching after the first match with any of the selectors
|
||||
* in the list.
|
||||
*
|
||||
* By default, the callback will be triggered for each selector list.
|
||||
* That is, if your node matches different selector lists, it will be
|
||||
* returned multiple times in the callback.
|
||||
*
|
||||
* For example:
|
||||
* HTML: <div id="ok"><span>test</span></div>
|
||||
* Selectors: div, div[id="ok"], div:has(:not(a))
|
||||
*
|
||||
* The default behavior will cause three callbacks with the same node (div).
|
||||
* Because it will be found by every selector in the list.
|
||||
*
|
||||
* This option allows you to end the element check after the first match on
|
||||
* any of the selectors. That is, the callback will be called only once
|
||||
* for example above. This way we get rid of duplicates in the search.
|
||||
*/
|
||||
LXB_SELECTORS_OPT_MATCH_FIRST = 1 << 2,
|
||||
|
||||
/* Quirks mode (sigh) */
|
||||
LXB_SELECTORS_OPT_QUIRKS_MODE = 1 << 3,
|
||||
}
|
||||
lxb_selectors_opt_t;
|
||||
|
||||
typedef struct lxb_selectors lxb_selectors_t;
|
||||
typedef struct lxb_selectors_entry lxb_selectors_entry_t;
|
||||
typedef struct lxb_selectors_nested lxb_selectors_nested_t;
|
||||
|
||||
typedef lxb_status_t
|
||||
(*lxb_selectors_cb_f)(const xmlNode *node,
|
||||
lxb_css_selector_specificity_t spec, void *ctx);
|
||||
|
||||
typedef lxb_selectors_entry_t *
|
||||
(*lxb_selectors_state_cb_f)(lxb_selectors_t *selectors,
|
||||
lxb_selectors_entry_t *entry);
|
||||
|
||||
typedef struct {
|
||||
const xmlChar *name;
|
||||
bool interned;
|
||||
} lxb_selectors_adapted_id;
|
||||
|
||||
struct lxb_selectors_entry {
|
||||
lxb_selectors_adapted_id id;
|
||||
lxb_css_selector_combinator_t combinator;
|
||||
const lxb_css_selector_t *selector;
|
||||
const xmlNode *node;
|
||||
lxb_selectors_entry_t *next;
|
||||
lxb_selectors_entry_t *prev;
|
||||
lxb_selectors_entry_t *following;
|
||||
lxb_selectors_nested_t *nested;
|
||||
};
|
||||
|
||||
struct lxb_selectors_nested {
|
||||
lxb_selectors_entry_t *entry;
|
||||
lxb_selectors_state_cb_f return_state;
|
||||
|
||||
lxb_selectors_cb_f cb;
|
||||
void *ctx;
|
||||
|
||||
const xmlNode *root;
|
||||
lxb_selectors_entry_t *last;
|
||||
lxb_selectors_nested_t *parent;
|
||||
|
||||
size_t index;
|
||||
bool found;
|
||||
};
|
||||
|
||||
struct lxb_selectors {
|
||||
lxb_selectors_state_cb_f state;
|
||||
lexbor_dobject_t *objs;
|
||||
lexbor_dobject_t *nested;
|
||||
|
||||
lxb_selectors_nested_t *current;
|
||||
lxb_selectors_entry_t *first;
|
||||
|
||||
lxb_selectors_opt_t options;
|
||||
lxb_status_t status;
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* Initialization of lxb_selectors_t object.
|
||||
*
|
||||
* Caches are initialized in this function.
|
||||
*
|
||||
* @param[in] lxb_selectors_t *
|
||||
*
|
||||
* @return LXB_STATUS_OK if successful, otherwise an error status value.
|
||||
*/
|
||||
LXB_API lxb_status_t
|
||||
lxb_selectors_init(lxb_selectors_t *selectors);
|
||||
|
||||
/*
|
||||
* Clears the object. Returns object to states as after initialization.
|
||||
*
|
||||
* After each call to lxb_selectors_find() and lxb_selectors_find_for_node(),
|
||||
* the lxb_selectors_t object is cleared. That is, you don't need to call this
|
||||
* function every time after searching by a selector.
|
||||
*
|
||||
* @param[in] lxb_url_parser_t *
|
||||
*/
|
||||
LXB_API void
|
||||
lxb_selectors_clean(lxb_selectors_t *selectors);
|
||||
|
||||
/*
|
||||
* Destroy lxb_selectors_t object.
|
||||
*
|
||||
* Destroying all caches.
|
||||
*
|
||||
* @param[in] lxb_selectors_t *. Can be NULL.
|
||||
* if true: destroys the lxb_selectors_t object and all internal caches.
|
||||
*/
|
||||
LXB_API void
|
||||
lxb_selectors_destroy(lxb_selectors_t *selectors);
|
||||
|
||||
/*
|
||||
* Search for nodes by selector list.
|
||||
*
|
||||
* Default Behavior:
|
||||
* 1. The root node does not participate in the search, only its child nodes.
|
||||
* 2. If a node matches multiple selector lists, a callback with that node
|
||||
* will be called on each list.
|
||||
* For example:
|
||||
* HTML: <div id="ok"><span></span></div>
|
||||
* Selectors: div, div[id="ok"], div:has(:not(a))
|
||||
* For each selector list, a callback with a "div" node will be called.
|
||||
*
|
||||
* To change the search behavior, see lxb_selectors_opt_set().
|
||||
*
|
||||
* @param[in] lxb_selectors_t *.
|
||||
* @param[in] const xmlNode *. The node from which the search will begin.
|
||||
* @param[in] const lxb_css_selector_list_t *. Selectors List.
|
||||
* @param[in] lxb_selectors_cb_f. Callback for a found node.
|
||||
* @param[in] void *. Context for the callback.
|
||||
* if true: destroys the lxb_selectors_t object and all internal caches.
|
||||
*
|
||||
* @return LXB_STATUS_OK if successful, otherwise an error status value.
|
||||
*/
|
||||
LXB_API lxb_status_t
|
||||
lxb_selectors_find(lxb_selectors_t *selectors, const xmlNode *root,
|
||||
const lxb_css_selector_list_t *list,
|
||||
lxb_selectors_cb_f cb, void *ctx);
|
||||
|
||||
/*
|
||||
* Match a node to a Selectors List.
|
||||
*
|
||||
* In other words, the function checks which selector lists will find the
|
||||
* specified node.
|
||||
*
|
||||
* Default Behavior:
|
||||
* 1. If a node matches multiple selector lists, a callback with that node
|
||||
* will be called on each list.
|
||||
* For example:
|
||||
* HTML: <div id="ok"><span></span></div>
|
||||
* Node: div
|
||||
* Selectors: div, div[id="ok"], div:has(:not(a))
|
||||
* For each selector list, a callback with a "div" node will be called.
|
||||
*
|
||||
* To change the search behavior, see lxb_selectors_opt_set().
|
||||
*
|
||||
* @param[in] lxb_selectors_t *.
|
||||
* @param[in] const xmlNode *. The node from which the search will begin.
|
||||
* @param[in] const lxb_css_selector_list_t *. Selectors List.
|
||||
* @param[in] lxb_selectors_cb_f. Callback for a found node.
|
||||
* @param[in] void *. Context for the callback.
|
||||
* if true: destroys the lxb_selectors_t object and all internal caches.
|
||||
*
|
||||
* @return LXB_STATUS_OK if successful, otherwise an error status value.
|
||||
*/
|
||||
LXB_API lxb_status_t
|
||||
lxb_selectors_match_node(lxb_selectors_t *selectors, const xmlNode *node,
|
||||
const lxb_css_selector_list_t *list,
|
||||
lxb_selectors_cb_f cb, void *ctx);
|
||||
|
||||
/*
|
||||
* Inline functions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* The function sets the node search options.
|
||||
*
|
||||
* For more information, see lxb_selectors_opt_t.
|
||||
*
|
||||
* @param[in] lxb_selectors_t *.
|
||||
* @param[in] lxb_selectors_opt_t.
|
||||
*/
|
||||
lxb_inline void
|
||||
lxb_selectors_opt_set(lxb_selectors_t *selectors, lxb_selectors_opt_t opt)
|
||||
{
|
||||
selectors->options = opt;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the current selector.
|
||||
*
|
||||
* Function to get the selector by which the node was found.
|
||||
* Use context (void *ctx) to pass the lxb_selectors_t object to the callback.
|
||||
*
|
||||
* @param[in] const lxb_selectors_t *.
|
||||
*
|
||||
* @return const lxb_css_selector_list_t *.
|
||||
*/
|
||||
lxb_inline const lxb_css_selector_list_t *
|
||||
lxb_selectors_selector(const lxb_selectors_t *selectors)
|
||||
{
|
||||
return selectors->current->entry->selector->list;
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif
|
||||
|
||||
#endif /* LEXBOR_SELECTORS_H */
|
282
ext/dom/parentnode/css_selectors.c
Normal file
282
ext/dom/parentnode/css_selectors.c
Normal file
@ -0,0 +1,282 @@
|
||||
/*
|
||||
+----------------------------------------------------------------------+
|
||||
| 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: |
|
||||
| https://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: Niels Dossche <nielsdos@php.net> |
|
||||
+----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "php.h"
|
||||
#if defined(HAVE_LIBXML) && defined(HAVE_DOM)
|
||||
#include "../php_dom.h"
|
||||
|
||||
#include "lexbor/css/parser.h"
|
||||
#include "lexbor/selectors-adapted/selectors.h"
|
||||
|
||||
// TODO: optimization idea: cache the parsed selectors in an LRU fashion?
|
||||
|
||||
typedef struct {
|
||||
HashTable *list;
|
||||
dom_object *intern;
|
||||
} dom_query_selector_all_ctx;
|
||||
|
||||
typedef struct {
|
||||
const xmlNode *reference;
|
||||
bool result;
|
||||
} dom_query_selector_matches_ctx;
|
||||
|
||||
static lxb_selectors_opt_t dom_quirks_opt(lxb_selectors_opt_t options, const dom_object *intern)
|
||||
{
|
||||
if (intern->document != NULL && intern->document->quirks_mode) {
|
||||
options |= LXB_SELECTORS_OPT_QUIRKS_MODE;
|
||||
}
|
||||
return options;
|
||||
}
|
||||
|
||||
lxb_status_t dom_query_selector_find_single_callback(const xmlNode *node, lxb_css_selector_specificity_t spec, void *ctx)
|
||||
{
|
||||
xmlNodePtr *result = (xmlNodePtr *) ctx;
|
||||
*result = (xmlNodePtr) node;
|
||||
return LXB_STATUS_STOP;
|
||||
}
|
||||
|
||||
lxb_status_t dom_query_selector_find_array_callback(const xmlNode *node, lxb_css_selector_specificity_t spec, void *ctx)
|
||||
{
|
||||
dom_query_selector_all_ctx *qsa_ctx = (dom_query_selector_all_ctx *) ctx;
|
||||
zval object;
|
||||
php_dom_create_object((xmlNodePtr) node, &object, qsa_ctx->intern);
|
||||
zend_hash_next_index_insert_new(qsa_ctx->list, &object);
|
||||
return LXB_STATUS_OK;
|
||||
}
|
||||
|
||||
lxb_status_t dom_query_selector_find_matches_callback(const xmlNode *node, lxb_css_selector_specificity_t spec, void *ctx)
|
||||
{
|
||||
dom_query_selector_matches_ctx *matches_ctx = (dom_query_selector_matches_ctx *) ctx;
|
||||
if (node == matches_ctx->reference) {
|
||||
matches_ctx->result = true;
|
||||
return LXB_STATUS_STOP;
|
||||
}
|
||||
return LXB_STATUS_OK;
|
||||
}
|
||||
|
||||
static lxb_css_selector_list_t *dom_parse_selector(
|
||||
lxb_css_parser_t *parser,
|
||||
lxb_selectors_t *selectors,
|
||||
const zend_string *selectors_str,
|
||||
lxb_selectors_opt_t options,
|
||||
const dom_object *intern
|
||||
)
|
||||
{
|
||||
lxb_status_t status;
|
||||
|
||||
memset(parser, 0, sizeof(lxb_css_parser_t));
|
||||
status = lxb_css_parser_init(parser, NULL);
|
||||
ZEND_ASSERT(status == LXB_STATUS_OK);
|
||||
|
||||
memset(selectors, 0, sizeof(lxb_selectors_t));
|
||||
status = lxb_selectors_init(selectors);
|
||||
ZEND_ASSERT(status == LXB_STATUS_OK);
|
||||
lxb_selectors_opt_set(selectors, dom_quirks_opt(options, intern));
|
||||
|
||||
lxb_css_selector_list_t *list = lxb_css_selectors_parse(parser, (const lxb_char_t *) ZSTR_VAL(selectors_str), ZSTR_LEN(selectors_str));
|
||||
if (UNEXPECTED(list == NULL)) {
|
||||
size_t nr_of_messages = lexbor_array_obj_length(&parser->log->messages);
|
||||
if (nr_of_messages > 0) {
|
||||
lxb_css_log_message_t *msg = lexbor_array_obj_get(&parser->log->messages, 0);
|
||||
char *error;
|
||||
zend_spprintf(&error, 0, "Invalid selector (%.*s)", (int) msg->text.length, msg->text.data);
|
||||
php_dom_throw_error_with_message(SYNTAX_ERR, error, true);
|
||||
efree(error);
|
||||
} else {
|
||||
php_dom_throw_error_with_message(SYNTAX_ERR, "Invalid selector", true);
|
||||
}
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
static lxb_status_t dom_check_css_execution_status(lxb_status_t status)
|
||||
{
|
||||
if (UNEXPECTED(status != LXB_STATUS_OK && status != LXB_STATUS_STOP)) {
|
||||
zend_argument_value_error(1, "contains an unsupported selector");
|
||||
return status;
|
||||
}
|
||||
return LXB_STATUS_OK;
|
||||
}
|
||||
|
||||
static void dom_selector_cleanup(lxb_css_parser_t *parser, lxb_selectors_t *selectors, lxb_css_selector_list_t *list)
|
||||
{
|
||||
lxb_css_selector_list_destroy_memory(list);
|
||||
lxb_selectors_destroy(selectors);
|
||||
(void) lxb_css_parser_destroy(parser, false);
|
||||
}
|
||||
|
||||
static lxb_status_t dom_query_selector_common(
|
||||
const xmlNode *root,
|
||||
const dom_object *intern,
|
||||
const zend_string *selectors_str,
|
||||
lxb_selectors_cb_f cb,
|
||||
void *ctx,
|
||||
lxb_selectors_opt_t options
|
||||
)
|
||||
{
|
||||
lxb_status_t status;
|
||||
|
||||
lxb_css_parser_t parser;
|
||||
lxb_selectors_t selectors;
|
||||
|
||||
lxb_css_selector_list_t *list = dom_parse_selector(&parser, &selectors, selectors_str, options, intern);
|
||||
if (UNEXPECTED(list == NULL)) {
|
||||
status = LXB_STATUS_ERROR;
|
||||
} else {
|
||||
status = lxb_selectors_find(&selectors, root, list, cb, ctx);
|
||||
status = dom_check_css_execution_status(status);
|
||||
}
|
||||
|
||||
dom_selector_cleanup(&parser, &selectors, list);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static lxb_status_t dom_query_matches(
|
||||
const xmlNode *root,
|
||||
const dom_object *intern,
|
||||
const zend_string *selectors_str,
|
||||
void *ctx
|
||||
)
|
||||
{
|
||||
lxb_status_t status;
|
||||
|
||||
lxb_css_parser_t parser;
|
||||
lxb_selectors_t selectors;
|
||||
|
||||
lxb_css_selector_list_t *list = dom_parse_selector(&parser, &selectors, selectors_str, LXB_SELECTORS_OPT_MATCH_FIRST, intern);
|
||||
if (UNEXPECTED(list == NULL)) {
|
||||
status = LXB_STATUS_ERROR;
|
||||
} else {
|
||||
status = lxb_selectors_match_node(&selectors, root, list, dom_query_selector_find_matches_callback, ctx);
|
||||
status = dom_check_css_execution_status(status);
|
||||
}
|
||||
|
||||
dom_selector_cleanup(&parser, &selectors, list);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static const xmlNode *dom_query_closest(
|
||||
const xmlNode *root,
|
||||
const dom_object *intern,
|
||||
const zend_string *selectors_str
|
||||
)
|
||||
{
|
||||
const xmlNode *ret = NULL;
|
||||
|
||||
lxb_css_parser_t parser;
|
||||
lxb_selectors_t selectors;
|
||||
|
||||
lxb_css_selector_list_t *list = dom_parse_selector(&parser, &selectors, selectors_str, LXB_SELECTORS_OPT_MATCH_FIRST, intern);
|
||||
if (EXPECTED(list != NULL)) {
|
||||
const xmlNode *current = root;
|
||||
while (current != NULL) {
|
||||
dom_query_selector_matches_ctx ctx = { current, false };
|
||||
lxb_status_t status = lxb_selectors_match_node(&selectors, current, list, dom_query_selector_find_matches_callback, &ctx);
|
||||
status = dom_check_css_execution_status(status);
|
||||
if (UNEXPECTED(status != LXB_STATUS_OK)) {
|
||||
break;
|
||||
}
|
||||
if (ctx.result) {
|
||||
ret = current;
|
||||
break;
|
||||
}
|
||||
current = current->parent;
|
||||
}
|
||||
}
|
||||
|
||||
dom_selector_cleanup(&parser, &selectors, list);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* https://dom.spec.whatwg.org/#dom-parentnode-queryselector */
|
||||
void dom_parent_node_query_selector(xmlNodePtr thisp, dom_object *intern, zval *return_value, const zend_string *selectors_str)
|
||||
{
|
||||
xmlNodePtr result = NULL;
|
||||
|
||||
if (dom_query_selector_common(
|
||||
thisp,
|
||||
intern,
|
||||
selectors_str,
|
||||
dom_query_selector_find_single_callback,
|
||||
&result,
|
||||
LXB_SELECTORS_OPT_MATCH_FIRST
|
||||
) != LXB_STATUS_OK || result == NULL) {
|
||||
RETURN_NULL();
|
||||
} else {
|
||||
DOM_RET_OBJ(result, intern);
|
||||
}
|
||||
}
|
||||
|
||||
/* https://dom.spec.whatwg.org/#dom-parentnode-queryselectorall */
|
||||
void dom_parent_node_query_selector_all(xmlNodePtr thisp, dom_object *intern, zval *return_value, const zend_string *selectors_str)
|
||||
{
|
||||
HashTable *list = zend_new_array(0);
|
||||
dom_query_selector_all_ctx ctx = { list, intern };
|
||||
|
||||
if (dom_query_selector_common(
|
||||
thisp,
|
||||
intern,
|
||||
selectors_str,
|
||||
dom_query_selector_find_array_callback,
|
||||
&ctx,
|
||||
LXB_SELECTORS_OPT_DEFAULT
|
||||
) != LXB_STATUS_OK) {
|
||||
zend_array_destroy(list);
|
||||
RETURN_THROWS();
|
||||
} else {
|
||||
php_dom_create_iterator(return_value, DOM_NODELIST, true);
|
||||
dom_object *ret_obj = Z_DOMOBJ_P(return_value);
|
||||
dom_nnodemap_object *mapptr = (dom_nnodemap_object *) ret_obj->ptr;
|
||||
ZVAL_ARR(&mapptr->baseobj_zv, list);
|
||||
mapptr->nodetype = DOM_NODESET;
|
||||
}
|
||||
}
|
||||
|
||||
/* https://dom.spec.whatwg.org/#dom-element-matches */
|
||||
void dom_element_matches(xmlNodePtr thisp, dom_object *intern, zval *return_value, const zend_string *selectors_str)
|
||||
{
|
||||
dom_query_selector_matches_ctx ctx = { thisp, false };
|
||||
|
||||
if (dom_query_matches(
|
||||
thisp,
|
||||
intern,
|
||||
selectors_str,
|
||||
&ctx
|
||||
) != LXB_STATUS_OK) {
|
||||
RETURN_THROWS();
|
||||
} else {
|
||||
RETURN_BOOL(ctx.result);
|
||||
}
|
||||
}
|
||||
|
||||
/* https://dom.spec.whatwg.org/#dom-element-closest */
|
||||
void dom_element_closest(xmlNodePtr thisp, dom_object *intern, zval *return_value, const zend_string *selectors_str)
|
||||
{
|
||||
const xmlNode *result = dom_query_closest(thisp, intern, selectors_str);
|
||||
if (EXPECTED(result != NULL)) {
|
||||
DOM_RET_OBJ((xmlNodePtr) result, intern);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
@ -1,6 +1,4 @@
|
||||
/*
|
||||
+----------------------------------------------------------------------+
|
||||
| PHP Version 7 |
|
||||
+----------------------------------------------------------------------+
|
||||
| Copyright (c) The PHP Group |
|
||||
+----------------------------------------------------------------------+
|
||||
@ -23,9 +21,9 @@
|
||||
|
||||
#include "php.h"
|
||||
#if defined(HAVE_LIBXML) && defined(HAVE_DOM)
|
||||
#include "php_dom.h"
|
||||
#include "internal_helpers.h"
|
||||
#include "dom_properties.h"
|
||||
#include "../php_dom.h"
|
||||
#include "../internal_helpers.h"
|
||||
#include "../dom_properties.h"
|
||||
|
||||
/* {{{ firstElementChild DomParentNode
|
||||
readonly=yes
|
@ -130,8 +130,6 @@ zend_object *dom_nnodemap_objects_new(zend_class_entry *class_type);
|
||||
zend_object *dom_xpath_objects_new(zend_class_entry *class_type);
|
||||
#endif
|
||||
bool dom_get_strict_error(php_libxml_ref_obj *document);
|
||||
void php_dom_throw_error(dom_exception_code error_code, bool strict_error);
|
||||
void php_dom_throw_error_with_message(dom_exception_code error_code, const char *error_message, bool strict_error);
|
||||
void node_list_unlink(xmlNodePtr node);
|
||||
int dom_check_qname(char *qname, char **localname, char **prefix, int uri_len, int name_len);
|
||||
xmlNsPtr dom_get_ns(xmlNodePtr node, char *uri, int *errorcode, char *prefix);
|
||||
@ -199,6 +197,10 @@ bool php_dom_fragment_insertion_hierarchy_check_replace(xmlNodePtr parent, xmlNo
|
||||
void php_dom_node_append(php_libxml_ref_obj *document, xmlNodePtr node, xmlNodePtr parent);
|
||||
bool php_dom_pre_insert(php_libxml_ref_obj *document, xmlNodePtr node, xmlNodePtr parent, xmlNodePtr insertion_point);
|
||||
bool php_dom_pre_insert_is_parent_invalid(xmlNodePtr parent);
|
||||
void dom_parent_node_query_selector(xmlNodePtr thisp, dom_object *intern, zval *return_value, const zend_string *selectors_str);
|
||||
void dom_parent_node_query_selector_all(xmlNodePtr thisp, dom_object *intern, zval *return_value, const zend_string *selectors_str);
|
||||
void dom_element_matches(xmlNodePtr thisp, dom_object *intern, zval *return_value, const zend_string *selectors_str);
|
||||
void dom_element_closest(xmlNodePtr thisp, dom_object *intern, zval *return_value, const zend_string *selectors_str);
|
||||
|
||||
/* nodemap and nodelist APIs */
|
||||
xmlNodePtr php_dom_named_node_map_get_named_item(dom_nnodemap_object *objmap, const zend_string *named, bool may_transform);
|
||||
|
@ -1106,6 +1106,9 @@ namespace Dom
|
||||
public function append(Node|string ...$nodes): void;
|
||||
public function prepend(Node|string ...$nodes): void;
|
||||
public function replaceChildren(Node|string ...$nodes): void;
|
||||
|
||||
public function querySelector(string $selectors): ?Element;
|
||||
public function querySelectorAll(string $selectors): NodeList;
|
||||
}
|
||||
|
||||
interface ChildNode
|
||||
@ -1372,6 +1375,11 @@ namespace Dom
|
||||
public function prepend(Node|string ...$nodes): void {}
|
||||
/** @implementation-alias DOMElement::replaceChildren */
|
||||
public function replaceChildren(Node|string ...$nodes): void {}
|
||||
|
||||
public function querySelector(string $selectors): ?Element {}
|
||||
public function querySelectorAll(string $selectors): NodeList {}
|
||||
public function closest(string $selectors): ?Element {}
|
||||
public function matches(string $selectors): bool {}
|
||||
}
|
||||
|
||||
class HTMLElement extends Element
|
||||
@ -1492,6 +1500,11 @@ namespace Dom
|
||||
public function prepend(Node|string ...$nodes): void {}
|
||||
/** @implementation-alias DOMElement::replaceChildren */
|
||||
public function replaceChildren(Node|string ...$nodes): void {}
|
||||
|
||||
/** @implementation-alias Dom\Element::querySelector */
|
||||
public function querySelector(string $selectors): ?Element {}
|
||||
/** @implementation-alias Dom\Element::querySelectorAll */
|
||||
public function querySelectorAll(string $selectors): NodeList {}
|
||||
}
|
||||
|
||||
class Entity extends Node
|
||||
@ -1585,6 +1598,11 @@ namespace Dom
|
||||
|
||||
public function importLegacyNode(\DOMNode $node, bool $deep = false): Node {}
|
||||
|
||||
/** @implementation-alias Dom\Element::querySelector */
|
||||
public function querySelector(string $selectors): ?Element {}
|
||||
/** @implementation-alias Dom\Element::querySelectorAll */
|
||||
public function querySelectorAll(string $selectors): NodeList {}
|
||||
|
||||
public ?HTMLElement $body;
|
||||
/** @readonly */
|
||||
public ?HTMLElement $head;
|
||||
|
42
ext/dom/php_dom_arginfo.h
generated
42
ext/dom/php_dom_arginfo.h
generated
@ -1,5 +1,5 @@
|
||||
/* This is a generated file, edit the .stub.php file instead.
|
||||
* Stub hash: fd650b1e64c4ed4ce66b0dad9c681a51cb8ff1ae */
|
||||
* Stub hash: 28365949d78a2d0254cfdb0da6549e282d2eb436 */
|
||||
|
||||
ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_dom_import_simplexml, 0, 1, DOMElement, 0)
|
||||
ZEND_ARG_TYPE_INFO(0, node, IS_OBJECT, 0)
|
||||
@ -567,6 +567,14 @@ ZEND_END_ARG_INFO()
|
||||
|
||||
#define arginfo_class_Dom_ParentNode_replaceChildren arginfo_class_Dom_ParentNode_append
|
||||
|
||||
ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_Dom_ParentNode_querySelector, 0, 1, Dom\\Element, 1)
|
||||
ZEND_ARG_TYPE_INFO(0, selectors, IS_STRING, 0)
|
||||
ZEND_END_ARG_INFO()
|
||||
|
||||
ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_Dom_ParentNode_querySelectorAll, 0, 1, Dom\\\116odeList, 0)
|
||||
ZEND_ARG_TYPE_INFO(0, selectors, IS_STRING, 0)
|
||||
ZEND_END_ARG_INFO()
|
||||
|
||||
#define arginfo_class_Dom_ChildNode_remove arginfo_class_DOMChildNode_remove
|
||||
|
||||
#define arginfo_class_Dom_ChildNode_before arginfo_class_Dom_ParentNode_append
|
||||
@ -836,6 +844,16 @@ ZEND_END_ARG_INFO()
|
||||
|
||||
#define arginfo_class_Dom_Element_replaceChildren arginfo_class_Dom_ParentNode_append
|
||||
|
||||
#define arginfo_class_Dom_Element_querySelector arginfo_class_Dom_ParentNode_querySelector
|
||||
|
||||
#define arginfo_class_Dom_Element_querySelectorAll arginfo_class_Dom_ParentNode_querySelectorAll
|
||||
|
||||
#define arginfo_class_Dom_Element_closest arginfo_class_Dom_ParentNode_querySelector
|
||||
|
||||
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Dom_Element_matches, 0, 1, _IS_BOOL, 0)
|
||||
ZEND_ARG_TYPE_INFO(0, selectors, IS_STRING, 0)
|
||||
ZEND_END_ARG_INFO()
|
||||
|
||||
#define arginfo_class_Dom_Attr_isId arginfo_class_Dom_Node_hasChildNodes
|
||||
|
||||
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Dom_CharacterData_substringData, 0, 2, IS_STRING, 0)
|
||||
@ -893,6 +911,10 @@ ZEND_END_ARG_INFO()
|
||||
|
||||
#define arginfo_class_Dom_DocumentFragment_replaceChildren arginfo_class_Dom_ParentNode_append
|
||||
|
||||
#define arginfo_class_Dom_DocumentFragment_querySelector arginfo_class_Dom_ParentNode_querySelector
|
||||
|
||||
#define arginfo_class_Dom_DocumentFragment_querySelectorAll arginfo_class_Dom_ParentNode_querySelectorAll
|
||||
|
||||
#define arginfo_class_Dom_Document_getElementsByTagName arginfo_class_Dom_Element_getElementsByTagName
|
||||
|
||||
#define arginfo_class_Dom_Document_getElementsByTagNameNS arginfo_class_Dom_Element_getElementsByTagNameNS
|
||||
@ -988,6 +1010,10 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_Dom_Document_importLegacyNo
|
||||
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, deep, _IS_BOOL, 0, "false")
|
||||
ZEND_END_ARG_INFO()
|
||||
|
||||
#define arginfo_class_Dom_Document_querySelector arginfo_class_Dom_ParentNode_querySelector
|
||||
|
||||
#define arginfo_class_Dom_Document_querySelectorAll arginfo_class_Dom_ParentNode_querySelectorAll
|
||||
|
||||
ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_Dom_HTMLDocument_createEmpty, 0, 0, Dom\\HTMLDocument, 0)
|
||||
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, encoding, IS_STRING, 0, "\"UTF-8\"")
|
||||
ZEND_END_ARG_INFO()
|
||||
@ -1272,6 +1298,10 @@ ZEND_METHOD(Dom_Element, getElementsByTagNameNS);
|
||||
ZEND_METHOD(Dom_Element, insertAdjacentElement);
|
||||
ZEND_METHOD(Dom_Element, insertAdjacentText);
|
||||
ZEND_METHOD(Dom_Element, setIdAttributeNode);
|
||||
ZEND_METHOD(Dom_Element, querySelector);
|
||||
ZEND_METHOD(Dom_Element, querySelectorAll);
|
||||
ZEND_METHOD(Dom_Element, closest);
|
||||
ZEND_METHOD(Dom_Element, matches);
|
||||
ZEND_METHOD(Dom_CharacterData, appendData);
|
||||
ZEND_METHOD(Dom_CharacterData, insertData);
|
||||
ZEND_METHOD(Dom_CharacterData, deleteData);
|
||||
@ -1559,6 +1589,8 @@ static const zend_function_entry class_Dom_ParentNode_methods[] = {
|
||||
ZEND_RAW_FENTRY("append", NULL, arginfo_class_Dom_ParentNode_append, ZEND_ACC_PUBLIC|ZEND_ACC_ABSTRACT, NULL, NULL)
|
||||
ZEND_RAW_FENTRY("prepend", NULL, arginfo_class_Dom_ParentNode_prepend, ZEND_ACC_PUBLIC|ZEND_ACC_ABSTRACT, NULL, NULL)
|
||||
ZEND_RAW_FENTRY("replaceChildren", NULL, arginfo_class_Dom_ParentNode_replaceChildren, ZEND_ACC_PUBLIC|ZEND_ACC_ABSTRACT, NULL, NULL)
|
||||
ZEND_RAW_FENTRY("querySelector", NULL, arginfo_class_Dom_ParentNode_querySelector, ZEND_ACC_PUBLIC|ZEND_ACC_ABSTRACT, NULL, NULL)
|
||||
ZEND_RAW_FENTRY("querySelectorAll", NULL, arginfo_class_Dom_ParentNode_querySelectorAll, ZEND_ACC_PUBLIC|ZEND_ACC_ABSTRACT, NULL, NULL)
|
||||
ZEND_FE_END
|
||||
};
|
||||
|
||||
@ -1671,6 +1703,10 @@ static const zend_function_entry class_Dom_Element_methods[] = {
|
||||
ZEND_RAW_FENTRY("append", zim_DOMElement_append, arginfo_class_Dom_Element_append, ZEND_ACC_PUBLIC, NULL, NULL)
|
||||
ZEND_RAW_FENTRY("prepend", zim_DOMElement_prepend, arginfo_class_Dom_Element_prepend, ZEND_ACC_PUBLIC, NULL, NULL)
|
||||
ZEND_RAW_FENTRY("replaceChildren", zim_DOMElement_replaceChildren, arginfo_class_Dom_Element_replaceChildren, ZEND_ACC_PUBLIC, NULL, NULL)
|
||||
ZEND_ME(Dom_Element, querySelector, arginfo_class_Dom_Element_querySelector, ZEND_ACC_PUBLIC)
|
||||
ZEND_ME(Dom_Element, querySelectorAll, arginfo_class_Dom_Element_querySelectorAll, ZEND_ACC_PUBLIC)
|
||||
ZEND_ME(Dom_Element, closest, arginfo_class_Dom_Element_closest, ZEND_ACC_PUBLIC)
|
||||
ZEND_ME(Dom_Element, matches, arginfo_class_Dom_Element_matches, ZEND_ACC_PUBLIC)
|
||||
ZEND_FE_END
|
||||
};
|
||||
|
||||
@ -1726,6 +1762,8 @@ static const zend_function_entry class_Dom_DocumentFragment_methods[] = {
|
||||
ZEND_RAW_FENTRY("append", zim_DOMElement_append, arginfo_class_Dom_DocumentFragment_append, ZEND_ACC_PUBLIC, NULL, NULL)
|
||||
ZEND_RAW_FENTRY("prepend", zim_DOMElement_prepend, arginfo_class_Dom_DocumentFragment_prepend, ZEND_ACC_PUBLIC, NULL, NULL)
|
||||
ZEND_RAW_FENTRY("replaceChildren", zim_DOMElement_replaceChildren, arginfo_class_Dom_DocumentFragment_replaceChildren, ZEND_ACC_PUBLIC, NULL, NULL)
|
||||
ZEND_RAW_FENTRY("querySelector", zim_Dom_Element_querySelector, arginfo_class_Dom_DocumentFragment_querySelector, ZEND_ACC_PUBLIC, NULL, NULL)
|
||||
ZEND_RAW_FENTRY("querySelectorAll", zim_Dom_Element_querySelectorAll, arginfo_class_Dom_DocumentFragment_querySelectorAll, ZEND_ACC_PUBLIC, NULL, NULL)
|
||||
ZEND_FE_END
|
||||
};
|
||||
|
||||
@ -1773,6 +1811,8 @@ static const zend_function_entry class_Dom_Document_methods[] = {
|
||||
ZEND_RAW_FENTRY("prepend", zim_DOMElement_prepend, arginfo_class_Dom_Document_prepend, ZEND_ACC_PUBLIC, NULL, NULL)
|
||||
ZEND_RAW_FENTRY("replaceChildren", zim_DOMDocument_replaceChildren, arginfo_class_Dom_Document_replaceChildren, ZEND_ACC_PUBLIC, NULL, NULL)
|
||||
ZEND_ME(Dom_Document, importLegacyNode, arginfo_class_Dom_Document_importLegacyNode, ZEND_ACC_PUBLIC)
|
||||
ZEND_RAW_FENTRY("querySelector", zim_Dom_Element_querySelector, arginfo_class_Dom_Document_querySelector, ZEND_ACC_PUBLIC, NULL, NULL)
|
||||
ZEND_RAW_FENTRY("querySelectorAll", zim_Dom_Element_querySelectorAll, arginfo_class_Dom_Document_querySelectorAll, ZEND_ACC_PUBLIC, NULL, NULL)
|
||||
ZEND_FE_END
|
||||
};
|
||||
|
||||
|
118
ext/dom/tests/modern/css_selectors/attribute.phpt
Normal file
118
ext/dom/tests/modern/css_selectors/attribute.phpt
Normal file
@ -0,0 +1,118 @@
|
||||
--TEST--
|
||||
CSS Selectors - Attribute
|
||||
--EXTENSIONS--
|
||||
dom
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
require __DIR__ . '/test_utils.inc';
|
||||
|
||||
$dom = DOM\XMLDocument::createFromString(<<<XML
|
||||
<container>
|
||||
<a title="http://example.com" lang="en-us"/>
|
||||
<a title="http://example.be"/>
|
||||
<a title="ftp://example.be" lang="nl-be" tokens="abc def ghi"/>
|
||||
<a title="ftp://example.nl" lang="nl-be"/>
|
||||
</container>
|
||||
XML);
|
||||
|
||||
echo "=== Case sensitive ===\n";
|
||||
|
||||
test_helper($dom, 'a[title]');
|
||||
test_helper($dom, 'a[title="http://example.com"]');
|
||||
test_helper($dom, 'a[title="http://example."]');
|
||||
test_helper($dom, 'a[title*="example"]');
|
||||
test_helper($dom, 'a[title*=""]');
|
||||
test_helper($dom, 'a[title^="HTTP"]');
|
||||
test_helper($dom, 'a[title^="http"]');
|
||||
test_helper($dom, 'a[title^="http"][title$=".be"]');
|
||||
test_helper($dom, 'a[title$=".com"]');
|
||||
test_helper($dom, 'a[title$=".foo"]');
|
||||
test_helper($dom, 'a[lang|="nl"]');
|
||||
test_helper($dom, 'a[lang|="nl-be"]');
|
||||
test_helper($dom, 'a[tokens~="def"]');
|
||||
test_helper($dom, 'a[tokens~="de"]');
|
||||
test_helper($dom, 'a[tokens~="def ghi"]');
|
||||
|
||||
echo "=== Case insensitive ===\n";
|
||||
|
||||
test_helper($dom, 'a[title]');
|
||||
test_helper($dom, 'a[title="http://example.COM" i]');
|
||||
test_helper($dom, 'a[title="http://EXAMPLE." i]');
|
||||
test_helper($dom, 'a[title*="ExAmPlE" i]');
|
||||
test_helper($dom, 'a[title^="HTTP" i]');
|
||||
test_helper($dom, 'a[title^="HTTP" i][title$=".be"]');
|
||||
test_helper($dom, 'a[title$=".COM" i]');
|
||||
test_helper($dom, 'a[lang|="NL" i]');
|
||||
test_helper($dom, 'a[lang|="NL-BE" i]');
|
||||
test_helper($dom, 'a[tokens~="DE" i]');
|
||||
test_helper($dom, 'a[tokens~="DEF" i]');
|
||||
test_helper($dom, 'a[tokens~="DEF GHI" i]');
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
=== Case sensitive ===
|
||||
--- Selector: a[title] ---
|
||||
<a title="http://example.com" lang="en-us"/>
|
||||
<a title="http://example.be"/>
|
||||
<a title="ftp://example.be" lang="nl-be" tokens="abc def ghi"/>
|
||||
<a title="ftp://example.nl" lang="nl-be"/>
|
||||
--- Selector: a[title="http://example.com"] ---
|
||||
<a title="http://example.com" lang="en-us"/>
|
||||
--- Selector: a[title="http://example."] ---
|
||||
--- Selector: a[title*="example"] ---
|
||||
<a title="http://example.com" lang="en-us"/>
|
||||
<a title="http://example.be"/>
|
||||
<a title="ftp://example.be" lang="nl-be" tokens="abc def ghi"/>
|
||||
<a title="ftp://example.nl" lang="nl-be"/>
|
||||
--- Selector: a[title*=""] ---
|
||||
--- Selector: a[title^="HTTP"] ---
|
||||
--- Selector: a[title^="http"] ---
|
||||
<a title="http://example.com" lang="en-us"/>
|
||||
<a title="http://example.be"/>
|
||||
--- Selector: a[title^="http"][title$=".be"] ---
|
||||
<a title="http://example.be"/>
|
||||
--- Selector: a[title$=".com"] ---
|
||||
<a title="http://example.com" lang="en-us"/>
|
||||
--- Selector: a[title$=".foo"] ---
|
||||
--- Selector: a[lang|="nl"] ---
|
||||
<a title="ftp://example.be" lang="nl-be" tokens="abc def ghi"/>
|
||||
<a title="ftp://example.nl" lang="nl-be"/>
|
||||
--- Selector: a[lang|="nl-be"] ---
|
||||
<a title="ftp://example.be" lang="nl-be" tokens="abc def ghi"/>
|
||||
<a title="ftp://example.nl" lang="nl-be"/>
|
||||
--- Selector: a[tokens~="def"] ---
|
||||
<a title="ftp://example.be" lang="nl-be" tokens="abc def ghi"/>
|
||||
--- Selector: a[tokens~="de"] ---
|
||||
--- Selector: a[tokens~="def ghi"] ---
|
||||
=== Case insensitive ===
|
||||
--- Selector: a[title] ---
|
||||
<a title="http://example.com" lang="en-us"/>
|
||||
<a title="http://example.be"/>
|
||||
<a title="ftp://example.be" lang="nl-be" tokens="abc def ghi"/>
|
||||
<a title="ftp://example.nl" lang="nl-be"/>
|
||||
--- Selector: a[title="http://example.COM" i] ---
|
||||
<a title="http://example.com" lang="en-us"/>
|
||||
--- Selector: a[title="http://EXAMPLE." i] ---
|
||||
--- Selector: a[title*="ExAmPlE" i] ---
|
||||
<a title="http://example.com" lang="en-us"/>
|
||||
<a title="http://example.be"/>
|
||||
<a title="ftp://example.be" lang="nl-be" tokens="abc def ghi"/>
|
||||
<a title="ftp://example.nl" lang="nl-be"/>
|
||||
--- Selector: a[title^="HTTP" i] ---
|
||||
<a title="http://example.com" lang="en-us"/>
|
||||
<a title="http://example.be"/>
|
||||
--- Selector: a[title^="HTTP" i][title$=".be"] ---
|
||||
<a title="http://example.be"/>
|
||||
--- Selector: a[title$=".COM" i] ---
|
||||
<a title="http://example.com" lang="en-us"/>
|
||||
--- Selector: a[lang|="NL" i] ---
|
||||
<a title="ftp://example.be" lang="nl-be" tokens="abc def ghi"/>
|
||||
<a title="ftp://example.nl" lang="nl-be"/>
|
||||
--- Selector: a[lang|="NL-BE" i] ---
|
||||
<a title="ftp://example.be" lang="nl-be" tokens="abc def ghi"/>
|
||||
<a title="ftp://example.nl" lang="nl-be"/>
|
||||
--- Selector: a[tokens~="DE" i] ---
|
||||
--- Selector: a[tokens~="DEF" i] ---
|
||||
<a title="ftp://example.be" lang="nl-be" tokens="abc def ghi"/>
|
||||
--- Selector: a[tokens~="DEF GHI" i] ---
|
49
ext/dom/tests/modern/css_selectors/closest.phpt
Normal file
49
ext/dom/tests/modern/css_selectors/closest.phpt
Normal file
@ -0,0 +1,49 @@
|
||||
--TEST--
|
||||
Test DOM\Element::closest() method: legit cases
|
||||
--EXTENSIONS--
|
||||
dom
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
$xml = <<<XML
|
||||
<root>
|
||||
<a/>
|
||||
<div class="foo" xml:id="div1">
|
||||
<div xml:id="div2">
|
||||
<div class="bar" xml:id="div3"/>
|
||||
</div>
|
||||
</div>
|
||||
</root>
|
||||
XML;
|
||||
|
||||
$dom = DOM\XMLDocument::createFromString($xml);
|
||||
|
||||
function test($el, $selector) {
|
||||
echo "--- Selector: $selector ---\n";
|
||||
var_dump($el->closest($selector)?->getAttribute('xml:id'));
|
||||
}
|
||||
|
||||
test($dom->getElementById('div3'), 'div');
|
||||
test($dom->getElementById('div3'), '[class="foo"]');
|
||||
test($dom->getElementById('div3'), ':not(root)');
|
||||
test($dom->getElementById('div3'), ':not(div)');
|
||||
test($dom->getElementById('div3'), 'a');
|
||||
test($dom->getElementById('div3'), 'root :not(div[class])');
|
||||
test($dom->getElementById('div3'), 'root > :not(div[class])');
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
--- Selector: div ---
|
||||
string(4) "div3"
|
||||
--- Selector: [class="foo"] ---
|
||||
string(4) "div1"
|
||||
--- Selector: :not(root) ---
|
||||
string(4) "div3"
|
||||
--- Selector: :not(div) ---
|
||||
NULL
|
||||
--- Selector: a ---
|
||||
NULL
|
||||
--- Selector: root :not(div[class]) ---
|
||||
string(4) "div2"
|
||||
--- Selector: root > :not(div[class]) ---
|
||||
NULL
|
@ -0,0 +1,18 @@
|
||||
--TEST--
|
||||
Test DOM\Element::closest() method: invalid selector
|
||||
--EXTENSIONS--
|
||||
dom
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
$dom = DOM\XMLDocument::createFromString("<root/>");
|
||||
|
||||
try {
|
||||
var_dump($dom->documentElement->closest('@invalid'));
|
||||
} catch (DOMException $e) {
|
||||
echo $e->getMessage();
|
||||
}
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
Invalid selector (Selectors. Unexpected token: @invalid)
|
113
ext/dom/tests/modern/css_selectors/combinators.phpt
Normal file
113
ext/dom/tests/modern/css_selectors/combinators.phpt
Normal file
@ -0,0 +1,113 @@
|
||||
--TEST--
|
||||
CSS Selectors - Combinators
|
||||
--EXTENSIONS--
|
||||
dom
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
require __DIR__ . '/test_utils.inc';
|
||||
|
||||
$dom = DOM\HTMLDocument::createFromString(<<<HTML
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<body>
|
||||
<p>First p</p>
|
||||
<p>Second p</p>
|
||||
<img src="1.png">
|
||||
<div class="">
|
||||
<p>Third p</p>
|
||||
<img src="2.png">
|
||||
<img src="3.png">
|
||||
<div class="foo bar baz">
|
||||
<p>Fourth p</p>
|
||||
</div>
|
||||
</div>
|
||||
<article title="foo" class="bar">
|
||||
<p class="bar">Fifth p</p>
|
||||
</article>
|
||||
<table border="1">
|
||||
<colgroup>
|
||||
<col class="selected">
|
||||
</colgroup>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>A</td>
|
||||
<td>B</td>
|
||||
<td>C</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</body>
|
||||
</html>
|
||||
HTML);
|
||||
|
||||
test_helper($dom, 'nonsense');
|
||||
test_helper($dom, 'p');
|
||||
test_helper($dom, 'p, img');
|
||||
test_helper($dom, 'body p');
|
||||
test_helper($dom, 'body div p');
|
||||
test_helper($dom, 'div > *');
|
||||
test_helper($dom, 'div > p');
|
||||
test_helper($dom, 'div > p + img');
|
||||
test_helper($dom, 'div > p ~ img');
|
||||
test_helper($dom, 'body > img');
|
||||
test_helper($dom, 'div.bar.baz > p');
|
||||
test_helper($dom, 'article[title].bar p');
|
||||
|
||||
try {
|
||||
test_helper($dom, 'col.selected||td');
|
||||
} catch (ValueError $e) {
|
||||
echo $e->getMessage(), "\n";
|
||||
}
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
--- Selector: nonsense ---
|
||||
--- Selector: p ---
|
||||
<p xmlns="http://www.w3.org/1999/xhtml">First p</p>
|
||||
<p xmlns="http://www.w3.org/1999/xhtml">Second p</p>
|
||||
<p xmlns="http://www.w3.org/1999/xhtml">Third p</p>
|
||||
<p xmlns="http://www.w3.org/1999/xhtml">Fourth p</p>
|
||||
<p xmlns="http://www.w3.org/1999/xhtml" class="bar">Fifth p</p>
|
||||
--- Selector: p, img ---
|
||||
<p xmlns="http://www.w3.org/1999/xhtml">First p</p>
|
||||
<p xmlns="http://www.w3.org/1999/xhtml">Second p</p>
|
||||
<img xmlns="http://www.w3.org/1999/xhtml" src="1.png" />
|
||||
<p xmlns="http://www.w3.org/1999/xhtml">Third p</p>
|
||||
<img xmlns="http://www.w3.org/1999/xhtml" src="2.png" />
|
||||
<img xmlns="http://www.w3.org/1999/xhtml" src="3.png" />
|
||||
<p xmlns="http://www.w3.org/1999/xhtml">Fourth p</p>
|
||||
<p xmlns="http://www.w3.org/1999/xhtml" class="bar">Fifth p</p>
|
||||
--- Selector: body p ---
|
||||
<p xmlns="http://www.w3.org/1999/xhtml">First p</p>
|
||||
<p xmlns="http://www.w3.org/1999/xhtml">Second p</p>
|
||||
<p xmlns="http://www.w3.org/1999/xhtml">Third p</p>
|
||||
<p xmlns="http://www.w3.org/1999/xhtml">Fourth p</p>
|
||||
<p xmlns="http://www.w3.org/1999/xhtml" class="bar">Fifth p</p>
|
||||
--- Selector: body div p ---
|
||||
<p xmlns="http://www.w3.org/1999/xhtml">Third p</p>
|
||||
<p xmlns="http://www.w3.org/1999/xhtml">Fourth p</p>
|
||||
--- Selector: div > * ---
|
||||
<p xmlns="http://www.w3.org/1999/xhtml">Third p</p>
|
||||
<img xmlns="http://www.w3.org/1999/xhtml" src="2.png" />
|
||||
<img xmlns="http://www.w3.org/1999/xhtml" src="3.png" />
|
||||
<div xmlns="http://www.w3.org/1999/xhtml" class="foo bar baz">
|
||||
<p>Fourth p</p>
|
||||
</div>
|
||||
<p xmlns="http://www.w3.org/1999/xhtml">Fourth p</p>
|
||||
--- Selector: div > p ---
|
||||
<p xmlns="http://www.w3.org/1999/xhtml">Third p</p>
|
||||
<p xmlns="http://www.w3.org/1999/xhtml">Fourth p</p>
|
||||
--- Selector: div > p + img ---
|
||||
<img xmlns="http://www.w3.org/1999/xhtml" src="2.png" />
|
||||
--- Selector: div > p ~ img ---
|
||||
<img xmlns="http://www.w3.org/1999/xhtml" src="2.png" />
|
||||
<img xmlns="http://www.w3.org/1999/xhtml" src="3.png" />
|
||||
--- Selector: body > img ---
|
||||
<img xmlns="http://www.w3.org/1999/xhtml" src="1.png" />
|
||||
--- Selector: div.bar.baz > p ---
|
||||
<p xmlns="http://www.w3.org/1999/xhtml">Fourth p</p>
|
||||
--- Selector: article[title].bar p ---
|
||||
<p xmlns="http://www.w3.org/1999/xhtml" class="bar">Fifth p</p>
|
||||
--- Selector: col.selected||td ---
|
||||
Dom\Document::querySelectorAll(): Argument #1 ($selectors) contains an unsupported selector
|
26
ext/dom/tests/modern/css_selectors/entities.phpt
Normal file
26
ext/dom/tests/modern/css_selectors/entities.phpt
Normal file
@ -0,0 +1,26 @@
|
||||
--TEST--
|
||||
CSS Selectors - Handling entities
|
||||
--EXTENSIONS--
|
||||
dom
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
$dom = Dom\XMLDocument::createFromString(<<<XML
|
||||
<!DOCTYPE root [
|
||||
<!ENTITY ent "box">
|
||||
]>
|
||||
<root>
|
||||
<input type="check&ent;" checked="" class="a&ent;a" id="a&ent;a" xmlns="http://www.w3.org/1999/xhtml"/>
|
||||
</root>
|
||||
XML);
|
||||
var_dump($dom->querySelector('input:checked')->nodeName);
|
||||
var_dump($dom->querySelector('input[type$="ox"]')->nodeName);
|
||||
var_dump($dom->querySelector('input.aboxa')->nodeName);
|
||||
var_dump($dom->querySelector('input#aboxa')->nodeName);
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
string(5) "input"
|
||||
string(5) "input"
|
||||
string(5) "input"
|
||||
string(5) "input"
|
35
ext/dom/tests/modern/css_selectors/id.phpt
Normal file
35
ext/dom/tests/modern/css_selectors/id.phpt
Normal file
@ -0,0 +1,35 @@
|
||||
--TEST--
|
||||
CSS Selectors - ID
|
||||
--EXTENSIONS--
|
||||
dom
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
require __DIR__ . '/test_utils.inc';
|
||||
|
||||
$dom = DOM\XMLDocument::createFromString(<<<XML
|
||||
<container>
|
||||
<test id="test1"/>
|
||||
<test/>
|
||||
<test id="test2"/>
|
||||
<test id="test3"/>
|
||||
<test xmlns:x="urn:x" x:id="test4"/>
|
||||
</container>
|
||||
XML);
|
||||
|
||||
test_helper($dom, '#test');
|
||||
test_helper($dom, '#test1');
|
||||
test_helper($dom, '#test2');
|
||||
test_helper($dom, '#test3');
|
||||
test_helper($dom, '#test4');
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
--- Selector: #test ---
|
||||
--- Selector: #test1 ---
|
||||
<test id="test1"/>
|
||||
--- Selector: #test2 ---
|
||||
<test id="test2"/>
|
||||
--- Selector: #test3 ---
|
||||
<test id="test3"/>
|
||||
--- Selector: #test4 ---
|
@ -0,0 +1,18 @@
|
||||
--TEST--
|
||||
Test DOM\Element::matches() method: invalid selector
|
||||
--EXTENSIONS--
|
||||
dom
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
$dom = DOM\XMLDocument::createFromString("<root/>");
|
||||
|
||||
try {
|
||||
var_dump($dom->documentElement->matches('@invalid'));
|
||||
} catch (DOMException $e) {
|
||||
echo $e->getMessage();
|
||||
}
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
Invalid selector (Selectors. Unexpected token: @invalid)
|
54
ext/dom/tests/modern/css_selectors/namespaces.phpt
Normal file
54
ext/dom/tests/modern/css_selectors/namespaces.phpt
Normal file
@ -0,0 +1,54 @@
|
||||
--TEST--
|
||||
CSS Selectors - Namespaces
|
||||
--EXTENSIONS--
|
||||
dom
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
require __DIR__ . '/test_utils.inc';
|
||||
|
||||
$dom = DOM\XMLDocument::createFromString(<<<XML
|
||||
<root>
|
||||
<container align="left"/>
|
||||
<only>
|
||||
<a xmlns="urn:a"/>
|
||||
<a xmlns="urn:a"/>
|
||||
<a xmlns="urn:b"/>
|
||||
<a xmlns=""/>
|
||||
<a/>
|
||||
</only>
|
||||
</root>
|
||||
XML);
|
||||
|
||||
$container = $dom->documentElement->firstElementChild;
|
||||
$container->setAttribute("foo:bar", "baz");
|
||||
$container->setAttributeNS("urn:a", "a:bar", "baz");
|
||||
|
||||
test_helper($dom, 'container[align]');
|
||||
test_helper($dom, 'container[foo\\:bar]');
|
||||
test_helper($dom, 'container[a\\:bar]');
|
||||
test_helper($dom, 'container[bar]');
|
||||
|
||||
test_helper($dom, 'a:first-of-type');
|
||||
test_helper($dom, 'a:last-of-type');
|
||||
|
||||
test_failure($dom, 'container[* | bar]');
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
--- Selector: container[align] ---
|
||||
<container align="left" foo:bar="baz" xmlns:a="urn:a" a:bar="baz"/>
|
||||
--- Selector: container[foo\:bar] ---
|
||||
<container align="left" foo:bar="baz" xmlns:a="urn:a" a:bar="baz"/>
|
||||
--- Selector: container[a\:bar] ---
|
||||
--- Selector: container[bar] ---
|
||||
--- Selector: a:first-of-type ---
|
||||
<a xmlns="urn:a"/>
|
||||
<a xmlns="urn:b"/>
|
||||
<a xmlns=""/>
|
||||
--- Selector: a:last-of-type ---
|
||||
<a xmlns="urn:a"/>
|
||||
<a xmlns="urn:b"/>
|
||||
<a/>
|
||||
--- Selector: container[* | bar] ---
|
||||
Code 12 Invalid selector (Selectors. Unexpected token: *)
|
23
ext/dom/tests/modern/css_selectors/pseudo_classes_blank.phpt
Normal file
23
ext/dom/tests/modern/css_selectors/pseudo_classes_blank.phpt
Normal file
@ -0,0 +1,23 @@
|
||||
--TEST--
|
||||
CSS Selectors - Pseudo classes: blank
|
||||
--EXTENSIONS--
|
||||
dom
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
require __DIR__ . '/test_utils.inc';
|
||||
|
||||
$dom = DOM\XMLDocument::createFromString(<<<XML
|
||||
<container/>
|
||||
XML);
|
||||
|
||||
try {
|
||||
test_helper($dom, ':blank');
|
||||
} catch (DOMException $e) {
|
||||
echo $e->getMessage(), "\n";
|
||||
}
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
--- Selector: :blank ---
|
||||
:blank selector is not implemented because CSSWG has not yet decided its semantics (https://github.com/w3c/csswg-drafts/issues/1967)
|
@ -0,0 +1,27 @@
|
||||
--TEST--
|
||||
CSS Selectors - Pseudo classes: checked
|
||||
--EXTENSIONS--
|
||||
dom
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
require __DIR__ . '/test_utils.inc';
|
||||
|
||||
$dom = DOM\XMLDocument::createFromString(<<<XML
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<input type="checkbox" checked="checked" />
|
||||
<input type="radio" checked="checked" />
|
||||
<option selected="" />
|
||||
<option xmlns="" selected="" />
|
||||
<input/>
|
||||
</html>
|
||||
XML);
|
||||
|
||||
test_helper($dom, ':checked');
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
--- Selector: :checked ---
|
||||
<input xmlns="http://www.w3.org/1999/xhtml" type="checkbox" checked="checked" />
|
||||
<input xmlns="http://www.w3.org/1999/xhtml" type="radio" checked="checked" />
|
||||
<option xmlns="http://www.w3.org/1999/xhtml" selected=""></option>
|
@ -0,0 +1,21 @@
|
||||
--TEST--
|
||||
CSS Selectors - Pseudo classes: current
|
||||
--EXTENSIONS--
|
||||
dom
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
require __DIR__ . '/test_utils.inc';
|
||||
|
||||
$dom = DOM\XMLDocument::createFromString(<<<XML
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<div/>
|
||||
</html>
|
||||
XML);
|
||||
|
||||
test_helper($dom, ':current(div)');
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
--- Selector: :current(div) ---
|
||||
<div xmlns="http://www.w3.org/1999/xhtml"></div>
|
27
ext/dom/tests/modern/css_selectors/pseudo_classes_dir.phpt
Normal file
27
ext/dom/tests/modern/css_selectors/pseudo_classes_dir.phpt
Normal file
@ -0,0 +1,27 @@
|
||||
--TEST--
|
||||
CSS Selectors - Pseudo classes: dir
|
||||
--EXTENSIONS--
|
||||
dom
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
require __DIR__ . '/test_utils.inc';
|
||||
|
||||
$dom = DOM\XMLDocument::createFromString(<<<XML
|
||||
<container dir="rtl">
|
||||
<p>1</p>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" dir="ltr">
|
||||
<p>2</p>
|
||||
</html>
|
||||
</container>
|
||||
XML);
|
||||
|
||||
test_failure($dom, ':dir(rtl)', true);
|
||||
test_failure($dom, ':dir(ltr)', true);
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
--- Selector: :dir(rtl) ---
|
||||
Code 12 Invalid selector (Selectors. Not supported: dir)
|
||||
--- Selector: :dir(ltr) ---
|
||||
Code 12 Invalid selector (Selectors. Not supported: dir)
|
@ -0,0 +1,107 @@
|
||||
--TEST--
|
||||
CSS Selectors - Pseudo classes: disabled/enabled
|
||||
--EXTENSIONS--
|
||||
dom
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
require __DIR__ . '/test_utils.inc';
|
||||
|
||||
$dom = DOM\XMLDocument::createFromString(<<<XML
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<button/>
|
||||
<button xmlns="" disabled="disabled"/>
|
||||
<button disabled="disabled"/>
|
||||
<input disabled="disabled"/>
|
||||
<select disabled="disabled"/>
|
||||
<textarea disabled="disabled"/>
|
||||
<optgroup disabled="disabled"/>
|
||||
|
||||
<!-- fieldset rules are complicated -->
|
||||
<fieldset disabled="disabled"/>
|
||||
<fieldset disabled="disabled">
|
||||
<fieldset id="1"/>
|
||||
</fieldset>
|
||||
<fieldset disabled="disabled">
|
||||
<!-- foo -->
|
||||
<legend>
|
||||
<div>
|
||||
<fieldset id="2"/>
|
||||
</div>
|
||||
</legend>
|
||||
<div>
|
||||
<fieldset id="3"/>
|
||||
</div>
|
||||
</fieldset>
|
||||
</html>
|
||||
XML);
|
||||
|
||||
test_helper($dom, '*:disabled');
|
||||
test_helper($dom, '*:enabled');
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
--- Selector: *:disabled ---
|
||||
<button xmlns="http://www.w3.org/1999/xhtml" disabled="disabled"></button>
|
||||
<input xmlns="http://www.w3.org/1999/xhtml" disabled="disabled" />
|
||||
<select xmlns="http://www.w3.org/1999/xhtml" disabled="disabled"></select>
|
||||
<textarea xmlns="http://www.w3.org/1999/xhtml" disabled="disabled"></textarea>
|
||||
<optgroup xmlns="http://www.w3.org/1999/xhtml" disabled="disabled"></optgroup>
|
||||
<fieldset xmlns="http://www.w3.org/1999/xhtml" disabled="disabled"></fieldset>
|
||||
<fieldset xmlns="http://www.w3.org/1999/xhtml" disabled="disabled">
|
||||
<fieldset id="1"></fieldset>
|
||||
</fieldset>
|
||||
<fieldset xmlns="http://www.w3.org/1999/xhtml" disabled="disabled">
|
||||
<!-- foo -->
|
||||
<legend>
|
||||
<div>
|
||||
<fieldset id="2"></fieldset>
|
||||
</div>
|
||||
</legend>
|
||||
<div>
|
||||
<fieldset id="3"></fieldset>
|
||||
</div>
|
||||
</fieldset>
|
||||
<fieldset xmlns="http://www.w3.org/1999/xhtml" id="3"></fieldset>
|
||||
--- Selector: *:enabled ---
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<button></button>
|
||||
<button xmlns="" disabled="disabled"/>
|
||||
<button disabled="disabled"></button>
|
||||
<input disabled="disabled" />
|
||||
<select disabled="disabled"></select>
|
||||
<textarea disabled="disabled"></textarea>
|
||||
<optgroup disabled="disabled"></optgroup>
|
||||
|
||||
<!-- fieldset rules are complicated -->
|
||||
<fieldset disabled="disabled"></fieldset>
|
||||
<fieldset disabled="disabled">
|
||||
<fieldset id="1"></fieldset>
|
||||
</fieldset>
|
||||
<fieldset disabled="disabled">
|
||||
<!-- foo -->
|
||||
<legend>
|
||||
<div>
|
||||
<fieldset id="2"></fieldset>
|
||||
</div>
|
||||
</legend>
|
||||
<div>
|
||||
<fieldset id="3"></fieldset>
|
||||
</div>
|
||||
</fieldset>
|
||||
</html>
|
||||
<button xmlns="http://www.w3.org/1999/xhtml"></button>
|
||||
<button xmlns="" disabled="disabled"/>
|
||||
<fieldset xmlns="http://www.w3.org/1999/xhtml" id="1"></fieldset>
|
||||
<legend xmlns="http://www.w3.org/1999/xhtml">
|
||||
<div>
|
||||
<fieldset id="2"></fieldset>
|
||||
</div>
|
||||
</legend>
|
||||
<div xmlns="http://www.w3.org/1999/xhtml">
|
||||
<fieldset id="2"></fieldset>
|
||||
</div>
|
||||
<fieldset xmlns="http://www.w3.org/1999/xhtml" id="2"></fieldset>
|
||||
<div xmlns="http://www.w3.org/1999/xhtml">
|
||||
<fieldset id="3"></fieldset>
|
||||
</div>
|
38
ext/dom/tests/modern/css_selectors/pseudo_classes_empty.phpt
Normal file
38
ext/dom/tests/modern/css_selectors/pseudo_classes_empty.phpt
Normal file
@ -0,0 +1,38 @@
|
||||
--TEST--
|
||||
CSS Selectors - Pseudo classes: empty
|
||||
--EXTENSIONS--
|
||||
dom
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
require __DIR__ . '/test_utils.inc';
|
||||
|
||||
$dom = DOM\XMLDocument::createFromString(<<<XML
|
||||
<container>
|
||||
<div class="empty">
|
||||
<p>Element with no content:</p>
|
||||
<div></div>
|
||||
|
||||
<p>Element with comment:</p>
|
||||
<div><!-- Simple Comment --></div>
|
||||
|
||||
<p>Element with PI:</p>
|
||||
<div><?foo?></div>
|
||||
|
||||
<p>Element with CDATA:</p>
|
||||
<div><![CDATA[foo]]></div>
|
||||
|
||||
<p>Element with nested empty element:</p>
|
||||
<div><p></p></div>
|
||||
</div>
|
||||
</container>
|
||||
XML);
|
||||
|
||||
test_helper($dom, '.empty > div:empty');
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
--- Selector: .empty > div:empty ---
|
||||
<div/>
|
||||
<div><!-- Simple Comment --></div>
|
||||
<div><?foo ?></div>
|
@ -0,0 +1,29 @@
|
||||
--TEST--
|
||||
CSS Selectors - Pseudo classes: first/last child
|
||||
--EXTENSIONS--
|
||||
dom
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
require __DIR__ . '/test_utils.inc';
|
||||
|
||||
$dom = DOM\XMLDocument::createFromString(<<<XML
|
||||
<container>
|
||||
<?foo?>
|
||||
<!--bar-->
|
||||
<first />
|
||||
&
|
||||
<last/>
|
||||
<![CDATA[skip me]]>
|
||||
</container>
|
||||
XML);
|
||||
|
||||
test_helper($dom, 'container > *:first-child');
|
||||
test_helper($dom, 'container > *:last-child');
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
--- Selector: container > *:first-child ---
|
||||
<first/>
|
||||
--- Selector: container > *:last-child ---
|
||||
<last/>
|
33
ext/dom/tests/modern/css_selectors/pseudo_classes_has.phpt
Normal file
33
ext/dom/tests/modern/css_selectors/pseudo_classes_has.phpt
Normal file
@ -0,0 +1,33 @@
|
||||
--TEST--
|
||||
CSS Selectors - Pseudo classes: has
|
||||
--EXTENSIONS--
|
||||
dom
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
require __DIR__ . '/test_utils.inc';
|
||||
|
||||
$dom = DOM\XMLDocument::createFromString(<<<XML
|
||||
<container>
|
||||
<div>
|
||||
<p class="foo"/>
|
||||
</div>
|
||||
<div>
|
||||
<p/>
|
||||
</div>
|
||||
</container>
|
||||
XML);
|
||||
|
||||
test_helper($dom, 'div:has(p.foo)');
|
||||
test_helper($dom, 'div:has(:not(p.foo))');
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
--- Selector: div:has(p.foo) ---
|
||||
<div>
|
||||
<p class="foo"/>
|
||||
</div>
|
||||
--- Selector: div:has(:not(p.foo)) ---
|
||||
<div>
|
||||
<p/>
|
||||
</div>
|
@ -0,0 +1,34 @@
|
||||
--TEST--
|
||||
CSS Selectors - Pseudo classes: is/where
|
||||
--EXTENSIONS--
|
||||
dom
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
require __DIR__ . '/test_utils.inc';
|
||||
|
||||
$dom = DOM\XMLDocument::createFromString(<<<XML
|
||||
<container>
|
||||
<article>
|
||||
<p>1</p>
|
||||
</article>
|
||||
<main>
|
||||
<p>2</p>
|
||||
</main>
|
||||
<div>
|
||||
<p>3</p>
|
||||
</div>
|
||||
</container>
|
||||
XML);
|
||||
|
||||
test_helper($dom, ':is(article, main) p');
|
||||
test_helper($dom, ':where(article, main) p');
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
--- Selector: :is(article, main) p ---
|
||||
<p>1</p>
|
||||
<p>2</p>
|
||||
--- Selector: :where(article, main) p ---
|
||||
<p>1</p>
|
||||
<p>2</p>
|
27
ext/dom/tests/modern/css_selectors/pseudo_classes_lang.phpt
Normal file
27
ext/dom/tests/modern/css_selectors/pseudo_classes_lang.phpt
Normal file
@ -0,0 +1,27 @@
|
||||
--TEST--
|
||||
CSS Selectors - Pseudo classes: lang
|
||||
--EXTENSIONS--
|
||||
dom
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
require __DIR__ . '/test_utils.inc';
|
||||
|
||||
$dom = DOM\XMLDocument::createFromString(<<<XML
|
||||
<container lang="en">
|
||||
<p>1</p>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" lang="nl">
|
||||
<p>2</p>
|
||||
</html>
|
||||
</container>
|
||||
XML);
|
||||
|
||||
test_failure($dom, ':lang(en)', true);
|
||||
test_failure($dom, ':lang(nl)', true);
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
--- Selector: :lang(en) ---
|
||||
Code 12 Invalid selector (Selectors. Not supported: lang)
|
||||
--- Selector: :lang(nl) ---
|
||||
Code 12 Invalid selector (Selectors. Not supported: lang)
|
31
ext/dom/tests/modern/css_selectors/pseudo_classes_links.phpt
Normal file
31
ext/dom/tests/modern/css_selectors/pseudo_classes_links.phpt
Normal file
@ -0,0 +1,31 @@
|
||||
--TEST--
|
||||
CSS Selectors - Pseudo classes: links
|
||||
--EXTENSIONS--
|
||||
dom
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
require __DIR__ . '/test_utils.inc';
|
||||
|
||||
$dom = DOM\XMLDocument::createFromString(<<<XML
|
||||
<container>
|
||||
<a href="http://example.com">Link</a>
|
||||
<a xmlns="http://www.w3.org/1999/xhtml" href="http://example.com">Link</a>
|
||||
<area xmlns="http://www.w3.org/1999/xhtml" href="http://example.com">Link</area>
|
||||
</container>
|
||||
XML);
|
||||
|
||||
test_helper($dom, ':any-link');
|
||||
test_helper($dom, ':link');
|
||||
test_helper($dom, 'a:not(:any-link)');
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
--- Selector: :any-link ---
|
||||
<a xmlns="http://www.w3.org/1999/xhtml" href="http://example.com">Link</a>
|
||||
<area xmlns="http://www.w3.org/1999/xhtml" href="http://example.com">Link</area>
|
||||
--- Selector: :link ---
|
||||
<a xmlns="http://www.w3.org/1999/xhtml" href="http://example.com">Link</a>
|
||||
<area xmlns="http://www.w3.org/1999/xhtml" href="http://example.com">Link</area>
|
||||
--- Selector: a:not(:any-link) ---
|
||||
<a href="http://example.com">Link</a>
|
83
ext/dom/tests/modern/css_selectors/pseudo_classes_no_op.phpt
Normal file
83
ext/dom/tests/modern/css_selectors/pseudo_classes_no_op.phpt
Normal file
@ -0,0 +1,83 @@
|
||||
--TEST--
|
||||
CSS Selectors - Pseudo classes: no-ops
|
||||
--EXTENSIONS--
|
||||
dom
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
require __DIR__ . '/test_utils.inc';
|
||||
|
||||
$dom = DOM\XMLDocument::createFromString(<<<XML
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<div/>
|
||||
<a href="#foo"/>
|
||||
</html>
|
||||
XML);
|
||||
|
||||
// These will always fail or return an empty list because they are display-specific or require user interaction
|
||||
test_failure($dom, ':default');
|
||||
test_failure($dom, ':focus');
|
||||
test_failure($dom, ':focus-visible');
|
||||
test_failure($dom, ':focus-within');
|
||||
test_failure($dom, ':fullscreen');
|
||||
test_failure($dom, ':hover');
|
||||
test_failure($dom, ':in-range');
|
||||
test_failure($dom, ':indeterminate');
|
||||
test_failure($dom, ':invalid');
|
||||
test_failure($dom, ':out-of-range');
|
||||
test_failure($dom, ':past');
|
||||
test_failure($dom, ':future');
|
||||
test_failure($dom, ':scope');
|
||||
test_failure($dom, ':target');
|
||||
test_failure($dom, ':target-within');
|
||||
test_failure($dom, ':user-invalid');
|
||||
test_failure($dom, ':valid');
|
||||
test_failure($dom, ':visited');
|
||||
test_failure($dom, ':warning');
|
||||
test_failure($dom, ':local-link');
|
||||
test_failure($dom, ':active');
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
--- Selector: :default ---
|
||||
Code 12 Invalid selector (Selectors. Not supported: default)
|
||||
--- Selector: :focus ---
|
||||
int(0)
|
||||
--- Selector: :focus-visible ---
|
||||
Code 12 Invalid selector (Selectors. Not supported: focus-visible)
|
||||
--- Selector: :focus-within ---
|
||||
Code 12 Invalid selector (Selectors. Not supported: focus-within)
|
||||
--- Selector: :fullscreen ---
|
||||
Code 12 Invalid selector (Selectors. Not supported: fullscreen)
|
||||
--- Selector: :hover ---
|
||||
int(0)
|
||||
--- Selector: :in-range ---
|
||||
Code 12 Invalid selector (Selectors. Not supported: in-range)
|
||||
--- Selector: :indeterminate ---
|
||||
Code 12 Invalid selector (Selectors. Not supported: indeterminate)
|
||||
--- Selector: :invalid ---
|
||||
Code 12 Invalid selector (Selectors. Not supported: invalid)
|
||||
--- Selector: :out-of-range ---
|
||||
Code 12 Invalid selector (Selectors. Not supported: out-of-range)
|
||||
--- Selector: :past ---
|
||||
Code 12 Invalid selector (Selectors. Not supported: past)
|
||||
--- Selector: :future ---
|
||||
Code 12 Invalid selector (Selectors. Not supported: future)
|
||||
--- Selector: :scope ---
|
||||
Code 12 Invalid selector (Selectors. Not supported: scope)
|
||||
--- Selector: :target ---
|
||||
Code 12 Invalid selector (Selectors. Not supported: target)
|
||||
--- Selector: :target-within ---
|
||||
Code 12 Invalid selector (Selectors. Not supported: target-within)
|
||||
--- Selector: :user-invalid ---
|
||||
Code 12 Invalid selector (Selectors. Not supported: user-invalid)
|
||||
--- Selector: :valid ---
|
||||
Code 12 Invalid selector (Selectors. Not supported: valid)
|
||||
--- Selector: :visited ---
|
||||
Code 12 Invalid selector (Selectors. Not supported: visited)
|
||||
--- Selector: :warning ---
|
||||
Code 12 Invalid selector (Selectors. Not supported: warning)
|
||||
--- Selector: :local-link ---
|
||||
Code 12 Invalid selector (Selectors. Not supported: local-link)
|
||||
--- Selector: :active ---
|
||||
int(0)
|
@ -0,0 +1,56 @@
|
||||
--TEST--
|
||||
CSS Selectors - Pseudo classes: nth-child
|
||||
--EXTENSIONS--
|
||||
dom
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
require __DIR__ . '/test_utils.inc';
|
||||
|
||||
$dom = DOM\XMLDocument::createFromString(<<<XML
|
||||
<container>
|
||||
<h2>1</h2>
|
||||
<h2>2</h2>
|
||||
<h2>3</h2>
|
||||
<h2>4</h2>
|
||||
<h2>5</h2>
|
||||
</container>
|
||||
XML);
|
||||
|
||||
test_helper($dom, 'h2:nth-of-type(n+2):nth-last-of-type(n+2)');
|
||||
test_helper($dom, 'h2:not(:first-of-type):not(:last-of-type)'); // Equivalent to the above
|
||||
test_helper($dom, 'h2:nth-child(2)');
|
||||
test_helper($dom, 'h2:nth-last-child(2)');
|
||||
test_helper($dom, 'h2:nth-child(2n + 1)');
|
||||
test_helper($dom, 'h2:nth-last-child(2n + 1)');
|
||||
test_helper($dom, 'h2:nth-child(3n - 2)');
|
||||
test_helper($dom, 'h2:nth-last-child(3n - 2)');
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
--- Selector: h2:nth-of-type(n+2):nth-last-of-type(n+2) ---
|
||||
<h2>2</h2>
|
||||
<h2>3</h2>
|
||||
<h2>4</h2>
|
||||
--- Selector: h2:not(:first-of-type):not(:last-of-type) ---
|
||||
<h2>2</h2>
|
||||
<h2>3</h2>
|
||||
<h2>4</h2>
|
||||
--- Selector: h2:nth-child(2) ---
|
||||
<h2>2</h2>
|
||||
--- Selector: h2:nth-last-child(2) ---
|
||||
<h2>4</h2>
|
||||
--- Selector: h2:nth-child(2n + 1) ---
|
||||
<h2>1</h2>
|
||||
<h2>3</h2>
|
||||
<h2>5</h2>
|
||||
--- Selector: h2:nth-last-child(2n + 1) ---
|
||||
<h2>1</h2>
|
||||
<h2>3</h2>
|
||||
<h2>5</h2>
|
||||
--- Selector: h2:nth-child(3n - 2) ---
|
||||
<h2>1</h2>
|
||||
<h2>4</h2>
|
||||
--- Selector: h2:nth-last-child(3n - 2) ---
|
||||
<h2>2</h2>
|
||||
<h2>5</h2>
|
@ -0,0 +1,56 @@
|
||||
--TEST--
|
||||
CSS Selectors - Pseudo classes: nth-child of
|
||||
--EXTENSIONS--
|
||||
dom
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
require __DIR__ . '/test_utils.inc';
|
||||
|
||||
$dom = DOM\XMLDocument::createFromString(<<<XML
|
||||
<container>
|
||||
<h2 class="hi">1</h2>
|
||||
<h2>2</h2>
|
||||
<h2 class="hi">3</h2>
|
||||
<h2 class="hi">4</h2>
|
||||
<h2>5</h2>
|
||||
<h2 class="hi">6</h2>
|
||||
</container>
|
||||
XML);
|
||||
|
||||
test_helper($dom, 'h2:nth-child(even of .hi)');
|
||||
test_helper($dom, 'h2.hi:nth-child(even)');
|
||||
test_helper($dom, 'h2:nth-child(odd of .hi)');
|
||||
test_helper($dom, 'h2.hi:nth-child(odd)');
|
||||
|
||||
test_helper($dom, 'h2:nth-last-child(even of .hi)');
|
||||
test_helper($dom, 'h2.hi:nth-last-child(even)');
|
||||
test_helper($dom, 'h2:nth-last-child(odd of .hi)');
|
||||
test_helper($dom, 'h2.hi:nth-last-child(odd)');
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
--- Selector: h2:nth-child(even of .hi) ---
|
||||
<h2 class="hi">3</h2>
|
||||
<h2 class="hi">6</h2>
|
||||
--- Selector: h2.hi:nth-child(even) ---
|
||||
<h2 class="hi">4</h2>
|
||||
<h2 class="hi">6</h2>
|
||||
--- Selector: h2:nth-child(odd of .hi) ---
|
||||
<h2 class="hi">1</h2>
|
||||
<h2 class="hi">4</h2>
|
||||
--- Selector: h2.hi:nth-child(odd) ---
|
||||
<h2 class="hi">1</h2>
|
||||
<h2 class="hi">3</h2>
|
||||
--- Selector: h2:nth-last-child(even of .hi) ---
|
||||
<h2 class="hi">1</h2>
|
||||
<h2 class="hi">4</h2>
|
||||
--- Selector: h2.hi:nth-last-child(even) ---
|
||||
<h2 class="hi">1</h2>
|
||||
<h2 class="hi">3</h2>
|
||||
--- Selector: h2:nth-last-child(odd of .hi) ---
|
||||
<h2 class="hi">3</h2>
|
||||
<h2 class="hi">6</h2>
|
||||
--- Selector: h2.hi:nth-last-child(odd) ---
|
||||
<h2 class="hi">4</h2>
|
||||
<h2 class="hi">6</h2>
|
@ -0,0 +1,36 @@
|
||||
--TEST--
|
||||
CSS Selectors - Pseudo classes: nth-(last-)col
|
||||
--EXTENSIONS--
|
||||
dom
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
require __DIR__ . '/test_utils.inc';
|
||||
|
||||
$dom = DOM\HTMLDocument::createFromString(<<<HTML
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<body>
|
||||
<table>
|
||||
<tr>
|
||||
<th>Col 1</th>
|
||||
<th>Col 2</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>1</td>
|
||||
<td>2</td>
|
||||
</tr>
|
||||
</table>
|
||||
</body>
|
||||
</html>
|
||||
HTML);
|
||||
|
||||
test_failure($dom, ':nth-col(1)', true);
|
||||
test_failure($dom, ':nth-last-col(1)', true);
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
--- Selector: :nth-col(1) ---
|
||||
Code 12 Invalid selector (Selectors. Not supported: nth-col)
|
||||
--- Selector: :nth-last-col(1) ---
|
||||
Code 12 Invalid selector (Selectors. Not supported: nth-last-col)
|
@ -0,0 +1,29 @@
|
||||
--TEST--
|
||||
CSS Selectors - Pseudo classes: only-child
|
||||
--EXTENSIONS--
|
||||
dom
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
require __DIR__ . '/test_utils.inc';
|
||||
|
||||
$dom = DOM\XMLDocument::createFromString(<<<XML
|
||||
<container>
|
||||
<div class="only-child1">
|
||||
<p>Lonely</p>
|
||||
</div>
|
||||
<div class="only-child2">
|
||||
<p>With 2</p>
|
||||
<p>With 2</p>
|
||||
</div>
|
||||
</container>
|
||||
XML);
|
||||
|
||||
test_helper($dom, '.only-child1 p:only-child');
|
||||
test_helper($dom, '.only-child2 p:only-child');
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
--- Selector: .only-child1 p:only-child ---
|
||||
<p>Lonely</p>
|
||||
--- Selector: .only-child2 p:only-child ---
|
@ -0,0 +1,36 @@
|
||||
--TEST--
|
||||
CSS Selectors - Pseudo classes: only-of-type
|
||||
--EXTENSIONS--
|
||||
dom
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
require __DIR__ . '/test_utils.inc';
|
||||
|
||||
$dom = DOM\XMLDocument::createFromString(<<<XML
|
||||
<container>
|
||||
<div class="only-of-type1">
|
||||
<p>Alone</p>
|
||||
</div>
|
||||
<div class="only-of-type2">
|
||||
<p>With 2</p>
|
||||
<p>With 2</p>
|
||||
</div>
|
||||
<div class="only-of-type3">
|
||||
<p>With 2</p>
|
||||
<div/>
|
||||
<p>With 2</p>
|
||||
</div>
|
||||
</container>
|
||||
XML);
|
||||
|
||||
test_helper($dom, '.only-of-type1 p:only-of-type');
|
||||
test_helper($dom, '.only-of-type2 p:only-of-type');
|
||||
test_helper($dom, '.only-of-type3 p:only-of-type');
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
--- Selector: .only-of-type1 p:only-of-type ---
|
||||
<p>Alone</p>
|
||||
--- Selector: .only-of-type2 p:only-of-type ---
|
||||
--- Selector: .only-of-type3 p:only-of-type ---
|
@ -0,0 +1,35 @@
|
||||
--TEST--
|
||||
CSS Selectors - Pseudo classes: optional/required
|
||||
--EXTENSIONS--
|
||||
dom
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
require __DIR__ . '/test_utils.inc';
|
||||
|
||||
$dom = DOM\XMLDocument::createFromString(<<<XML
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<input type="checkbox" required="required" />
|
||||
<select required="required" />
|
||||
<textarea required="" />
|
||||
<input type="checkbox" />
|
||||
<select />
|
||||
<textarea />
|
||||
<input xmlns=""/>
|
||||
<input xmlns="" required="" />
|
||||
</html>
|
||||
XML);
|
||||
|
||||
test_helper($dom, ':required');
|
||||
test_helper($dom, ':optional');
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
--- Selector: :required ---
|
||||
<input xmlns="http://www.w3.org/1999/xhtml" type="checkbox" required="required" />
|
||||
<select xmlns="http://www.w3.org/1999/xhtml" required="required"></select>
|
||||
<textarea xmlns="http://www.w3.org/1999/xhtml" required=""></textarea>
|
||||
--- Selector: :optional ---
|
||||
<input xmlns="http://www.w3.org/1999/xhtml" type="checkbox" />
|
||||
<select xmlns="http://www.w3.org/1999/xhtml"></select>
|
||||
<textarea xmlns="http://www.w3.org/1999/xhtml"></textarea>
|
@ -0,0 +1,27 @@
|
||||
--TEST--
|
||||
CSS Selectors - Pseudo classes: placeholder-shown
|
||||
--EXTENSIONS--
|
||||
dom
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
require __DIR__ . '/test_utils.inc';
|
||||
|
||||
$dom = DOM\XMLDocument::createFromString(<<<XML
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<input type="text" placeholder="" />
|
||||
<textarea placeholder="" />
|
||||
<input xmlns="" type="text" placeholder="" />
|
||||
<textarea xmlns="" placeholder="" />
|
||||
<input type="text" />
|
||||
<textarea />
|
||||
</html>
|
||||
XML);
|
||||
|
||||
test_helper($dom, ':placeholder-shown');
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
--- Selector: :placeholder-shown ---
|
||||
<input xmlns="http://www.w3.org/1999/xhtml" type="text" placeholder="" />
|
||||
<textarea xmlns="http://www.w3.org/1999/xhtml" placeholder=""></textarea>
|
@ -0,0 +1,53 @@
|
||||
--TEST--
|
||||
CSS Selectors - Pseudo classes: read-write/read-only
|
||||
--EXTENSIONS--
|
||||
dom
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
require __DIR__ . '/test_utils.inc';
|
||||
|
||||
$dom = DOM\XMLDocument::createFromString(<<<XML
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<input type="text" readonly="" />
|
||||
<textarea readonly="" />
|
||||
<input type="text" disabled="" />
|
||||
<textarea disabled="" />
|
||||
<input type="text" xmlns="" />
|
||||
<textarea xmlns="" />
|
||||
<input type="text" />
|
||||
<textarea />
|
||||
<p contenteditable="" />
|
||||
<p contenteditable="false" />
|
||||
</html>
|
||||
XML);
|
||||
|
||||
test_helper($dom, ':read-write');
|
||||
test_helper($dom, ':read-only');
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
--- Selector: :read-write ---
|
||||
<input xmlns="http://www.w3.org/1999/xhtml" type="text" />
|
||||
<textarea xmlns="http://www.w3.org/1999/xhtml"></textarea>
|
||||
<p xmlns="http://www.w3.org/1999/xhtml" contenteditable=""></p>
|
||||
--- Selector: :read-only ---
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<input type="text" readonly="" />
|
||||
<textarea readonly=""></textarea>
|
||||
<input type="text" disabled="" />
|
||||
<textarea disabled=""></textarea>
|
||||
<input xmlns="" type="text"/>
|
||||
<textarea xmlns=""/>
|
||||
<input type="text" />
|
||||
<textarea></textarea>
|
||||
<p contenteditable=""></p>
|
||||
<p contenteditable="false"></p>
|
||||
</html>
|
||||
<input xmlns="http://www.w3.org/1999/xhtml" type="text" readonly="" />
|
||||
<textarea xmlns="http://www.w3.org/1999/xhtml" readonly=""></textarea>
|
||||
<input xmlns="http://www.w3.org/1999/xhtml" type="text" disabled="" />
|
||||
<textarea xmlns="http://www.w3.org/1999/xhtml" disabled=""></textarea>
|
||||
<input xmlns="" type="text"/>
|
||||
<textarea xmlns=""/>
|
||||
<p xmlns="http://www.w3.org/1999/xhtml" contenteditable="false"></p>
|
29
ext/dom/tests/modern/css_selectors/pseudo_classes_root.phpt
Normal file
29
ext/dom/tests/modern/css_selectors/pseudo_classes_root.phpt
Normal file
@ -0,0 +1,29 @@
|
||||
--TEST--
|
||||
CSS Selectors - Pseudo classes: root
|
||||
--EXTENSIONS--
|
||||
dom
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
require __DIR__ . '/test_utils.inc';
|
||||
|
||||
$dom = DOM\XMLDocument::createFromString(<<<XML
|
||||
<container/>
|
||||
XML);
|
||||
|
||||
test_helper($dom, ':root', true);
|
||||
$fragment = $dom->createDocumentFragment();
|
||||
$fragment->appendXML('<div><p></p></div>');
|
||||
test_helper($fragment, ':root', true);
|
||||
test_helper($dom->createElement("foo"), ':root', true);
|
||||
|
||||
test_helper($dom->createElement("not-a-root"), ":root", true);
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
--- Selector: :root ---
|
||||
container
|
||||
--- Selector: :root ---
|
||||
div
|
||||
--- Selector: :root ---
|
||||
--- Selector: :root ---
|
55
ext/dom/tests/modern/css_selectors/pseudo_elements.phpt
Normal file
55
ext/dom/tests/modern/css_selectors/pseudo_elements.phpt
Normal file
@ -0,0 +1,55 @@
|
||||
--TEST--
|
||||
CSS Selectors - Pseudo elements
|
||||
--EXTENSIONS--
|
||||
dom
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
require __DIR__ . '/test_utils.inc';
|
||||
|
||||
$dom = DOM\XMLDocument::createFromString(<<<XML
|
||||
<container>
|
||||
<a/>
|
||||
</container>
|
||||
XML);
|
||||
|
||||
// Pseudo-elements can't work in a static DOM
|
||||
test_failure($dom, 'a::after');
|
||||
test_failure($dom, 'a::before');
|
||||
test_failure($dom, 'a::backdrop');
|
||||
test_failure($dom, 'a::first-letter');
|
||||
test_failure($dom, 'a::first-line');
|
||||
test_failure($dom, 'a::grammar-error');
|
||||
test_failure($dom, 'a::inactive-selection');
|
||||
test_failure($dom, 'a::marker');
|
||||
test_failure($dom, 'a::placeholder');
|
||||
test_failure($dom, 'a::selection');
|
||||
test_failure($dom, 'a::spelling-error');
|
||||
test_failure($dom, 'a::target-text');
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
--- Selector: a::after ---
|
||||
Code 12 Invalid selector (Selectors. Not supported: after)
|
||||
--- Selector: a::before ---
|
||||
Code 12 Invalid selector (Selectors. Not supported: before)
|
||||
--- Selector: a::backdrop ---
|
||||
Code 12 Invalid selector (Selectors. Not supported: backdrop)
|
||||
--- Selector: a::first-letter ---
|
||||
Code 12 Invalid selector (Selectors. Not supported: first-letter)
|
||||
--- Selector: a::first-line ---
|
||||
Code 12 Invalid selector (Selectors. Not supported: first-line)
|
||||
--- Selector: a::grammar-error ---
|
||||
Code 12 Invalid selector (Selectors. Not supported: grammar-error)
|
||||
--- Selector: a::inactive-selection ---
|
||||
Code 12 Invalid selector (Selectors. Not supported: inactive-selection)
|
||||
--- Selector: a::marker ---
|
||||
Code 12 Invalid selector (Selectors. Not supported: marker)
|
||||
--- Selector: a::placeholder ---
|
||||
Code 12 Invalid selector (Selectors. Not supported: placeholder)
|
||||
--- Selector: a::selection ---
|
||||
Code 12 Invalid selector (Selectors. Not supported: selection)
|
||||
--- Selector: a::spelling-error ---
|
||||
Code 12 Invalid selector (Selectors. Not supported: spelling-error)
|
||||
--- Selector: a::target-text ---
|
||||
Code 12 Invalid selector (Selectors. Not supported: target-text)
|
68
ext/dom/tests/modern/css_selectors/quirks_mode.phpt
Normal file
68
ext/dom/tests/modern/css_selectors/quirks_mode.phpt
Normal file
@ -0,0 +1,68 @@
|
||||
--TEST--
|
||||
CSS Selectors - Quirks mode test
|
||||
--EXTENSIONS--
|
||||
dom
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
require __DIR__ . '/test_utils.inc';
|
||||
|
||||
echo "\n=== Document in quirks mode ===\n\n";
|
||||
|
||||
$dom = DOM\HTMLDocument::createFromString(<<<HTML
|
||||
<html>
|
||||
<div class="HElLoWorLD"/>
|
||||
<div id="hI"/>
|
||||
</html>
|
||||
HTML, LIBXML_NOERROR);
|
||||
|
||||
test_helper($dom, 'div.helloworld');
|
||||
test_helper($dom, 'div.HElLoWorLD');
|
||||
test_helper($dom, '#hi');
|
||||
test_helper($dom, '#hI');
|
||||
|
||||
echo "\n=== Document not in quirks mode ===\n\n";
|
||||
|
||||
$dom = DOM\HTMLDocument::createFromString(<<<HTML
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<div class="HElLoWorLD"/>
|
||||
<div id="hI"/>
|
||||
</html>
|
||||
HTML, LIBXML_NOERROR);
|
||||
|
||||
test_helper($dom, 'div.helloworld');
|
||||
test_helper($dom, 'div.HElLoWorLD');
|
||||
test_helper($dom, '#hi');
|
||||
test_helper($dom, '#hI');
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
=== Document in quirks mode ===
|
||||
|
||||
--- Selector: div.helloworld ---
|
||||
<div xmlns="http://www.w3.org/1999/xhtml" class="HElLoWorLD">
|
||||
<div id="hI">
|
||||
</div></div>
|
||||
--- Selector: div.HElLoWorLD ---
|
||||
<div xmlns="http://www.w3.org/1999/xhtml" class="HElLoWorLD">
|
||||
<div id="hI">
|
||||
</div></div>
|
||||
--- Selector: #hi ---
|
||||
<div xmlns="http://www.w3.org/1999/xhtml" id="hI">
|
||||
</div>
|
||||
--- Selector: #hI ---
|
||||
<div xmlns="http://www.w3.org/1999/xhtml" id="hI">
|
||||
</div>
|
||||
|
||||
=== Document not in quirks mode ===
|
||||
|
||||
--- Selector: div.helloworld ---
|
||||
--- Selector: div.HElLoWorLD ---
|
||||
<div xmlns="http://www.w3.org/1999/xhtml" class="HElLoWorLD">
|
||||
<div id="hI">
|
||||
</div></div>
|
||||
--- Selector: #hi ---
|
||||
--- Selector: #hI ---
|
||||
<div xmlns="http://www.w3.org/1999/xhtml" id="hI">
|
||||
</div>
|
46
ext/dom/tests/modern/css_selectors/test_utils.inc
Normal file
46
ext/dom/tests/modern/css_selectors/test_utils.inc
Normal file
@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
function test_helper(DOM\ParentNode $dom, string $selector, bool $only_name = false)
|
||||
{
|
||||
echo "--- Selector: $selector ---\n";
|
||||
|
||||
$all = $dom->querySelectorAll($selector);
|
||||
$single = $dom->querySelector($selector);
|
||||
|
||||
if ((count($all) === 0 && $single !== null) || ($all[0] !== $single)) {
|
||||
throw new Exception('Mismatch in querySelector and querySelectorAll');
|
||||
}
|
||||
|
||||
$list = [];
|
||||
foreach ($all as $node) {
|
||||
$list[] = $node;
|
||||
|
||||
if ($only_name) {
|
||||
echo $node->nodeName, "\n";
|
||||
continue;
|
||||
}
|
||||
|
||||
echo $dom->saveXML($node), "\n";
|
||||
}
|
||||
|
||||
// If the element is in the list, then it must match, otherwise it must not
|
||||
// This loops over all the elements in the document and checks them
|
||||
foreach ($dom->querySelectorAll('*') as $node) {
|
||||
if (in_array($node, $list, true) !== $node->matches($selector)) {
|
||||
var_dump($node, $selector, in_array($node, $list, true), $node->matches($selector));
|
||||
echo $dom->saveXML($node), "\n";
|
||||
throw new Exception('Bug in Element::matches()');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function test_failure(DOM\ParentNode $dom, string $selector)
|
||||
{
|
||||
echo "--- Selector: $selector ---\n";
|
||||
|
||||
try {
|
||||
var_dump(count($dom->querySelectorAll($selector)));
|
||||
} catch (DOMException $e) {
|
||||
echo "Code ", $e->getCode(), " ", $e->getMessage(), "\n";
|
||||
}
|
||||
}
|
@ -1350,6 +1350,7 @@ PHP_LIBXML_API int php_libxml_increment_doc_ref(php_libxml_node_object *object,
|
||||
object->document->private_data = NULL;
|
||||
object->document->class_type = PHP_LIBXML_CLASS_UNSET;
|
||||
object->document->handlers = &php_libxml_default_document_handlers;
|
||||
object->document->quirks_mode = false;
|
||||
}
|
||||
|
||||
return ret_refcount;
|
||||
|
@ -102,7 +102,8 @@ typedef struct _php_libxml_ref_obj {
|
||||
php_libxml_private_data_header *private_data;
|
||||
const php_libxml_document_handlers *handlers;
|
||||
int refcount;
|
||||
php_libxml_class_type class_type;
|
||||
php_libxml_class_type class_type : 8;
|
||||
bool quirks_mode;
|
||||
} php_libxml_ref_obj;
|
||||
|
||||
typedef struct _php_libxml_node_ptr {
|
||||
|
Loading…
Reference in New Issue
Block a user