Skip to content

Commit

Permalink
Add simplexml_load_stream()
Browse files Browse the repository at this point in the history
  • Loading branch information
nielsdos committed Jan 29, 2025
1 parent 6091820 commit 086ea4c
Show file tree
Hide file tree
Showing 7 changed files with 219 additions and 36 deletions.
107 changes: 72 additions & 35 deletions ext/simplexml/simplexml.c
Original file line number Diff line number Diff line change
Expand Up @@ -2183,17 +2183,37 @@ sxe_object_new(zend_class_entry *ce)
}
/* }}} */

static void sxe_create_obj_from_doc(zval *return_value, xmlDocPtr docp, zend_class_entry *ce, zend_string *ns, bool isprefix)
{
if (!docp) {
RETURN_FALSE;
}

zend_function *fptr_count;
if (!ce) {
ce = ce_SimpleXMLElement;
fptr_count = NULL;
} else {
fptr_count = php_sxe_find_fptr_count(ce);
}
php_sxe_object *sxe = php_sxe_object_new(ce, fptr_count);
sxe->iter.nsprefix = ZSTR_LEN(ns) ? zend_string_copy(ns) : NULL;
sxe->iter.isprefix = isprefix;
php_libxml_increment_doc_ref((php_libxml_node_object *)sxe, docp);
php_libxml_increment_node_ptr((php_libxml_node_object *)sxe, xmlDocGetRootElement(docp), NULL);

RETURN_OBJ(&sxe->zo);
}

