Skip to content

Commit 4cab7f9

Browse files
committed
[RFC] Implement XMLReader::fromUri() and XMLReader::fromString()
1 parent c24b8fe commit 4cab7f9

9 files changed

+218
-11
lines changed

NEWS

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -324,7 +324,7 @@ PHP NEWS
324324

325325
- XMLReader:
326326
. Declares class constant types. (Ayesh)
327-
. Add XMLReader::fromStream(). (nielsdos)
327+
. Add XMLReader::fromStream(), XMLReader::fromUri(), XMLReader::fromString(). (nielsdos)
328328

329329
- XMLWriter:
330330
. Add XMLWriter::toStream(), XMLWriter::toUri(), XMLWriter::toMemory(). (nielsdos)

UPGRADING

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -629,7 +629,7 @@ PHP 8.4 UPGRADE NOTES
629629
RFC: https://wiki.php.net/rfc/array_find
630630

631631
- XMLReader:
632-
. Added XMLReader::fromStream().
632+
. Added XMLReader::fromStream(), XMLReader::fromUri(), XMLReader::fromString().
633633
RFC: https://wiki.php.net/rfc/xmlreader_writer_streams
634634

635635
- XMLWriter:

ext/xmlreader/php_xmlreader.c

Lines changed: 47 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -815,7 +815,7 @@ static bool xmlreader_valid_encoding(const char *encoding)
815815
}
816816

817817
/* {{{ Sets the URI that the XMLReader will parse. */
818-
PHP_METHOD(XMLReader, open)
818+
static void xml_reader_from_uri(INTERNAL_FUNCTION_PARAMETERS, zend_class_entry *instance_ce, bool use_exceptions)
819819
{
820820
zval *id;
821821
size_t source_len = 0, encoding_len = 0;
@@ -856,12 +856,20 @@ PHP_METHOD(XMLReader, open)
856856
}
857857

858858
if (reader == NULL) {
859-
php_error_docref(NULL, E_WARNING, "Unable to open source data");
860-
RETURN_FALSE;
859+
if (use_exceptions) {
860+
zend_throw_error(NULL, "Unable to open source data");
861+
RETURN_THROWS();
862+
} else {
863+
php_error_docref(NULL, E_WARNING, "Unable to open source data");
864+
RETURN_FALSE;
865+
}
861866
}
862867

863868
if (id == NULL) {
864-
object_init_ex(return_value, xmlreader_class_entry);
869+
if (UNEXPECTED(object_init_with_constructor(return_value, instance_ce, 0, NULL, NULL) != SUCCESS)) {
870+
xmlFreeTextReader(reader);
871+
RETURN_THROWS();
872+
}
865873
intern = Z_XMLREADER_P(return_value);
866874
intern->ptr = reader;
867875
return;
@@ -872,6 +880,16 @@ PHP_METHOD(XMLReader, open)
872880
RETURN_TRUE;
873881

874882
}
883+
884+
PHP_METHOD(XMLReader, open)
885+
{
886+
xml_reader_from_uri(INTERNAL_FUNCTION_PARAM_PASSTHRU, xmlreader_class_entry, false);
887+
}
888+
889+
PHP_METHOD(XMLReader, fromUri)
890+
{
891+
xml_reader_from_uri(INTERNAL_FUNCTION_PARAM_PASSTHRU, Z_CE_P(ZEND_THIS), true);
892+
}
875893
/* }}} */
876894

877895
static int xml_reader_stream_read(void *context, char *buffer, int len)
@@ -1068,7 +1086,7 @@ XMLPUBFUN int XMLCALL
10681086
*/
10691087

