Skip to content

Commit 5a4c3f9

Browse files
authored
Merge pull request #81 from veewee/xpath-xslt-callback-functions
Introduce XPath and XSLT callback functions
2 parents d0933f1 + 15c7d96 commit 5a4c3f9

File tree

11 files changed

+183
-9
lines changed

11 files changed

+183
-9
lines changed

docs/dom.md

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1543,7 +1543,19 @@ use VeeWee\Xml\Dom\Document;
15431543
use function VeeWee\Xml\Dom\Xpath\Configurator\functions;
15441544

15451545
$doc = Document::fromXmlFile('data.xml');
1546-
$xpath = $doc->xpath(functions(['has_multiple']));
1546+
$xpath = $doc->xpath(functions(['has_multiple' => has_multiple(...)]));
1547+
```
1548+
1549+
#### namespaced_functions
1550+
1551+
Registers a list of namespaced functions to the XPath object, allowing you to use `prefix:somefunction()` inside your XPath query.
1552+
1553+
```php
1554+
use VeeWee\Xml\Dom\Document;
1555+
use function VeeWee\Xml\Dom\Xpath\Configurator\namespaced_functions;
1556+
1557+
$doc = Document::fromXmlFile('data.xml');
1558+
$xpath = $doc->xpath(namespaced_functions('http://ns', 'prefix', ['has_multiple' => has_multiple(...)]));
15471559
```
15481560

15491561
#### namespaces

docs/xslt.md

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,22 @@ use function VeeWee\Xml\Xslt\Configurator\functions;
5858

5959
$processor = Processor::fromTemplateDocument(
6060
Document::fromXmlFile('xml-to-yaml-converter.xslt'),
61-
functions(['ucfirst'])
61+
functions(['ucfirst' => ucfirst(...)])
62+
);
63+
```
64+
65+
#### namespaced_functions
66+
67+
Registers specific namespaced functions to the XSLTProcessor object, allowing you to use `prefix:function('ucfirst',string(uid))` inside your XSLT Template.
68+
69+
```php
70+
use VeeWee\Xml\Dom\Document;
71+
use VeeWee\Xml\Xslt\Processor;
72+
use function VeeWee\Xml\Xslt\Configurator\namespaced_functions;
73+
74+
$processor = Processor::fromTemplateDocument(
75+
Document::fromXmlFile('xml-to-yaml-converter.xslt'),
76+
namespaced_functions('http://ns', ['ucfirst' => ucfirst(...)])
6277
);
6378
```
6479

src/Xml/Dom/Xpath/Configurator/functions.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
use Dom\XPath;
99

1010
/**
11-
* @param non-empty-list<string> $functions
11+
* @param array<string, (callable(mixed...): mixed)> $functions
1212
*
1313
* @return Closure(XPath): XPath
1414
*/
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace VeeWee\Xml\Dom\Xpath\Configurator;
6+
7+
use Closure;
8+
use Dom\XPath;
9+
10+
/**
11+
* @param array<string, (callable(mixed...): mixed)> $functions
12+
*
13+
* @return Closure(XPath): XPath
14+
*/
15+
function namespaced_functions(string $namespace, string $prefix, array $functions): Closure
16+
{
17+
return static function (XPath $xpath) use ($namespace, $prefix, $functions) : XPath {
18+
namespaces([$prefix => $namespace])($xpath);
19+
foreach ($functions as $functionName => $callback) {
20+
$xpath->registerPhpFunctionNS($namespace, $functionName, $callback);
21+
}
22+
23+
return $xpath;
24+
};
25+
}

src/Xml/Xslt/Configurator/functions.php

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,7 @@
88
use XSLTProcessor;
99

1010
/**
11-
* TODO : Add support for callables : https://wiki.php.net/rfc/improve_callbacks_dom_and_xsl (either here or through a separate configurator)
12-
*
13-
* @param non-empty-list<string> $functions
11+
* @param array<string, (callable(mixed...): mixed)> $functions
1412
*
1513
* @return Closure(XSLTProcessor): XSLTProcessor
1614
*/
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace VeeWee\Xml\Xslt\Configurator;
6+
7+
use Closure;
8+
use XSLTProcessor;
9+
10+
/**
11+
* @param array<string, (callable(mixed...): mixed)> $functions
12+
*
13+
* @return Closure(XSLTProcessor): XSLTProcessor
14+
*/
15+
function namespaced_functions(string $namespace, array $functions): Closure
16+
{
17+
return static function (XSLTProcessor $processor) use ($namespace, $functions) : XSLTProcessor {
18+
foreach ($functions as $functionName => $callback) {
19+
$processor->registerPhpFunctionNS($namespace, $functionName, $callback);
20+
}
21+
22+
return $processor;
23+
};
24+
}

