On Sat, Jul 4, 2026 at 7:52 AM Rowan Tommins [IMSoP] <[email protected]>
wrote:
> On 02/07/2026 15:41, Michael Morris wrote:
> > An idea that popped into my head an hour ago, that I don't have
> > another PHP Dev to talk to about, what if you could register a
> > function that is called by PHP the first time it sees a namespace
> > declared?
> >
> > This would allow functions and for that matter constants of the
> > namespace to be declared before they get called. It isn't as clean as
> > waiting until the function call itself is being called, but maybe
> > that's for the best. After all, if you have a dozen 10 line utility
> > functions do you necessarily want 10 files.
>
>
> I think this is a really interesting idea.
>
> As you say, grouping a bunch of functions with the same prefix into one
> file is probably quite a common pattern anyway - it would certainly give
> a very straight-forward migration for purely-static classes, e.g.
> GuzzleHttp\Utils::headersFromLines() could become
> GuzzleHttp\Utils\headersFromLines(), still defined in src/Utils.php
>
> To answer points that have come up in other replies:
>
> - I don't think it needs any special handling of sub-namespaces. The
> same handler passed "Acme\Foo\Widgets\Green" might load both a
> project-level setup at "$packageRoot/setup.php" and a namespace-specific
> file "$packageRoot/src/Widgets/Green/functions.php". If it wants "run
> once" behaviour, it can use require_once, "if ( defined('some_constant')
> ) return;", or just unregister itself from the stack.
>
> - I don't think loaders should trigger for "use" statements at all. In
> fact, I don't think "use" statements are even visible at run-time -
> they're basically a compiler string expansion, which is why you can
> write "use Foo\Bar\Baz as B; echo B::class;" without "Foo\Bar\Baz" ever
> existing as a symbol.
>
>
If it can be avoided, great, but this code worries me:
```
namespace A;
use function B\foo;
foo();
```
What happens? I think you give a reply below but I want to be clear that I
understand correctly:
> I think it would be enough to call the handler in three places:
>
> - on a "namespace" declaration (mostly once per file, but a block form
> does exist)
> - just before calling the normal class autoloader
> - just before throwing an "undefined function/constant" error for a
> *qualified* function/constant name
>
>
So, if I'm following, the use statement means the qualified name of B\foo()
is what is called under the hood. Therefore, the callback can be called for
the B namespace before the undefined function/constant error is thrown.
> This really nicely side-steps the global-vs-namespace sequence problem
> that function autoloading always runs into: by the time the engine
> reaches an unqualified name, the namespace loader has already run for
> the current namespace, and defined any functions it wants. The engine
> can fall back to the global namespace, and error immediately if that
> fails, without an extra autoload call.
>
> To illustrate:
>
> namespace Acme\Foo\Widgets;
> use Omnicorp\Bar\Helpers;
> $name = make_widget_name();
> echo strlen($name);
> Helpers\spin_wheels();
>
> Line 1: All registered namespace loaders called with argument
> 'Acme\Foo\Widgets'
>
> Line 2: Use statement, has no run-time effect
>
> Line 3: Attempt to call Acme\Foo\Widgets\make_widget_name(); this might
> have been defined by the loader at line 1. If it doesn't exist, attempt
> to call \make_widget_name(); if that doesn't exist, throw an Error.
>
> Line 4: Attempt to call Acme\Foo\Widgets\strlen(); if that doesn't
> exist, call \strlen() which is built-in, so never fails.
>
> Line 5: Attempt to call Omnicorp\Bar\Helpers\spin_wheels(); if it
> doesn't exist, call all registered namespace loaders with argument
> 'Omnicorp\Bar\Helpers' and try again. If it still doesn't exist, throw
> an Error.
>
Yeah, you've accounted for it too. I just read too quickly the first time
over. We're on the same page. I'll still post my reasoning out of this
because it might help someone else follow it as well.
>
> The only thing that might need a bit more thought is the interaction
> with classes: at the moment, the engine doesn't need to "remember" which
> autoload calls have already happened, it can just assume that a
> successful call won't happen again because the class is no longer
> undefined. But with a namespace loader, code like"$client = new
> Acme\Foo\Client; $server = new Acme\Foo\Server;" is going to trigger two
> calls for "Acme\Foo". If the engine has an explicit cache to avoid the
> duplicate call, do we need to invalidate that cache when new handlers
> are registered?
>
One possible way to quietly bypass this is if the namespace and class
autoloaders are the same function, don't double call. I imagine that if
this was added composer would likely be upgraded to take this into account.
I know that if I had an autoload system I would setup a common entry point.
A backwards compatible way to do this is to start giving the callback
supplied to spl_autoload_register both a $class and $namespace argument.
Existing callbacks will ignore the unexpected (to them) 2nd argument, but
new callbacks would be able to work with it. So
```
function myAutoloader(string $class, string $namespace = null) {
/*
* namespace register only sends one argument for the namespace, so
* if the second arg is null, spl_namespace_register is the caller
*/
if ($namespace === null) {
$namespace = class;
$class = null;
}
/* further resolve logic here */
}
spl_autoload_register("myAutoloader");
spl_namespace_register("myAutoloader");
```
> The alternative is never to call the namespace loader for class
> references, but that means it can't be used for other initialisation,
> like setting namespace-level settings or loading data or whatever.
>
There's no reason a class autoloader cannot also initialize a namespace by
including a setup file before the class file. The last time I wrote an
autoloader I did this, but I had to be careful that all initial references
to a namespace where class requests (which was brittle).
As far as invalidating the cache when a new handler is registered - that is
the suboptimal but safest approach. Just as with class autoloaders, ideally
an application shouldn't need multiple namespace autoloaders.
>
> This feels very promising to me as an idea, though, thanks for
> contributing it.
>
>
You're welcome.