moto / autoload
Requires
- php: ^8.4
Requires (Dev)
- pds/composer-script-names: ^1.0
- pds/skeleton: ^1.0
- phpstan/phpstan: ^2.0
- phpunit/phpunit: ^11.0
This package is not auto-updated.
Last update: 2025-07-23 12:59:23 UTC
README
This project defines a name-to-file resolution algorithm for autoloaders that allows more-than-one class per file.
Theory of Operation
One Class Per File
Historically, PHP class-to-file naming conventions have supported only one class per file.
Horde/PEAR
In the pre-namespacing versions of PHP, the original Horde/PEAR resolution logic converted underscores to directory separators to generate a file path:
Foo_Bar_Baz_Dib => /classes/Foo/Bar/Baz/Dib.php
Foo_Bar_Baz_Dib_Zim => /classes/Foo/Bar/Baz/Dib/Zim.php
PSR-0
PSR-0 modified this logic to recognized the formal namespaces introduced in PHP 5.3, so that ...
- namespace separators were converted to directory separators, and
- underscores only in the terminating class name portion were converted to directory separators:
Foo_Bar\Baz\Dib => /classes/Foo_Bar/Baz/Dib.php
Foo_Bar\Baz\Dib_Zim => /classes/Foo_Bar/Baz/Dib/Zim.php
PSR-4
PSR-4 modified PSR-0 so that ...
- underscores in the terminating class name portion would no longer be converted to directory separators, and
- a namespace prefix could be mapped to a directory prefix:
/**
* Foo_Bar\Baz\ maps to /classes/foo-bar/baz/src/
*/
Foo_Bar\Baz\Dib => /classes/foo-bar/baz/src/Dib.php
Foo_Bar\Baz\Dib_Zim => /classes/foo-bar/baz/src/Dib_Zim.php
More-Than-One Class Per File
The Moto name-to-file resolution algorithm modifies this even further, so that everything after an underscore in the terminating class name portion is ignored:
/**
* Foo_Bar\Baz\ maps to /classes/foo-bar/baz/src/
*/
Foo_Bar\Baz\Dib => /classes/foo-bar/baz/src/Dib.php
Foo_Bar\Baz\Dib_Zim => /classes/foo-bar/baz/src/Dib.php
Foo_Bar\Baz\Dib_Gir => /classes/foo-bar/baz/src/Dib.php
Foo_Bar\Baz\Dib_Irk => /classes/foo-bar/baz/src/Dib.php
This means the Dib.php
file can contain any number of underscore-suffixed classes so long as they are prefixed with Dib
...
// /classes/foo-bar/src/Baz/Dib.php namespace Foo_Bar/Baz; class Dib { } class Dib_Zim { } class Dib_Gir { } class Dib_Irk { }
... and a Moto-compliant resolver will find the correct file for that class.
Resolution Algorithm
Given ...
-
a fully qualified
namespace-prefix
(including the rightmost namespace separator), and -
an absolute
directory-prefix
for thenamespace-prefix
(including the rightmost directory separator)
... the following algorithm resolves a fully-qualified-name
to an absolute-file-path
or to null
:
-
If the
fully-qualified-name
does not begin with thenamespace-prefix
, thefully-qualified-name
resolves tonull
. -
Otherwise, set the
qualified-name
by capturing all characters after thenamespace-prefix
. -
If the
qualified-name
contains a namespace separator ...A. Set the
partial-namespace
by capturing all characters up to and including the rightmost namespace separator, and set thepartial-name
by capturing all characters after the rightmost namespace separator.B. Otherwise, set the
partial-namespace
to an empty string, and set thepartial-name
to thequalified-name
. -
If the
partial-name
contains an underscore, remove the first underscore and all characters thereafter. -
If the
partial-name
is empty, thefully-qualified-name
resolves tonull
. -
Otherwise, set the
partial-directory
by converting each namespace separator in thepartial-namespace
to aDIRECTORY_SEPARATOR
. -
Set the
absolute-file-path
by concatenating thedirectory-prefix
,partial-directory
,partial-name
, and afilename-suffix
(typically.php
). -
If the
absolute-file-path
exists, thefully-qualified-name
resolves to theabsolute-file-path
; otherwise, it resolves tonull
.
Cf. the reference implementation resolutionAlgotrithm()
method for an example.
Implications
-
Directory names with underscores WILL be resolvable.
-
Base file names with underscores WILL NOT be resolvable.
-
The
partial-name
itself does not have to exist at theabsolute-file-path
; that is, the file might contain only underscore-suffixed names:// Foo_Bar\Baz => /vendor/foo-bar/baz/src/Dib.php namespace Foo_Bar\Baz; // no Dib {} class class Dib_Zim {} class Dib_Gir {} class Dib_Irk {}
-
If function autoloading becomes supported by PHP, the Moto name-to-file resolution algorithm can support multiple functions per file:
// Foo_Bar\Baz => /vendor/foo-bar/baz/src/dib.php namespace Foo_Bar\Baz; function dib() {} function dib_zim() {} function dib_gir() {} function dib_irk() {}