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