On Fri, Jan 3, 2020 at 12:22 PM Rasmus Schultz <ras...@mindplay.dk> wrote:
> On Fri, Jan 3, 2020 at 10:35 AM Nikita Popov <nikita....@gmail.com> wrote: > >> On Fri, Jan 3, 2020 at 2:51 AM tyson andre <tysonandre...@hotmail.com> >> 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