【问题标题】:PSR-4: Autoloader (composer) and extending namespaces ensuring fallback phpPSR-4:自动加载器(composer)和扩展命名空间确保回退php
【发布时间】:2016-05-30 21:51:03
【问题描述】:

我的命名空间回退和在 Composer 中使用 PSR-4 加载程序时遇到问题。

我想要做的是:

  1. 有一个可以覆盖/扩展的核心。
  2. 核心基于接口。

目录结构是这样的:

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 (注意我还没有完成界面部分......)。

我的 site/app/View/Example.php 代码如下:

namespace ACME\App\Site\View;

class ViewExample extends View {

当我也有 site/app/View/View.php 时,哪个有效。看起来像:

namespace ACME\App\Site\View;

class View extends \ACME\App\View\View {

site/src/app/View/View.php 如下所示:

namespace ACME\APP\View;

class View {

这个应该用接口(我还没试过)。

所以我真正想做的就是做到这一点,这样我就不必拥有 site/app/View/View.php,也不必拥有 site/app/View/Example.php - 它可以使用site/src/ACME/app/View/Example.php。

对不起,我是命名空间的新手,所以我可能不会很好地表达它。

我的意思是我认为 ACME\App\Site 会回退到 ACME\App - 它不会?还是我做错了?目前它需要所有文件。

【问题讨论】:

    标签: php namespaces composer-php psr-4


    【解决方案1】:

    编辑:原来我错了,可以让你的例子与 PSR-4 一起工作!您只需要为可以从不同位置加载的命名空间指定一个目录数组。

    简单的解决方案

    {
        "autoload": {
            "psr-4": {
                "ACME\\App\\Site\\": ["app/", "src/ACME/app"],
                "ACME\\App\\": "src/ACME/app/"
            }
        }
    }
    

    就个人而言,我宁愿更明确地命名我的命名空间,见下文。

    原答案

    composer PSR-4 加载器在尝试加载不存在的文件时不会后退。它只是立即失败。它的流程是这样的:

    1. \ACME\App\Site\View 未加载
    2. 扫描 PSR-4 条目以查找匹配的命名空间
    3. 类名匹配命名空间\ACME\App\Site(您的第一个 PSR-4 条目)。
    4. 加载文件app/View.php
    5. 文件不存在。错误。

    它永远不会回到第 3 步并尝试下一个命名空间。

    那么我该如何解决呢?

    您似乎想将可重用的库代码与站点代码分开。如果是这种情况,我会使用单独的命名空间。例如,使用ACME\Site 命名空间来保存您的可重用代码,并使用ACME\MySiteName 来保存您的站点特定代码。这样就不会有歧义了,composer 加载你的类也不会有问题。

    但我不想重新排列我的命名空间!

    好的,这很好,但您必须使用 hack 来解决您的问题。 Composer 有一个 classmap 加载器,您必须使用它而不是首选的 PSR-4 加载器。

    {
        "autoload": {
            "classmap": ["app/", "src/"]
        }
    }
    

    【讨论】:

    • 好的,谢谢 - 我以为我做错了什么。我在他们谈到后备的 PHP 网站上阅读了命名空间文档,因此假设作曲家会有后备。我想这不是大多数人想要的。我正在从我自己的自动加载器转移到这个,这样就失去了意义,所以黑客攻击似乎有点傻。
    • 对不起!我错了。我不能让这个休息,不得不尝试一下。我实际上找到了一个使用 PSR-4 的简单解决方案。现在你可以选择了。
    • 好的,我明白了。现在应该做我想做的事,但我认为它不会完全正确,因此可能会使用两个命名空间:) 至少我知道可以做到。
    • 您确定您的提案能够运行具有继承自不存在的类的类的代码吗?
    【解决方案2】:

    命名空间和自动加载不是这项工作的正确工具。命名空间只是一种确保两个人(或代码的一部分)不会使用相同名称来表示不同事物的方法。自动加载只是一种避免列出要从中加载代码的每个源文件的方法。

    当你在另一个类中重写一个类的行为时,它们就不是同一个类了;通常,您会希望继承默认操作并重用其中的一部分。

    您可能希望为不同的目的创建多个子类,因此您需要在某个地方保存要使用的逻辑。处理这个问题的组件称为“服务定位器”,有时也称为“DI 容器”。

    命名空间让您可以将短名称映射到更长的、唯一的类名;自动加载让您将特定的唯一类名映射到源文件;服务位置是您在特定情况下选择要使用的独特类的方式。

    【讨论】:

    • 我只能强调这个答案。使用自动加载在不同目录中搜索一个类是错误的方法。
    【解决方案3】:

    让我们稍微分开一下,因为现在它们都混在一起了。

    我想要做的是:

    1. 有一个可以覆盖/扩展的核心。
    2. 核心基于接口。

    这听起来像是基本的面向对象继承。一个接口定义了提议的公共行为,核心实现了所需的基础,细节实现改变了一些部分,并重用了其他部分。

    让我们以 PHP 使用绝对命名空间名称的方式编写示例代码:

    class \ACME\App\Site\View\ViewExample extends \ACME\App\Site\View\View {}
    
    class \ACME\App\Site\View\View extends \ACME\App\View\View {}
    
    class \ACME\App\View\View {}
    

    您有三个明确命名的类。您需要三个与命名空间和类名匹配的文件。自动加载不需要对是否存在类进行任何检测 - 因为您不能选择性地从不存在的类继承,或者以其他方式忽略它。

    另一方面,默认实现三级继承很可能是太多了。对我来说,这看起来像是糟糕的设计,并且会使维护代码变得比必要的更难。根据您想要实现的目标,有很多替代方案可以让您更轻松地获得想要的目标。比如改变一些行为的细节,有装饰器模式或者策略模式。

    所以我真正想做的就是做到这一点,这样我就不必拥有 site/app/View/View.php,也不必拥有 site/app/View/Example.php - 它可以使用site/src/ACME/app/View/Example.php。

    你不能拥有这个。您的代码明确声明它继承自 \ACME\App\Site\View\View,因此此类必须存在于某处。

    这与任何自动加载无关。为了进行实验,您可以将所有代码添加到一个文件中,然后运行它。这将使所有类立即为 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 OtherNamespace\Classname 的文件中以第二个名称导入一个类。但这只是该文件范围内的别名(即不会影响其他文件或全局范围)。

    【讨论】:

    • 好的,这是有道理的,我来自使用设置包含路径,所以在应用程序站点上它将包含 SITE/app 并且如果文件存在并且具有相同的类名,它将加载到接下来包括路径 CORE/app。这意味着我想在本地替换的文件可以。我可能想多了,强迫自己使用命名空间——因为作曲家有自己的自动加载器,我想我应该试试。
    猜你喜欢
    • 2016-01-22
    • 2014-09-24
    • 2015-08-23
    • 2014-03-17
    • 2013-11-30
    • 2014-07-25
    • 2019-03-09
    • 2016-05-18
    • 1970-01-01
    相关资源
    最近更新 更多