I'm writing a plugin that uses a few third-party libraries (one from a long-abandoned repo that I'm modifying). Library A uses Library B, and my code uses both.
In Library A are 50-odd files with things like this at the top;
namespace LibraryA\Folder1;
use LibraryA\Folder1\Class1;
use LibraryA\Folder2\Class2;
use LibraryB\Class3;
...
public function __construct($val)
{
$obj = new LibraryB\Class3($val);
...
}
Now, I want to autoload stuff and keep these libraries self-contained in my plugin's own directory so it doesn't pollute the outside environment, and for ease of installation/updating. I've added a simple autoloader to my project in the hope that it will pick up these libraries from my plugin folder (no, I'm not using composer).
function my_autoloader($className) {
$file = '/path/to/plugin/'.str_replace('\\', DIRECTORY_SEPARATOR, $className) . '.php';
if (file_exists($file)) {
require_once $file;
}
}
spl_autoload_register('my_autoloader');
The trouble is, the third-party library A uses library B and $className is being passed the fully qualified - and incorrect - namespace, resulting in:
Uncaught Error: Class "LibraryA\Folder1\Class1\LibraryB\Class3.php" not found...
How do I work around this most effectively?
I've tried messing with adding a single backslash before the class names in the use statements. It didn't seem to do anything, though I'm still experimenting. In any case, even if it does work, I don't relish the thought of going through every class in Library A and changing all references to Library B to use \ prefixes so they 'revert' to the root, top level directory.
Do I need to use spl_autoload_register() multiple times to register each namespace independently, so the autoloader somehow treats them differently when it encounters the various use and new statements? If so, how? Do I need separate autoloaders? One for each library?
Or can I code my way out of this by improving my_autoloader() to detect bogus class names and strip off the LibraryA\Unwanted\Paths\ before using require_once()?
Any guidance on approaches welcome (including hints that I'm maybe using the autoloader wrong).
Most answers on SO I've found so far say "use composer" and that's not an option at the moment.
I appreciate that the above isn't very specific, and apologise for that, but I don't know how to attach a bunch of separate, namespaced files to the post that I could use to demonstrate a reproducible test case, so I've outlined the problem instead with the bare minimum code. If that's not clear enough then I'll try and find a way to improve it.
MyProject\SomeFeature\Widgets\Frobulatorinto a file calledthe_best_class_ever.phpif you wanted; or maybe your autoloader could beinclude md5($class_name) . '.php';and the file name would be3a08845e941a98a622e311b47708d3d2.php. I'm not clear if you really want a different class name, or just to put that class in a different file.use LibraryB\Baz; new Baz()it finds it in /vendor/LibraryB/Baz. But in the self-contained case, it tries to look for ./LibraryA/Foo/Bar/LibraryB/Baz 🤔use LibraryB\Baz; new Baz()means the class name is\LibraryB\Baz, contradicting your previous example.namespaceandusestatements are looked at, by the compiler, to create a long class name; then, the autoloader is given that class name, and needs to load that class or the program will crash. The autoloader can't change what name is expected, that decision has already been made. So if there is any magic, rather than just a misunderstanding, it would have to edit the source code of the classes, so that they have different class names orusestatements actually written in them.