Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:107971 Return-Path: Delivered-To: mailing list internals@lists.php.net Received: (qmail 7724 invoked from network); 3 Jan 2020 13:33:36 -0000 Received: from unknown (HELO php-smtp4.php.net) (45.112.84.5) by pb1.pair.com with SMTP; 3 Jan 2020 13:33:36 -0000 Received: from php-smtp4.php.net (localhost [127.0.0.1]) by php-smtp4.php.net (Postfix) with ESMTP id 4C4A81804A8 for ; Fri, 3 Jan 2020 03:37:43 -0800 (PST) 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,HTML_MESSAGE, RCVD_IN_DNSWL_NONE,RCVD_IN_MSPIKE_H2,SPF_HELO_NONE,SPF_PASS 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-lf1-f50.google.com (mail-lf1-f50.google.com [209.85.167.50]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange ECDHE (P-256) server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by php-smtp4.php.net (Postfix) with ESMTPS for ; Fri, 3 Jan 2020 03:37:42 -0800 (PST) Received: by mail-lf1-f50.google.com with SMTP id y1so31691240lfb.6 for ; Fri, 03 Jan 2020 03:37:42 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=mime-version:references:in-reply-to:from:date:message-id:subject:to :cc; bh=6ZBI8oae/EaOZUyp9mnBewZXSIE3hsw3AsnPksXypEU=; b=Ry1ucVL9p7szbMlqp4UASE02av37ts4alqEGYB891RN+mvgY45hfhhV2yRlCTWsPeO IdbXm4kx5BxY7VSwNyQuK/hHMKzz08jAyffOpNM6zeJp7QP0F9Ta9woQz89nOEgqOFKI 1gCadc8CApdBLvQCtzZkOrHNOHbPNArKzg45FeH75JXfMyBnUhCMe99h4mOXRn/m8DDJ +CUtZhZj1gEa3nhvNoE2DaQjrNWaZbiXgPY2h/PHq4jUNKfXE8nS5rImV7yGHE1yxxsx cOFH0T9DZe9xydwGKfzw5j48YZ1MaCEmr6SZYaiI3MWHRKRS1ZBxqq8aIcPY2pBPWNs0 V29w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:mime-version:references:in-reply-to:from:date :message-id:subject:to:cc; bh=6ZBI8oae/EaOZUyp9mnBewZXSIE3hsw3AsnPksXypEU=; b=A1zy51DEat1NiI0EeS4JHY7jn7Y3N03HHmkgLZoc9cWIsm+91Anr6IZa5nyuWyVE2+ IdU0UN0Kf0YCDom6fReBqtAXtooNHaMMK5RWUqA3uKF7X5SGzuOTmO9q6g/Ls3zOJS8C Z4XpNYcU3vSTpylgxFPylFFR34aon3uQKnJbXc/mvSPx+W8+TMVZ+SH3OTeDGghl7/or 82JPNabws5O2ZX4wFfGl2Kgxf4pysyJjfg6r7Pjum2dOGMNNPKu6qJw6v47IjXhlwqmH 7fy32KQi4AdkijGG6xd0+dVPKGmFCfOf3qLa77DMkAhJRHAK9eGIw6gPHFPeM4osDGz6 P9Ow== X-Gm-Message-State: APjAAAViICFvVISqA+ykVHy3gdswcb17+mX7fmQPreNeqH4vDJ9LGC0G KEtYp70ex9IsvMIf/0pvgQ+3uVJRGxiE4wN15/8= X-Google-Smtp-Source: APXvYqz8WLafay/Ua4Ev1SlNmRk11oKJQBaGS4kHD3fDEopJwzAQEIBObtctUuxxRPc69jDFusysIDpcbuviXPcCY34= X-Received: by 2002:a19:c205:: with SMTP id l5mr49073732lfc.159.1578051459738; Fri, 03 Jan 2020 03:37:39 -0800 (PST) MIME-Version: 1.0 References: In-Reply-To: Date: Fri, 3 Jan 2020 12:37:23 +0100 Message-ID: To: Rasmus Schultz Cc: tyson andre , "internals@lists.php.net" Content-Type: multipart/alternative; boundary="000000000000b1f035059b3abc6b" Subject: Re: [PHP-DEV] Autoloading functions/consts without a performance impact From: nikita.ppv@gmail.com (Nikita Popov) --000000000000b1f035059b3abc6b Content-Type: text/plain; charset="UTF-8" On Fri, Jan 3, 2020 at 12:22 PM Rasmus Schultz wrote: > On Fri, Jan 3, 2020 at 10:35 AM Nikita Popov wrote: > >> On Fri, Jan 3, 2020 at 2:51 AM tyson andre >> wrote: >> >> > After a quick search, it turns out I've mostly reinvented >> > https://wiki.php.net/rfc/autofunc which I hadn't remembered. >> > The `spl_autoload_*()` changes it mentions is what I had in mind >> > >> > There's been changes to php since then, it's been 7 years, >> > and this adds thoughts on some implementation details. >> > >> > It's possible to support function / const autoloading >> > in a way that keeps existing performance. >> > Some of the objections at the time no longer apply. >> > >> > - `SOME_CONSTANT` no longer falls back to the literal string in php 8. >> > It throws an Error. >> > - APC (used by some implementations) is no longer supported. >> > - Many of the objections were against the available implementations >> > based on `function __autoload()`, not the concept of autoloading. >> > >> > https://www.mail-archive.com/internals@lists.php.net/msg52307.html >> > - There wasn't much traction from leads for the concept >> > or a Proof of Concept to vote on for that RFC (I think). >> > >> > What if an ambiguous `function_name()` or const outside the global >> > namespace would attempt to autoload functions in the global namespace >> > if it would throw an Error, but not the current namespace? >> > >> > `function_exists()` or `defined()` would not autoload, >> > to preserve the behavior/performance of current programs. >> > Anything with the callable type hint (e.g. `array_map`) would also need >> to >> > be modified, >> > but I think it already needs to do that for method arrays and >> > 'MyClass::method'. >> > >> > - Same for global constants >> > - That should avoid the performance hit - this autoloading would only be >> > triggered >> > when php would previously throw an Error or warning >> > for undefined functions/constants. >> > - This would also avoid the need to load polyfill files (e.g. mbstring) >> > or test framework files when their functions/constants are unused. >> > - One blocker for other autoload proposals >> > seemed to be performance if I understood correctly >> > (e.g. attempting to autoload NS\strlen() every time strlen was >> called). >> > >> > https://externals.io/message/54425#54616 and >> > https://externals.io/message/54425#54655 >> > detail this - choosing a different order of checks avoids the >> > performance hit. >> > - In addition to the RFC, changing the signatures to `defined(string >> > $name, bool $autoload = false)` >> > and `function_exists($name, bool $autoload = false)` >> > might be useful ways to allow users to choose to autoload when >> checking >> > for existence. >> > >> > And there'd have to be a hash map, flag, or other check >> > to avoid recursion on the same constant/function. >> > >> > Background: A function call or constant usage is >> > ambiguous if it could look for the function in two namespaces, >> > as described by the rules documented in the below link. >> > It's unambiguous if it ends up looking in only one namespace. >> > >> > https://www.php.net/manual/en/language.namespaces.rules.php >> > The only type of ambiguous function call or global constant use is (7.): >> > >> > > 7. For unqualified names, >> > > if no import rule applies and the name refers to a function or >> > constant >> > > and the code is outside the global namespace, the name is resolved >> at >> > runtime. >> > > Assuming the code is in namespace A\B, here is how a call to >> function >> > foo() is resolved: >> > > 1. It looks for a function from the current namespace: A\B\foo(). >> > > 2. It tries to find and call the global function foo(). >> > >> > My approach should be in line with the current name resolution >> > for unqualified names outside the global namespace. >> > >> > 1. It (first) looks for a function from the current namespace: >> A\B\foo(). >> > 2. It (next) tries to find and call the global function foo(). >> > 3. (Optional) NEW ADDITION: Next, if both functions were undefined, >> > the autoloader(s) attempt to autoload the function A\B\foo() >> > (and call it instead of throwing if found) before proceeding to step >> > (4.) >> > 4. NEW ADDITION: If both functions were undefined, >> > the autoloader(s) attempt to autoload the global function foo() >> > (and call it instead of throwing if found) before throwing an Error >> > >> > And for unambiguous calls, find and autoload the only name it has. >> > >> > The fact that one of the two possible functions gets cached by the php >> VM >> > in CACHED_PTR >> > (for the lifetime of the request) the first time the function is found >> > in either namespace will remain unchanged with this proposal. >> > >> >> I believe the problem here is this: The fact that the global function is >> being cached, and continues to be cached when a namespaced function is >> later defined, is a bug. See for example >> https://bugs.php.net/bug.php?id=64346. We've never prioritized fixing >> this >> issue, but implementing the proposed autoloading behavior would make >> fixing >> it essentially impossible, as we certainly can't perform an autoloader >> invocation on each call. >> >> Nikita >> > > I will just briefly mention an alternative idea I've brought up before. > (there's no RFC for this.) > > use function MyClass::myStaticFunction; > > myStaticFunction(); > > This would allow importing a static function and calling it just like a > flat function. > > The way most people normally write code in PHP, they're favoring static > functions over flat functions, since aggressively preloading flat functions > (e.g. "file" in "composer.json") doesn't scale, and using manual > include/require statements is clumsy and error-prone. (and not really > possible with Composer.) > > Many existing PHP codebases already have "classes" that are really just > "pseudo-namespaces" for collections of functions, so this approach takes > into account the existing ecosystem. > > It also implicitly addresses autoloading, which would just automatically > work with Composer (or any class auto-loader) with no further changes. > > It also bypasses any learning curve surrounding function name resolution > rules, if this were to become more complex by adding more rules and details > with regards to resolving and autoloading flat functions. > > Is there a really good reason to encourage the entire community to migrate > all of their existing codebases from static classes to flat functions? > Expecting Composer to add support for it. Expecting package vendors to list > all function names in package metadata files, and so on. > > In many cases, migrating from static to flat functions also means > migrating any constants from class constants to namespaced constants - so > this likely leads down the rabbit hole to autoloading constants as well, > and/or having to use some mix of class constants and flat functions which > isn't very desirable. > > Is there any practical return on that investment? > > Static functions aren't really any different from flat functions. > > (if you're thinking "state", that's really up to the author - your flat > functions could depend on global state just like a static function could > depend on static properties...) > > Just my five cents on the matter :-) > Hey Rasmus, One issue I see with important static methods in this manner, is that PHP has no concept of a static method call -- rather, it performs scoped calls, that may refer to static or instance methods. use function A::foo; class B extends A { public function test() { foo(); // What does this do? } } If the call to foo() is simply treated equivalently to a call to A::foo(), then this may have some quite surprising behavior: If A::foo() is an instance method, then this call to foo() will inherit $this, which is something that normally does not happen with free-standing function calls. Possibly this would need a new call type that enforces that the method is actually static? Overall though, I do tend to agree that the use of static methods is at this point more idiomatic than the use of free-standing functions, and it might make more sense to go in that direction. Nikita --000000000000b1f035059b3abc6b--