Autoloading functions/consts without a performance impact
| From: | tyson andre | Date: | Fri, 03 Jan 2020 01:51:07 +0000 |
| Subject: | Autoloading functions/consts without a performance impact | ||
| Groups: | php.internals | ||
| Request: | Send a blank email to internals+get-107967@lists.php.net to get a copy of this message | ||
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.
(Out of scope of the "use global functions/consts" RFC,
so I'm splitting out this discussion. There is no proof of concept yet.)
------
Pros:
- Don't need to preload entire files and dependencies up-front whether or not they're used
(e.g. with composer),
to supply functional libraries or frameworks, or to supply polyfills for native modules.
- Supporting this encourages using functions/global constants from namespaces,
where they were previously discouraged by lack of autoloading support,
or needed manual require_once statements.
- Preloading support makes it easier to avoid \NS\FooDoer::foo() instead of \NS\do_foo() in cases
which would be unnatural (e.g. classes with just one method)