Skip to content

Commit

Permalink
Prototype XSLTProcessor::transformToStream()
Browse files Browse the repository at this point in the history
  • Loading branch information
nielsdos committed Jan 30, 2025
1 parent 7f27a81 commit 1e55758
Show file tree
Hide file tree
Showing 6 changed files with 185 additions and 1 deletion.
6 changes: 6 additions & 0 deletions ext/xsl/php_xsl.stub.php
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,12 @@ public function importStylesheet(object $stylesheet): bool {}
*/
public function transformToDoc(object $document, ?string $returnClass = null): object|false {}

/**
* @param DOMDocument|Dom\Document|SimpleXMLElement $document
* @param resource $stream
*/
public function transformToStream(object $document, $stream, ?string $encoding = null): int {}

/**
* @param DOMDocument|Dom\Document|SimpleXMLElement $document
* @tentative-return-type
Expand Down
10 changes: 9 additions & 1 deletion ext/xsl/php_xsl_arginfo.h

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

38 changes: 38 additions & 0 deletions ext/xsl/tests/transform_to_stream.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
--TEST--
XSLTProcessor::transformToStream() function - normal
--EXTENSIONS--
xsl
--FILE--
<?php
include("prepare.inc");
$proc->importStylesheet($xsl);

$stream = fopen('php://output', 'w');
$written = $proc->transformToStream($dom, $stream);
fclose($stream);

echo "\n";
var_dump($written);

$stream = fopen('php://output', 'w');
$written = $proc->transformToStream($dom, $stream, 'iso-8859-1');
fclose($stream);

echo "\n";
var_dump($written);
?>
--EXPECT--
<?xml version="1.0" encoding="iso-8859-1"?>
<html><body>bar
a1 b1 c1 <br/>
a2 c2 <br/>
ä3 b3 c3 <br/>
</body></html>
int(120)
<?xml version="1.0" encoding="iso-8859-1"?>
<html><body>bar
a1 b1 c1 <br/>
a2 c2 <br/>
ä3 b3 c3 <br/>
</body></html>
int(119)
52 changes: 52 additions & 0 deletions ext/xsl/tests/transform_to_stream_broken_stream.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
--TEST--
XSLTProcessor::transformToStream() function - broken stream
--EXTENSIONS--
xsl
--FILE--
<?php
class MyStream {
public $context;
private bool $first = true;

public function stream_write(string $data): int {
if ($this->first) {
$this->first = false;
var_dump($data);
}
throw new Error("broken");
}

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);

include("prepare.inc");
$proc->importStylesheet($xsl);

$stream = fopen('foo://', 'w');
stream_set_chunk_size($stream, 4);
$written = $proc->transformToStream($dom, $stream);
fclose($stream);

echo "\n";
var_dump($written);
?>
--EXPECTF--
string(4) "<?xm"

Fatal error: Uncaught Error: broken in %s:%d
Stack trace:
#0 [internal function]: MyStream->stream_write('<?xm')
#1 %s(%d): XSLTProcessor->transformToStream(Object(DOMDocument), Resource id #%d)
#2 {main}
thrown in %s on line %d
19 changes: 19 additions & 0 deletions ext/xsl/tests/transform_to_stream_errors.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
--TEST--
XSLTProcessor::transformToStream() function - errors
--EXTENSIONS--
xsl
--FILE--
<?php
include("prepare.inc");
$proc->importStylesheet($xsl);

$stream = fopen('php://output', 'w');
try {
$proc->transformToStream($dom, $stream, 'nope');
} catch (ValueError $e) {
echo $e->getMessage(), "\n";
}
fclose($stream);
?>
--EXPECT--
XSLTProcessor::transformToStream(): Argument #3 ($encoding) is not a valid document encoding
61 changes: 61 additions & 0 deletions ext/xsl/xsltprocessor.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@

#include "php.h"
#include "php_xsl.h"
#include "Zend/zend_exceptions.h"
#include <libxslt/variables.h>
#include "ext/libxml/php_libxml.h"
#include "ext/dom/namespace_compat.h"
Expand Down Expand Up @@ -481,6 +482,66 @@ PHP_METHOD(XSLTProcessor, transformToDoc)
}
/* }}} end XSLTProcessor::transformToDoc */

static int xsl_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;
}

PHP_METHOD(XSLTProcessor, transformToStream)
{
zval *docp, *stream_zv;
php_stream *stream;
const char *encoding = NULL;
size_t encoding_len;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "or|p!", &docp, &stream_zv, &encoding, &encoding_len) == FAILURE) {
RETURN_THROWS();
}

php_stream_from_res(stream, Z_RES_P(stream_zv));

xmlCharEncodingHandlerPtr handler = NULL;
if (encoding) {
handler = xmlFindCharEncodingHandler(encoding);
if (UNEXPECTED(!handler)) {
zend_argument_value_error(3, "is not a valid document encoding");
RETURN_THROWS();
}
}

xsl_object *intern = Z_XSL_P(ZEND_THIS);
xsltStylesheetPtr sheetp = intern->ptr;

xmlOutputBufferPtr out = xmlOutputBufferCreateIO(xsl_stream_write, NULL, stream->res, handler);
if (UNEXPECTED(!out)) {
zend_throw_error(zend_ce_exception, "Failed to create output buffer");
RETURN_THROWS();
}

xmlDocPtr newdocp = php_xsl_apply_stylesheet(ZEND_THIS, intern, sheetp, docp);

int ret = -1;
if (newdocp) {
ret = xsltSaveResultTo(out, newdocp, sheetp);
xmlFreeDoc(newdocp);
}

xmlOutputBufferClose(out);

if (ret < 0) {
if (!EG(exception)) {
zend_throw_error(zend_ce_exception, "Failed to transform and write document");
}
RETURN_THROWS();
}

RETURN_LONG(ret);
}

/* {{{ */
PHP_METHOD(XSLTProcessor, transformToUri)
{
Expand Down

0 comments on commit 1e55758

Please sign in to comment.