ppokatilo/services-loader
A Laravel package for loading and registering services, helping you organize service providers and bootstrap application services through a simple, configurable loader.
The package is installable but effectively dormant — last release January 2016, one star, zero dependents. Treat it as a small piece of vendored source rather than a living dependency.
composer require ppokatilo/services-loader
If Packagist resolution fails (common for stale packages), pull the repo directly and copy src/ into your project. It's MIT-licensed and small enough to audit in minutes.
Where to look first:
composer.json — confirm the PHP constraint (expect >=5.5/>=7.0 era) and the autoload map. The namespace is almost certainly Ppokatilo\ServicesLoader\.src/ — the loader class is the package. Read the constructor (which will take a directory path) and the single public load()/__invoke() method.example/ or tests/ directory — likely absent given the star count, so budget for reverse-engineering the API.First use case — populating a DI container from a services.d/ directory:
$loader = new \Ppokatilo\ServicesLoader\Loader(__DIR__ . '/services.d');
$container = new \Symfony\Component\DependencyInjection\ContainerBuilder();
$loader->load($container);
The mental model is systemd/runit .d directories: every file in the directory contributes service definitions, and the loader merges them into whatever container you pass in.
File-per-service. One definition per file, named after the service id (db.connection.php, mailer.php). Mirrors the .d convention and keeps diffs small.
Return a definition, don't mutate. Each file should return an array/closure/Definition object. Do not call $container->set(...) inside the file — that couples the file to a specific container and defeats the loader's whole purpose.
Lazy closures. If the loader accepts callables, store construction logic in a closure resolved on get(). Keeps container boot cheap even with dozens of service files.
Environment overlays. Point the loader at services.d/common and then services.d/{environment} (instantiate twice, or rely on multi-directory support if present). Later files should override earlier ones — verify the merge order in source.
Compile once, deploy. Run the loader at build time and dump the compiled container to PHP. Never scan the directory per request in production.
Laravel angle. This package predates and does not target Laravel. If you want the same shape in a Laravel app, skip it: put one service provider per concern under app/Providers/ and let config/app.php order them. Closures in $app->bind() give you the same lazy-construction behavior the loader would.
Testing. Create a fixture services.d/ tree per test case and instantiate the loader against it. This is the cleanest isolation you get with this package.
create_function, each(), and old Symfony DI APIs (Symfony\Component\DependencyInjection\Definition signatures changed in 3.x and 4.x). Patch locally or fork — the surface area is tiny.src/ before you commit to it.FilesystemIterator order varies by OS and filesystem. If your definitions can override each other, sort filenames explicitly (ksort on a glob result) before loading, or enforce unique service ids.realpath($dir) . DIRECTORY_SEPARATOR.require'd and the resulting id. Common culprits: wrong extension, unreadable permission, or a file that returns null instead of a definition.services.d files declare classes (rather than just returning definitions), PSR-4 mapping won't cover them automatically. require them explicitly or register an autoload rule.LoaderInterface), decorate it to add logging or validation around each file load. This is the practical way to get observability out of an unmaintained package.services.yaml + imports, or league/container's addDefinitions(). Both are maintained, PHP 8-ready, and give you the same services.d ergonomics without the 2016 time capsule.How can I help you explore Laravel packages today?