Re: [PHP-DEV] Pre-RFC spitball - Namespace Load event

[email protected] ("Rowan Tommins [IMSoP]") Sat, 4 Jul 2026 12:52:05 +0100
Newsgroups php.internals
Message-ID <[email protected]>
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.

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

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.

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?

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.

This feels very promising to me as an idea, though, thanks for 
contributing it.

-- 
Rowan Tommins
[IMSoP]