Re: Autoloading functions/consts without a performance impact
| From: | Nikita Popov | Date: | Fri, 03 Jan 2020 09:34:34 +0000 |
| Subject: | Re: Autoloading functions/consts without a performance impact | ||
| References: | 1 | Groups: | php.internals |
| Request: | Send a blank email to internals+get-107969@lists.php.net to get a copy of this message | ||
On Fri, Jan 3, 2020 at 2:51 AM tyson andre <tysonandre775@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