【问题标题】:Custom Composer Autoloader自定义 Composer 自动加载器
【发布时间】:2013-01-16 03:39:26
【问题描述】:

是否可以在 Composer 中使用自定义自动加载器?

我有一些包含数百个类的库,它们按照 PSR-0 排列在目录结构中。

但是它们也包含一个静态构造器(类似于 Java 的静态初始化器)。

我在 composer 之前使用的自动加载器:

if (method_exists($class, '__static')) {
    call_user_func(array($class, '__static'));
}

那么有没有办法扩展 Composers 默认的自动加载器来做到这一点?

【问题讨论】:

  • 虽然@Jon 似乎在这里准备了一个相当完整的答案,但我仍然想强调,像这样的自定义实例化听起来有点错误。如果你真的想保留它,把 Class::__static();在类的文件中可能是一种更简单的方法来实现这一点,而不必破解自动加载器。我认为依靠自定义自动加载对于可移植性和健全性来说并不是一个好主意。新开发人员查看代码不会明白发生了什么。
  • @Seldaek 我不“想要”保留它。我只是没有预算批准花几天时间重构数百个课程。
  • 很公平。想要不是正确的术语,但我仍然认为我的观点是如果你必须保留它:)

标签: php static constructor composer-php autoloader


【解决方案1】:

黑客作曲家

您可以通过修改 ClassLoader.php(在供应商目录中找到)来破解 Composer 的自动加载器以支持此功能:

public function loadClass($class)
{
    if ($file = $this->findFile($class)) {
        include $file;

        // Added your custom code:
        if (method_exists($class, '__static')) {
            call_user_func(array($class, '__static'));
        }

        return true;
    }
}

但是,每次运行composer update时都会更新这个文件(源代码包含在composer.phar中),所以修改一次并不好;您需要一种使这种修改“永久”的方法。

我能想到的方法有两种:

修改 Composer 本身

这个解决方案涉及修改composer.phar,意思是:

  • 一旦执行修改,黑客将始终有效,无需任何额外设置
  • 运行 composer self-update 将用最新的官方版本覆盖修改后的 Composer 源,撤消 hack

我编写了一个简短的 PHP 脚本,它在 Composer 源上执行有针对性的查找/替换;它只会修改它所针对的确切代码,这意味着您每次都需要在未修改的 Composer 版本上运行它(它会拒绝触及已经修改的版本):

<?php

if (!Phar::canWrite()) {
    die ('The config setting phar.readonly must be set to 0 in php.ini');
}

$phar = new Phar('composer.phar');
$fileName = 'src/Composer/Autoload/ClassLoader.php';

try {
    $source = file_get_contents($phar[$fileName]);
}
catch (BadMethodCallException $e) {
    echo $e->getMessage();
    die;
}

$find = <<<'END_ORIGINAL'
    public function loadClass($class)
    {
        if ($file = $this->findFile($class)) {
            include $file;

            return true;
        }
    }
END_ORIGINAL;

$replaceWith = <<< 'END_REPLACEMENT'
    public function loadClass($class)
    {
        if ($file = $this->findFile($class)) {
            include $file;

            // Add your custom code here!
            return true;
        }
    }
END_REPLACEMENT;

$find = preg_replace('/\s+/', '\\s+', preg_quote($find));
$modified = preg_replace('/'.$find.'/', $replaceWith, $source);

if ($source == $modified) {
    echo 'Could not find replacement target, aborting';
    die;
}

file_put_contents($phar[$fileName], $modified);
echo 'Replacement done, file saved';

使用更新后脚本

Composer 允许您将scripts 附加到您的根包;修改 ClassLoader.php 的更新后脚本是一种在保持 Composer 源完整的同时使更改持续更新的好方法。

修改上一节中的代码来执行此操作非常简单。理想情况下,脚本应该是一个静态 PHP 类方法,但为了做到这一点,您必须创建一个新的 Composer 包,该包使用 Composer 的 PSR-0 自动加载器自动加载,因为该类必须自动加载,目前它是not possible 在 Composer 更新过程中使用类映射自动加载类。

【讨论】:

  • 感谢您的信息。我确实注意到,如果我修改了自动加载器,每次更新时它都会被吹走。虽然您的选择似乎可行,但我可能会将修改后的自动加载器提交到项目存储库,因此每次运行更新时,我都可以将其检出。这也将有助于确定是否对自动加载器核心进行了任何更改。
【解决方案2】:

无论文件如何加载(自动加载、显式要求或包含),一个更安全的替代方法是在类定义之后简单地调用静态初始化器:

class Foo {
    private static $__constructed = false;

    public static function __static() {
        if (self::$__constructed) return;

        // do initialization
        self::$__constructed = true;
    }
}

Foo::__static();

【讨论】:

    【解决方案3】:

    我最终做的是:

    • 修改vendor/composer/ClassLoader.php
    • 提交修改后的类加载到项目中(但忽略供应商目录的其余部分)
    • git checkout vendor/composer/ClassLoader.phpcomposer update 之后

    【讨论】:

      猜你喜欢
      • 2016-04-17
      • 2019-12-05
      • 2019-11-06
      • 2014-10-11
      • 2020-07-22
      • 2013-01-13
      • 1970-01-01
      • 2012-08-28
      • 1970-01-01
      相关资源
      最近更新 更多