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-- +