|
24 | 24 | #include "php_dom.h"
|
25 | 25 | #include "namespace_compat.h"
|
26 | 26 |
|
| 27 | +#include "lexbor/css/parser.h" |
| 28 | +#include "lexbor/selectors-adapted/selectors.h" |
| 29 | + |
27 | 30 | /*
|
28 | 31 | * class DOMElement extends DOMNode
|
29 | 32 | *
|
@@ -1640,4 +1643,135 @@ PHP_METHOD(DOMElement, toggleAttribute)
|
1640 | 1643 | }
|
1641 | 1644 | /* }}} end DOMElement::prepend */
|
1642 | 1645 |
|
| 1646 | +/** |
| 1647 | + * CSS selector implementation below |
| 1648 | + */ |
| 1649 | + |
| 1650 | +typedef struct { |
| 1651 | + HashTable *list; |
| 1652 | + dom_object *intern; |
| 1653 | +} dom_query_selector_all_ctx; |
| 1654 | + |
| 1655 | +lxb_status_t php_dom_query_selector_find_single_callback(const xmlNode *node, lxb_css_selector_specificity_t spec, void *ctx) |
| 1656 | +{ |
| 1657 | + xmlNodePtr *result = (xmlNodePtr *) ctx; |
| 1658 | + *result = (xmlNodePtr) node; |
| 1659 | + return LXB_STATUS_STOP; |
| 1660 | +} |
| 1661 | + |
| 1662 | +lxb_status_t php_dom_query_selector_find_array_callback(const xmlNode *node, lxb_css_selector_specificity_t spec, void *ctx) |
| 1663 | +{ |
| 1664 | + dom_query_selector_all_ctx *qsa_ctx = (dom_query_selector_all_ctx *) ctx; |
| 1665 | + zval object; |
| 1666 | + php_dom_create_object((xmlNodePtr) node, &object, qsa_ctx->intern); |
| 1667 | + zend_hash_next_index_insert_new(qsa_ctx->list, &object); |
| 1668 | + return LXB_STATUS_OK; |
| 1669 | +} |
| 1670 | + |
| 1671 | +static lxb_status_t php_dom_query_selector_common(zval *return_value, dom_object *intern, const xmlNode *root, zend_string *selectors_str, lxb_selectors_cb_f cb, void *ctx) |
| 1672 | +{ |
| 1673 | + lxb_status_t status; |
| 1674 | + |
| 1675 | + if (root->type == XML_DOCUMENT_NODE || root->type == XML_HTML_DOCUMENT_NODE) { |
| 1676 | + root = xmlDocGetRootElement((const xmlDoc *) root); |
| 1677 | + } |
| 1678 | + |
| 1679 | + lxb_css_parser_t parser; |
| 1680 | + status = lxb_css_parser_init(&parser, NULL); |
| 1681 | + ZEND_ASSERT(status == LXB_STATUS_OK); |
| 1682 | + |
| 1683 | + lxb_selectors_t selectors; |
| 1684 | + status = lxb_selectors_init(&selectors); |
| 1685 | + ZEND_ASSERT(status == LXB_STATUS_OK); |
| 1686 | + |
| 1687 | + lxb_css_selector_list_t *list = lxb_css_selectors_parse(&parser, (const lxb_char_t *) ZSTR_VAL(selectors_str), ZSTR_LEN(selectors_str)); |
| 1688 | + if (UNEXPECTED(list == NULL)) { |
| 1689 | + size_t nr_of_messages = lexbor_array_obj_length(&parser.log->messages); |
| 1690 | + if (nr_of_messages > 0) { |
| 1691 | + lxb_css_log_message_t *msg = lexbor_array_obj_get(&parser.log->messages, 0); |
| 1692 | + zend_argument_value_error(1, "must be a valid selector (%.*s)", msg->text.length, msg->text.data); |
| 1693 | + } else { |
| 1694 | + zend_argument_value_error(1, "must be a valid selector"); |
| 1695 | + } |
| 1696 | + status = LXB_STATUS_ERROR; |
| 1697 | + } else { |
| 1698 | + status = lxb_selectors_find(&selectors, root, list, cb, ctx); |
| 1699 | + if (UNEXPECTED(status != LXB_STATUS_OK && status != LXB_STATUS_STOP)) { |
| 1700 | + /* Shouldn't happen, but for safety reasons let's throw here... */ |
| 1701 | + zend_throw_error(NULL, "Unexpected failure during evaluation of selector"); |
| 1702 | + } |
| 1703 | + } |
| 1704 | + |
| 1705 | + lxb_css_selector_list_destroy_memory(list); |
| 1706 | + (void) lxb_selectors_destroy(&selectors); |
| 1707 | + (void) lxb_css_parser_destroy(&parser, false); |
| 1708 | + |
| 1709 | + return status; |
| 1710 | +} |
| 1711 | + |
| 1712 | +PHP_METHOD(DOMElement, querySelector) |
| 1713 | +{ |
| 1714 | + zend_string *selectors_str; |
| 1715 | + |
| 1716 | + ZEND_PARSE_PARAMETERS_START(1, 1) |
| 1717 | + Z_PARAM_STR(selectors_str) |
| 1718 | + ZEND_PARSE_PARAMETERS_END(); |
| 1719 | + |
| 1720 | + xmlNodePtr thisp; |
| 1721 | + dom_object *intern; |
| 1722 | + zval *id; |
| 1723 | + DOM_GET_THIS_OBJ(thisp, id, xmlNodePtr, intern); |
| 1724 | + |
| 1725 | + xmlNodePtr result = NULL; |
| 1726 | + |
| 1727 | + if (php_dom_query_selector_common( |
| 1728 | + return_value, |
| 1729 | + intern, |
| 1730 | + thisp, |
| 1731 | + selectors_str, |
| 1732 | + php_dom_query_selector_find_single_callback, |
| 1733 | + &result |
| 1734 | + ) != LXB_STATUS_OK || result == NULL) { |
| 1735 | + RETURN_NULL(); |
| 1736 | + } else { |
| 1737 | + int ret; |
| 1738 | + DOM_RET_OBJ(result, &ret, intern); |
| 1739 | + } |
| 1740 | +} |
| 1741 | + |
| 1742 | +PHP_METHOD(DOMElement, querySelectorAll) |
| 1743 | +{ |
| 1744 | + zend_string *selectors_str; |
| 1745 | + |
| 1746 | + ZEND_PARSE_PARAMETERS_START(1, 1) |
| 1747 | + Z_PARAM_STR(selectors_str) |
| 1748 | + ZEND_PARSE_PARAMETERS_END(); |
| 1749 | + |
| 1750 | + xmlNodePtr thisp; |
| 1751 | + dom_object *intern; |
| 1752 | + zval *id; |
| 1753 | + DOM_GET_THIS_OBJ(thisp, id, xmlNodePtr, intern); |
| 1754 | + |
| 1755 | + HashTable *list = zend_new_array(0); |
| 1756 | + dom_query_selector_all_ctx ctx = { list, intern }; |
| 1757 | + |
| 1758 | + if (php_dom_query_selector_common( |
| 1759 | + return_value, |
| 1760 | + intern, |
| 1761 | + thisp, |
| 1762 | + selectors_str, |
| 1763 | + php_dom_query_selector_find_array_callback, |
| 1764 | + &ctx |
| 1765 | + ) != LXB_STATUS_OK) { |
| 1766 | + zend_array_destroy(list); |
| 1767 | + RETURN_THROWS(); |
| 1768 | + } else { |
| 1769 | + php_dom_create_iterator(return_value, DOM_NODELIST); |
| 1770 | + dom_object *ret_obj = Z_DOMOBJ_P(return_value); |
| 1771 | + dom_nnodemap_object *mapptr = (dom_nnodemap_object *) ret_obj->ptr; |
| 1772 | + ZVAL_ARR(&mapptr->baseobj_zv, list); |
| 1773 | + mapptr->nodetype = DOM_NODESET; |
| 1774 | + } |
| 1775 | +} |
| 1776 | + |
1643 | 1777 | #endif
|
0 commit comments