In this PHP internals message we read:

I have participated in some internals discussions that point out a fact from the other direction: PHP makes no imposition about file structure. 1-class-per-file is strictly a PSR-4 implementation of a custom autoloader which we have gotten used to for so long. I would really love to be able to declare small related classes/interfaces in a single file ...

There is something to be said for more-than-one class per file, and after a few evenings of tinkering, I can now present the moto/autoload package.

The Moto name-to-file resolution algorithm is an ideological descendant from Horde/PEAR through PSR-0 and PSR-4, with the latest evolution being 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.

And 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() {}

I'm soliciting feedback on this approach, so please post your comments and concerns at Github as issues and pull requests.

Are you stuck with a legacy PHP application? You should buy my book because it gives you a step-by-step guide to improving you codebase, all while keeping it running the whole time.