简体   繁体   English

重新初始化作曲家自动加载

[英]Reinit composer autoload

After updating via the Composer i want to initialize the application and send it events 通过Composer更新后,我想初始化应用程序并发送事件

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

handler 处理程序

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())
}

If run the update through the composer.phar then everything works fine. 如果通过composer.phar运行更新,则一切正常。

But I need to run the update from the application. 但是我需要从应用程序运行更新。 I add composer to requirements and call bin\\composer update . 我将composer添加到需求中,并调用bin\\composer update

In this case there is a conflict of autoloader. 在这种情况下,自动装带器会发生冲突。 Composer connects the autoloader from the application, change it, and does not connect it again. Composer从应用程序连接自动加载器,对其进行更改,然后不再连接。

Need to destroy the old and create a new autoloader. 需要销毁旧的并创建新的自动装带器。 I found out that the old autoloader can be accessed via $GLOBALS['loader'] . 我发现可以通过$GLOBALS['loader']访问旧的自动$GLOBALS['loader']

I came to this decision 我做出了这个决定

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';
    // ...

But this option does not work because autoloading via file broadcast Composer in normal require and leads to connection conflict. 但是,此选项不起作用,因为通过文件广播作曲自动加载正常require ,导致连接冲突。

For example: 例如:

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

translate to 翻译成

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

throw error 抛出错误

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

I create autoloader and duplicate the code of /vendor/composer/autoload_real.php and /app/autoload.php . 我创建了自动加载器,并复制了/vendor/composer/autoload_real.php/app/autoload.php的代码。 Recommendation from Seldaek #2474 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;
}

I think I am a bit scared about what you are about to do: You want to update the components of a running application while these components are actively used. 我想我对要做什么感到有些害怕:您想在积极使用正在运行的应用程序的组件时对其进行更新。 You hope that any update will run perfectly well so that after the update the application will continue to work - especially will continue to be able to do further updates. 您希望任何更新都能完美运行,以便更新后应用程序将继续运行,尤其是将继续进行进一步的更新。

I don't think this is a valid assumption! 我认为这不是一个正确的假设! I have been using Composer for a while, and I have seen plenty of reasons why it did not update some parts of my dependencies, which most of the time were due to some network failure. 我使用Composer已有一段时间了,并且我已经看到很多原因,为什么它没有更新我的依赖关系的某些部分,而大多数情况下这是由于某些网络故障所致。 Just think of using something from Github, and then Github is down. 只要考虑使用Github上的东西,然后Github就会崩溃。

What would happen then? 那会发生什么呢? You were probably able to download some parts, unable to download some more, and the autoloader was not updated. 您可能能够下载某些部件,而无法下载其他部件,并且自动装带器未更新。 So the updated part now requires something new that got also downloaded, but cannot be autoloaded because some component after that failed to download. 因此,更新后的部件现在需要一些也已下载的新内容,但无法自动加载,因为此后的某些组件无法下载。 And this component is essential to repeat the update! 而且此组件对于重复更新至关重要! You just broke your application, and you cannot fix it. 您刚刚中断了应用程序,无法修复它。

I can also think about very strange effects happening if the autoloader is partially loading old classes, then gets updated, and after that loads new classes that use new versions of the already loaded old classes that changed incompatible. 我还可以考虑一下,如果自动加载器部分加载旧类,然后进行更新,然后加载使用已加载的旧类的新版本的新版本,这些新版本发生了不兼容的变化,那么会发生非常奇怪的影响。 So the assumption that you can change the components of the application during the runtime of one request seems to be very odd. 因此,您可以在一个请求的运行期间更改应用程序组件的假设似乎很奇怪。

There is no way to unload a class in PHP. 无法卸载PHP中的类。 Once it is declared, it cannot be changed (not taking the "runkit" extension into account). 一旦声明,就无法更改(不考虑“ runkit”扩展名)。

If you indeed want to get an update of your application, I think it is a better idea to duplicate everything, update the non-active copy of the application, then check if the update was successful, and after that copy it back, ie use symlinks to point to the current and next version and switch these. 如果您确实想要更新应用程序,我认为最好复制所有内容,更新应用程序的非活动副本,然后检查更新是否成功,然后将其复制回去,即使用符号链接指向当前和下一个版本并进行切换。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM