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