# [Pre-RFC][Discussion] Native Engine-Level PSR-4 Autoloader

- Thread: https://externals.io/message/131956
- Original archive: https://news-web.php.net/php.internals/131956
- Started by fennic log on 2026-07-16T04:25:34+00:00
- Messages: 6
- RFC: SplClassLoader (Declined) — https://externals.io/rfc/splclassloader.md
- Reply by email to internals@lists.php.net
- More for agents: search with https://externals.io/search.md?q={query} · RFCs at https://externals.io/rfc.md · JSON API at https://externals.io/api · documentation at https://externals.io/llms.txt

---

## #131956 — fennic log — 2026-07-16T04:25:34+00:00

URL: https://externals.io/message/131956#131956

PSR-4 has been the community standard for autoloading namespaced classes
since 2014 and is used by virtually every modern PHP package via
Composer. However,
the actual autoloading still requires userspace code:
`spl_autoload_register()` with a closure or class method (almost always
generated by Composer) that performs prefix matching and
`file_exists`/`require`.

This RFC proposes adding a small set of native functions that allow the
engine itself to perform PSR-4 autoloading directly from registered prefix
→ base directory mappings. Package managers and applications can opt into
the faster, engine-native path instead of (or in addition to) registering a
userspace callback.

With emphasis on PSR-4 compliance, the engine implementation MUST follow
the PSR-4 specification exactly for prefix matching, relative path
construction, file extension (`.php`), and case sensitivity of the
terminating class name.
This can provide a performance improvement for PSR-4 heavy workloads by
eliminating userspace callback overhead.

To fully support PSR-4 autoloading I would propose a function
 `spl_autoload_psr4_register` and complimenting functions.
Here is a proposed API.

```PHP

/** * Register a PSR-4 namespace prefix → base directory mapping for native
engine autoloading. * * @param string $prefix Namespace prefix (trailing \
recommended; will be normalized) * @param string|array $baseDirs One base
directory (string) or list of base directories (array) * @param bool
$prepend Insert at front of search order (like spl_autoload_register) *
@return bool True on success */ function spl_autoload_psr4_register(string
$prefix, string|array $baseDirs, bool $prepend = false): bool {}

/** * Remove a previously registered PSR-4 prefix (and its base
directories). */ function spl_autoload_psr4_unregister(string $prefix):
bool {}

/** * Enable/disable the native PSR-4 fast path. When disabled, mappings
are kept but ignored. */ function spl_autoload_psr4_enable(bool $enable =
true): void {}

/** * Whether the native PSR-4 mechanism is currently active. */ function
spl_autoload_psr4_enabled(): bool {}

/** * Return current mappings as [prefix => [baseDir, ...], ...] preserving
registration order. */ function spl_autoload_psr4_get_mappings(): array {}
``` There would be no backwards compatibility issues and a simple
`function_exists('spl_autoload_psr4_register')` call would be used to
trigger use of the new functionality or fallback to userland callback
register.

Discussion points:
Should there be an `spl_autoload_psr4_enable(bool)` function to enable this
functionality or should the usage of `spl_autoload_psr4_register`
auto-enable the autoloader?


References

- PSR-4 Specification: https://www.php-fig.org/psr/psr-4/ - PSR-4 Example
Implementations: https://www.php-fig.org/psr/psr-4/examples/ - Old related
RFC: https://wiki.php.net/rfc/splclassloader (declined, PSR-0 focused) -
Composer Autoloader class:
https://github.com/composer/composer/blob/main/src/Composer/Autoload/ClassLoader.php
----- This idea originally spawned from my idea to try an eliminate the
standard bootstrapping call of `include_once(__DIR__ .
'/vendor/autoload.php')` While this does not achieve this, i believe it can
be a good first step.

---

## #131957 — Heinz Wiesinger — 2026-07-16T06:41:06+00:00

URL: https://externals.io/message/131956#131957 · In reply to: #131956

