自定义自动加载与 Composer 的自动加载冲突?

Posted

技术标签:

【中文标题】自定义自动加载与 Composer 的自动加载冲突?【英文标题】:Custom autoload conflicts with Composer's autoload? 【发布时间】:2019-12-05 00:54:01 【问题描述】:

我有一个 php 项目,它加载一个名为 custom_funcs.php 的引导文件,该文件位于 Web 根目录中。这个文件包含一堆函数,定义了几个常量,并且做了以下事情:

    require dirname( __DIR__ ) . '/lib/php/vendor/autoload.php';
    spl_autoload_register( function ($class_name)  include __DIR__ ."/classes/$class_name.php"; );
    set_include_path( get_include_path() . PATH_SEPARATOR . SITEROOT );

第一行加载 Composer 自动加载器。 (请注意,作曲家库位于根 Web 目录之外)。第二行告诉我的代码在哪里寻找无法识别的类,这样我就不必经常手动加载我曾经使用过的类文件。第三行将 Web 根目录添加到 PHP 的 PATH 中。

这几年来一直运行良好,还有几个 Composer 包。

然后...我安装了 PhpUnit。总的来说,我可以正常工作,除非我运行测试:

    如果没有错误,则正常工作。 如果出现错误,我还会收到 PHP 警告:
Warning: include(C:\...path_to_web_root.../classes/SebastianBergmann\Invoker\Invoker.php): failed to open stream: No such file or directory in C:\...path_to_web_root...\common_funcs.php on line 14
Warning: include(): Failed opening 'C:\...path_to_web_root.../classes/SebastianBergmann\Invoker\Invoker.php' for inclusion (include_path='xxxxxxx') in C:\...path_to_web_root...\common_funcs.php on line 14

所以 PhpUnit 正在尝试自动加载一个名为“Invoker”的类,但代码使用的是我自己的由 spl_autoload_register 设置的自动加载路径。

有没有办法解决这个问题?这是 PhpUnit 中的错误吗?

我可以通过在包含行之前使用“@”来隐藏错误,但我会不惜一切代价避免@hiding 错误

注意:我在通过以下方式进行测试之前加载custom_funcs.php

    public static function setUpBeforeClass(): void 
        require 'common_funcs.php';
    

文件结构可能会更清楚:

c:/some_path/
..lib/
....php/
......vendor/
........(third-party Composer libraries)
..webroot/
....custom_funcs.php
....classes/
......(namespace)/
........(my custom classes)

我正在使用命名空间:MyCompany\Portal。所以我的自定义类在<webroot>/classes/MyCompany/Portal/

【问题讨论】:

将您的 custom_func 设为一个类并将其添加到 PSR4 自动加载器。见getcomposer.org/doc/01-basic-usage.md#autoloading 这听起来很像“重写你的输入代码库”。除了一些值得注意的(和最近的)例外,这是一个完全程序化的遗留系统。 custom_funcs.php 没有什么可以构成一个有凝聚力的、明智的 Object 类——它是我在整个站点中使用的一堆可重用函数。 “第二行告诉我的代码在哪里寻找无法识别的类,这样我就不必经常手动加载我曾经使用过的类文件。”这正是作曲家的用途。我不完全理解这里的问题?将您自己的类添加到作曲家自动加载器并删除该行。 如果有办法用 Composer 自动加载器替换我的 spl_autoload_register 调用,我愿意接受这个想法,但要了解 Composer 和自定义类位于两个不同的位置。或者你的意思是我应该把我的自定义类放在 Composer 的供应商文件夹中? 您是否在自己的类文件中使用命名空间?如果是,您可以教作曲家在哪里可以找到这些命名空间。 (请参阅我第一条评论中的链接)他们可以随心所欲地坐在任何地方(只要它们可读)。 【参考方案1】:

最简单的方法是修复你的自动加载器——如果他不能加载类,自动加载器不应该抛出这样的错误。在这种情况下,什么都不做是正确的行为:

spl_autoload_register(function ($class_name) 
    if (file_exists(__DIR__ . "/classes/$class_name.php")) 
        include __DIR__ . "/classes/$class_name.php";
        return true;
    
);

【讨论】:

一种优雅、直接的解决方法,但我选择了能够正常工作的解决方法 @StephenR 这不是一种解决方法,在这种情况下,您应该这样做实现自己的自动加载器。尽管使用 composer 自动加载器可能是更简单和更有效的解决方案,但在某些情况下,您可能需要自定义实现,而您不能使用 composer 中的自动加载器。 好点。我实际上已经实施了这两个建议,所以谢谢。 @rob006 不错!怎么样:if (is_readable(__DIR__ . "/classes/$class_name.php"))【参考方案2】:

您也许可以在composer.json 中用classmap autoloader 替换您的自定义自动加载器功能:


    "autoload": 
        "classmap": ["classes/"],
        "files": ["custom_funcs.php"]
    

此映射是通过扫描给定目录/文件中所有.php.inc 文件中的类而构建的。

每当您创建一个新类时,您可能必须使用composer dump-autoload 更新作曲家的自动加载器才能被拾取。不过,我不确定这是默认情况还是仅在优化自动加载器时。无论如何,这可以通过对新类采用 PSR-0 或 PSR-4 命名约定来解决。

编辑:由于所有文件都将使用作曲家的自动加载器为您自动加载,因此不再需要手动 require 它们。因此,您也可以删除 setupBeforeClass-method

【讨论】:

我试过这个,但我需要知道“类映射”和“文件”路径是相对的。站点根目录? composer.json? phpunit.xml?带有phpunit 可执行文件的目录?无法让它工作,我可以在网上找到的每个示例似乎都假设一个非常特定的目录结构 注意:我无法删除 setupBeforeClass 方法,因为它首先会加载包含我正在测试的函数的文件。但我能够从该文件中删除手动类调用程序 (common_funcs.php)。 路径应该相对于您的composer.json,在大多数情况下与项目根目录(而不是站点根目录)相同。所以可能你必须做webroot/classes 而不是类。您可以执行composer dump-autoload -vvv,这将为您提供它收集的类的详细输出。 dump-autoload 是关键。现在一切似乎都在工作。我能够删除手册spl_autoload_register;虽然我留下了 common_funcs 文件,因为它有我的标准功能。谢谢 请注意,每次重命名或添加新类时都需要运行composer dump-autoload。对于陈旧的代码库,使用 classmap 可能没问题,但如果这是实时的,你应该改用 psr-4

以上是关于自定义自动加载与 Composer 的自动加载冲突?的主要内容,如果未能解决你的问题,请参考以下文章

json composer.json自动加载冲突?

json composer.json自动加载冲突?

PSR-4 自动加载如何在 composer 中为自定义库工作?

自定义包的类不会使用composer autoloader自动加载

自动加载

laravel自定义公共函数