Reputation: 13
Using spl_autoload_register() with a class that uses namespace, causes this error message: Fatal error: Uncaught Error: Class "MyClass" not found...
Before this error message, the autoloader does echo "...MyClass.php IS found".
Two files are used: "/index.php" (in the root directory) and "/classes/MyClass.php".
Index.php:
declare(strict_types=1);
define ('ROOT_DIR', __DIR__ . '/');
define ('CLASS_DIR', ROOT_DIR . 'classes/');
spl_autoload_register(function($class) {
$filepath = CLASS_DIR . str_replace('\\', DIRECTORY_SEPARATOR, $class) . '.php';
if (file_exists($filepath)) {
require_once $filepath;
echo('<br /><b>' . $filepath . '</b> IS found.<br />');
} else {
echo('<br /><b>' . $filepath . '</b> not found.<br />');
exit;
}
});
$myclass = new MyClass();
MyClass.php:
declare(strict_types=1);
namespace App\Classes
class MyClass
{
// ...
}
Does anyone know how to solve this?
According to https://www.php-fig.org/psr/psr-4/, a vendor name is required, so I am using "App" as a top-level namespace name, even though it is not a directory (because my website uses the root of the server).
I also tried adding "use App/classes/Myclass" to index.php before "$myclass = new MyClass()" but this causes even more problems, because the autoloader will look for the directory "/classes/App/Classes"...
With the namespace removed from the class, everything works fine, and I can use the functions of the class through $myclass. But I would like to start using namespaces... Any help would be really appreciated! <3
Conclusion: Either the line "
$myclass = new App\Classes\MyClass();
" or "use App\Classes\MyClass;
" should be used.So it is not possible to use the root of the server while also having a top-level namespace name ("App") with this autoload function. The function has to be expanded to allow for this possibility. And the "classes" directory will be renamed to "Classes". I will post my solution when it is ready!
For more details, read the comments below the answer by @IMSoP (Thank you very much for your help!)
Solution:
declare(strict_types=1);
namespace App;
define ('ROOT_DIR', $_SERVER['DOCUMENT_ROOT'] . '/');
define ('BASE_DIR', __DIR__ . '/');
define ('TOP_LEVEL_NAMESPACE_NAME', __NAMESPACE__ . '/');
spl_autoload_register(function($class) {
if (BASE_DIR == ROOT_DIR . TOP_LEVEL_NAMESPACE_NAME) {
$filepath = ROOT_DIR . str_replace('\\', DIRECTORY_SEPARATOR, $class) . '.php';
} else {
$filepath = BASE_DIR . str_replace('\\', DIRECTORY_SEPARATOR, $class) . '.php';
$filepath = str_replace(TOP_LEVEL_NAMESPACE_NAME, '', $filepath);
}
if (file_exists($filepath)) {
require_once $filepath;
} else {
echo('Class <b>' . end(explode('\\', $class)) . '.php</b> was not found.');
exit;
}
});
use App\Classes\MyClass;
$myclass = new MyClass();
This solution works whether the application is in the directory with the same name as the top-level namespace name, or anywhere else!
Upvotes: 0
Views: 993
Reputation: 97658
You might want to read through the manual pages on autoloading and namespaces to make sure you understand the key concepts.
You have declared a class called MyClass
inside the namespace App\Classes
; that means that its fully qualified name is App\Classes\MyClass
- that's the name you need to call it by from outside that namespace. There could simultaneously be a different class whose fully-qualified name was just MyClass
, because it wasn't in any namespace, and any number of others in other namespaces, like App\Security\MyClass
, App\UI\MyClass
, etc.
Then you've attempted to reference a class in index.php called MyClass
, which triggers the autoloader. The autoloader translates it to a path like .../classes/MyClass.php
, and loads the right file; but that file defines your namespaced class. So after the autoloader has finished, there is no class called MyClass
, only App\Classes\MyClass
and the code fails.
If instead you write new App\Classes\MyClass
, you'll get the opposite problem: the string passed to your autoloader is 'App\Classes\MyClass'
and you translate that to a file path like '.../classes/App/Classes/MyClass.php'
- but that's not where your file is. (Adding use App\ClassesMyClass
does the same thing - use
statements are just compiler assistance to avoid writing out the fully-qualified name as often.)
What you need to do is both:
use
)Upvotes: 2