英文:
Autoload with Namespace - Fatal error: Uncaught Error: Class "MyClass" not found
问题
使用带有命名空间的类的spl_autoload_register()会导致以下错误消息:致命错误:未捕获的错误:找不到类"MyClass"…
在出现此错误消息之前,自动加载器会输出*"...MyClass.php已找到"*。
使用了两个文件:"/index.php"(在根目录)和"/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>已找到.<br />');
} else {
echo('<br /><b>' . $filepath . '</b>未找到.<br />');
exit;
}
});
$myclass = new MyClass();
MyClass.php:
declare(strict_types=1);
namespace App\Classes;
class MyClass
{
// ...
}
有人知道如何解决这个问题吗?
根据https://www.php-fig.org/psr/psr-4/,需要一个供应商名称,所以我使用"App"作为顶级命名空间名称,即使它不是一个目录(因为我的网站使用服务器的根目录)。
我还尝试在index.php中的"$myclass = new MyClass()"之前添加"use App\classes\Myclass",但这会导致更多问题,因为自动加载器将在目录"/classes/App/Classes"中查找...
去掉类的命名空间后,一切都可以正常工作,我可以通过$myclass使用类的函数。但我想开始使用命名空间... 任何帮助将不胜感激! <3
结论:可以使用"
$myclass = new App\Classes\MyClass();
"或"use App\Classes\MyClass;
"。
所以,使用此autoload函数时,无法同时使用服务器根目录和顶级命名空间名称"App"。必须扩展此功能以允许这种可能性。并且"classes"目录将重命名为"Classes"。当准备好解决方案时,我将发布我的解决方案!
解决方案:
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('类 <b>' . end(explode('\\', $class)) . '.php</b> 未找到。');
exit;
}
});
use App\Classes\MyClass;
$myclass = new MyClass();
这个解决方案可以在应用程序位于与顶级命名空间名称相同的目录或其他任何地方的情况下使用!
英文:
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!
答案1
得分: 2
你可能需要仔细阅读自动加载和命名空间的手册页面,以确保你理解关键概念。
你声明了一个名为MyClass
的类,位于命名空间App\Classes
内;这意味着它的完全限定名称是App\Classes\MyClass
- 这是你在该命名空间外部调用它时需要使用的名称。同时,可能还会有一个不同的类,其完全限定名称只是MyClass
,因为它没有在任何命名空间中,还有其他命名空间中的类,比如App\Security\MyClass
、App\UI\MyClass
等。
然后,你尝试在index.php
中引用一个名为MyClass
的类,这会触发自动加载器。自动加载器将其转换为类似.../classes/MyClass.php
的路径,并加载正确的文件;但该文件定义了你的带命名空间的类。因此,在自动加载器完成后,就不存在名为MyClass
的类了,只有App\Classes\MyClass
,代码因此失败。
如果你改为写成new App\Classes\MyClass
,你会遇到相反的问题:传递给你的自动加载器的字符串是'App\Classes\MyClass'
,你将其转换为类似'.../classes/App/Classes/MyClass.php'
的文件路径 - 但你的文件并不在那个位置。(添加use App\Classes\MyClass
也会产生相同的结果 - use
语句只是编译器的辅助工具,避免频繁写出完全限定名称。)
你需要同时做到以下两点:
- 一致地使用完全限定的类名(或使用
use
别名) - 将文件布局设计得与命名空间结构匹配,以便你的自动加载器可以找到它们,通常意味着每个命名空间一个目录。
英文:
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:
- Consistently use fully-qualified class names (or alias them with
use
) - Lay out your files to match your namespace structure, so that your autoloader can find them, which generally means a directory per namespace
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论