> PSR-4 has been the community standard for autoloading namespaced classes
> since 2014 and is used by virtually every modern PHP package via
> Composer. However,
> the actual autoloading still requires userspace code:
> `spl_autoload_register()` with a closure or class method (almost always
> generated by Composer) that performs prefix matching and
> `file_exists`/`require`.
> 
> This RFC proposes adding a small set of native functions that allow the
> engine itself to perform PSR-4 autoloading directly from registered prefix
> → base directory mappings. Package managers and applications can opt into
> the faster, engine-native path instead of (or in addition to) registering a
> userspace callback.
> 
> With emphasis on PSR-4 compliance, the engine implementation MUST follow
> the PSR-4 specification exactly for prefix matching, relative path
> construction, file extension (`.php`), and case sensitivity of the
> terminating class name.
> This can provide a performance improvement for PSR-4 heavy workloads by
> eliminating userspace callback overhead.
> 
> To fully support PSR-4 autoloading I would propose a function
>  `spl_autoload_psr4_register` and complimenting functions.

I implemented a PECL extension doing pretty much this in https://github.com/
pprkut/autoload-psr, minus the complimenting functions.

I can't speak for performance benefits as I didn't do any benchmarks, I don't 
think they'd be very noticeable though. My primary motivation for writing it 
was to have an autoloader available from script start. The extension supports 
PSR-0 as well, which fits even better (IMHO) with that workflow since it means 
truly zero setup compared to PSR-4.

Anyway, perhaps have a look. At the very least it might serve as reference 
point.

Grs,
Heinz

---

## #131962 — Alex Rock — 2026-07-16T09:25:30+00:00

URL: https://externals.io/message/131956#131962 · In reply to: #131956

Le 16/07/2026 à 06:25, fennic log a écrit :
>
> PSR-4 has been the community standard for autoloading namespaced 
> classes since 2014 and is used by virtually every modern PHP package 
> via Composer. However, the actual autoloading still requires userspace 
> code: `spl_autoload_register()` with a closure or class method (almost 
> always generated by Composer) that performs prefix matching and 
> `file_exists`/`require`.
>
> This RFC proposes adding a small set of native functions that allow 
> the engine itself to perform PSR-4 autoloading directly from 
> registered prefix → base directory mappings. Package managers and 
> applications can opt into the faster, engine-native path instead of 
> (or in addition to) registering a userspace callback.
>
> With emphasis on PSR-4 compliance, the engine implementation MUST 
> follow the PSR-4 specification exactly for prefix matching, relative 
> path construction, file extension (`.php`), and case sensitivity of 
> the terminating class name. This can provide a performance improvement 
> for PSR-4 heavy workloads by eliminating userspace callback overhead.
>
> To fully support PSR-4 autoloading I would propose a 
> function `spl_autoload_psr4_register` and complimenting functions.
> Here is a proposed API.
>
> ```PHP
>
> /** * Register a PSR-4 namespace prefix → base directory mapping for 
> native engine autoloading. * * @param string $prefix Namespace prefix 
> (trailing \ recommended; will be normalized) * @param string|array 
> $baseDirs One base directory (string) or list of base directories 
> (array) * @param bool $prepend Insert at front of search order (like 
> spl_autoload_register) * @return bool True on success */ function 
> spl_autoload_psr4_register(string $prefix, string|array $baseDirs, 
> bool $prepend = false): bool {}
>
> /** * Remove a previously registered PSR-4 prefix (and its base 
> directories). */ function spl_autoload_psr4_unregister(string 
> $prefix): bool {}
>
> /** * Enable/disable the native PSR-4 fast path. When disabled, 
> mappings are kept but ignored. */ function 
> spl_autoload_psr4_enable(bool $enable = true): void {}
>
> /** * Whether the native PSR-4 mechanism is currently active. */ 
> function spl_autoload_psr4_enabled(): bool {}
>
> /** * Return current mappings as [prefix => [baseDir, ...], ...] 
> preserving registration order. */ function 
> spl_autoload_psr4_get_mappings(): array {} ``` There would be no 
> backwards compatibility issues and a simple 
> `function_exists('spl_autoload_psr4_register')` call would be used to 
> trigger use of the new functionality or fallback to userland callback 
> register.
>
> Discussion points:
> Should there be an `spl_autoload_psr4_enable(bool)` function to enable 
> this functionality or should the usage of `spl_autoload_psr4_register` 
> auto-enable the autoloader?
>
> References
>
> - PSR-4 Specification: https://www.php-fig.org/psr/psr-4/ - PSR-4 
> Example Implementations: https://www.php-fig.org/psr/psr-4/examples/ - 
> Old related RFC: https://wiki.php.net/rfc/splclassloader (declined, 
> PSR-0 focused) - Composer Autoloader class: 
> https://github.com/composer/composer/blob/main/src/Composer/Autoload/ClassLoader.php 
> -----
>
I don't really see the advantage of having "spl_autoload_psr4_enable" 
and "spl_autoload_psr4_enabled" functions: since there's a new 
"spl_autoload_psr4_register" function, it means the feature should be 
enabled by default, but registering PSR-4 namespaces is still opt-in. 
Composer's autoloader might evolve in time to use it, but it can 
internally detect PHP's version and dump the same autoloader as today 
for current PHP versions, and dump a "new" autoloader for PHP versions 
using built-in PSR-4 autoloading.


