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 :-)