Skip to content

Commit 086ea4c

Browse files
committed
Add simplexml_load_stream()
1 parent 6091820 commit 086ea4c

7 files changed

+219
-36
lines changed

ext/simplexml/simplexml.c

Lines changed: 72 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -2183,17 +2183,37 @@ sxe_object_new(zend_class_entry *ce)
21832183
}
21842184
/* }}} */
21852185

2186+
static void sxe_create_obj_from_doc(zval *return_value, xmlDocPtr docp, zend_class_entry *ce, zend_string *ns, bool isprefix)
2187+
{
2188+
if (!docp) {
2189+
RETURN_FALSE;
2190+
}
2191+
2192+
zend_function *fptr_count;
2193+
if (!ce) {
2194+
ce = ce_SimpleXMLElement;
2195+
fptr_count = NULL;
2196+
} else {
2197+
fptr_count = php_sxe_find_fptr_count(ce);
2198+
}
2199+
php_sxe_object *sxe = php_sxe_object_new(ce, fptr_count);
2200+
sxe->iter.nsprefix = ZSTR_LEN(ns) ? zend_string_copy(ns) : NULL;
2201+
sxe->iter.isprefix = isprefix;
2202+
php_libxml_increment_doc_ref((php_libxml_node_object *)sxe, docp);
2203+
php_libxml_increment_node_ptr((php_libxml_node_object *)sxe, xmlDocGetRootElement(docp), NULL);
2204+
2205+
RETURN_OBJ(&sxe->zo);
2206+
}
2207+
21862208
/* {{{ Load a filename and return a simplexml_element object to allow for processing */
21872209
PHP_FUNCTION(simplexml_load_file)
21882210
{
2189-
php_sxe_object *sxe;
21902211
char *filename;
21912212
size_t filename_len;
21922213
xmlDocPtr docp;
21932214
zend_string *ns = zend_empty_string;
21942215
zend_long options = 0;
21952216
zend_class_entry *ce= ce_SimpleXMLElement;
2196-
zend_function *fptr_count;
21972217
bool isprefix = 0;
21982218

21992219
if (zend_parse_parameters(ZEND_NUM_ARGS(), "p|C!lSb", &filename, &filename_len, &ce, &options, &ns, &isprefix) == FAILURE) {
@@ -2209,37 +2229,70 @@ PHP_FUNCTION(simplexml_load_file)
22092229
docp = xmlReadFile(filename, NULL, (int)options);
22102230
PHP_LIBXML_RESTORE_GLOBALS(read_file);
22112231

2212-
if (!docp) {
2213-
RETURN_FALSE;
2232+
sxe_create_obj_from_doc(return_value, docp, ce, ns, isprefix);
2233+
}
2234+
/* }}} */
2235+
2236+
static int sxe_stream_read(void *context, char *buffer, int len)
2237+
{
2238+
zend_resource *resource = context;
2239+
if (EXPECTED(resource->ptr)) {
2240+
php_stream *stream = resource->ptr;
2241+
return php_stream_read(stream, buffer, len);
22142242
}
2243+
return -1;
2244+
}
22152245

2216-
if (!ce) {
2217-
ce = ce_SimpleXMLElement;
2218-
fptr_count = NULL;
2219-
} else {
2220-
fptr_count = php_sxe_find_fptr_count(ce);
2246+
PHP_FUNCTION(simplexml_load_stream)
2247+
{
2248+
zval *stream_zv;
2249+
php_stream *stream;
2250+
xmlDocPtr docp;
2251+
zend_string *ns = zend_empty_string;
2252+
zend_long options = 0;
2253+
zend_class_entry *ce = ce_SimpleXMLElement;
2254+
bool isprefix = 0;
2255+
const char *encoding = NULL;
2256+
const char *document_uri = NULL;
2257+
size_t encoding_len, document_uri_len;
2258+
2259+
if (zend_parse_parameters(ZEND_NUM_ARGS(), "r|p!p!C!lSb",
2260+
&stream_zv, &encoding, &encoding_len, &document_uri, &document_uri_len, &ce, &options, &ns, &isprefix) == FAILURE) {
2261+
RETURN_THROWS();
22212262
}
2222-
sxe = php_sxe_object_new(ce, fptr_count);
2223-
sxe->iter.nsprefix = ZSTR_LEN(ns) ? zend_string_copy(ns) : NULL;
2224-
sxe->iter.isprefix = isprefix;
2225-
php_libxml_increment_doc_ref((php_libxml_node_object *)sxe, docp);
2226-
php_libxml_increment_node_ptr((php_libxml_node_object *)sxe, xmlDocGetRootElement(docp), NULL);
22272263

2228-
RETURN_OBJ(&sxe->zo);
2264+
php_stream_from_res(stream, Z_RES_P(stream_zv));
2265+
2266+
if (!php_libxml_is_valid_encoding(encoding)) {
2267+
zend_argument_value_error(2, "must be a valid character encoding");
2268+
RETURN_THROWS();
2269+
}
2270+
2271+
if (ZEND_LONG_EXCEEDS_INT(options)) {
2272+
zend_argument_value_error(5, "is too large");
2273+
RETURN_THROWS();
2274+
}
2275+
2276+
if (encoding) {
2277+
options |= XML_PARSE_IGNORE_ENC;
2278+
}
2279+
2280+
PHP_LIBXML_SANITIZE_GLOBALS(read_file);
2281+
docp = xmlReadIO(sxe_stream_read, NULL, stream->res, document_uri, encoding, (int) options);
2282+
PHP_LIBXML_RESTORE_GLOBALS(read_file);
2283+
2284+
sxe_create_obj_from_doc(return_value, docp, ce, ns, isprefix);
22292285
}
2230-
/* }}} */
22312286

22322287
/* {{{ Load a string and return a simplexml_element object to allow for processing */
22332288
PHP_FUNCTION(simplexml_load_string)
22342289
{
2235-
php_sxe_object *sxe;
22362290
char *data;
22372291
size_t data_len;
22382292
xmlDocPtr docp;
22392293
zend_string *ns = zend_empty_string;
22402294
zend_long options = 0;
22412295
zend_class_entry *ce= ce_SimpleXMLElement;
2242-
zend_function *fptr_count;
22432296
bool isprefix = 0;
22442297

22452298
if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|C!lSb", &data, &data_len, &ce, &options, &ns, &isprefix) == FAILURE) {
@@ -2263,23 +2316,7 @@ PHP_FUNCTION(simplexml_load_string)
22632316
docp = xmlReadMemory(data, (int)data_len, NULL, NULL, (int)options);
22642317
PHP_LIBXML_RESTORE_GLOBALS(read_memory);
22652318

2266-
if (!docp) {
2267-
RETURN_FALSE;
2268-
}
2269-
2270-
if (!ce) {
2271-
ce = ce_SimpleXMLElement;
2272-
fptr_count = NULL;
2273-
} else {
2274-
fptr_count = php_sxe_find_fptr_count(ce);
2275-
}
2276-
sxe = php_sxe_object_new(ce, fptr_count);
2277-
sxe->iter.nsprefix = ZSTR_LEN(ns) ? zend_string_copy(ns) : NULL;
2278-
sxe->iter.isprefix = isprefix;
2279-
php_libxml_increment_doc_ref((php_libxml_node_object *)sxe, docp);
2280-
php_libxml_increment_node_ptr((php_libxml_node_object *)sxe, xmlDocGetRootElement(docp), NULL);
2281-
2282-
RETURN_OBJ(&sxe->zo);
2319+
sxe_create_obj_from_doc(return_value, docp, ce, ns, isprefix);
22832320
}
22842321
/* }}} */
22852322

ext/simplexml/simplexml.stub.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@
44

55
function simplexml_load_file(string $filename, ?string $class_name = SimpleXMLElement::class, int $options = 0, string $namespace_or_prefix = "", bool $is_prefix = false): SimpleXMLElement|false {}
66

7+
/** @param resource $stream */
8+
function simplexml_load_stream($stream, ?string $encoding = null, ?string $document_uri = null, ?string $class_name = SimpleXMLElement::class, int $options = 0, string $namespace_or_prefix = "", bool $is_prefix = false): SimpleXMLElement|false {}
9+
710
function simplexml_load_string(string $data, ?string $class_name = SimpleXMLElement::class, int $options = 0, string $namespace_or_prefix = "", bool $is_prefix = false): SimpleXMLElement|false {}
811

912
function simplexml_import_dom(object $node, ?string $class_name = SimpleXMLElement::class): ?SimpleXMLElement {}

ext/simplexml/simplexml_arginfo.h

Lines changed: 13 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
--TEST--
2+
simplexml_load_stream() - from broken stream
3+
--EXTENSIONS--
4+
simplexml
5+
--FILE--
6+
<?php
7+
8+
class MyStream {
9+
public $context;
10+
private bool $first = true;
11+
12+
public function stream_read(int $count): string|false {
13+
var_dump($count);
14+
if ($this->first) {
15+
$this->first = false;
16+
return "<root><child/>";
17+
}
18+
return false;
19+
}
20+
21+
public function stream_open(string $path, string $mode, int $options, ?string &$opened_path) {
22+
return true;
23+
}
24+
25+
public function stream_close(): void {
26+
}
27+
28+
public function stream_eof(): bool {
29+
return !$this->first;
30+
}
31+
}
32+
33+
stream_wrapper_register("foo", MyStream::class);
34+
35+
$tmp = fopen("foo://", "r");
36+
$sxe = simplexml_load_stream($tmp);
37+
fclose($tmp);
38+
39+
var_dump($sxe);
40+
41+
?>
42+
--EXPECTF--
43+
int(8192)
44+
int(8192)
45+
%A
46+
Warning: simplexml_load_stream(): Entity: line 1: parser error : Premature end of data in tag root line 1 in %s on line %d
47+
48+
Warning: simplexml_load_stream(): <root><child/> in %s on line %d
49+
50+
Warning: simplexml_load_stream(): ^ in %s on line %d
51+
bool(false)
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
--TEST--
2+
simplexml_load_stream() - errors
3+
--EXTENSIONS--
4+
simplexml
5+
--FILE--
6+
<?php
7+
8+
$tmp = fopen("php://memory", "w+");
9+
try {
10+
simplexml_load_stream($tmp, "doesnotexist");
11+
} catch (ValueError $e) {
12+
echo $e->getMessage(), "\n";
13+
}
14+
fclose($tmp);
15+
16+
?>
17+
--EXPECT--
18+
simplexml_load_stream(): Argument #2 ($encoding) must be a valid character encoding
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
--TEST--
2+
simplexml_load_stream() - from memory stream
3+
--EXTENSIONS--
4+
simplexml
5+
--FILE--
6+
<?php
7+
8+
$tmp = fopen("php://memory", "w+");
9+
fwrite($tmp, "<root><child1/><child2/></root>");
10+
rewind($tmp);
11+
$sxe1 = simplexml_load_stream($tmp);
12+
rewind($tmp);
13+
$sxe2 = simplexml_load_stream($tmp, document_uri: 'http://example.com');
14+
fclose($tmp);
15+
16+
var_dump($sxe1, $sxe2);
17+
18+
?>
19+
--EXPECTF--
20+
object(SimpleXMLElement)#%d (2) {
21+
["child1"]=>
22+
object(SimpleXMLElement)#%d (0) {
23+
}
24+
["child2"]=>
25+
object(SimpleXMLElement)#%d (0) {
26+
}
27+
}
28+
object(SimpleXMLElement)#%d (2) {
29+
["child1"]=>
30+
object(SimpleXMLElement)#%d (0) {
31+
}
32+
["child2"]=>
33+
object(SimpleXMLElement)#%d (0) {
34+
}
35+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
--TEST--
2+
simplexml_load_stream() - from memory stream with encoding
3+
--EXTENSIONS--
4+
simplexml
5+
--FILE--
6+
<?php
7+
8+
$tmp = fopen("php://memory", "w+");
9+
fwrite($tmp, '<?xml version="1.0" encoding="Shift-JIS"?><root>ééé</root>');
10+
rewind($tmp);
11+
$sxe1 = simplexml_load_stream($tmp, encoding: 'UTF-8');
12+
rewind($tmp);
13+
$sxe2 = simplexml_load_stream($tmp);
14+
fclose($tmp);
15+
16+
var_dump($sxe1, $sxe2);
17+
18+
?>
19+
--EXPECTF--
20+
object(SimpleXMLElement)#%d (1) {
21+
[0]=>
22+
string(6) "ééé"
23+
}
24+
object(SimpleXMLElement)#%d (1) {
25+
[0]=>
26+
string(18) "テゥテゥテゥ"
27+
}

0 commit comments

Comments
 (0)