diff --git a/UPGRADING b/UPGRADING
index 11cca55bd1735..274f45d8f401b 100644
--- a/UPGRADING
+++ b/UPGRADING
@@ -82,6 +82,11 @@ PHP 8.5 UPGRADE NOTES
. A ValueError is now thrown when trying to set a cursor name that is too
long on a PDOStatement resulting from the Firebird driver.
+- SimpleXML:
+ - Passing an XPath expression that returns something other than a node set
+ to SimpleXMLElement::xpath() will now emit a warning and return false,
+ instead of silently failing and returning an empty array.
+
- SPL:
. ArrayObject no longer accepts enums, as modifying the $name or $value
properties can break engine assumptions.
diff --git a/ext/simplexml/simplexml.c b/ext/simplexml/simplexml.c
index 1de7ccc6e74e8..877280fb378dc 100644
--- a/ext/simplexml/simplexml.c
+++ b/ext/simplexml/simplexml.c
@@ -1215,6 +1215,21 @@ static int sxe_objects_compare(zval *object1, zval *object2) /* {{{ */
}
/* }}} */
+static const char *sxe_get_object_type_name(xmlXPathObjectType type)
+{
+ switch (type) {
+ case XPATH_BOOLEAN: return "bool";
+ case XPATH_NUMBER: return "number";
+ case XPATH_STRING: return "string";
+#ifdef LIBXML_XPTR_LOCS_ENABLED
+ case XPATH_POINT: return "point";
+ case XPATH_RANGE: return "range";
+ case XPATH_LOCATIONSET: return "location set";
+#endif
+ default: return "undefined";
+ }
+}
+
/* {{{ Runs XPath query on the XML data */
PHP_METHOD(SimpleXMLElement, xpath)
{
@@ -1271,6 +1286,13 @@ PHP_METHOD(SimpleXMLElement, xpath)
RETURN_FALSE;
}
+ if (UNEXPECTED(retval->type != XPATH_NODESET)) {
+ php_error_docref(NULL, E_WARNING, "XPath expression must return a node set, %s returned",
+ sxe_get_object_type_name(retval->type));
+ xmlXPathFreeObject(retval);
+ RETURN_FALSE;
+ }
+
result = retval->nodesetval;
if (result != NULL) {
diff --git a/ext/simplexml/tests/008.phpt b/ext/simplexml/tests/008.phpt
index c946c36dafe63..dea6f98eacfcc 100644
--- a/ext/simplexml/tests/008.phpt
+++ b/ext/simplexml/tests/008.phpt
@@ -39,8 +39,9 @@ array(1) {
}
}
}
-array(0) {
-}
-Warning: SimpleXMLElement::xpath(): Invalid expression in %s on line %d%A
+Warning: SimpleXMLElement::xpath(): XPath expression must return a node set, number returned in %s on line %d
+bool(false)
+
+Warning: SimpleXMLElement::xpath(): Invalid expression in %s on line %d
bool(false)
diff --git a/ext/simplexml/tests/gh12231.phpt b/ext/simplexml/tests/gh12231.phpt
new file mode 100644
index 0000000000000..efacd92b76f95
--- /dev/null
+++ b/ext/simplexml/tests/gh12231.phpt
@@ -0,0 +1,26 @@
+--TEST--
+GH-12231 (SimpleXML xpath should warn when returning other return types than node lists)
+--EXTENSIONS--
+simplexml
+--FILE--
+";
+$sxe = simplexml_load_string($xml);
+
+var_dump($sxe->xpath("count(//foo)"));
+var_dump($sxe->xpath("string(//foo)"));
+var_dump($sxe->xpath("boolean(//foo)"));
+var_dump(count($sxe->xpath("//foo")));
+
+?>
+--EXPECTF--
+Warning: SimpleXMLElement::xpath(): XPath expression must return a node set, number returned in %s on line %d
+bool(false)
+
+Warning: SimpleXMLElement::xpath(): XPath expression must return a node set, string returned in %s on line %d
+bool(false)
+
+Warning: SimpleXMLElement::xpath(): XPath expression must return a node set, bool returned in %s on line %d
+bool(false)
+int(2)