/* {{{ Load a filename and return a simplexml_element object to allow for processing */
PHP_FUNCTION(simplexml_load_file)
{
php_sxe_object *sxe;
char *filename;
size_t filename_len;
xmlDocPtr docp;
zend_string *ns = zend_empty_string;
zend_long options = 0;
zend_class_entry *ce= ce_SimpleXMLElement;
zend_function *fptr_count;
bool isprefix = 0;

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

if (!docp) {
RETURN_FALSE;
sxe_create_obj_from_doc(return_value, docp, ce, ns, isprefix);
}
/* }}} */

static int sxe_stream_read(void *context, char *buffer, int len)
{
zend_resource *resource = context;
if (EXPECTED(resource->ptr)) {
php_stream *stream = resource->ptr;
return php_stream_read(stream, buffer, len);
}
return -1;
}

if (!ce) {
ce = ce_SimpleXMLElement;
fptr_count = NULL;
} else {
fptr_count = php_sxe_find_fptr_count(ce);
PHP_FUNCTION(simplexml_load_stream)
{
zval *stream_zv;
php_stream *stream;
xmlDocPtr docp;
zend_string *ns = zend_empty_string;
zend_long options = 0;
zend_class_entry *ce = ce_SimpleXMLElement;
bool isprefix = 0;
const char *encoding = NULL;
const char *document_uri = NULL;
size_t encoding_len, document_uri_len;

if (zend_parse_parameters(ZEND_NUM_ARGS(), "r|p!p!C!lSb",
&stream_zv, &encoding, &encoding_len, &document_uri, &document_uri_len, &ce, &options, &ns, &isprefix) == FAILURE) {
RETURN_THROWS();
}
sxe = php_sxe_object_new(ce, fptr_count);
sxe->iter.nsprefix = ZSTR_LEN(ns) ? zend_string_copy(ns) : NULL;
sxe->iter.isprefix = isprefix;
php_libxml_increment_doc_ref((php_libxml_node_object *)sxe, docp);
php_libxml_increment_node_ptr((php_libxml_node_object *)sxe, xmlDocGetRootElement(docp), NULL);

RETURN_OBJ(&sxe->zo);
php_stream_from_res(stream, Z_RES_P(stream_zv));

if (!php_libxml_is_valid_encoding(encoding)) {
zend_argument_value_error(2, "must be a valid character encoding");
RETURN_THROWS();
}

if (ZEND_LONG_EXCEEDS_INT(options)) {
zend_argument_value_error(5, "is too large");
RETURN_THROWS();
}

if (encoding) {
options |= XML_PARSE_IGNORE_ENC;
}

PHP_LIBXML_SANITIZE_GLOBALS(read_file);
docp = xmlReadIO(sxe_stream_read, NULL, stream->res, document_uri, encoding, (int) options);
PHP_LIBXML_RESTORE_GLOBALS(read_file);

sxe_create_obj_from_doc(return_value, docp, ce, ns, isprefix);
}
/* }}} */

/* {{{ Load a string and return a simplexml_element object to allow for processing */
PHP_FUNCTION(simplexml_load_string)
{
php_sxe_object *sxe;
char *data;
size_t data_len;
xmlDocPtr docp;
zend_string *ns = zend_empty_string;
zend_long options = 0;
zend_class_entry *ce= ce_SimpleXMLElement;
zend_function *fptr_count;
bool isprefix = 0;

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

if (!docp) {
RETURN_FALSE;
}

if (!ce) {
ce = ce_SimpleXMLElement;
fptr_count = NULL;
} else {
fptr_count = php_sxe_find_fptr_count(ce);
}
sxe = php_sxe_object_new(ce, fptr_count);
sxe->iter.nsprefix = ZSTR_LEN(ns) ? zend_string_copy(ns) : NULL;
sxe->iter.isprefix = isprefix;
php_libxml_increment_doc_ref((php_libxml_node_object *)sxe, docp);
php_libxml_increment_node_ptr((php_libxml_node_object *)sxe, xmlDocGetRootElement(docp), NULL);

RETURN_OBJ(&sxe->zo);
sxe_create_obj_from_doc(return_value, docp, ce, ns, isprefix);
}
/* }}} */

Expand Down
3 changes: 3 additions & 0 deletions ext/simplexml/simplexml.stub.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@

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

/** @param resource $stream */
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 {}

function simplexml_load_string(string $data, ?string $class_name = SimpleXMLElement::class, int $options = 0, string $namespace_or_prefix = "", bool $is_prefix = false): SimpleXMLElement|false {}

function simplexml_import_dom(object $node, ?string $class_name = SimpleXMLElement::class): ?SimpleXMLElement {}
Expand Down
14 changes: 13 additions & 1 deletion ext/simplexml/simplexml_arginfo.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

51 changes: 51 additions & 0 deletions ext/simplexml/tests/simplexml_load_stream_broken.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
--TEST--
simplexml_load_stream() - from broken stream
--EXTENSIONS--
simplexml
--FILE--
<?php

class MyStream {
public $context;
private bool $first = true;

public function stream_read(int $count): string|false {
var_dump($count);
if ($this->first) {
$this->first = false;
return "<root><child/>";
}
return false;
}

public function stream_open(string $path, string $mode, int $options, ?string &$opened_path) {
return true;
}

public function stream_close(): void {
}

public function stream_eof(): bool {
return !$this->first;
}
}

stream_wrapper_register("foo", MyStream::class);

$tmp = fopen("foo://", "r");
$sxe = simplexml_load_stream($tmp);
fclose($tmp);

var_dump($sxe);

?>
--EXPECTF--
int(8192)
int(8192)
%A
Warning: simplexml_load_stream(): Entity: line 1: parser error : Premature end of data in tag root line 1 in %s on line %d

Warning: simplexml_load_stream(): <root><child/> in %s on line %d

Warning: simplexml_load_stream(): ^ in %s on line %d
bool(false)
18 changes: 18 additions & 0 deletions ext/simplexml/tests/simplexml_load_stream_errors.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
--TEST--
simplexml_load_stream() - errors
--EXTENSIONS--
simplexml
--FILE--
<?php

$tmp = fopen("php://memory", "w+");
try {
simplexml_load_stream($tmp, "doesnotexist");
} catch (ValueError $e) {
echo $e->getMessage(), "\n";
}
fclose($tmp);

?>
--EXPECT--
simplexml_load_stream(): Argument #2 ($encoding) must be a valid character encoding
35 changes: 35 additions & 0 deletions ext/simplexml/tests/simplexml_load_stream_memory.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
--TEST--
simplexml_load_stream() - from memory stream
--EXTENSIONS--
simplexml
--FILE--
<?php

$tmp = fopen("php://memory", "w+");
fwrite($tmp, "<root><child1/><child2/></root>");
rewind($tmp);
$sxe1 = simplexml_load_stream($tmp);
rewind($tmp);
$sxe2 = simplexml_load_stream($tmp, document_uri: 'http://example.com');
fclose($tmp);

var_dump($sxe1, $sxe2);

?>
--EXPECTF--
object(SimpleXMLElement)#%d (2) {
["child1"]=>
object(SimpleXMLElement)#%d (0) {
}
["child2"]=>
object(SimpleXMLElement)#%d (0) {
}
}
object(SimpleXMLElement)#%d (2) {
["child1"]=>
object(SimpleXMLElement)#%d (0) {
}
["child2"]=>
object(SimpleXMLElement)#%d (0) {
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
--TEST--
simplexml_load_stream() - from memory stream with encoding
--EXTENSIONS--
simplexml
--FILE--
<?php

$tmp = fopen("php://memory", "w+");
fwrite($tmp, '<?xml version="1.0" encoding="Shift-JIS"?><root>ééé</root>');
rewind($tmp);
$sxe1 = simplexml_load_stream($tmp, encoding: 'UTF-8');
rewind($tmp);
$sxe2 = simplexml_load_stream($tmp);
fclose($tmp);

var_dump($sxe1, $sxe2);

?>
--EXPECTF--
object(SimpleXMLElement)#%d (1) {
[0]=>
string(6) "ééé"
}
object(SimpleXMLElement)#%d (1) {
[0]=>
string(18) "テゥテゥテゥ"
}

0 comments on commit 086ea4c

Please sign in to comment.