Hi Frederik

Sorry for the resend... I accidentally replied to you only without including 
the list the first time.

On 15/10/2023 21:37, Frederik Bosch wrote:
> Dear Niels,
> 
> First of all, thanks for all your hard work already on the DOM and SimpleXML 
> extensions. I have been following your work in PHP-SRC, great! I am the 
> author of this XSL 2.0 Transpiler in PHP package 
> (https://github.com/genkgo/xsl). It is indeed possible to use workarounds for 
> closures or object methods. I am relying on them in my package.
> 
> My suggestion for the future would be to add the following method.
> 
> public XSLTProcessor::registerFunctionsNS(string $namespace, 
> array|ArrayAccess $functions): void
> 
> Then a user can register functions like this.
> 
> $xsltProcessorOrDomXpath->registerFunctionsNS('urn:my.namespace', 
> array('upper-case', 'strtoupper', 'pow' => fn ($a, $b) => $a[0]->textContent 
> ** $b[0]->textContent, 'other' => [$obj, 'method']);
> 

Interesting suggestion. So you want to be able to use something like 
`my.namespace:function(...)` as I understand it.
I'm not sure that adding this is that much more beneficial though (complexity 
vs benefit trade-off).
I assume this is motivated by the fact that you can then use third party 
libraries while having to worry less about name clashes?
Let's say we add non-namespace `registerFunction(string $name, callable 
$callback): void`, you can then still use a convention of using a prefix, thus 
_kinda_ achieving the same.

In any case, this is going to be hard to support in combination with the 
underlying library (libxslt).
That's because the function namespace registration is process-wide, so this 
cannot be changed at runtime and certainly not for ZTS SAPIs.

> The registered functions should use the same methodology as php:function(). 
> Hence, string casting of arguments is something the library user should do. I 
> would leave registerPHPFunctions as is, and maybe discourage it in favor of 
> the method above. What if both are called? I think it would be most clear if 
> the registerFunctionsNS method throws InvalidArgumentException when 
> http://php.net/xsl or http://php.net/xpath is passed as namespace.
> 
> Cheers,
> Frederik

Cheers
Niels

> 
> 
> On 13-10-2023 00:39, Niels Dossche wrote:
>> I'm looking to extend the functionality of calling PHP functions from within 
>> the DOMXPath or XSLTProcessor classes.
>>
>> In case you're unfamiliar here's a quick rundown.
>> The DOMXPath class allows you to execute XPath queries on a DOM tree to 
>> lookup certain nodes satisfying a filter.
>> PHP allows the user to execute function callbacks within these. For example 
>> (from the manual):
>>    $xpath->query('//book[php:functionString("substr", title, 0, 3) = 
>> "PHP"]');
>> This will read the title element's text content, call substr on it, and then 
>> compare the output against "PHP".
>> You can not only call builtin functions, but also user functions.
>>
>> To be able to call PHP functions, you need to use 
>> DOMXPath::registerPhpFunctions() 
>> (https://www.php.net/manual/en/domxpath.registerphpfunctions.php).
>> You either pass in NULL to allow all functions, or pass in which function 
>> names are allowed to be called.
>>
>> Similarly, XSLTProcessor has the same registerPhpFunctions() method.
>> For XSLT it's mostly used for performing arbitrary manipulations on input 
>> data.
>> Normally the output of the function is put into the resulting document.
>>
>>
>> So what's the problem?
>> The current system doesn't allow you to call closures or object methods.
>> There are tricks you can do with global variables and global functions to 
>> try to work around this, but that's quite cumbersome.
>>
>> There are two feature requests for this on the old bugtracker:
>>    - https://bugs.php.net/bug.php?id=38595
>>    - https://bugs.php.net/bug.php?id=49567
>>
>> It's not hard to implement support for this, the question is just what API 
>> we should go with.
>> Based on what I've read, there are at least two obvious options:
>>
>>
>> OPTION 1) Extend registerPHPFunctions() such that you can pass in callables
>>
>> ```
>> // Adapted from https://bugs.php.net/bug.php?id=38595
>> $xslt->registerPHPFunctions(array(
>>     'functionblah', // Like we used to
>>     'func2' => fn ($x) => ...,
>>     'func3' => array($obj, 'method'), // etc
>> ));
>> ```
>>
>> Example: Using php:function("func3") inside XPath/XSLT in this case will 
>> result in calling method on $obj.
>> Similarly func2 will call the closure, and functionblah in the snippet just 
>> allowlists calling functionblah.
>>
>> It's a backwards compatible solution and a natural extension to the current 
>> method.
>> It may be hard to discover this feature compared to having a new API though.
>>
>> Furthermore, once you pass in function names to registerPHPFunctions(), 
>> you're restricting what can be called.
>> For example: imagine you want to call both ucfirst() and $obj->method(), so 
>> you pass in an entry like func3 in the above example.
>> Now you have to pass in ucfirst to registerPHPFunctions() too, because 
>> registerPHPFunctions() acts as an allowlist. May be a bit inconvenient.
>>
>>
>> OPTION 2) Add new methods to register / unregister callables
>>
>> This may be the cleaner way to go about it on first sight, but there's a 
>> potential BC break when new methods clash in user-defined subclasses.
>>
>> Question here is: what about the interaction with registerPHPFunction?
>> What if both registerPHPFunction() and the register method add something 
>> with the same name?
>> What if registerPHPFunction() didn't allowlist a function but the register 
>> method added it, may be a bit confusing for users.
>> The interaction may be surprising.
>>
>>
>>
>> Please let me know your thoughts.
>>
>> Cheers
>> Niels
>>
> 

-- 
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: https://www.php.net/unsub.php

Reply via email to