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