我的命名空间回退和在作曲家中使用 PSR-4 加载器时遇到问题。
我想做的是这样的:
- 有一个可以覆盖/扩展的核心。
- 内核基于接口。
目录结构如下所示:
site/app/View/Example.php
site/src/ACME/app/View/Example.php
site/src/ACME/app/Interface/View.php
我没有设置此配置,所以如果您有更好的建议,那就去做吧。
我的作曲家 json 对于 psr-4 是这样的:
"autoload": {
"psr-4": {
"ACME\App\Site\" : "app/",
"ACME\App\" : "src/AMCE/app/"
}
}
我认为如果找不到站点,这将使 ACME\App\Site\View 回退到 ACME\App\View(注意我还没有完成界面部分......
我的网站/应用程序/视图/示例.php的代码如下所示:
namespace ACMEAppSiteView;
class ViewExample extends View {
当我有网站/应用程序/视图/视图时.php这也有效。这看起来像:
namespace ACMEAppSiteView;
class View extends ACMEAppViewView {
site/src/app/View/View.php如下所示:
namespace ACMEAPPView;
class View {
这个应该使用界面(我还没有尝试过)。
所以我真正想做的是让它我不必拥有site/app/View/View.php,也不必拥有site/app/View/Example.php - 它可以使用site/src/ACME/app/View/Example.php。
抱歉,我是命名空间的新手,所以我可能说得不是很好。
我得到的是我认为ACME\App\Site会回退到ACME\App - 它没有?还是我做错了?目前,它需要所有文件到位。
编辑:事实证明我最初错了,有可能让你的例子使用 PSR-4!您只需要为可以从不同位置加载的命名空间指定一个目录数组。
简单的解决方案
{
"autoload": {
"psr-4": {
"ACME\App\Site\": ["app/", "src/ACME/app"],
"ACME\App\": "src/ACME/app/"
}
}
}
就个人而言,我宁愿更明确地命名我的命名空间,见下文。
原始答案
作曲家 PSR-4 加载程序在尝试加载不存在的文件时不会回退。它只是立即失败。其流程如下所示:
- 未加载
ACMEAppSiteView
- 扫描 PSR-4 条目以查找匹配的命名空间
- 类名与命名空间
ACMEAppSite
匹配(您的第一个 PSR-4 条目)。 - 加载文件
app/View.php
- 文件不存在。错误。
它永远不会返回到步骤 3 并尝试下一个命名空间。
那么我该如何解决呢?
看起来您想将可重用的库代码与站点代码分开。如果是这种情况,我会使用单独的命名空间。例如,使用 ACMESite
命名空间保存可重用代码,并将 ACMEMySiteName
用于特定于站点的代码。这样就不会有歧义,作曲家加载你的类也不会有问题。
但我不想重新排列我的命名空间!
好的,这很好,但是您必须使用黑客来解决您的问题。作曲家有一个classmap
加载器,你必须使用它而不是首选的PSR-4加载器。
{
"autoload": {
"classmap": ["app/", "src/"]
}
}
这些东西分开一点,因为它们现在都混在一起了。
我想做的是这样的:
- 有一个可以覆盖/扩展的核心。
- 内核基于接口。
这听起来像是基本的面向对象继承。接口定义了建议的公共行为,核心实现了所需的基础知识,细节实现更改了一些部分,并重用了其他部分。
让我们用 PHP 使用绝对命名空间名称的方式编写示例代码:
class ACMEAppSiteViewViewExample extends ACMEAppSiteViewView {}
class ACMEAppSiteViewView extends ACMEAppViewView {}
class ACMEAppViewView {}
您有三个显式命名的类。您需要三个与命名空间和类名匹配的文件。自动加载不需要执行任何检测是否存在类 - 因为您不能选择从不存在的类继承,或者省略它。
另一方面,默认实现三个级别的继承很可能太多了。对我来说,它看起来像是糟糕的设计,并且会使维护代码变得比必要的更难。根据您想要实现的目标,有很多替代方案可以更轻松地获得您想要的东西。例如,要更改行为的某些细节,可以使用装饰器模式或策略模式。
所以我真正想做的是让它我不必拥有site/app/View/View.php,也不必拥有site/app/View/Example.php - 它可以使用site/src/ACME/app/View/Example.php。
你不能拥有这个。你的代码明确声明它继承自ACMEAppSiteViewView
,所以这个类必须存在于某个地方。
这与任何自动加载无关。若要进行试验,可以将所有代码添加到一个文件中,然后运行它。这将使 PHP 立即知道所有类,并且问题将变得明显:当其他类同时继承一个类时,您无法删除该类。
抱歉,我是命名空间的新手,所以我可能说得不是很好。
命名空间没什么特别的,如果你使用带有下划线的 PSR-0 样式类名,也会出现同样的问题:
class ACME_App_Site_View_ViewExample extends ACME_App_Site_View_View {}
// This class MUST be present for the above class to work
class ACME_App_Site_View_View extends ACME_App_View_View {}
class ACME_App_View_View {}
命名空间的主要新功能是您可以在带有 use OtherNamespaceClassname
的文件中以第二个名称导入一个类。但这只是此文件范围内的别名(即它不会影响其他文件或全局范围)。
和自动加载不是适合这项工作的工具。命名空间只是确保两个人(或代码的一部分)不会使用相同的名称来表示不同事物的一种方式。自动加载只是一种避免列出要从中加载代码的每个源文件的方法。
当您在另一个类中重写一个类的行为时,它们不是同一个类;通常,您需要继承默认操作并重用其中的一部分。
您可能希望为不同的目的创建多个子类,因此您需要在某个地方保存要使用的逻辑。处理此问题的组件称为"服务定位器",有时称为"DI 容器"。
命名空间允许您将短名称映射到较长的唯一类名;自动加载允许您将特定的唯一类名映射到源文件;服务位置是选择在特定情况下要使用的唯一类的方式。