diff --git a/docs/dom.md b/docs/dom.md index 9bb54c38..c489d165 100644 --- a/docs/dom.md +++ b/docs/dom.md @@ -1543,7 +1543,19 @@ use VeeWee\Xml\Dom\Document; use function VeeWee\Xml\Dom\Xpath\Configurator\functions; $doc = Document::fromXmlFile('data.xml'); -$xpath = $doc->xpath(functions(['has_multiple'])); +$xpath = $doc->xpath(functions(['has_multiple' => has_multiple(...)])); +``` + +#### namespaced_functions + +Registers a list of namespaced functions to the XPath object, allowing you to use `prefix:somefunction()` inside your XPath query. + +```php +use VeeWee\Xml\Dom\Document; +use function VeeWee\Xml\Dom\Xpath\Configurator\namespaced_functions; + +$doc = Document::fromXmlFile('data.xml'); +$xpath = $doc->xpath(namespaced_functions('http://ns', 'prefix', ['has_multiple' => has_multiple(...)])); ``` #### namespaces diff --git a/docs/xslt.md b/docs/xslt.md index ba3ea024..8cbf7342 100644 --- a/docs/xslt.md +++ b/docs/xslt.md @@ -58,7 +58,22 @@ use function VeeWee\Xml\Xslt\Configurator\functions; $processor = Processor::fromTemplateDocument( Document::fromXmlFile('xml-to-yaml-converter.xslt'), - functions(['ucfirst']) + functions(['ucfirst' => ucfirst(...)]) +); +``` + +#### namespaced_functions + +Registers specific namespaced functions to the XSLTProcessor object, allowing you to use `prefix:function('ucfirst',string(uid))` inside your XSLT Template. + +```php +use VeeWee\Xml\Dom\Document; +use VeeWee\Xml\Xslt\Processor; +use function VeeWee\Xml\Xslt\Configurator\namespaced_functions; + +$processor = Processor::fromTemplateDocument( + Document::fromXmlFile('xml-to-yaml-converter.xslt'), + namespaced_functions('http://ns', ['ucfirst' => ucfirst(...)]) ); ``` diff --git a/src/Xml/Dom/Xpath/Configurator/functions.php b/src/Xml/Dom/Xpath/Configurator/functions.php index cbc3ed7f..7b4eb807 100644 --- a/src/Xml/Dom/Xpath/Configurator/functions.php +++ b/src/Xml/Dom/Xpath/Configurator/functions.php @@ -8,7 +8,7 @@ use Dom\XPath; /** - * @param non-empty-list $functions + * @param array $functions * * @return Closure(XPath): XPath */ diff --git a/src/Xml/Dom/Xpath/Configurator/namespaced_functions.php b/src/Xml/Dom/Xpath/Configurator/namespaced_functions.php new file mode 100644 index 00000000..f8479a97 --- /dev/null +++ b/src/Xml/Dom/Xpath/Configurator/namespaced_functions.php @@ -0,0 +1,25 @@ + $functions + * + * @return Closure(XPath): XPath + */ +function namespaced_functions(string $namespace, string $prefix, array $functions): Closure +{ + return static function (XPath $xpath) use ($namespace, $prefix, $functions) : XPath { + namespaces([$prefix => $namespace])($xpath); + foreach ($functions as $functionName => $callback) { + $xpath->registerPhpFunctionNS($namespace, $functionName, $callback); + } + + return $xpath; + }; +} diff --git a/src/Xml/Xslt/Configurator/functions.php b/src/Xml/Xslt/Configurator/functions.php index 210b1705..15a383e2 100644 --- a/src/Xml/Xslt/Configurator/functions.php +++ b/src/Xml/Xslt/Configurator/functions.php @@ -8,9 +8,7 @@ use XSLTProcessor; /** - * TODO : Add support for callables : https://wiki.php.net/rfc/improve_callbacks_dom_and_xsl (either here or through a separate configurator) - * - * @param non-empty-list $functions + * @param array $functions * * @return Closure(XSLTProcessor): XSLTProcessor */ diff --git a/src/Xml/Xslt/Configurator/namespaced_functions.php b/src/Xml/Xslt/Configurator/namespaced_functions.php new file mode 100644 index 00000000..f0d31a05 --- /dev/null +++ b/src/Xml/Xslt/Configurator/namespaced_functions.php @@ -0,0 +1,24 @@ + $functions + * + * @return Closure(XSLTProcessor): XSLTProcessor + */ +function namespaced_functions(string $namespace, array $functions): Closure +{ + return static function (XSLTProcessor $processor) use ($namespace, $functions) : XSLTProcessor { + foreach ($functions as $functionName => $callback) { + $processor->registerPhpFunctionNS($namespace, $functionName, $callback); + } + + return $processor; + }; +} diff --git a/src/bootstrap.php b/src/bootstrap.php index 392fd8e9..1341e878 100644 --- a/src/bootstrap.php +++ b/src/bootstrap.php @@ -86,6 +86,7 @@ 'Xml\Dom\Validator\xsd_validator' => __DIR__.'/Xml/Dom/Validator/xsd_validator.php', 'Xml\Dom\Xpath\Configurator\all_functions' => __DIR__.'/Xml/Dom/Xpath/Configurator/all_functions.php', 'Xml\Dom\Xpath\Configurator\functions' => __DIR__.'/Xml/Dom/Xpath/Configurator/functions.php', + 'Xml\Dom\Xpath\Configurator\namespaced_functions' => __DIR__.'/Xml/Dom/Xpath/Configurator/namespaced_functions.php', 'Xml\Dom\Xpath\Configurator\namespaces' => __DIR__.'/Xml/Dom/Xpath/Configurator/namespaces.php', 'Xml\Dom\Xpath\Configurator\php_namespace' => __DIR__.'/Xml/Dom/Xpath/Configurator/php_namespace.php', 'Xml\Dom\Xpath\Locator\evaluate' => __DIR__.'/Xml/Dom/Xpath/Locator/evaluate.php', @@ -170,6 +171,7 @@ 'Xml\Xslt\Configurator\all_functions' => __DIR__.'/Xml/Xslt/Configurator/all_functions.php', 'Xml\Xslt\Configurator\functions' => __DIR__.'/Xml/Xslt/Configurator/functions.php', 'Xml\Xslt\Configurator\loader' => __DIR__.'/Xml/Xslt/Configurator/loader.php', + 'Xml\Xslt\Configurator\namespaced_functions' => __DIR__.'/Xml/Xslt/Configurator/namespaced_functions.php', 'Xml\Xslt\Configurator\parameters' => __DIR__.'/Xml/Xslt/Configurator/parameters.php', 'Xml\Xslt\Configurator\profiler' => __DIR__.'/Xml/Xslt/Configurator/profiler.php', 'Xml\Xslt\Configurator\security_preferences' => __DIR__.'/Xml/Xslt/Configurator/security_preferences.php', diff --git a/tests/Xml/Dom/Xpath/Configurator/FunctionsTest.php b/tests/Xml/Dom/Xpath/Configurator/FunctionsTest.php index 1a17054a..5d8102ac 100644 --- a/tests/Xml/Dom/Xpath/Configurator/FunctionsTest.php +++ b/tests/Xml/Dom/Xpath/Configurator/FunctionsTest.php @@ -16,7 +16,7 @@ public function test_it_can_evaluate_with_php_function(): void $doc = Document::fromXmlString( $xml = 'Jos' ); - $xpath = Xpath::fromDocument($doc, Xpath\Configurator\functions(['str_replace'])); + $xpath = Xpath::fromDocument($doc, Xpath\Configurator\functions(['str_replace' => str_replace(...)])); $result = $xpath->evaluate('php:functionString("str_replace", "J", "B", string(//item))', Type\string()); static::assertSame($result, 'Bos'); diff --git a/tests/Xml/Dom/Xpath/Configurator/NamespacedFunctionsTest.php b/tests/Xml/Dom/Xpath/Configurator/NamespacedFunctionsTest.php new file mode 100644 index 00000000..7c7f852e --- /dev/null +++ b/tests/Xml/Dom/Xpath/Configurator/NamespacedFunctionsTest.php @@ -0,0 +1,28 @@ +Jos' + ); + $xpath = Xpath::fromDocument($doc, Xpath\Configurator\namespaced_functions( + 'http://my-ns', + 'my', + ['str_replace' => str_replace(...)] + )); + + $result = $xpath->evaluate('my:str_replace("J", "B", string(//item))', Type\string()); + static::assertSame($result, 'Bos'); + } +} diff --git a/tests/Xml/Xslt/Configurator/FunctionsTest.php b/tests/Xml/Xslt/Configurator/FunctionsTest.php index 105d64b7..cf17e5a9 100644 --- a/tests/Xml/Xslt/Configurator/FunctionsTest.php +++ b/tests/Xml/Xslt/Configurator/FunctionsTest.php @@ -16,7 +16,7 @@ public function test_it_can_use_php_functions(): void { $processor = Processor::fromTemplateDocument( $this->createTemplate(), - functions(['strtoupper']) + functions(['strtoupper' => strtoupper(...)]) ); $result = $processor->transformDocumentToString( @@ -36,7 +36,7 @@ public function test_it_throws_exception_on_unkown_function(): void { $processor = Processor::fromTemplateDocument( $this->createTemplate(), - functions(['substr']) + functions(['substr' => substr(...)]) ); $doc = Document::fromXmlString( <<createTemplate(), + namespaced_functions('http://my-xls', ['strtoupper' => strtoupper(...)]) + ); + + $result = $processor->transformDocumentToString( + Document::fromXmlString( + << + World + + EOXML + ) + ); + + static::assertSame('WORLD', $result); + } + + public function test_it_throws_exception_on_unkown_function(): void + { + $processor = Processor::fromTemplateDocument( + $this->createTemplate(), + namespaced_functions('http://my-xls', ['substr' => substr(...)]) + ); + $doc = Document::fromXmlString( + << + World + + EOXML + ); + + $this->expectException(RuntimeException::class); + $this->expectExceptionMessage('strtoupper'); + $processor->transformDocumentToString($doc); + } + + private function createTemplate(): Document + { + return Document::fromXmlString( + << + + + + + + EOXML + ); + } +}