首页 文章

重新启动作曲家自动加载

提问于
浏览
0

通过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

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

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

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

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

    没有办法在PHP中卸载一个类 . 一旦声明,就无法更改(不考虑“runkit”扩展) .

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

相关问题