1--TEST--
2registerPHPFunctionNS() function - legit cases
3--EXTENSIONS--
4xsl
5--FILE--
6<?php
7
8class TrampolineClass {
9    public static function __callStatic(string $name, array $arguments): mixed {
10        var_dump($name, $arguments);
11        return "foo";
12    }
13}
14
15class StatefulClass {
16    public array $state = [];
17
18    public function __call(string $name, array $arguments): mixed {
19        $this->state[] = [$name, $arguments[0]];
20        return $arguments[0];
21    }
22}
23
24function createProcessor($inputs) {
25    $xsl = new DomDocument();
26    $xsl->loadXML('<?xml version="1.0" encoding="iso-8859-1" ?>
27    <xsl:stylesheet version="1.0"
28    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
29    xmlns:foo="urn:foo"
30    xmlns:bar="urn:bar">
31    <xsl:template match="//a">'
32    . implode('', array_map(fn($input) => '<xsl:value-of select="' . $input . '" />', $inputs)) .
33    '</xsl:template>
34    </xsl:stylesheet>');
35
36    $proc = new XSLTProcessor();
37    $proc->importStylesheet($xsl);
38    return $proc;
39}
40
41$inputdom = new DomDocument();
42$inputdom->loadXML('<?xml version="1.0" encoding="iso-8859-1"?><a href="https://php.net">hello</a>');
43
44echo "--- Legit cases: none ---\n";
45
46$proc = createProcessor(["foo:var_dump(string(@href))"]);
47try {
48    $proc->transformToXml($inputdom);
49} catch (Error $e) {
50    echo $e->getMessage(), "\n";
51}
52
53echo "--- Legit cases: global function callable ---\n";
54
55$proc = createProcessor(["foo:var_dump(string(@href))"]);
56$proc->registerPHPFunctionNS('urn:foo', 'var_dump', var_dump(...));
57$proc->transformToXml($inputdom);
58
59echo "--- Legit cases: global string callable ---\n";
60
61$proc = createProcessor(["foo:var_dump(string(@href))"]);
62$proc->registerPHPFunctionNS('urn:foo', 'var_dump', 'var_dump');
63$proc->transformToXml($inputdom);
64
65echo "--- Legit cases: trampoline callable ---\n";
66
67$proc = createProcessor(["foo:trampoline(string(@href))"]);
68$proc->registerPHPFunctionNS('urn:foo', 'trampoline', TrampolineClass::test(...));
69var_dump($proc->transformToXml($inputdom));
70
71echo "--- Legit cases: instance class method callable ---\n";
72
73$state = new StatefulClass;
74$proc = createProcessor(["foo:test(string(@href))"]);
75$proc->registerPHPFunctionNS('urn:foo', 'test', $state->test(...));
76var_dump($proc->transformToXml($inputdom));
77var_dump($state->state);
78
79echo "--- Legit cases: multiple namespaces ---\n";
80
81$proc = createProcessor(["foo:test(string(@href))", "bar:test(string(@href))"]);
82$proc->registerPHPFunctionNS('urn:foo', 'test', strrev(...));
83$proc->registerPHPFunctionNS('urn:bar', 'test', strtoupper(...));
84var_dump($proc->transformToXml($inputdom));
85
86?>
87--EXPECTF--
88--- Legit cases: none ---
89
90Warning: XSLTProcessor::transformToXml(): xmlXPathCompOpEval: function var_dump not found in %s on line %d
91
92Warning: XSLTProcessor::transformToXml(): Unregistered function in %s on line %d
93
94Warning: XSLTProcessor::transformToXml(): runtime error: file %s line 6 element value-of in %s on line %d
95
96Warning: XSLTProcessor::transformToXml(): XPath evaluation returned no result. in %s on line %d
97--- Legit cases: global function callable ---
98string(15) "https://php.net"
99--- Legit cases: global string callable ---
100string(15) "https://php.net"
101--- Legit cases: trampoline callable ---
102string(4) "test"
103array(1) {
104  [0]=>
105  string(15) "https://php.net"
106}
107string(26) "<?xml version="1.0"?>
108foo
109"
110--- Legit cases: instance class method callable ---
111string(38) "<?xml version="1.0"?>
112https://php.net
113"
114array(1) {
115  [0]=>
116  array(2) {
117    [0]=>
118    string(4) "test"
119    [1]=>
120    string(15) "https://php.net"
121  }
122}
123--- Legit cases: multiple namespaces ---
124string(53) "<?xml version="1.0"?>
125ten.php//:sptthHTTPS://PHP.NET
126"
127