mirror of
https://github.com/php/php-src.git
synced 2026-03-24 00:02:20 +01:00
[RFC] Add stream open functions to XML{Reader,Writer}
RFC: https://wiki.php.net/rfc/xmlreader_writer_streams
This commit is contained in:
@@ -45,12 +45,8 @@ typedef int (*xmlwriter_read_int_t)(xmlTextWriterPtr writer);
|
||||
|
||||
static zend_object_handlers xmlwriter_object_handlers;
|
||||
|
||||
/* {{{{ xmlwriter_object_dtor */
|
||||
static void xmlwriter_object_dtor(zend_object *object)
|
||||
static zend_always_inline void xmlwriter_destroy_libxml_objects(ze_xmlwriter_object *intern)
|
||||
{
|
||||
ze_xmlwriter_object *intern = php_xmlwriter_fetch_object(object);
|
||||
|
||||
/* freeing the resource here may leak, but otherwise we may use it after it has been freed */
|
||||
if (intern->ptr) {
|
||||
xmlFreeTextWriter(intern->ptr);
|
||||
intern->ptr = NULL;
|
||||
@@ -59,6 +55,15 @@ static void xmlwriter_object_dtor(zend_object *object)
|
||||
xmlBufferFree(intern->output);
|
||||
intern->output = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* {{{{ xmlwriter_object_dtor */
|
||||
static void xmlwriter_object_dtor(zend_object *object)
|
||||
{
|
||||
ze_xmlwriter_object *intern = php_xmlwriter_fetch_object(object);
|
||||
|
||||
/* freeing the resource here may leak, but otherwise we may use it after it has been freed */
|
||||
xmlwriter_destroy_libxml_objects(intern);
|
||||
zend_objects_destroy_object(object);
|
||||
}
|
||||
/* }}} */
|
||||
@@ -583,7 +588,7 @@ PHP_FUNCTION(xmlwriter_start_document)
|
||||
int retval;
|
||||
zval *self;
|
||||
|
||||
if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "O|s!s!s!", &self, xmlwriter_class_entry_ce, &version, &version_len, &enc, &enc_len, &alone, &alone_len) == FAILURE) {
|
||||
if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "O|s!p!s!", &self, xmlwriter_class_entry_ce, &version, &version_len, &enc, &enc_len, &alone, &alone_len) == FAILURE) {
|
||||
RETURN_THROWS();
|
||||
}
|
||||
XMLWRITER_FROM_OBJECT(ptr, self);
|
||||
@@ -818,12 +823,7 @@ PHP_FUNCTION(xmlwriter_open_uri)
|
||||
}
|
||||
|
||||
if (self) {
|
||||
if (ze_obj->ptr) {
|
||||
xmlFreeTextWriter(ze_obj->ptr);
|
||||
}
|
||||
if (ze_obj->output) {
|
||||
xmlBufferFree(ze_obj->output);
|
||||
}
|
||||
xmlwriter_destroy_libxml_objects(ze_obj);
|
||||
ze_obj->ptr = ptr;
|
||||
ze_obj->output = NULL;
|
||||
RETURN_TRUE;
|
||||
@@ -867,12 +867,7 @@ PHP_FUNCTION(xmlwriter_open_memory)
|
||||
}
|
||||
|
||||
if (self) {
|
||||
if (ze_obj->ptr) {
|
||||
xmlFreeTextWriter(ze_obj->ptr);
|
||||
}
|
||||
if (ze_obj->output) {
|
||||
xmlBufferFree(ze_obj->output);
|
||||
}
|
||||
xmlwriter_destroy_libxml_objects(ze_obj);
|
||||
ze_obj->ptr = ptr;
|
||||
ze_obj->output = buffer;
|
||||
RETURN_TRUE;
|
||||
@@ -886,6 +881,62 @@ PHP_FUNCTION(xmlwriter_open_memory)
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
static int xml_writer_stream_write(void *context, const char *buffer, int len)
|
||||
{
|
||||
zend_resource *resource = context;
|
||||
if (EXPECTED(resource->ptr)) {
|
||||
php_stream *stream = resource->ptr;
|
||||
return php_stream_write(stream, buffer, len);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int xml_writer_stream_close(void *context)
|
||||
{
|
||||
zend_resource *resource = context;
|
||||
/* Don't close it as others may still use it! We don't own the resource!
|
||||
* Just delete our reference (and clean up if we're the last one). */
|
||||
zend_list_delete(resource);
|
||||
return 0;
|
||||
}
|
||||
|
||||
PHP_METHOD(XMLWriter, toStream)
|
||||
{
|
||||
zval *stream_zv;
|
||||
php_stream *stream;
|
||||
|
||||
ZEND_PARSE_PARAMETERS_START(1, 1)
|
||||
Z_PARAM_RESOURCE(stream_zv)
|
||||
ZEND_PARSE_PARAMETERS_END();
|
||||
|
||||
php_stream_from_res(stream, Z_RES_P(stream_zv));
|
||||
|
||||
xmlOutputBufferPtr output_buffer = xmlOutputBufferCreateIO(xml_writer_stream_write, xml_writer_stream_close, stream->res, NULL);
|
||||
if (UNEXPECTED(output_buffer == NULL)) {
|
||||
zend_throw_error(NULL, "Could not construct libxml output buffer");
|
||||
RETURN_THROWS();
|
||||
}
|
||||
|
||||
/* When the buffer is closed (even in error paths) the reference is destroyed. */
|
||||
Z_ADDREF_P(stream_zv);
|
||||
|
||||
xmlTextWriterPtr writer = xmlNewTextWriter(output_buffer);
|
||||
if (UNEXPECTED(writer == NULL)) {
|
||||
xmlOutputBufferClose(output_buffer);
|
||||
zend_throw_error(NULL, "Could not construct libxml writer");
|
||||
RETURN_THROWS();
|
||||
}
|
||||
|
||||
if (object_init_with_constructor(return_value, Z_CE_P(ZEND_THIS), 0, NULL, NULL) == SUCCESS) {
|
||||
ze_xmlwriter_object *intern = Z_XMLWRITER_P(return_value);
|
||||
intern->ptr = writer;
|
||||
/* output_buffer is owned by writer, and so writer will clean that up for us. */
|
||||
intern->output = NULL;
|
||||
} else {
|
||||
xmlFreeTextWriter(writer);
|
||||
}
|
||||
}
|
||||
|
||||
/* {{{ php_xmlwriter_flush */
|
||||
static void php_xmlwriter_flush(INTERNAL_FUNCTION_PARAMETERS, int force_string) {
|
||||
xmlTextWriterPtr ptr;
|
||||
|
||||
@@ -102,6 +102,9 @@ class XMLWriter
|
||||
*/
|
||||
public function openMemory(): bool {}
|
||||
|
||||
/** @param resource $stream */
|
||||
public static function toStream($stream): static {}
|
||||
|
||||
/**
|
||||
* @tentative-return-type
|
||||
* @alias xmlwriter_set_indent
|
||||
|
||||
8
ext/xmlwriter/php_xmlwriter_arginfo.h
generated
8
ext/xmlwriter/php_xmlwriter_arginfo.h
generated
@@ -1,5 +1,5 @@
|
||||
/* This is a generated file, edit the .stub.php file instead.
|
||||
* Stub hash: 820ad2d68166b189b9163c2c3dfcc76806d41b7d */
|
||||
* Stub hash: c65d664c3c84742dfda4cb3e2682036ec4fe893a */
|
||||
|
||||
ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_xmlwriter_open_uri, 0, 1, XMLWriter, MAY_BE_FALSE)
|
||||
ZEND_ARG_TYPE_INFO(0, uri, IS_STRING, 0)
|
||||
@@ -182,6 +182,10 @@ ZEND_END_ARG_INFO()
|
||||
ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_class_XMLWriter_openMemory, 0, 0, _IS_BOOL, 0)
|
||||
ZEND_END_ARG_INFO()
|
||||
|
||||
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_XMLWriter_toStream, 0, 1, IS_STATIC, 0)
|
||||
ZEND_ARG_INFO(0, stream)
|
||||
ZEND_END_ARG_INFO()
|
||||
|
||||
ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_class_XMLWriter_setIndent, 0, 1, _IS_BOOL, 0)
|
||||
ZEND_ARG_TYPE_INFO(0, enable, _IS_BOOL, 0)
|
||||
ZEND_END_ARG_INFO()
|
||||
@@ -369,6 +373,7 @@ ZEND_FUNCTION(xmlwriter_end_dtd_entity);
|
||||
ZEND_FUNCTION(xmlwriter_write_dtd_entity);
|
||||
ZEND_FUNCTION(xmlwriter_output_memory);
|
||||
ZEND_FUNCTION(xmlwriter_flush);
|
||||
ZEND_METHOD(XMLWriter, toStream);
|
||||
|
||||
static const zend_function_entry ext_functions[] = {
|
||||
ZEND_FE(xmlwriter_open_uri, arginfo_xmlwriter_open_uri)
|
||||
@@ -419,6 +424,7 @@ static const zend_function_entry ext_functions[] = {
|
||||
static const zend_function_entry class_XMLWriter_methods[] = {
|
||||
ZEND_RAW_FENTRY("openUri", zif_xmlwriter_open_uri, arginfo_class_XMLWriter_openUri, ZEND_ACC_PUBLIC, NULL, NULL)
|
||||
ZEND_RAW_FENTRY("openMemory", zif_xmlwriter_open_memory, arginfo_class_XMLWriter_openMemory, ZEND_ACC_PUBLIC, NULL, NULL)
|
||||
ZEND_ME(XMLWriter, toStream, arginfo_class_XMLWriter_toStream, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
|
||||
ZEND_RAW_FENTRY("setIndent", zif_xmlwriter_set_indent, arginfo_class_XMLWriter_setIndent, ZEND_ACC_PUBLIC, NULL, NULL)
|
||||
ZEND_RAW_FENTRY("setIndentString", zif_xmlwriter_set_indent_string, arginfo_class_XMLWriter_setIndentString, ZEND_ACC_PUBLIC, NULL, NULL)
|
||||
ZEND_RAW_FENTRY("startComment", zif_xmlwriter_start_comment, arginfo_class_XMLWriter_startComment, ZEND_ACC_PUBLIC, NULL, NULL)
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
--TEST--
|
||||
XMLWriter::toStream() - custom constructor
|
||||
--EXTENSIONS--
|
||||
xmlwriter
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
class CustomXMLWriter extends XMLWriter {
|
||||
public int $myField;
|
||||
|
||||
public function __construct() {
|
||||
$this->myField = 1234;
|
||||
echo "hello world\n";
|
||||
}
|
||||
}
|
||||
|
||||
$h = fopen("php://output", "w");
|
||||
|
||||
$writer = CustomXMLWriter::toStream($h);
|
||||
var_dump($writer);
|
||||
$writer->startElement("root");
|
||||
$writer->endElement();
|
||||
$writer->flush();
|
||||
|
||||
?>
|
||||
--EXPECTF--
|
||||
hello world
|
||||
object(CustomXMLWriter)#%d (1) {
|
||||
["myField"]=>
|
||||
int(1234)
|
||||
}
|
||||
<root/>
|
||||
@@ -0,0 +1,24 @@
|
||||
--TEST--
|
||||
XMLWriter::toStream() - custom constructor error
|
||||
--EXTENSIONS--
|
||||
xmlwriter
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
class CustomXMLWriter extends XMLWriter {
|
||||
public function __construct() {
|
||||
throw new Error('nope');
|
||||
}
|
||||
}
|
||||
|
||||
$h = fopen("php://output", "w");
|
||||
|
||||
try {
|
||||
CustomXMLWriter::toStream($h);
|
||||
} catch (Throwable $e) {
|
||||
echo $e->getMessage(), "\n";
|
||||
}
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
nope
|
||||
18
ext/xmlwriter/tests/xmlwriter_toStream_encoding_gbk.phpt
Normal file
18
ext/xmlwriter/tests/xmlwriter_toStream_encoding_gbk.phpt
Normal file
@@ -0,0 +1,18 @@
|
||||
--TEST--
|
||||
XMLWriter::toStream() with encoding - test GBK
|
||||
--EXTENSIONS--
|
||||
xmlwriter
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
$h = fopen("php://output", "w");
|
||||
|
||||
$writer = XMLWriter::toStream($h);
|
||||
$writer->startDocument(encoding: "GBK");
|
||||
$writer->writeComment("\u{00E9}\u{00E9}\u{00E9}");
|
||||
unset($writer);
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
<?xml version="1.0" encoding="GBK"?>
|
||||
<!--¨¦¨¦¨¦-->
|
||||
18
ext/xmlwriter/tests/xmlwriter_toStream_encoding_utf8.phpt
Normal file
18
ext/xmlwriter/tests/xmlwriter_toStream_encoding_utf8.phpt
Normal file
@@ -0,0 +1,18 @@
|
||||
--TEST--
|
||||
XMLWriter::toStream() with encoding - test UTF-8
|
||||
--EXTENSIONS--
|
||||
xmlwriter
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
$h = fopen("php://output", "w");
|
||||
|
||||
$writer = XMLWriter::toStream($h);
|
||||
$writer->startDocument(encoding: "UTF-8");
|
||||
$writer->writeComment('ééé');
|
||||
unset($writer);
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--ééé-->
|
||||
@@ -0,0 +1,20 @@
|
||||
--TEST--
|
||||
XMLWriter::toStream() - invalidating stream
|
||||
--EXTENSIONS--
|
||||
xmlwriter
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
$h = fopen("php://output", "w");
|
||||
|
||||
$writer = XMLWriter::toStream($h);
|
||||
$writer->startElement("root");
|
||||
fclose($h);
|
||||
$writer->writeAttribute("align", "left");
|
||||
$writer->endElement();
|
||||
var_dump($writer->flush());
|
||||
unset($writer);
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
int(-1)
|
||||
30
ext/xmlwriter/tests/xmlwriter_toStream_normal_usage.phpt
Normal file
30
ext/xmlwriter/tests/xmlwriter_toStream_normal_usage.phpt
Normal file
@@ -0,0 +1,30 @@
|
||||
--TEST--
|
||||
XMLWriter::toStream() - normal usage
|
||||
--EXTENSIONS--
|
||||
xmlwriter
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
$h = fopen("php://output", "w");
|
||||
|
||||
$writer = XMLWriter::toStream($h);
|
||||
$writer->startElement("root");
|
||||
$writer->writeAttribute("align", "left");
|
||||
$writer->writeComment("hello");
|
||||
$writer->endElement();
|
||||
$amount = $writer->flush();
|
||||
echo "\nFlush amount: ";
|
||||
var_dump($amount);
|
||||
|
||||
// Force destroying the held stream
|
||||
unset($writer);
|
||||
|
||||
// Test that the stream wasn't closed or destroyed
|
||||
fwrite($h, "\nthis is the end\n");
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
<root align="left"><!--hello--></root>
|
||||
Flush amount: int(38)
|
||||
|
||||
this is the end
|
||||
@@ -0,0 +1,19 @@
|
||||
--TEST--
|
||||
XMLWriter::toStream() - open invalidated stream
|
||||
--EXTENSIONS--
|
||||
xmlwriter
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
$h = fopen("php://output", "w");
|
||||
fclose($h);
|
||||
|
||||
try {
|
||||
XMLWriter::toStream($h);
|
||||
} catch (TypeError $e) {
|
||||
echo $e->getMessage(), "\n";
|
||||
}
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
XMLWriter::toStream(): supplied resource is not a valid stream resource
|
||||
Reference in New Issue
Block a user