在 PHP 中使用命名空间和自动加载从变量创建对象

Posted

技术标签:

【中文标题】在 PHP 中使用命名空间和自动加载从变量创建对象【英文标题】:Creating object from variable using Namespaces and Autoload in PHP 【发布时间】:2011-12-18 08:28:02 【问题描述】:

之后编辑我自己的评论

我认为问题在于,当 php 解析文件以“编译”时,首先它将类名转换为完全限定的 姓名。所以Index会被翻译成Controller\Home\Index。后 那就是 PHP 将变量转换为它们的值的时候。所以如果我使用 变量作为类名,它不会限定其名称,因为 步骤已经发生。这就是为什么找不到课程的原因。 这只是一个猜测,但很可能是这样的 块引用

结束编辑

我正在使用 Symfony2 项目中的 UniversalClassLoader 来自动加载我的类,但我发现了一些我无法解决的奇怪错误。

自动加载工作正常,但后来我遇到了这个问题:

$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

前两个案例工作得很好。在第一个中,因为我使用“使用 Controller\Home;”在我的脚本开始时,我可以只使用“new Index();”没有什么问题。但是,如果我不写“Index”,而是使用像 $var = “Index”这样的字符串变量,它就不起作用。我不明白为什么。 我需要这个脚本是动态的,这就是为什么我需要一个变量。

谢谢!


额外的长尾搜索:

来自变量的 php 完全限定名 php 从变量中的别名实例化类

【问题讨论】:

您的脚本顶部是否有namespaceuse 声明?这可能就是 new Index() 起作用的原因。 是的,我愿意。这就是为什么它适用于第一种情况,但我希望第三种情况也能正常工作。我该怎么办? 我的猜测是 PHP 在定位非限定符号而不是字符串时只使用 use 子句。看起来您需要在字符串中指定全名,即$controller_name = '\Controller\Home\Index'; 我认为问题在于当 PHP 解析文件以“编译”时,它首先将类名转换为它们的完全限定名。所以Index会被翻译成Controller\Home\Index。在那之后,就是 PHP 将变量转换为它们的值的时候。所以如果我使用一个变量作为类名,它不会限定它的名字,因为那一步已经发生了。这就是为什么找不到课程的原因。这只是一个猜测,但很可能就是这样。 【参考方案1】:

Name-spacing 意味着 PHP 不仅能够找到具有给定路径的文件(与 require_once 几乎相同),而且类名实际上也与命名空间指令匹配。唯一的区别?下划线的斜线(下面的示例 #2)。

// This may be working because Symfony's 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's autloader should have picked this up, but didn't
// It's possible that with this construct Symfony cannot find the class and b/c
// it's not namespaced, PHP has no choice but to fail
$controller_name = "Index";
$controller3     = new $controller_name();

检查您的 Symfony 项目缓存目录,有一些 PHP 数组的平面文件也可能对您有所帮助。

【讨论】:

【参考方案2】:

你试过了吗:

use Controller\Home\Index as Index;

然后

$controller3 = new Index();

与 Zend 的 amazon S3 服务发生在我身上的事情是一样的。我使用以下方法解决了它:

use Zend\Service\Amazon\S3\S3 as Zend_Service_Amazon_S3;

$s3 = new Zend_Service_Amazon_S3($key, $secret);

当然,在自动加载文件中将其命名为:

'Zend'                => __DIR__.'/../vendor/Zend/library',

【讨论】:

【参考方案3】:

有趣的问题。从外观上看,没有办法绕过它。 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();

【讨论】:

以上是关于在 PHP 中使用命名空间和自动加载从变量创建对象的主要内容,如果未能解决你的问题,请参考以下文章

PHP 命名空间与自动加载机制

在另一个命名空间中自动加载命名空间时出错

Zend Framework 1.11:如何自动加载使用命名空间的类

PHP中的use命名空间引入类文件自动加载类的理解

浅析PHP类的自动加载和命名空间

php使用命名空间时自动加载机制