Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:121286 Return-Path: Delivered-To: mailing list internals@lists.php.net Received: (qmail 59531 invoked from network); 12 Oct 2023 22:39:36 -0000 Received: from unknown (HELO php-smtp4.php.net) (45.112.84.5) by pb1.pair.com with SMTP; 12 Oct 2023 22:39:36 -0000 Received: from php-smtp4.php.net (localhost [127.0.0.1]) by php-smtp4.php.net (Postfix) with ESMTP id 554B81804BC for ; Thu, 12 Oct 2023 15:39:35 -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=-2.1 required=5.0 tests=BAYES_00,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-wm1-f44.google.com (mail-wm1-f44.google.com [209.85.128.44]) (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 ; Thu, 12 Oct 2023 15:39:35 -0700 (PDT) Received: by mail-wm1-f44.google.com with SMTP id 5b1f17b1804b1-4054496bde3so15526615e9.1 for ; Thu, 12 Oct 2023 15:39:35 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1697150374; x=1697755174; darn=lists.php.net; h=content-transfer-encoding:subject:from:to:content-language :user-agent:mime-version:date:message-id:from:to:cc:subject:date :message-id:reply-to; bh=1cTX1cN7CrsfC5W3b75jR3nZAGCzTuIT8HGpi5OxzOk=; b=E3AkKrAELLuarc7Pq8rxA3p8SJU7wcwYWexzIqsD4EEzkiOY1PC1FbMSyzUxk23Cvp E1wLYTVW0N+2yKHH1kaW7tAIa/TPVdjs6gmh27GwQW6Kd8nEdo4EB0z9a8t9ehjxeoaM quFaSr+LTv9XhqzVu2oagveCYPbFTs7HX+k/q7lSYs+SWgMe53y8Y1NPnUD+uUUuaZN9 0R7uQB0Mo21/fd1b5rSXjQLx4aJSSzd4pcXSHWTIFB2bmyu30TLrwQLVA8ZPChSyMwqR //FjIN7D7uLU3i7mkZmC18ZqtO06Jf2EBMVbcSH00BSvZl6Wj8SttD6OlMr1Z/SUasNv MvIA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1697150374; x=1697755174; h=content-transfer-encoding:subject:from:to:content-language :user-agent:mime-version:date:message-id:x-gm-message-state:from:to :cc:subject:date:message-id:reply-to; bh=1cTX1cN7CrsfC5W3b75jR3nZAGCzTuIT8HGpi5OxzOk=; b=EtGLZO37KAYc1odi/jsGsb8K1y0M5BrELJdKlwwUNFw+56W+6mHjQ8uqCg9m9ewE1h k3lRT6jmO7+m4xrQaxyNRAeYATGsP57OsA2Pwe6cq04/fMEpE68ZVFFlO5Djyo9HHiG3 qqqfTKoFiL5a0PHD29d22v2en/8IEIjgzcdNL65IQkLo9FVcmFMgidH4pEEgYBDnQU0g LqqXW2HHcbYUHmSkd6br6KxknFRByvpbEPO/lUgfHTTo3poakNSFBA9ouDfU5lxC8N/V W0urw1KKBZP6HF0fikW73FqAIcQVh0A2d1x4QdD4TwUgnOfxhQrX4zbcd3bS1Mjo1Xn0 IDIQ== X-Gm-Message-State: AOJu0YwRydbfhLf24MbRUs2skvkr/wlnmLn2YP2orMt0Ov6VTRUmsFeF 18/4JIRT9huPAe15nhMExkVpz9XknmA= X-Google-Smtp-Source: AGHT+IFJER//bs3CXXIUHSTVvn+bGfBHTiqt5/z1HDe4pQgcrHVgVbHAEOACdMbZjwIVXI2aN0E1JA== X-Received: by 2002:a7b:c40a:0:b0:401:23fc:1f92 with SMTP id k10-20020a7bc40a000000b0040123fc1f92mr23166811wmi.25.1697150373510; Thu, 12 Oct 2023 15:39:33 -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 dj16-20020a0560000b1000b003198a9d758dsm2363006wrb.78.2023.10.12.15.39.32 for (version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128); Thu, 12 Oct 2023 15:39:33 -0700 (PDT) Message-ID: <0e6ee882-472f-43e1-822e-c156d9048e0c@gmail.com> Date: Fri, 13 Oct 2023 00:39:32 +0200 MIME-Version: 1.0 User-Agent: Mozilla Thunderbird Content-Language: en-US To: PHP internals Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 7bit Subject: DOMXPath / XSLTProcessor function callbacks From: dossche.niels@gmail.com (Niels Dossche) Hi internals 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