通过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添加到需求中,并调用bincomposer 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;
}
我想我有点害怕你要做的事情:你想在活跃使用这些组件的同时更新正在运行的应用程序的组件。您希望任何更新都能运行良好,以便在更新后应用程序能够继续工作,尤其是能够继续进行进一步的更新。
我认为这不是一个有效的假设!我使用Composer已经有一段时间了,我已经看到了很多原因,为什么它没有更新我的依赖项的某些部分,大多数时候是由于一些网络故障。只需考虑使用Github中的一些东西,然后Github就坏了。
那会发生什么?您可能能够下载一些部件,但无法下载更多部件,并且自动加载器没有更新。因此,更新后的部分现在需要一些新的内容,这些内容也已下载,但由于之后的某些组件无法下载,因此无法自动加载。这个组件对于重复更新是至关重要的!你刚刚破坏了你的应用程序,你无法修复它。
我还可以想象,如果自动加载器部分加载旧类,然后进行更新,然后加载新类,这些新类使用已加载的、不兼容的旧类的新版本,会产生非常奇怪的效果。因此,假设您可以在一个请求的运行时更改应用程序的组件似乎非常奇怪。
PHP中没有卸载类的方法。一旦声明了它,就不能更改它(不考虑"runkit"扩展)。
如果你确实想更新你的应用程序,我认为最好复制所有内容,更新应用程序的非活动副本,然后检查更新是否成功,然后将其复制回来,即使用符号链接指向当前和下一个版本并切换它们。