Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:121340 Return-Path: Delivered-To: mailing list internals@lists.php.net Received: (qmail 64470 invoked from network); 16 Oct 2023 21:19:06 -0000 Received: from unknown (HELO php-smtp4.php.net) (45.112.84.5) by pb1.pair.com with SMTP; 16 Oct 2023 21:19:06 -0000 Received: from php-smtp4.php.net (localhost [127.0.0.1]) by php-smtp4.php.net (Postfix) with ESMTP id B6391180087 for ; Mon, 16 Oct 2023 14:19:05 -0700 (PDT) X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on php-smtp4.php.net X-Spam-Level: X-Spam-Status: No, score=-0.6 required=5.0 tests=BAYES_00,BODY_8BITS, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,FREEMAIL_FROM, RCVD_IN_DNSWL_NONE,RCVD_IN_MSPIKE_H2,SPF_HELO_NONE,SPF_PASS, T_SCC_BODY_TEXT_LINE autolearn=no autolearn_force=no version=3.4.2 X-Spam-ASN: AS15169 209.85.128.0/17 X-Spam-Virus: No X-Envelope-From: Received: from mail-ed1-f51.google.com (mail-ed1-f51.google.com [209.85.208.51]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange ECDHE (P-256) server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by php-smtp4.php.net (Postfix) with ESMTPS for ; Mon, 16 Oct 2023 14:19:05 -0700 (PDT) Received: by mail-ed1-f51.google.com with SMTP id 4fb4d7f45d1cf-53e751aeb3cso4223664a12.2 for ; Mon, 16 Oct 2023 14:19:05 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1697491144; x=1698095944; darn=lists.php.net; h=content-transfer-encoding:in-reply-to:from:references:to :content-language:subject:user-agent:mime-version:date:message-id :from:to:cc:subject:date:message-id:reply-to; bh=tIvrdySbDL7+iOKHT4SxprZc/q0IDuWNhjq6MzNp+yc=; b=g0wa8sn5qsCgJeVHIswO6VHe/kAGkgzqiqRimkiKHaJmSeMq3y1XEEDiaQNFHcT/wg VeT98KJCex3iVncE+4PLNW4aar4aY1Y9f7o73ubayDFjUuGEMEHZdutEH/VaxCxcQnGX +4z/JVgmyzLorM9O0ZUnZIKdQc9nbHmXhD4fdem4wxOnpiY+aK+yI9rahVoTiETK/xwM 5o5q9NfF0fzO2eR+Yz8gJ0dwv1jithFE7Y4hKGU+WF6eun2btwzhRZDELDAskWUK2+Jz 8NqMDS2zQOtmYNjQsthzoAtndogVb8qRRZ99zmxZHaFWnBjDKeGBJZvvr2DmqRx9ZZuB TYWQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1697491144; x=1698095944; h=content-transfer-encoding:in-reply-to:from:references:to :content-language:subject:user-agent:mime-version:date:message-id :x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=tIvrdySbDL7+iOKHT4SxprZc/q0IDuWNhjq6MzNp+yc=; b=p42eiDeqpNBjJ6Q3j4HiJ5vHYvluDWSYyiKqrcg5vJ+Pyxoyg1eELv4OQXgEoXNsrH jjeY8PzZzNbRJKYd1RzukycS08P6/bK6bo59u6eqXootiKHoknHu9FPBc/STh0cPa+Zg rU1wJmTCOmUw/KVX9lpYAfZkugUlNAZhkFxrMaIgfGdCxqsZh0wkbV5jkLTdZW3L2rMz ojiYIfka18Dq1D9LFqDEwWoPeRjbp/8FVRDEws48v2K26leWxCnfitlUFcWYKysLCwCd OvtmeTNBUVLsH7/cifYJ6N82gKMexm2iWo1SVXPLXKG00iQPV3/L29uCRnH/aO1XDicP HoWA== X-Gm-Message-State: AOJu0YwxtWdqD2BvFvkRnc12wE+kDTHvo1W1x/jVnLKinXsBN+00TKJb B4VaKWVJO7QtMy2b7PFGkGqKx1/f6jY= X-Google-Smtp-Source: AGHT+IHRUcGJwOwDG7bdxhp1LNOjc/jJUENS73X9rh/ueSzmXNgLAv1PjYRDHSUYvfcyuQptoQX4tQ== X-Received: by 2002:a50:8ad0:0:b0:53d:a26a:521f with SMTP id k16-20020a508ad0000000b0053da26a521fmr312307edk.34.1697491143743; Mon, 16 Oct 2023 14:19:03 -0700 (PDT) Received: from ?IPV6:2a02:1811:cc83:ee50:280e:1e36:3a00:824? (ptr-dtfv08akcem5xburtic.18120a2.ip6.access.telenet.be. [2a02:1811:cc83:ee50:280e:1e36:3a00:824]) by smtp.gmail.com with ESMTPSA id e17-20020a50a691000000b00534e791296bsm65705edc.37.2023.10.16.14.19.03 for (version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128); Mon, 16 Oct 2023 14:19:03 -0700 (PDT) Message-ID: Date: Mon, 16 Oct 2023 23:19:02 +0200 MIME-Version: 1.0 User-Agent: Mozilla Thunderbird Content-Language: en-US To: PHP internals References: <0e6ee882-472f-43e1-822e-c156d9048e0c@gmail.com> In-Reply-To: Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Subject: Re: DOMXPath / XSLTProcessor function callbacks From: dossche.niels@gmail.com (Niels Dossche) 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 >> >