2

无论如何都可以在 Composer 中使用自定义自动加载器吗?

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

然而,它们也包括一个静态构造器(类似于 Java 的静态初始化器)。

我在作曲家之前使用的自动加载器会:

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

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

4

3 回答 3

5

黑客作曲家

您可以通过修改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,这意味着:

  • 修改完成后,hack 将始终有效,无需任何额外设置
  • 运行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 允许您将脚本附加到您的根包;一个修改后的更新脚本ClassLoader.php是一个很好的方法,可以使更改在更新中保持不变,同时保持 Composer 的源完整。

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

于 2013-01-16T09:35:31.167 回答
1

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

class Foo {
    private static $__constructed = false;

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

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

Foo::__static();
于 2015-04-23T16:20:53.203 回答
0

我最终做的是:

  • 修改vendor/composer/ClassLoader.php
  • 提交修改后的类加载到项目(但忽略供应商目录的其余部分)
  • git checkout vendor/composer/ClassLoader.phpcomposer update
于 2013-01-16T23:16:20.267 回答