PSR-4:Autoloader(composer)和扩展名称空间,确保可回退php

PSR-4:Autoloader(composer)和扩展名称空间,确保可回退php,php,namespaces,composer-php,psr-4,Php,Namespaces,Composer Php,Psr 4,我的命名空间回退和在Composer中使用PSR-4加载程序时遇到问题 我想做的是: 具有可覆盖/扩展的核心 核心基于接口 目录结构如下所示: site/app/View/Example.php site/src/ACME/app/View/Example.php site/src/ACME/app/Interface/View.php namespace ACME\App\Site\View; class ViewExample extends View { 我没有设置此配置,因此如果您

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

我想做的是:

  • 具有可覆盖/扩展的核心
  • 核心基于接口
  • 目录结构如下所示:

    site/app/View/Example.php
    site/src/ACME/app/View/Example.php
    site/src/ACME/app/Interface/View.php
    
    namespace ACME\App\Site\View;
    
    class ViewExample extends View {
    
    我没有设置此配置,因此如果您有更好的建议,请尝试

    我的composer 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代码如下:

    site/app/View/Example.php
    site/src/ACME/app/View/Example.php
    site/src/ACME/app/Interface/View.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 {
    
    站点/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,但事实并非如此?还是我做错了?目前,它需要所有文件到位。

    编辑:原来我错了,可以让您的示例使用PSR-4!您只需要为可以从不同位置加载的名称空间指定一个目录数组

    简易解决方案

    {
        "autoload": {
            "psr-4": {
                "ACME\\App\\Site\\": ["app/", "src/ACME/app"],
                "ACME\\App\\": "src/ACME/app/"
            }
        }
    }
    
    就我个人而言,我宁愿更明确地命名我的名称空间,见下文

    原始答案

    当尝试加载不存在的文件时,composer PSR-4加载程序不会后退。它只是立即失效。其流程如下所示:

    namespace ACME\App\Site\View;
    
    class View extends \ACME\App\View\View {
    
  • 未加载
    \ACME\App\Site\View
  • 扫描PSR-4条目以查找匹配的名称空间
  • 类名与命名空间
    \ACME\App\Site
    (您的第一个PSR-4条目)匹配
  • 加载文件
    app/View.php
  • 文件不存在。错误
  • 它从不返回步骤3并尝试下一个名称空间

    那么如何修复它呢?

    看起来您希望将可重用库代码与站点代码分开。如果是这样,我将使用单独的名称空间。例如,使用
    ACME\Site
    名称空间保存可重用代码,并使用
    ACME\MySiteName
    保存特定于站点的代码。这样就不会有歧义,而且composer在加载您的类时也不会有问题

    但我不想重新安排我的名称空间

    好的,那很好,但是你必须用黑客来解决你的问题。Composer有一个
    classmap
    加载程序,您必须使用它而不是首选的PSR-4加载程序

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

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

    当您在另一个类中重写一个类的行为时,这些类不是同一个类;通常,您希望继承默认操作并重用其中的一部分

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


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

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

    我想做的是:

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

    让我们以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 {}
    
    您有三个显式命名的类。您需要三个与名称空间和类名匹配的文件。自动加载不需要检测是否存在类,因为您不能选择从不存在的类继承,或者忽略它

    另一方面,在默认情况下实现三个级别的继承很可能太多了。在我看来,这是一个糟糕的设计,会使维护代码变得比必要的更困难。根据你想要实现的目标,有很多选择可以让你更容易地实现你想要的。例如,要更改行为的某些细节,可以使用decorator模式或strategy模式

    所以我真正想做的是使它不必有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 {}
    
    名称空间的主要新特性是可以在seco下导入一个类