After feedback from Rowan Tommins, Larry Garfield, pmjones and Alex Rock
I'm going to move to a first draft of a spec for this idea.


INTRODUCTION
Numerous attempts have been made to create function and constant
autoloaders within namespaces, and they've ran into problems concerning
symbol lookup. This RFC proposes to sidestep this issue by allowing
userland to register an improved autoloader able to not only handle the
first time classes are sought out by the engine, but also the first time
the engine sees a namespace. This allows userland code to set up a
namespace - load its functions, constants and/or perform other
initialization work deemed necessary by the user.

PROPOSAL
Users will be able to provide a callback to spl_autoload_register() that
has two string arguments instead of one.  If they do those arguments are as
follows.

```
function (string $class, string $namespace) {}
```
When the engine encounters a class that it does not have on the symbol
table it will invoke the callback for the class as it presently does. The
namespace argument will only be populated if the engine has not seen this
namespace.  For example

```
$a = new A\AClass();
$b = new A\BClass();
```
Line 1: Autoloader will be called with arguments ("A\AClass", "A")
Line 2: Autoloader will be called with arguments ("A\BClass", null)

PHP will also call the autoloader when it encounters the namespace keyword
referencing a namespace it hasn't seen before. Hence

```
namespace B;
```
This will result in a call to the autoloader with arguments (null, "B")

If a namespace chain is encountered only one call is made. Hence

```
namespace A\B\C;
```

Will result in a call to the autoloader of (null, "A\B\C").

NOTE. The engine will mark namespaces "A" and "A\B" as seen in addition to
the specified namespace "A\B\C", so the autoloader needs to work out if it
should do anything for those namespaces as well.

BACKWARDS INCOMPATIBLE CHANGES
None foreseen.

I guess technically if someone has already written an autoloader that takes
2 or more arguments the code would break because the engine previously was
not sending 2 arguments. However, is this a realistic thing to worry about?
I can't think of a rational reason to write such an autoloader.

Note that autoloaders written to this spec should happily run on older PHP
versions because those versions will always give null to the 2nd argument
causing the autoloader to only act on its class code. An autoloader that
prepares namespaces based on classes is still possible, but unsafe since a
function from the namespace could get called.

PROPOSED PHP VERSIONS
This should be safe on any minor release. Realistically this won't be
available until 8.7 I would guess as we're already in 8.6 alpha. I don't
think this could be coded before feature freeze, but I don't know the
engine well.  I think I can do this myself though if needed, especially if
someone volunteers to coach me.

RFC IMPACT
This RFC allows a route to function autoloading by sidestepping issues it
has had. Quoting Rowan Tommins (with example updated to reflect this
proposal):

> 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 autoloaders that accept 2 arguments are called with arguments
> (null, '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 autoloaders that accept two arguments with
arguments
> (null, 'Omnicorp\Bar\Helpers') and try again. If it still doesn't exist,
throw
> an Error.

And to extend on his example, when a class is referenced from a previously
unknown namespace the autoloader will receive both arguments as outlined
above and be responsible for executing both setup processes.

This RFC will almost certainly trigger a related PSR standard that takes
namespace setup into account. Once that is settled the maintainers of
composer will likely implement such a standard.  One possible route is to
follow Python's lead of having a __setup__.php file in the
namespace's directory if the namespaces are set up in mirror to the file
system setup, but nothing in this RFC mandates that approach.

>

Reply via email to