Использование spl_autoload_register() с классом, использующим пространство имен, вызывает это сообщение об ошибке: Неустранимая ошибка: неперехваченная ошибка: класс "MyClass" не найден...
Перед этим сообщением об ошибке автозагрузчик выдает эхо "...MyClass.php IS found".
Используются два файла: "/index.php" (в корневом каталоге) и "/classes/MyClass.php".
Индекс.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();
МойКласс.php:
declare(strict_types=1);
namespace App\Classes
class MyClass
{
// ...
}
Кто-нибудь знает, как это решить?
Согласно https://www.php-fig.org/psr/psr-4/, требуется имя поставщика, поэтому я использую «Приложение» в качестве имени пространства имен верхнего уровня, хотя это не так. каталог (потому что мой сайт использует корень сервера).
Я также пытался добавить «use App/classes/Myclass» в index.php перед «$myclass = new MyClass()», но это вызывает еще больше проблем, потому что автозагрузчик будет искать каталог «/classes/App/Classes». ..
С удалением пространства имен из класса все работает нормально, и я могу использовать функции класса через $myclass. Но я хотел бы начать использовать пространства имен... Любая помощь будет очень признательна! <3
Вывод: Либо строка "
$myclass = new App\Classes\MyClass();
" или "use App\Classes\MyClass;
" следует использовать.Таким образом, невозможно использовать корень сервера, имея при этом имя пространства имен верхнего уровня ("App") с этой функцией автозагрузки. функция должна быть расширена, чтобы учесть эту возможность. А каталог «classes» будет переименован в «Classes». Я опубликую свое решение, когда оно будет готово!
Для получения более подробной информации прочитайте комментарии под ответом @IMSoP (Спасибо вам большое спасибо за помощь!)
Решение:
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();
Это решение работает независимо от того, находится ли приложение в каталоге с тем же именем, что и имя пространства имен верхнего уровня, или где-либо еще!
@deceze Спасибо! Я думал, что сделал это в первый раз правильно, но потом кто-то упомянул, что я не всегда использовал полные имена классов, и я подумал, что это должно быть место, где я должен что-то изменить (потому что это была единственная часть в моем коде, где я использую пространство имен)... Но я неправильно понял, и я должен использовать полное имя класса в строке с "$myclass = new MyClass()" (или вместо этого использовать "use")... Этим решениям нужен автозагрузчик функция должна быть скорректирована, что я и сделаю.. В любом случае, большое спасибо за ваш комментарий! Я изменил пространство имен обратно (без имени класса)
Возможно, вы захотите прочитать страницы руководств по автозагрузке и пространствам имен, чтобы убедиться, что вы понимаете ключевые понятия.
Вы объявили класс с именем 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\ClassesMyClass
делает то же самое — операторы use
просто помогают компилятору избегать слишком частой записи полного имени.)
Что вам нужно сделать, так это:
use
)Большое спасибо за Ваш ответ! У меня два вопроса: ...1) Я не использую полные имена классов? (я думал, что делаю это, как описано в psr-4, но я не смог использовать команду «использовать» в index.php, вероятно, по той причине, которую вы упомянули) ... 2) Нельзя ли использовать корень сервера, сохраняя при этом имя пространства имен верхнего уровня? ("Приложение" в данном случае)
@ 1337noobie 1) В этой строке кода в вашем вопросе не используется полное имя: $myclass = new MyClass();
Оно должно быть либо $myclass = new App\Classes\MyClass();
, либо иметь use App\Classes\MyClass;
вверху файла. 2) Вы можете расположить свои файлы так, как хотите, вы просто пишете любую логику, необходимую в вашей функции автозагрузчика, чтобы перевести имя в местоположение; но весь смысл пространств имен в том, что у вас может быть два класса с именами MyClass
, поэтому размещение всех ваших классов в одном плоском каталоге не сработает.
Я исправил пространство имен, изменил его с «App\Classes» на «App\Classes\MyClass», но та же проблема все еще возникает... Второе предложение, которое вы сделали, заключалось в том, чтобы соответствовать структуре пространства имен, но если я это сделаю, то мне пришлось бы переместить мои файлы в каталог под названием «Приложение», это правильно? Означает ли это, что тогда нельзя будет использовать корневой каталог сервера?
@ 1337noobie Я только что ответил на эту часть: разместите их в любых каталогах, которые вам нравятся, но если у вас есть два класса с именами App\UI\Control
и App\Permission\Control
, они не могут быть файлами с именем «Control.php» в одном и том же каталоге. Вы можете назвать их «App__UI__Control.php» и «App__Permission__Control.php», если хотите, но более распространенным является просто использование каталога для каждого пространства имен.
@ 1337noobie 1) Поймите, что если вы называете свой класс App\Classes\MyClass
, что вы сделали, используя пространство имен, то вам также нужно называть его этим именем с помощью new App\Classes\MyClass
. 2) Когда вы делаете new App\Classes\MyClass
и ваш автозагрузчик получает строку "App\Classes\MyClass"
, то вам нужно написать свой автозагрузчик таким образом и выложить ваши файлы таким образом, чтобы правильный файл мог быть загружен из имени "App\Classes\MyClass"
.
Кроме того, вы говорите «мое второе предложение», но я прямо сказал в ответе, что вам нужно исправить обе вещи.
@IMPSoP Еще раз спасибо за ваш ответ! Таким образом, единственным решением было бы изменить мою функцию автозагрузчика, чтобы вручную удалить любую часть «Приложения» и заменить «Классы» (верхний регистр в пространстве имен) на «классы» (нижний регистр, имя каталога), чтобы он будет правильно соответствовать моей структуре каталогов? ... Я снова прочитаю ваши комментарии, я чувствую себя глупо, если неправильно понял то, что вы написали раньше ...
@ 1337noobie «Классы» - довольно бессмысленное имя пространства имен. Если вы собираетесь поместить все в это пространство имен, зачем вообще беспокоиться о пространствах имен?
Я действительно очень ценю ваши отзывы! Вы не сказали это явно, но я предполагаю, что мне придется изменить функцию автозагрузчика так, как я упомянул выше (путем удаления «приложения» вручную из пути и т. д.), чтобы я мог использовать «новое приложение \ классы \ MyClass ()" без каких-либо проблем. @IMSoP Я все еще учусь, поэтому надеюсь, что не слишком раздражал вас своей медлительностью в понимании... Еще раз спасибо за вашу помощь!
@ 1337noobie Смысл пространств имен в том, чтобы структурировать ваш код таким образом, чтобы он имел для вас смысл. Обычно каталоги располагаются одинаково, потому что люди обычно считают полезным иметь связанные классы в одном каталоге.
@IMSoP Я согласен, это кажется действительно полезным ... До сих пор я хотел только проверить, могу ли я заставить работать автозагрузчик, поэтому у меня нет подкаталогов в моем каталоге классов (и пространстве имен) для более конкретных целей ... Честно говоря, я даже не думал, что далеко вперед, я просто хотел сначала исправить ошибки... Я просто чувствую разочарование, что я не могу использовать и корень моего сервера, и имя пространства имен верхнего уровня одновременно, с общая функция автозагрузчика, которую я использовал...
@ 1337noobie Я думаю, что это, возможно, случай попытки двигаться слишком медленно: пытаясь использовать пространства имен «немного», вы на самом деле пытались сделать что-то более сложное, потому что это не соответствует ни одному из примеров, которые вы смотрим. Если вместо App\Classes
вы сделаете свое первое пространство имен App\NotYetCategorised
, будет намного понятнее, как оно соотносится с примерами, которые вы найдете в чужом коде.
Это заняло некоторое время, но мне удалось решить эту проблему! :) Я добавил свое решение в пост... Спасибо за ваше время и ваш совет! @IMSoP
С
namespace App\Classes\MyClass
ваше полное имя класса на самом деле будетApp\Classes\MyClass\MyClass
, что является нонсенсом.