src/bootstrap.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@
8686
'Xml\Dom\Validator\xsd_validator' => __DIR__.'/Xml/Dom/Validator/xsd_validator.php',
8787
'Xml\Dom\Xpath\Configurator\all_functions' => __DIR__.'/Xml/Dom/Xpath/Configurator/all_functions.php',
8888
'Xml\Dom\Xpath\Configurator\functions' => __DIR__.'/Xml/Dom/Xpath/Configurator/functions.php',
89+
'Xml\Dom\Xpath\Configurator\namespaced_functions' => __DIR__.'/Xml/Dom/Xpath/Configurator/namespaced_functions.php',
8990
'Xml\Dom\Xpath\Configurator\namespaces' => __DIR__.'/Xml/Dom/Xpath/Configurator/namespaces.php',
9091
'Xml\Dom\Xpath\Configurator\php_namespace' => __DIR__.'/Xml/Dom/Xpath/Configurator/php_namespace.php',
9192
'Xml\Dom\Xpath\Locator\evaluate' => __DIR__.'/Xml/Dom/Xpath/Locator/evaluate.php',
@@ -170,6 +171,7 @@
170171
'Xml\Xslt\Configurator\all_functions' => __DIR__.'/Xml/Xslt/Configurator/all_functions.php',
171172
'Xml\Xslt\Configurator\functions' => __DIR__.'/Xml/Xslt/Configurator/functions.php',
172173
'Xml\Xslt\Configurator\loader' => __DIR__.'/Xml/Xslt/Configurator/loader.php',
174+
'Xml\Xslt\Configurator\namespaced_functions' => __DIR__.'/Xml/Xslt/Configurator/namespaced_functions.php',
173175
'Xml\Xslt\Configurator\parameters' => __DIR__.'/Xml/Xslt/Configurator/parameters.php',
174176
'Xml\Xslt\Configurator\profiler' => __DIR__.'/Xml/Xslt/Configurator/profiler.php',
175177
'Xml\Xslt\Configurator\security_preferences' => __DIR__.'/Xml/Xslt/Configurator/security_preferences.php',

tests/Xml/Dom/Xpath/Configurator/FunctionsTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ public function test_it_can_evaluate_with_php_function(): void
1616
$doc = Document::fromXmlString(
1717
$xml = '<hello><item>Jos</item></hello>'
1818
);
19-
$xpath = Xpath::fromDocument($doc, Xpath\Configurator\functions(['str_replace']));
19+
$xpath = Xpath::fromDocument($doc, Xpath\Configurator\functions(['str_replace' => str_replace(...)]));
2020

2121
$result = $xpath->evaluate('php:functionString("str_replace", "J", "B", string(//item))', Type\string());
2222
static::assertSame($result, 'Bos');
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace VeeWee\Tests\Xml\Dom\Xpath\Configurator;
6+
7+
use PHPUnit\Framework\TestCase;
8+
use Psl\Type;
9+
use VeeWee\Xml\Dom\Document;
10+
use VeeWee\Xml\Dom\Xpath;
11+
12+
final class NamespacedFunctionsTest extends TestCase
13+
{
14+
public function test_it_can_evaluate_with_php_function(): void
15+
{
16+
$doc = Document::fromXmlString(
17+
$xml = '<hello><item>Jos</item></hello>'
18+
);
19+
$xpath = Xpath::fromDocument($doc, Xpath\Configurator\namespaced_functions(
20+
'http://my-ns',
21+
'my',
22+
['str_replace' => str_replace(...)]
23+
));
24+
25+
$result = $xpath->evaluate('my:str_replace("J", "B", string(//item))', Type\string());
26+
static::assertSame($result, 'Bos');
27+
}
28+
}

tests/Xml/Xslt/Configurator/FunctionsTest.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ public function test_it_can_use_php_functions(): void
1616
{
1717
$processor = Processor::fromTemplateDocument(
1818
$this->createTemplate(),
19-
functions(['strtoupper'])
19+
functions(['strtoupper' => strtoupper(...)])
2020
);
2121

2222
$result = $processor->transformDocumentToString(
@@ -36,7 +36,7 @@ public function test_it_throws_exception_on_unkown_function(): void
3636
{
3737
$processor = Processor::fromTemplateDocument(
3838
$this->createTemplate(),
39-
functions(['substr'])
39+
functions(['substr' => substr(...)])
4040
);
4141
$doc = Document::fromXmlString(
4242
<<<EOXML

0 commit comments

Comments
 (0)