@@ -34,6 +34,18 @@ public function exists(): bool {
3434 return $ this ->get_element_by_xpath ( $ this ->data ['xpath ' ] ) instanceof DOMElement;
3535 }
3636
37+ /**
38+ * Check if the element exists using XPath with a snippet-based fallback.
39+ *
40+ * @return boolean
41+ */
42+ public function exists_with_fallback (): bool {
43+ return $ this ->get_element_by_xpath_with_snippet_fallback (
44+ $ this ->data ['xpath ' ] ?? null ,
45+ $ this ->data ['find ' ] ?? null
46+ ) instanceof DOMElement;
47+ }
48+
3749 /**
3850 * get_element_by_xpath
3951 * @param $xpath
@@ -60,18 +72,20 @@ public function get_element_by_xpath( $query ) {
6072 * @return DOMElement|null
6173 */
6274 public function get_element_by_xpath_with_snippet_fallback ( ?string $ xpath , ?string $ snippet ): ?DOMElement {
63- if ( ! $ xpath ) {
64- return null ;
75+ $ element = $ xpath ? $ this ->get_element_by_xpath ( $ xpath ) : null ;
76+
77+ // Without a snippet we can't validate or fall back, so return whatever XPath found.
78+ if ( null === $ snippet || '' === $ snippet ) {
79+ return $ element instanceof DOMElement ? $ element : null ;
6580 }
6681
67- $ element = $ this ->get_element_by_xpath ( $ xpath );
68- if ( $ element && ! $ this ->element_contains_snippet ( $ element , $ snippet ) ) {
69- // XPath result doesn't contain the snippet
82+ // If XPath found an element but its outer HTML doesn't contain the snippet, discard it.
83+ if ( $ element instanceof DOMElement && ! $ this ->element_contains_snippet ( $ element , $ snippet ) ) {
7084 $ element = null ;
7185 }
7286
73- // Fallback to snippet-based search
74- if ( ! $ element ) {
87+ // Fallback to snippet-based search.
88+ if ( ! $ element instanceof DOMElement ) {
7589 $ element = $ this ->get_element_by_snippet ( $ snippet );
7690 }
7791
@@ -161,8 +175,9 @@ public function create_element( $data ) : DomElement {
161175 public function __construct ( DOMDocument $ dom , $ data ) {
162176 $ this ->dom = $ dom ;
163177 $ this ->data = $ data ;
164- // if it's not global and not styles and element does not exist, move the remediation to the Frontend
165- if ( 'STYLES ' !== $ this ->data ['type ' ] && ! $ this ->data ['global ' ] && ! $ this ->exists () ) {
178+ // If it's not styles and the element can't be found by XPath or the snippet fallback,
179+ // move the remediation to the Frontend so a later DOM state can pick it up.
180+ if ( 'STYLES ' !== $ this ->data ['type ' ] && ! $ this ->exists_with_fallback () ) {
166181 $ this ->use_frontend = true ;
167182 return ;
168183 }
0 commit comments