简体   繁体   English

PSR-4:自动加载器(composer)和扩展名称空间,确保后备php

[英]PSR-4: Autoloader (composer) and extending namespaces ensuring fallback php

I am having a problem with my namespace fallbacks and using PSR-4 loader in Composer. 我的命名空间回退和在Composer中使用PSR-4加载程序时遇到问题。

What I am trying to do is this: 我想做的是这样的:

  1. Have a core which can overwritten / extended. 有一个可以覆盖/扩展的核心。
  2. The core is based off an interface. 核心基于接口。

The directory structure is like so: 目录结构如下:

site/app/View/Example.php
site/src/ACME/app/View/Example.php
site/src/ACME/app/Interface/View.php

I am not set on this configuration so if you have a better suggestion then go for it. 我没有设置此配置,因此,如果您有更好的建议,请继续使用。

My composer json is like so for psr-4: 我的作曲家json对于psr-4来说是这样的:

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

I thought this would make ACME\\App\\Site\\View fallback to ACME\\App\\View if the site one was not found (Note I haven't done the interface part yet...). 我以为如果找不到站点一,这会使ACME \\ App \\ Site \\ View退回到ACME \\ App \\ View(请注意,我还没有完成界面部分...)。

My code for site/app/View/Example.php is like so: 我的site / app / View / Example.php代码如下所示:

namespace ACME\App\Site\View;

class ViewExample extends View {

Which works, when I have site/app/View/View.php as well. 当我也有site / app / View / View.php时,哪个可行。 That looks like: 看起来像:

namespace ACME\App\Site\View;

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

The site/src/app/View/View.php look like this: site / src / app / View / View.php看起来像这样:

namespace ACME\APP\View;

class View {

This one should use the interface (I haven't tried yet). 这个应该使用接口(我还没有尝试过)。

So what I really want to do is make it so I don't have to have site/app/View/View.php, and I don't have to have site/app/View/Example.php - it can use site/src/ACME/app/View/Example.php. 因此,我真正想做的是制作它,因此我不必拥有site / app / View / View.php,也不必拥有site / app / View / Example.php-它可以使用site /src/ACME/app/View/Example.php。

Sorry I'm new to namespaces so I may not of phrased it very well. 抱歉,我是名称空间的新手,所以我可能说得不太好。

What I am getting at is I thought ACME\\App\\Site would fallback to ACME\\App - it doesn't? 我得到的是我认为ACME \\ App \\ Site会回退到ACME \\ App-不是吗? Or I am doing it wrong? 还是我做错了? At the moment it needs all the files in place. 目前,它需要所有文件。

Let's separate the things a bit, because they are all mixed up for now. 让我们稍微分开一些东西,因为现在它们已经混合在一起了。

What I am trying to do is this: 我想做的是这样的:

  1. Have a core which can overwritten / extended. 有一个可以覆盖/扩展的核心。
  2. The core is based off an interface. 核心基于接口。

This sounds like basic object oriented inheritance. 这听起来像基本的面向对象的继承。 An interface defines the proposed public behaviour, the core implements the needed basics, and the detail implementation changes some parts, and reuses the others. 接口定义了建议的公共行为,核心实现了所需的基础,详细实现更改了某些部分,并重用了其他部分。

Let's write your example code in a way PHP sees it with absolute namespace names: 让我们以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 {}

You have three classes explicitly named. 您有三个明确命名的类。 You'd need three files that match the namespace and class name. 您需要三个与名称空间和类名称匹配的文件。 The autoloading does not need to do any detection whether or not a class is present - because you cannot optionally inherit from a class that isn't there, or omit it otherwise. 自动加载不需要检测是否存在类-因为您不能选择从不存在的类继承,否则就可以忽略它。

On the other hand, implementing three levels of inheritance by default very likely is too much. 另一方面,默认情况下实现三个继承级别的可能性很大。 It looks like bad design to me, and will make maintaining the code harder than necessary. 对我来说,这看起来像是糟糕的设计,并且会使维护代码变得不必要。 Depending on what you want to achieve, there are plenty of alternatives to get what you want easier. 根据您要实现的目标,有很多替代方法可以使您更轻松地实现所需的目标。 For example, to change some details of behavior, there are the decorator pattern or the strategy pattern. 例如,要更改某些行为细节,可以使用装饰器模式或策略模式。

So what I really want to do is make it so I don't have to have site/app/View/View.php, and I don't have to have site/app/View/Example.php - it can use site/src/ACME/app/View/Example.php. 因此,我真正想做的是制作它,因此我不必拥有site / app / View / View.php,也不必拥有site / app / View / Example.php-它可以使用site /src/ACME/app/View/Example.php。

You cannot have this. 你不能有这个。 Your code explicitly states that it inherits from \\ACME\\App\\Site\\View\\View , so this class MUST be present somewhere. 您的代码明确声明它继承自\\ACME\\App\\Site\\View\\View ,因此此类必须存在于某个地方。

This is independent of any autoloading. 这与任何自动加载无关。 To experiment, you can add all your code into one single file and then run it. 为了进行实验,您可以将所有代码添加到一个文件中,然后运行它。 This will make all classes known to PHP immediately, and the problem will become obvious: You cannot remove a class when at the same time other classes inherit it. 这将使所有类立即为PHP所了解,并且问题将变得显而易见:您无法在其他类同时继承该类的同时删除该类。

Sorry I'm new to namespaces so I may not of phrased it very well. 抱歉,我是名称空间的新手,所以我可能说得不太好。

Namespaces are nothing really fancy, the same problem would arise if you would use the PSR-0 style classnames with underscores: 命名空间并不是什么花哨的东西,如果您使用带下划线的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 {}

The main new feature with namespaces is that you can import one class under a second name within a file with use OtherNamespace\\Classname . 命名空间的主要新功能是,您可以use OtherNamespace\\Classname在文件中以第二个名称导入一个类。 But this is only an alias within the scope of this file (ie it does not affect other files or the global scope). 但这只是该文件范围内的别名(即,它不影响其他文件或全局范围)。

Edit: Turns out I was originally wrong, it is possible to get your example working with PSR-4! 编辑:原来我原来错了,有可能让您的示例在PSR-4上工作! You just need to specify an array of directories for namespaces that can be loaded from different places. 您只需为可从不同位置加载的名称空间指定目录数组。

Easy solution 简单的解决方案

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

Personally, I would rather name my namespaces more explicitly, see below. 就个人而言,我宁愿更明确地命名我的命名空间,请参见下文。

Original Answer 原始答案

The composer PSR-4 loader does not fall back when trying to load files that do not exist. 尝试加载不存在的文件时,作曲家PSR-4加载器不会后退。 It just fails immediately. 它只是立即失败。 Its flow looks like: 其流程如下所示:

  1. \\ACME\\App\\Site\\View is not loaded \\ACME\\App\\Site\\View未加载
  2. Scan PSR-4 entries for matching namespaces 扫描PSR-4条目以查找匹配的名称空间
  3. Class name matches the namespace \\ACME\\App\\Site (your first PSR-4 entry). 类名称与名称空间\\ACME\\App\\Site (您的第一个PSR-4条目)匹配。
  4. Load file app/View.php 加载文件app/View.php
  5. File does not exist. 文件不存在。 Error. 错误。

It never goes back to step 3 and tries the next namespace. 它永远不会返回到步骤3并尝试下一个名称空间。

So how do I fix it? 那么我该如何解决呢?

It looks like you want to separate your reusable library code from your site code. 看来您想将可重用的库代码与站点代码分开。 If that's the case, I would use separate namespaces. 如果是这样,我将使用单独的名称空间。 For example, use the ACME\\Site namespace to hold your reusable code, and use ACME\\MySiteName for your site-specific code. 例如,使用ACME\\Site命名空间保存您的可重用代码,并使用ACME\\MySiteName作为站点特定的代码。 Then there will be no ambiguity, and composer will have no trouble loading your classes. 这样就不会有歧义,作曲家也不会在加载类时遇到麻烦。

But I don't want to rearrange my namespaces! 但是我不想重新排列我的名称空间!

Ok, that's fine, but you'll have to use a hack to get around your problem. 好的,那很好,但是您必须使用黑客解决您的问题。 Composer has a classmap loader, and you'll have to use that instead of the preferred PSR-4 loader. Composer有一个classmap加载程序,您必须使用它而不是首选的PSR-4加载程序。

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

Namespaces and autoloading are not the right tool for this job. 命名空间和自动加载不是完成此任务的正确工具。 A namespace is just a way of making sure two people (or parts of your code) don't use the same name to mean different things. 名称空间只是一种确保两个人(或代码的一部分)不使用相同名称来表示不同含义的一种方法。 Autoloading is just a way to avoid having to list every source file you want to load code from. 自动加载只是避免列出要加载代码的每个源文件的一种方法。

When you override the behaviour of one class in another, these are not the same class; 当您在另一个类中覆盖一个类的行为时,它们就不是同一类;它们是同一类。 often, you'll want to inherit the default actions and reuse parts of them. 通常,您需要继承默认操作并重用其中的一部分。

You might want to create several sub-classes for different purposes, so you need somewhere to hold the logic of which to use. 您可能需要创建多个子类以实现不同的用途,因此需要在某个地方保存要使用的逻辑。 The component which deals with this is called a "service locator" or sometimes a "DI container". 处理该问题的组件称为“服务定位器”,有时也称为“ DI容器”。

Namespaces let you map short names to longer, unique class names; 命名空间使您可以将短名称映射到更长的唯一类名称。 autoloading let's you map a specific unique class name to a source file; 自动加载让您将特定的唯一类名称映射到源文件; service location is how you choose which unique class you want to use in a specific circumstance. 服务位置是您在特定情况下选择要使用的唯一类的方式。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM