I'm using Laravel as framework to build an application framework which can be used to build their own application by fellow developers. Now I'm running into a little PSR-4 namespacing problem in the composer packages I'm developing.
Example of how my application framework is designed
- app
- MyApplicationName
- Mapper
BaseMapper.php
(namespace:App\MyApplicationName\Mapper
)
- Mapper
- MyApplicationName
- bootstrap
- config
- ...
- vendor
- MyVendorName
- MyApplicationName
- src
- Controller
BaseController.php
- Mapper
BaseMapper.php
(namespace:MyVendorName\MyApplicationName\Controller
)
- Controller
- src
- MyApplicationName
- MyVendorName
This works correctly. I can make the BaseMapper.php
in the app
folder extend the BaseMapper.php
in vendor
and in that way I can add extra methods and properties to the BaseMapper.php
while keeping (or overwriting) those in the base mapper file.
Contents of BaseMapper.php
in app
:
namespace App\MyApplicationName\Mapper;
use MyVendorName\MyApplicationName\Mapper as FrameworkMapper;
class BaseMapper extends FrameworkMapper;
{
public function executeFunction(): string
{
return "bar";
}
}
Contents of BaseMapper.php
in vendor
:
namespace MyVendorName\MyApplicationName\Mapper;
class BaseMapper
{
public function executeFunction(): string
{
return "foo";
}
}
When I call the mapper from within the Laravel project like:
$baseMapper = new \App\MyApplicationName\BaseMapper();
$result = $baseMapper->executeFunction(); // bar
I get bar
as a result and I'm very happy.
But here's my problem:
In the BaseController.php
in vendor
I call a BaseMapper.php
's method like:
$mapper = new \MyVendorName\MyApplicationName\Mapper\BaseMapper();
$result = $mapper->executeFunction(); // foo
Now $result
is foo
while I expect to get bar
.
I can solve it by adding:
$mapper = new \MyVendorName\MyApplicationName\Mapper\BaseMapper();
if (class_exists(\App\MyApplicationName\Mapper\BaseMapper::class)) {
$mapper = new \App\MyApplicationName\Mapper\BaseMapper();
}
$result = $mapper->executeFunction();
But I think it's ugly to add checks like that and besides it's easily forgotten to add a check like that when you instantiate a new class.
So I want to add some custom autoloading logic (in my package) to be able to search for the App
namespace first and to use the MyVendorName\MyApplicationName
namespace as a fallback.
I've already tried to do this in composer.json
:
"autoload": {
"psr-4": {
"MyVendorName\MyApplicationName": ["../../app/MyApplicationName/","src/"]
}
}
But this make it impossible to overwrite the class from within the app
folder as I'm using the same namespace.
So my only idea to achieve this is when there's a way to hook in on the autoloader of composer to add logic like this:
if (str_contains($className, "\\MyVendorName\\MyApplicationName\\")) {
$appClassName = str_replace("\\MyVendorName\\MyApplicationName\\", "\\App", $className);
if (class_exists($appClassName)) {
include_once $appClassName;
} else {
include_once $className;
}
}
Could you please help me out how to achieve this giving me another (better) solution for my problem?