PHP has the ability to have 'strongly typed' in a manner of speaking:
//Definition of Loader
interface Loader
{
public function load();
}
//In Another file:
//This is a snippet from a class in a custom framework
public function init(): void
{
$loaders = [
HelperLoader::class,
MiddlewareLoader::class,
ViewLoader::class,
ConfigLoader::class
];
foreach ($loaders as $loader) {
//Here each instance of the above classes implement the Loader interface and are passed to the load function
$this->load(new $loader());
}
}
private function load(Loader $loader): void
{
$loader->load();
}
You can ask in functions for either the Full object,
so
private function load (HelperLoader $loader)...
Or better the interface.
private function load (Loader $loader)...
If you try pass something that in the first case isn't HelperLoader, or in the second (correct) case doesn't implement Loader, php will error out.
The above is essentially what Laravel is doing, however, it uses reflection to determine what exactly to inject into the class. So if you've got a 'contract' defined in your service container that points to a concrete implementation, that will be bound to the service container and called when necessary.