重新启动作曲家自动加载

时间:2013-12-03 09:50:15

标签: php symfony composer-php autoload

通过Composer更新后,我想初始化应用程序并发送事件

"scripts": {
    "post-update-cmd": [
        "Acme\\Bundle\\DemoBundle\\Composer\\ScriptHandler::notify"

处理程序

public static function notify(CommandEvent $event)
{
    // init app
    require __DIR__.'/../../../../../../app/autoload.php';
    require __DIR__.'/../../../../../../app/AppKernel.php';
    $kernel = new \AppKernel('dev', true);
    $kernel->boot();

    // send event
    $dispatcher = $kernel->getContainer()->get('event_dispatcher');
    $dispatcher->dispatch('acme.installed', new Event())
}

如果通过composer.phar运行更新,那么一切正常。

但我需要从应用程序运行更新。我将composer添加到需求中并调用bin\composer update

在这种情况下,自动加载器存在冲突。 Composer从应用程序连接自动加载器,更改它,并且不再连接它。

需要销毁旧版并创建新的自动加载器。我发现可以通过$GLOBALS['loader']访问旧的自动加载器。

我做出了这个决定

public static function notify(CommandEvent $event)
{
    // init loader
    require __DIR__.'/../../../../../../vendor/composer/autoload_real.php';
    $GLOBALS['loader']->unregister();
    $GLOBALS['loader'] = require __DIR__.'/../../../../../../app/autoload.php';
    // init app
    require_once __DIR__.'/../../../../../../app/AppKernel.php';
    // ...

但是此选项不起作用,因为通过正常require的文件广播Composer进行自动加载会导致连接冲突。

例如:

"name": "kriswallsmith/assetic",
"autoload": {
    "files": [ "src/functions.php" ]
},

翻译为

require $vendorDir . '/kriswallsmith/assetic/src/functions.php';

抛出错误

PHP Fatal error:  Cannot redeclare assetic_init() (previously declared in /vendor/kriswallsmith/assetic/src/functions.php:20) in /vendor/kriswallsmith/assetic/src/functions.php on line 26

我创建自动加载器并复制/vendor/composer/autoload_real.php/app/autoload.php的代码。来自 Seldaek #2474

的推荐
public static function notify(CommandEvent $event)
{
    if (isset($GLOBALS['loader']) && $GLOBALS['loader'] instanceof ClassLoader) {
        $GLOBALS['loader']->unregister();
    }
    $GLOBALS['loader'] = $this->getClassLoader();

    require_once __DIR__.'/../../../../../../app/AppKernel.php';
    $kernel = new \AppKernel('dev', true);
    $kernel->boot();

    // send event
    $dispatcher = $kernel->getContainer()->get('event_dispatcher');
    $dispatcher->dispatch('acme.installed', new Event())
}

protected function getClassLoader()
{
    $loader = new ClassLoader();
    $vendorDir = __DIR__.'/../../../../../../vendor';
    $baseDir = dirname($vendorDir);

    $map = require $vendorDir . '/composer/autoload_namespaces.php';
    foreach ($map as $namespace => $path) {
        $loader->set($namespace, $path);
    }

    $classMap = require $vendorDir . '/composer/autoload_classmap.php';
    if ($classMap) {
        $loader->addClassMap($classMap);
    }

    $loader->register(true);

    $includeFiles = require $vendorDir . '/composer/autoload_files.php';
    foreach ($includeFiles as $file) {
        require_once $file;
    }

    // intl
    if (!function_exists('intl_get_error_code')) {
        require_once $vendorDir.'/symfony/symfony/src/Symfony/Component/Locale/Resources/stubs/functions.php';
        $loader->add('', $vendorDir.'/symfony/symfony/src/Symfony/Component/Locale/Resources/stubs');
    }

    AnnotationRegistry::registerLoader(array($loader, 'loadClass'));

    return $loader;
}

1 个答案:

答案 0 :(得分:0)

我认为我对你将要做的事感到有些害怕:你想在这些组件被积极使用的同时更新正在运行的应用程序的组件。您希望任何更新都能很好地运行,以便在更新后应用程序将继续工作 - 尤其是将继续能够进行进一步的更新。

我不认为这是一个有效的假设!我已经使用Composer一段时间了,我已经看到了很多原因,为什么它没有更新我的依赖项的某些部分,大部分时间都是由于某些网络故障。想想使用来自Github的东西,然后Github失败了。

那会怎么样?您可能下载了一些部件,无法下载更多部件,并且自动加载器未更新。所以更新的部分现在需要一些新的,也可以下载,但不能自动加载,因为之后的某个组件无法下载。这个组件对于重复更新至关重要!你刚刚破坏了你的应用程序,你无法解决它。

如果自动加载器部分加载旧类,然后更新,然后加载新类,使用已经加载的旧类更改不兼容的新版本,我也可以考虑发生非常奇怪的效果。因此,假设您可以在一个请求的运行时期间更改应用程序的组件,这似乎很奇怪。

无法在PHP中卸载类。一旦声明,就无法更改(不考虑“runkit”扩展名)。

如果您确实希望获得应用程序的更新,我认为最好复制所有内容,更新应用程序的非活动副本,然后检查更新是否成功,然后将其复制回来,即使用符号链接指向当前版本和下一版本并切换它们。