> This idea originally spawned from my idea to try an eliminate the 
> standard bootstrapping call of `include_once(__DIR__ . 
> '/vendor/autoload.php')` While this does not achieve this, i believe 
> it can be a good first step.
>

If you're using Composer, built-in PSR-4 autoloading is a good start, 
but in the end it won't be enough: Composer still supports PSR-0, and 
supports custom files and classmaps. Adding built-in support for PSR-0 
isn't necessarily useful (since it's supposed to have been deprecated 
for a long time now), but custom classmaps could be interesting. A 
function "spl_register_classmap_autoload(array $classmap, bool $prepend 
= false)" function (or for single registrations with "string $class, 
string $filepath" instead of an array, whatever) could also be used by 
Composer in the future.
And by extrapolating, we can have more functions to replace the process 
Composer goes on when generating a classmap with the "-o / --optimize" 
option in the "dump-autoload" command and make the first run of a 
process handling generate the classmap for certain registered psr4 
namespaces and put them in cache (opcache, memcache, apcu...) (something 
like "generate_psr4_classmap(string $nsPrefix)", or with the cache 
backend's name like "opcache_generate_classmap_from_psr4(string 
$nsPrefix)" ), so that further executions "just" fetch this classmap 
cache from memory, thus enhancing built-in PSR-4 autoloading (just like 
Composer's, but built-in, and classmap stored natively in memory instead 
of the generated classmap file being stored in opcache and having to be 
re-run). Benchmarks would be needed to check if built-in classmap would 
be better than Composer's (so it implies a cache backend to check if 
re-running the generated classmap is slower or not than a built-in 
memory hashmap of classes and files), but it's feasible.

WDYT? Should an extrapolation of your suggestion make it to the core 
too? (I personally think it should, either with this first RFC or with 
further RFCs once this one is accepted)

---

## #131986 — fennic log — 2026-07-17T00:43:31+00:00

URL: https://externals.io/message/131956#131986 · In reply to: #131962

> I don't really see the advantage of having "spl_autoload_psr4_enable" and
> "spl_autoload_psr4_enabled" functions: since there's a new
> "spl_autoload_psr4_register" function, it means the feature should be
> enabled by default, but registering PSR-4 namespaces is still opt-in.
> Composer's autoloader might evolve in time to use it, but it can internally
> detect PHP's version and dump the same autoloader as today for current PHP
> versions, and dump a "new" autoloader for PHP versions using built-in PSR-4
> autoloading.
>
I felt the need for a flag function because there may be a scenario where
an autoloader may need to toggle built in psr4 autoloading, I always air on
the side of verbosity over simplicity.

