Создание объекта из переменной с использованием пространств имен и автозагрузки в PHP
EDIT с моим собственным комментарием после
Я думаю, проблема в том, что когда PHP разбирает файл для "компиляции", сначала он переводит имена классов в их полностью квалифицированные имя. Таким образом, индекс будет переведен в Controller\Home\Index. После что, когда PHP переводит переменные в их значение. Поэтому, если я использую переменная как имя класса, она не будет определять свое имя, потому что это шаг уже произошел. И вот почему не находит класс. Это просто предположение, но, скорее всего, именно так Blockquote
Редактировать конец
Im, использующий UniversalClassLoader из проекта Symfony2 для автоматической загрузки моих классов, но я обнаружил некоторую странную ошибку, которую я не могу решить.
Функция автоматической загрузки работает нормально, но затем я столкнулся с этой проблемой:
$controller = new Index(); // It works!
$controller_name = "Controller\\Home\\Index";
$controller2 = new $controller_name(); // It works!
$controller_name = "Index";
$controller3 = new $controller_name(); // Fatal error: Class 'Index' not found
Два первых случая работают отлично. В первом, так как я использую "use Controller\Home;" в начале моего script я могу использовать только "новый Index();" без проблем. Но если вместо "Индекса" я использую строчную переменную типа $var = "Index", она НЕ работает. Я не понимаю, почему.
Мне нужно, чтобы этот script был динамическим, поэтому для этого мне нужна переменная.
Спасибо!
<суб > дополнительные поиски длинного хвоста:
- php полное имя из переменной
- PHP-экземпляр класса из псевдонима в переменной
суб >
Ответы
Ответ 1
Интересный вопрос. По внешнему виду, нет никакого способа обойти это. В php docs явно указано следующее:
Необходимо использовать полное имя (имя класса с префиксом пространства имен).
Возможным обходным решением в вашем случае будет определение базового пространства имен для ваших контроллеров в конфигурации и регистрация контроллеров по их относительному пространству имен.
Предположим, что базовое пространство имен MyApp\Controller\
, и мы будем держать наши контроллеры в массиве (для краткости):
$controllers = array(
'foo/bar' => 'MyFooController',
'baz' => 'Foo\\MyBarController'
);
// One way how the controllers could be instantiated:
// returns 'MyApp\\Controller\\'
$namespace = $cfg->get('namespaces.controller');
$controller = $namespace.$controllers['baz'];
$controller = new $controller();
Ответ 2
Интервал имен означает, что PHP не только сможет найти файл с заданным путем (в значительной степени такой же, как и require_once), но и имя класса также соответствует директиве пространства имен. Единственный diff? Слэши для подчеркивания (пример № 2 ниже).
// This may be working because Symfony autoloader has found a class named Index
$controller = new Index();
// A name-space call so PHP knows where to actually find the file
// Chances are the class name here is Controller_Home_Index
$controller_name = "Controller\\Home\\Index";
$controller2 = new $controller_name();
// Symfony autloader should have picked this up, but didn't
// It possible that with this construct Symfony cannot find the class and b/c
// it not namespaced, PHP has no choice but to fail
$controller_name = "Index";
$controller3 = new $controller_name();
Проверьте директорию кэша проекта Symfony, есть и некоторые плоские файлы массивов PHP, которые могут вам помочь.
Ответ 3
Вы пробовали:
use Controller\Home\Index as Index;
а затем
$controller3 = new Index();
- это то же самое, что случилось со мной с Zend Amazon S3 Service. Я решил это, используя:
use Zend\Service\Amazon\S3\S3 as Zend_Service_Amazon_S3;
$s3 = new Zend_Service_Amazon_S3($key, $secret);
Конечно, после того, как имена будут помещены в файл автозагрузки следующим образом:
'Zend' => __DIR__.'/../vendor/Zend/library',