Skip to content

Commit 1e55758

Browse files
committed
Prototype XSLTProcessor::transformToStream()
1 parent 7f27a81 commit 1e55758

6 files changed

+185
-1
lines changed

ext/xsl/php_xsl.stub.php

+6
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,12 @@ public function importStylesheet(object $stylesheet): bool {}
9191
*/
9292
public function transformToDoc(object $document, ?string $returnClass = null): object|false {}
9393

94+
/**
95+
* @param DOMDocument|Dom\Document|SimpleXMLElement $document
96+
* @param resource $stream
97+
*/
98+
public function transformToStream(object $document, $stream, ?string $encoding = null): int {}
99+
94100
/**
95101
* @param DOMDocument|Dom\Document|SimpleXMLElement $document
96102
* @tentative-return-type

ext/xsl/php_xsl_arginfo.h

+9-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
--TEST--
2+
XSLTProcessor::transformToStream() function - normal
3+
--EXTENSIONS--
4+
xsl
5+
--FILE--
6+
<?php
7+
include("prepare.inc");
8+
$proc->importStylesheet($xsl);
9+
10+
$stream = fopen('php://output', 'w');
11+
$written = $proc->transformToStream($dom, $stream);
12+
fclose($stream);
13+
14+
echo "\n";
15+
var_dump($written);
16+
17+
$stream = fopen('php://output', 'w');
18+
$written = $proc->transformToStream($dom, $stream, 'iso-8859-1');
19+
fclose($stream);
20+
21+
echo "\n";
22+
var_dump($written);
23+
?>
24+
--EXPECT--
25+
<?xml version="1.0" encoding="iso-8859-1"?>
26+
<html><body>bar
27+
a1 b1 c1 <br/>
28+
a2 c2 <br/>
29+
ä3 b3 c3 <br/>
30+
</body></html>
31+
int(120)
32+
<?xml version="1.0" encoding="iso-8859-1"?>
33+
<html><body>bar
34+
a1 b1 c1 <br/>
35+
a2 c2 <br/>
36+
ä3 b3 c3 <br/>
37+
</body></html>
38+
int(119)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
--TEST--
2+
XSLTProcessor::transformToStream() function - broken stream
3+
--EXTENSIONS--
4+
xsl
5+
--FILE--
6+
<?php
7+
class MyStream {
8+
public $context;
9+
private bool $first = true;
10+
11+
public function stream_write(string $data): int {
12+
if ($this->first) {
13+
$this->first = false;
14+
var_dump($data);
15+
}
16+
throw new Error("broken");
17+
}
18+
19+
public function stream_open(string $path, string $mode, int $options, ?string &$opened_path) {
20+
return true;
21+
}
22+
23+
public function stream_close(): void {
24+
}
25+
26+
public function stream_eof(): bool {
27+
return !$this->first;
28+
}
29+
}
30+
31+
stream_wrapper_register("foo", MyStream::class);
32+
33+
include("prepare.inc");
34+
$proc->importStylesheet($xsl);
35+
36+
$stream = fopen('foo://', 'w');
37+
stream_set_chunk_size($stream, 4);
38+
$written = $proc->transformToStream($dom, $stream);
39+
fclose($stream);
40+
41+
echo "\n";
42+
var_dump($written);
43+
?>
44+
--EXPECTF--
45+
string(4) "<?xm"
46+
47+
Fatal error: Uncaught Error: broken in %s:%d
48+
Stack trace:
49+
#0 [internal function]: MyStream->stream_write('<?xm')
50+
#1 %s(%d): XSLTProcessor->transformToStream(Object(DOMDocument), Resource id #%d)
51+
#2 {main}
52+
thrown in %s on line %d
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
--TEST--
2+
XSLTProcessor::transformToStream() function - errors
3+
--EXTENSIONS--
4+
xsl
5+
--FILE--
6+
<?php
7+
include("prepare.inc");
8+
$proc->importStylesheet($xsl);
9+
10+
$stream = fopen('php://output', 'w');
11+
try {
12+
$proc->transformToStream($dom, $stream, 'nope');
13+
} catch (ValueError $e) {
14+
echo $e->getMessage(), "\n";
15+
}
16+
fclose($stream);
17+
?>
18+
--EXPECT--
19+
XSLTProcessor::transformToStream(): Argument #3 ($encoding) is not a valid document encoding

ext/xsl/xsltprocessor.c

+61
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121

2222
#include "php.h"
2323
#include "php_xsl.h"
24+
#include "Zend/zend_exceptions.h"
2425
#include <libxslt/variables.h>
2526
#include "ext/libxml/php_libxml.h"
2627
#include "ext/dom/namespace_compat.h"
@@ -481,6 +482,66 @@ PHP_METHOD(XSLTProcessor, transformToDoc)
481482
}
482483
/* }}} end XSLTProcessor::transformToDoc */
483484

485+
static int xsl_stream_write(void *context, const char *buffer, int len)
486+
{
487+
zend_resource *resource = context;
488+
if (EXPECTED(resource->ptr)) {
489+
php_stream *stream = resource->ptr;
490+
return php_stream_write(stream, buffer, len);
491+
}
492+
return -1;
493+
}
494+
495+
PHP_METHOD(XSLTProcessor, transformToStream)
496+
{
497+
zval *docp, *stream_zv;
498+
php_stream *stream;
499+
const char *encoding = NULL;
500+
size_t encoding_len;
501+
if (zend_parse_parameters(ZEND_NUM_ARGS(), "or|p!", &docp, &stream_zv, &encoding, &encoding_len) == FAILURE) {
502+
RETURN_THROWS();
503+
}
504+
505+
php_stream_from_res(stream, Z_RES_P(stream_zv));
506+
507+
xmlCharEncodingHandlerPtr handler = NULL;
508+
if (encoding) {
509+
handler = xmlFindCharEncodingHandler(encoding);
510+
if (UNEXPECTED(!handler)) {
511+
zend_argument_value_error(3, "is not a valid document encoding");
512+
RETURN_THROWS();
513+
}
514+
}
515+
516+
xsl_object *intern = Z_XSL_P(ZEND_THIS);
517+
xsltStylesheetPtr sheetp = intern->ptr;
518+
519+
xmlOutputBufferPtr out = xmlOutputBufferCreateIO(xsl_stream_write, NULL, stream->res, handler);
520+
if (UNEXPECTED(!out)) {
521+
zend_throw_error(zend_ce_exception, "Failed to create output buffer");
522+
RETURN_THROWS();
523+
}
524+
525+
xmlDocPtr newdocp = php_xsl_apply_stylesheet(ZEND_THIS, intern, sheetp, docp);
526+
527+
int ret = -1;
528+
if (newdocp) {
529+
ret = xsltSaveResultTo(out, newdocp, sheetp);
530+
xmlFreeDoc(newdocp);
531+
}
532+
533+
xmlOutputBufferClose(out);
534+
535+
if (ret < 0) {
536+
if (!EG(exception)) {
537+
zend_throw_error(zend_ce_exception, "Failed to transform and write document");
538+
}
539+
RETURN_THROWS();
540+
}
541+
542+
RETURN_LONG(ret);
543+
}
544+
484545
/* {{{ */
485546
PHP_METHOD(XSLTProcessor, transformToUri)
486547
{

0 commit comments

Comments
 (0)