From 4cab7f90a14a365590dab997931c8a344000bc5e Mon Sep 17 00:00:00 2001
From: Niels Dossche <7771979+nielsdos@users.noreply.github.com>
Date: Thu, 27 Jun 2024 22:28:50 +0200
Subject: [PATCH] [RFC] Implement XMLReader::fromUri() and
XMLReader::fromString()
---
NEWS | 2 +-
UPGRADING | 2 +-
ext/xmlreader/php_xmlreader.c | 55 +++++++++++++++---
ext/xmlreader/php_xmlreader.stub.php | 4 ++
ext/xmlreader/php_xmlreader_arginfo.h | 18 +++++-
.../tests/fromString_custom_constructor.phpt | 56 +++++++++++++++++++
.../fromString_custom_constructor_error.phpt | 20 +++++++
.../tests/fromUri_custom_constructor.phpt | 36 ++++++++++++
.../fromUri_custom_constructor_error.phpt | 36 ++++++++++++
9 files changed, 218 insertions(+), 11 deletions(-)
create mode 100644 ext/xmlreader/tests/fromString_custom_constructor.phpt
create mode 100644 ext/xmlreader/tests/fromString_custom_constructor_error.phpt
create mode 100644 ext/xmlreader/tests/fromUri_custom_constructor.phpt
create mode 100644 ext/xmlreader/tests/fromUri_custom_constructor_error.phpt
diff --git a/NEWS b/NEWS
index d98b69766f1..202ed2f3d6b 100644
--- a/NEWS
+++ b/NEWS
@@ -324,7 +324,7 @@ PHP NEWS
- XMLReader:
. Declares class constant types. (Ayesh)
- . Add XMLReader::fromStream(). (nielsdos)
+ . Add XMLReader::fromStream(), XMLReader::fromUri(), XMLReader::fromString(). (nielsdos)
- XMLWriter:
. Add XMLWriter::toStream(), XMLWriter::toUri(), XMLWriter::toMemory(). (nielsdos)
diff --git a/UPGRADING b/UPGRADING
index 00bdd36c7e1..670cac49ea1 100644
--- a/UPGRADING
+++ b/UPGRADING
@@ -629,7 +629,7 @@ PHP 8.4 UPGRADE NOTES
RFC: https://wiki.php.net/rfc/array_find
- XMLReader:
- . Added XMLReader::fromStream().
+ . Added XMLReader::fromStream(), XMLReader::fromUri(), XMLReader::fromString().
RFC: https://wiki.php.net/rfc/xmlreader_writer_streams
- XMLWriter:
diff --git a/ext/xmlreader/php_xmlreader.c b/ext/xmlreader/php_xmlreader.c
index 50e98b8935a..a92d45ace65 100644
--- a/ext/xmlreader/php_xmlreader.c
+++ b/ext/xmlreader/php_xmlreader.c
@@ -815,7 +815,7 @@ static bool xmlreader_valid_encoding(const char *encoding)
}
/* {{{ Sets the URI that the XMLReader will parse. */
-PHP_METHOD(XMLReader, open)
+static void xml_reader_from_uri(INTERNAL_FUNCTION_PARAMETERS, zend_class_entry *instance_ce, bool use_exceptions)
{
zval *id;
size_t source_len = 0, encoding_len = 0;
@@ -856,12 +856,20 @@ PHP_METHOD(XMLReader, open)
}
if (reader == NULL) {
- php_error_docref(NULL, E_WARNING, "Unable to open source data");
- RETURN_FALSE;
+ if (use_exceptions) {
+ zend_throw_error(NULL, "Unable to open source data");
+ RETURN_THROWS();
+ } else {
+ php_error_docref(NULL, E_WARNING, "Unable to open source data");
+ RETURN_FALSE;
+ }
}
if (id == NULL) {
- object_init_ex(return_value, xmlreader_class_entry);
+ if (UNEXPECTED(object_init_with_constructor(return_value, instance_ce, 0, NULL, NULL) != SUCCESS)) {
+ xmlFreeTextReader(reader);
+ RETURN_THROWS();
+ }
intern = Z_XMLREADER_P(return_value);
intern->ptr = reader;
return;
@@ -872,6 +880,16 @@ PHP_METHOD(XMLReader, open)
RETURN_TRUE;
}
+
+PHP_METHOD(XMLReader, open)
+{
+ xml_reader_from_uri(INTERNAL_FUNCTION_PARAM_PASSTHRU, xmlreader_class_entry, false);
+}
+
+PHP_METHOD(XMLReader, fromUri)
+{
+ xml_reader_from_uri(INTERNAL_FUNCTION_PARAM_PASSTHRU, Z_CE_P(ZEND_THIS), true);
+}
/* }}} */
static int xml_reader_stream_read(void *context, char *buffer, int len)
@@ -1068,7 +1086,7 @@ XMLPUBFUN int XMLCALL
*/
/* {{{ Sets the string that the XMLReader will parse. */
-PHP_METHOD(XMLReader, XML)
+static void xml_reader_from_string(INTERNAL_FUNCTION_PARAMETERS, zend_class_entry *instance_ce, bool throw)
{
zval *id;
size_t source_len = 0, encoding_len = 0;
@@ -1125,7 +1143,12 @@ PHP_METHOD(XMLReader, XML)
ret = xmlTextReaderSetup(reader, NULL, uri, encoding, options);
if (ret == 0) {
if (id == NULL) {
- object_init_ex(return_value, xmlreader_class_entry);
+ if (UNEXPECTED(object_init_with_constructor(return_value, instance_ce, 0, NULL, NULL) != SUCCESS)) {
+ xmlFree(uri);
+ xmlFreeParserInputBuffer(inputbfr);
+ xmlFreeTextReader(reader);
+ RETURN_THROWS();
+ }
intern = Z_XMLREADER_P(return_value);
} else {
RETVAL_TRUE;
@@ -1151,11 +1174,27 @@ PHP_METHOD(XMLReader, XML)
if (inputbfr) {
xmlFreeParserInputBuffer(inputbfr);
}
- php_error_docref(NULL, E_WARNING, "Unable to load source data");
- RETURN_FALSE;
+
+ if (throw) {
+ zend_throw_error(NULL, "Unable to load source data");
+ RETURN_THROWS();
+ } else {
+ php_error_docref(NULL, E_WARNING, "Unable to load source data");
+ RETURN_FALSE;
+ }
}
/* }}} */
+PHP_METHOD(XMLReader, XML)
+{
+ xml_reader_from_string(INTERNAL_FUNCTION_PARAM_PASSTHRU, xmlreader_class_entry, false);
+}
+
+PHP_METHOD(XMLReader, fromString)
+{
+ xml_reader_from_string(INTERNAL_FUNCTION_PARAM_PASSTHRU, Z_CE_P(ZEND_THIS), true);
+}
+
/* {{{ Moves the position of the current instance to the next node in the stream. */
PHP_METHOD(XMLReader, expand)
{
diff --git a/ext/xmlreader/php_xmlreader.stub.php b/ext/xmlreader/php_xmlreader.stub.php
index e10d7bc79c1..c58ee2460ff 100644
--- a/ext/xmlreader/php_xmlreader.stub.php
+++ b/ext/xmlreader/php_xmlreader.stub.php
@@ -175,6 +175,8 @@ class XMLReader
/** @return bool|XMLReader */
public static function open(string $uri, ?string $encoding = null, int $flags = 0) {} // TODO Return type shouldn't be dependent on the call scope
+ public static function fromUri(string $uri, ?string $encoding = null, int $flags = 0): static {}
+
/** @param resource $stream */
public static function fromStream($stream, ?string $encoding = null, int $flags = 0, ?string $documentUri = null): static {}
@@ -202,6 +204,8 @@ class XMLReader
/** @return bool|XMLReader */
public static function XML(string $source, ?string $encoding = null, int $flags = 0) {} // TODO Return type shouldn't be dependent on the call scope
+ public static function fromString(string $source, ?string $encoding = null, int $flags = 0): static {}
+
/** @tentative-return-type */
public function expand(?DOMNode $baseNode = null): DOMNode|false {}
}
diff --git a/ext/xmlreader/php_xmlreader_arginfo.h b/ext/xmlreader/php_xmlreader_arginfo.h
index 7ff905f14d1..a5a0c12cd25 100644
--- a/ext/xmlreader/php_xmlreader_arginfo.h
+++ b/ext/xmlreader/php_xmlreader_arginfo.h
@@ -1,5 +1,5 @@
/* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 08ea43f5bbfa20407a6c1913fe3a51e99ba79fd8 */
+ * Stub hash: 551324d130f9755c4c61cebb5084953fb6f539c4 */
ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_class_XMLReader_close, 0, 0, IS_TRUE, 0)
ZEND_END_ARG_INFO()
@@ -59,6 +59,12 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_XMLReader_open, 0, 0, 1)
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, flags, IS_LONG, 0, "0")
ZEND_END_ARG_INFO()
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_XMLReader_fromUri, 0, 1, IS_STATIC, 0)
+ ZEND_ARG_TYPE_INFO(0, uri, IS_STRING, 0)
+ ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, encoding, IS_STRING, 1, "null")
+ ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, flags, IS_LONG, 0, "0")
+ZEND_END_ARG_INFO()
+
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_XMLReader_fromStream, 0, 1, IS_STATIC, 0)
ZEND_ARG_INFO(0, stream)
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, encoding, IS_STRING, 1, "null")
@@ -94,6 +100,12 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_XMLReader_XML, 0, 0, 1)
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, flags, IS_LONG, 0, "0")
ZEND_END_ARG_INFO()
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_XMLReader_fromString, 0, 1, IS_STATIC, 0)
+ ZEND_ARG_TYPE_INFO(0, source, IS_STRING, 0)
+ ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, encoding, IS_STRING, 1, "null")
+ ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, flags, IS_LONG, 0, "0")
+ZEND_END_ARG_INFO()
+
ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_XMLReader_expand, 0, 0, DOMNode, MAY_BE_FALSE)
ZEND_ARG_OBJ_INFO_WITH_DEFAULT_VALUE(0, baseNode, DOMNode, 1, "null")
ZEND_END_ARG_INFO()
@@ -114,6 +126,7 @@ ZEND_METHOD(XMLReader, moveToNextAttribute);
ZEND_METHOD(XMLReader, read);
ZEND_METHOD(XMLReader, next);
ZEND_METHOD(XMLReader, open);
+ZEND_METHOD(XMLReader, fromUri);
ZEND_METHOD(XMLReader, fromStream);
ZEND_METHOD(XMLReader, readInnerXml);
ZEND_METHOD(XMLReader, readOuterXml);
@@ -123,6 +136,7 @@ ZEND_METHOD(XMLReader, setParserProperty);
ZEND_METHOD(XMLReader, setRelaxNGSchema);
ZEND_METHOD(XMLReader, setRelaxNGSchemaSource);
ZEND_METHOD(XMLReader, XML);
+ZEND_METHOD(XMLReader, fromString);
ZEND_METHOD(XMLReader, expand);
static const zend_function_entry class_XMLReader_methods[] = {
@@ -142,6 +156,7 @@ static const zend_function_entry class_XMLReader_methods[] = {
ZEND_ME(XMLReader, read, arginfo_class_XMLReader_read, ZEND_ACC_PUBLIC)
ZEND_ME(XMLReader, next, arginfo_class_XMLReader_next, ZEND_ACC_PUBLIC)
ZEND_ME(XMLReader, open, arginfo_class_XMLReader_open, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
+ ZEND_ME(XMLReader, fromUri, arginfo_class_XMLReader_fromUri, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
ZEND_ME(XMLReader, fromStream, arginfo_class_XMLReader_fromStream, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
ZEND_ME(XMLReader, readInnerXml, arginfo_class_XMLReader_readInnerXml, ZEND_ACC_PUBLIC)
ZEND_ME(XMLReader, readOuterXml, arginfo_class_XMLReader_readOuterXml, ZEND_ACC_PUBLIC)
@@ -151,6 +166,7 @@ static const zend_function_entry class_XMLReader_methods[] = {
ZEND_ME(XMLReader, setRelaxNGSchema, arginfo_class_XMLReader_setRelaxNGSchema, ZEND_ACC_PUBLIC)
ZEND_ME(XMLReader, setRelaxNGSchemaSource, arginfo_class_XMLReader_setRelaxNGSchemaSource, ZEND_ACC_PUBLIC)
ZEND_ME(XMLReader, XML, arginfo_class_XMLReader_XML, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
+ ZEND_ME(XMLReader, fromString, arginfo_class_XMLReader_fromString, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
ZEND_ME(XMLReader, expand, arginfo_class_XMLReader_expand, ZEND_ACC_PUBLIC)
ZEND_FE_END
};
diff --git a/ext/xmlreader/tests/fromString_custom_constructor.phpt b/ext/xmlreader/tests/fromString_custom_constructor.phpt
new file mode 100644
index 00000000000..f76af8d6b84
--- /dev/null
+++ b/ext/xmlreader/tests/fromString_custom_constructor.phpt
@@ -0,0 +1,56 @@
+--TEST--
+XMLReader::fromString() - custom constructor
+--EXTENSIONS--
+xmlreader
+--FILE--
+myField = 1234;
+ echo "hello world\n";
+ }
+}
+
+$reader = CustomXMLReader::fromString("");
+var_dump($reader);
+var_dump($reader->read());
+var_dump($reader->nodeType);
+?>
+--EXPECTF--
+hello world
+object(CustomXMLReader)#%d (1) {
+ ["attributeCount"]=>
+ uninitialized(int)
+ ["baseURI"]=>
+ uninitialized(string)
+ ["depth"]=>
+ uninitialized(int)
+ ["hasAttributes"]=>
+ uninitialized(bool)
+ ["hasValue"]=>
+ uninitialized(bool)
+ ["isDefault"]=>
+ uninitialized(bool)
+ ["isEmptyElement"]=>
+ uninitialized(bool)
+ ["localName"]=>
+ uninitialized(string)
+ ["name"]=>
+ uninitialized(string)
+ ["namespaceURI"]=>
+ uninitialized(string)
+ ["nodeType"]=>
+ uninitialized(int)
+ ["prefix"]=>
+ uninitialized(string)
+ ["value"]=>
+ uninitialized(string)
+ ["xmlLang"]=>
+ uninitialized(string)
+ ["myField"]=>
+ int(1234)
+}
+bool(true)
+int(1)
diff --git a/ext/xmlreader/tests/fromString_custom_constructor_error.phpt b/ext/xmlreader/tests/fromString_custom_constructor_error.phpt
new file mode 100644
index 00000000000..9bcffddba70
--- /dev/null
+++ b/ext/xmlreader/tests/fromString_custom_constructor_error.phpt
@@ -0,0 +1,20 @@
+--TEST--
+XMLReader::fromString() - custom constructor with error
+--EXTENSIONS--
+xmlreader
+--FILE--
+");
+} catch (Throwable $e) {
+ echo $e->getMessage(), "\n";
+}
+?>
+--EXPECT--
+nope
diff --git a/ext/xmlreader/tests/fromUri_custom_constructor.phpt b/ext/xmlreader/tests/fromUri_custom_constructor.phpt
new file mode 100644
index 00000000000..b562adbfec0
--- /dev/null
+++ b/ext/xmlreader/tests/fromUri_custom_constructor.phpt
@@ -0,0 +1,36 @@
+--TEST--
+XMLReader::fromUri() - custom constructor
+--EXTENSIONS--
+xmlreader
+--FILE--
+myField = 1234;
+ echo "hello world\n";
+ }
+}
+
+$filename = __DIR__ . '/_fromUri_custom_constructor.xml';
+$xmlstring = '
+';
+file_put_contents($filename, $xmlstring);
+
+$reader = XMLReader::fromUri($filename);
+
+// Only go through
+while ($reader->read()) {
+ echo $reader->name."\n";
+}
+$reader->close();
+
+?>
+--EXPECT--
+books
+books
+--CLEAN--
+
diff --git a/ext/xmlreader/tests/fromUri_custom_constructor_error.phpt b/ext/xmlreader/tests/fromUri_custom_constructor_error.phpt
new file mode 100644
index 00000000000..ad36c1e0c22
--- /dev/null
+++ b/ext/xmlreader/tests/fromUri_custom_constructor_error.phpt
@@ -0,0 +1,36 @@
+--TEST--
+XMLReader::fromUri() - custom constructor with error
+--EXTENSIONS--
+xmlreader
+--FILE--
+getMessage(), "\n";
+}
+
+$filename = __DIR__ . '/_fromUri_custom_constructor_error.xml';
+$xmlstring = '
+';
+file_put_contents($filename, $xmlstring);
+
+try {
+ CustomXMLReader::fromUri($filename);
+} catch (Throwable $e) {
+ echo $e->getMessage(), "\n";
+}
+?>
+--EXPECT--
+Unable to open source data
+nope
+--CLEAN--
+