覆盖随 Composer 一起安装的库中的类的策略


Strategy to override a class in a library installed with Composer

我正在使用Codeigniter和Composer。其中一个要求是PHPExcel。现在我需要更改其中一个类中的函数。最好的策略应该是什么?我应该更改供应商文件夹中的代码吗?如果是这样,如何在所有实例中维护更改?如果不是,我该如何覆盖该特定类。虽然我提到PHPExcel,但我想要一个通用的解决方案。

我不确定这是否是这个问题的正确论坛。如果没有,我将删除它。如果需要更多详细信息,请告诉我。

谢谢。

在 composer.json 中,在 ["autoload"]["psr-4"] 下,添加一个条目,其中命名空间作为键,路径作为值:

{
     "autoload": {
         "psr-4": {
             "BuggyVendor''Namespace''": "myfixes/BuggyVendor/Namespace"
         }
     }
}

在该路径下复制要覆盖的文件(保留子命名空间目录结构)并在那里编辑它们。将优先选择它们,而不是库包的原始"类路径"。似乎以这种方式添加到 composer.json 的命名空间>路径映射在所需包添加的映射之前被考虑。注意:我刚刚尝试过并且它起作用了,尽管我不知道它是否是预期的功能或可能的陷阱是什么。

编辑:发现了一个陷阱。有时,当您随后需要另一个带有composer require vendor/package的软件包时,您将"丢失"覆盖。如果发生这种情况,您必须手动发出composer dump-autoload。这将恢复正确的自动加载顺序,以遵循您的覆盖。

将最后 2 行添加到composer.jsonautoload 部分是我想只覆盖 vendors 目录中的一个文件时对我有用的:

"autoload": {        
    "classmap": [
        "database"
    ],
    "psr-4": {
        "App''": "app/"
    },
    "exclude-from-classmap": ["vendor/somepackagehere/blah/Something.php"],
    "files": ["app/Overrides/Something.php"]
},

请记住,app/Overrides/Something.php 中的命名空间需要与原始命名空间vendor/somepackagehere/blah/Something.php 中的任何内容匹配。

请记住在编辑composer.json后运行composer dump-autoload

文档:https://getcomposer.org/doc/04-schema.md#files

还有一个

选择。如果您需要重写唯一的类,则可以像这样在composer.json中使用文件

 "autoload": {
     "files": ["path/to/rewritten/Class.php"]
  }

所以如果你想重写类Some'Namespace'MyClass这样说

#path/to/rewritten/Class.php
namespace Some'Namespace;
class MyClass {
  #do whatever you want here
}

根据每个请求,作曲家会将该文件加载到内存中,因此在使用Some'Namespace'MyClass时 - 将使用path/to/rewritten/Class.php的实现。

您也可以简单地复制文件并用自己的文件覆盖原始文件。假设创建一个目录,例如"供应商覆盖",你在其中放置你的固定文件,只需将其添加到你的作曲家:

"scripts": {
    "post-install-cmd": [
      "@php -r '"copy('vendor-overrides/path/to/your/fixed/file.php', 'vendor/path/to/your/broken/file.php');'""
    ],
    "post-update-cmd": [
"@php -r '"copy('vendor-overrides/path/to/your/fixed/file.php', 'vendor/path/to/your/broken/file.php');'""
    ]
  }

更改现有类违反了 OOP 和 SOLID 原则(对扩展开放/对修改原则关闭)。所以这里的解决方案不是直接更改代码,而是扩展代码以添加您的功能。

在理想的世界中,你永远不应该更改一段你不拥有的代码。事实上,使用 composer 您不能,因为在更新依赖项时您的更改将被覆盖。

在您的情况下,解决方案是在应用程序级别创建一个类,并扩展要更改的类(在库级别)以覆盖您的代码。如果您不知道如何扩展 PHP 中的类,请查看

然后,通常,您加载您的类而不是它们的类,这样,您将功能添加到其功能之上,并且在更新的情况下,不会中断任何内容(在非中断性更新的情况下)。