10701088
/* {{{ Sets the string that the XMLReader will parse. */
1071-
PHP_METHOD(XMLReader, XML)
1089+
static void xml_reader_from_string(INTERNAL_FUNCTION_PARAMETERS, zend_class_entry *instance_ce, bool throw)
10721090
{
10731091
zval *id;
10741092
size_t source_len = 0, encoding_len = 0;
@@ -1125,7 +1143,12 @@ PHP_METHOD(XMLReader, XML)
11251143
ret = xmlTextReaderSetup(reader, NULL, uri, encoding, options);
11261144
if (ret == 0) {
11271145
if (id == NULL) {
1128-
object_init_ex(return_value, xmlreader_class_entry);
1146+
if (UNEXPECTED(object_init_with_constructor(return_value, instance_ce, 0, NULL, NULL) != SUCCESS)) {
1147+
xmlFree(uri);
1148+
xmlFreeParserInputBuffer(inputbfr);
1149+
xmlFreeTextReader(reader);
1150+
RETURN_THROWS();
1151+
}
11291152
intern = Z_XMLREADER_P(return_value);
11301153
} else {
11311154
RETVAL_TRUE;
@@ -1151,11 +1174,27 @@ PHP_METHOD(XMLReader, XML)
11511174
if (inputbfr) {
11521175
xmlFreeParserInputBuffer(inputbfr);
11531176
}
1154-
php_error_docref(NULL, E_WARNING, "Unable to load source data");
1155-
RETURN_FALSE;
1177+
1178+
if (throw) {
1179+
zend_throw_error(NULL, "Unable to load source data");
1180+
RETURN_THROWS();
1181+
} else {
1182+
php_error_docref(NULL, E_WARNING, "Unable to load source data");
1183+
RETURN_FALSE;
1184+
}
11561185
}
11571186
/* }}} */
11581187

1188+
PHP_METHOD(XMLReader, XML)
1189+
{
1190+
xml_reader_from_string(INTERNAL_FUNCTION_PARAM_PASSTHRU, xmlreader_class_entry, false);
1191+
}
1192+
1193+
PHP_METHOD(XMLReader, fromString)
1194+
{
1195+
xml_reader_from_string(INTERNAL_FUNCTION_PARAM_PASSTHRU, Z_CE_P(ZEND_THIS), true);
1196+
}
1197+
11591198
/* {{{ Moves the position of the current instance to the next node in the stream. */
11601199
PHP_METHOD(XMLReader, expand)
11611200
{

ext/xmlreader/php_xmlreader.stub.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,8 @@ public function next(?string $name = null): bool {}
175175
/** @return bool|XMLReader */
176176
public static function open(string $uri, ?string $encoding = null, int $flags = 0) {} // TODO Return type shouldn't be dependent on the call scope
177177

178+
public static function fromUri(string $uri, ?string $encoding = null, int $flags = 0): static {}
179+
178180
/** @param resource $stream */
179181
public static function fromStream($stream, ?string $encoding = null, int $flags = 0, ?string $documentUri = null): static {}
180182

@@ -202,6 +204,8 @@ public function setRelaxNGSchemaSource(?string $source): bool {}
202204
/** @return bool|XMLReader */
203205
public static function XML(string $source, ?string $encoding = null, int $flags = 0) {} // TODO Return type shouldn't be dependent on the call scope
204206

207+
public static function fromString(string $source, ?string $encoding = null, int $flags = 0): static {}
208+
205209
/** @tentative-return-type */
206210
public function expand(?DOMNode $baseNode = null): DOMNode|false {}
207211
}

ext/xmlreader/php_xmlreader_arginfo.h

Lines changed: 17 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
--TEST--
2+
XMLReader::fromString() - custom constructor
3+
--EXTENSIONS--
4+
xmlreader
5+
--FILE--
6+
<?php
7+
class CustomXMLReader extends XMLReader {
8+
public int $myField;
9+
10+
public function __construct() {
11+
$this->myField = 1234;
12+
echo "hello world\n";
13+
}
14+
}
15+
16+
$reader = CustomXMLReader::fromString("<root/>");
17+
var_dump($reader);
18+
var_dump($reader->read());
19+
var_dump($reader->nodeType);
20+
?>
21+
--EXPECTF--
22+
hello world
23+
object(CustomXMLReader)#%d (1) {
24+
["attributeCount"]=>
25+
uninitialized(int)
26+
["baseURI"]=>
27+
uninitialized(string)
28+
["depth"]=>
29+
uninitialized(int)
30+
["hasAttributes"]=>
31+
uninitialized(bool)
32+
["hasValue"]=>
33+
uninitialized(bool)
34+
["isDefault"]=>
35+
uninitialized(bool)
36+
["isEmptyElement"]=>
37+
uninitialized(bool)
38+
["localName"]=>
39+
uninitialized(string)
40+
["name"]=>
41+
uninitialized(string)
42+
["namespaceURI"]=>
43+
uninitialized(string)
44+
["nodeType"]=>
45+
uninitialized(int)
46+
["prefix"]=>
47+
uninitialized(string)
48+
["value"]=>
49+
uninitialized(string)
50+
["xmlLang"]=>
51+
uninitialized(string)
52+
["myField"]=>
53+
int(1234)
54+
}
55+
bool(true)
56+
int(1)
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
--TEST--
2+
XMLReader::fromString() - custom constructor with error
3+
--EXTENSIONS--
4+
xmlreader
5+
--FILE--
6+
<?php
7+
class CustomXMLReader extends XMLReader {
8+
public function __construct() {
9+
throw new Error('nope');
10+
}
11+
}
12+
13+
try {
14+
CustomXMLReader::fromString("<root/>");
15+
} catch (Throwable $e) {
16+
echo $e->getMessage(), "\n";
17+
}
18+
?>
19+
--EXPECT--
20+
nope
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
--TEST--
2+
XMLReader::fromUri() - custom constructor
3+
--EXTENSIONS--
4+
xmlreader
5+
--FILE--
6+
<?php
7+
class CustomXMLReader extends XMLReader {
8+
public int $myField;
9+
10+
public function __construct() {
11+
$this->myField = 1234;
12+
echo "hello world\n";
13+
}
14+
}
15+
16+
$filename = __DIR__ . '/_fromUri_custom_constructor.xml';
17+
$xmlstring = '<?xml version="1.0" encoding="UTF-8"?>
18+
<books></books>';
19+
file_put_contents($filename, $xmlstring);
20+
21+
$reader = XMLReader::fromUri($filename);
22+
23+
// Only go through
24+
while ($reader->read()) {
25+
echo $reader->name."\n";
26+
}
27+
$reader->close();
28+
29+
?>
30+
--EXPECT--
31+
books
32+
books
33+
--CLEAN--
34+
<?php
35+
@unlink(__DIR__ . '/_fromUri_custom_constructor.xml');
36+
?>
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
--TEST--
2+
XMLReader::fromUri() - custom constructor with error
3+
--EXTENSIONS--
4+
xmlreader
5+
--FILE--
6+
<?php
7+
class CustomXMLReader extends XMLReader {
8+
public function __construct() {
9+
throw new Error('nope');
10+
}
11+
}
12+
13+
try {
14+
CustomXMLReader::fromUri("nonexistent");
15+
} catch (Throwable $e) {
16+
echo $e->getMessage(), "\n";
17+
}
18+
19+
$filename = __DIR__ . '/_fromUri_custom_constructor_error.xml';
20+
$xmlstring = '<?xml version="1.0" encoding="UTF-8"?>
21+
<books></books>';
22+
file_put_contents($filename, $xmlstring);
23+
24+
try {
25+
CustomXMLReader::fromUri($filename);
26+
} catch (Throwable $e) {
27+
echo $e->getMessage(), "\n";
28+
}
29+
?>
30+
--EXPECT--
31+
Unable to open source data
32+
nope
33+
--CLEAN--
34+
<?php
35+
@unlink(__DIR__ . '/_fromUri_custom_constructor_error.xml');
36+
?>

0 commit comments

Comments
 (0)