If you're using Composer, built-in PSR-4 autoloading is a good start, but
> in the end it won't be enough: Composer still supports PSR-0, and supports
> custom files and classmaps. Adding built-in support for PSR-0 isn't
> necessarily useful (since it's supposed to have been deprecated for a long
> time now), but custom classmaps could be interesting. A function
> "spl_register_classmap_autoload(array $classmap, bool $prepend = false)"
> function (or for single registrations with "string $class, string
> $filepath" instead of an array, whatever) could also be used by Composer in
> the future.
>

This is the same reason why i felt supporting PSR-0 wasnt needed. PSR-0 is
superseded by PSR-4 for years now and while is isnt deprecated yet, it is
widely understood that it shouldn't be used over PSR-4. PSR-0 was the
starting point of autoloading and is a bad solution to a good problem,
PSR-4 was the good solution to a good problem.


> And by extrapolating, we can have more functions to replace the process
> Composer goes on when generating a classmap with the "-o / --optimize"
> option in the "dump-autoload" command and make the first run of a process
> handling generate the classmap for certain registered psr4 namespaces and
> put them in cache (opcache, memcache, apcu...) (something like
> "generate_psr4_classmap(string $nsPrefix)", or with the cache backend's
> name like "opcache_generate_classmap_from_psr4(string $nsPrefix)" ), so
> that further executions "just" fetch this classmap cache from memory, thus
> enhancing built-in PSR-4 autoloading (just like Composer's, but built-in,
> and classmap stored natively in memory instead of the generated classmap
> file being stored in opcache and having to be re-run). Benchmarks would be
> needed to check if built-in classmap would be better than Composer's (so it
> implies a cache backend to check if re-running the generated classmap is
> slower or not than a built-in memory hashmap of classes and files), but
> it's feasible.


In my mind that would all be handled by the engine. You wouldn't need to
call `generate_*` or `opcache_*` functions as the classmap should be stored
in direct memory in a structured layout for optimal use. I would think that
the autoloader would have minimal AST, the autoloader would be a
blackbox to opcache. A class is called -> its not loaded -> it is
registered -> it is now loaded huzzah.

WDYT? Should an extrapolation of your suggestion make it to the core too?
> (I personally think it should, either with this first RFC or with further
> RFCs once this one is accepted)


Thats why this is a discussion, get a grasp of ideas, solutions and
gauge support for the proposal.

---

## #131987 — fennic log — 2026-07-17T00:54:57+00:00

URL: https://externals.io/message/131956#131987 · In reply to: #131957

>
>
>
> I implemented a PECL extension doing pretty much this in
> https://github.com/
> pprkut/autoload-psr <https://github.com/pprkut/autoload-psr>, minus the
> complimenting functions.
>
> I can't speak for performance benefits as I didn't do any benchmarks, I
> don't
> think they'd be very noticeable though. My primary motivation for writing
> it
> was to have an autoloader available from script start. The extension
> supports
> PSR-0 as well, which fits even better (IMHO) with that workflow since it
> means
> truly zero setup compared to PSR-4.
>
>
Thanks for pointing this out to me, I'll check it out.

---

## #131988 — fennic log — 2026-07-17T00:59:19+00:00

URL: https://externals.io/message/131956#131988 · In reply to: #131956

Here is an example script i whipped up.

```php
// vendor/autoload.php example

spl_autoload_psr4_enable(); spl_autoload_psr4_register('Acme\\Blog\\',
__DIR__ . '/src/'); spl_autoload_psr4_register('Acme\\Blog\\Tests\\',
__DIR__ . '/tests/', prepend: true);

// Our script.php: new \Acme\Blog\Post(); // Engine (C level): // -
normalizes prefix "Acme\Blog\Tests\" first (prepend) // - no local match →
"Acme\Blog\" // - match → relative = "Post" // - candidate = src/Post.php
// - if file exists → include it (silently on failure) → if class now
defined, success else normal undefined class error. // - else fall through
to any userspace spl_autoload stack

```
