Commit 40787e18 authored by totten's avatar totten

CRM-13244 - Civi\Core\Container - Allow hooks to modify container. Cache it.

Conflicts:
	composer.json
parent 1a574627
......@@ -1894,6 +1894,31 @@ abstract class CRM_Utils_Hook {
);
}
/**
* Modify the CiviCRM container - add new services, parameters, extensions, etc.
*
* @code
* use Symfony\Component\Config\Resource\FileResource;
* use Symfony\Component\DependencyInjection\Definition;
*
* function mymodule_civicrm_container($container) {
* $container->addResource(new FileResource(__FILE__));
* $container->setDefinition('mysvc', new Definition('My\Class', array()));
* }
* @endcode
*
* Tip: The container configuration will be compiled/cached. The default cache
* behavior is aggressive. When you first implement the hook, be sure to
* flush the cache. Additionally, you should relax caching during development.
* In `civicrm.settings.php`, set define('CIVICRM_CONTAINER_CACHE', 'auto').
*
* @param \Symfony\Component\DependencyInjection\ContainerBuilder $container
* @see http://symfony.com/doc/current/components/dependency_injection/index.html
*/
public static function container(\Symfony\Component\DependencyInjection\ContainerBuilder $container) {
self::singleton()->invoke(1, $container, self::$_nullObject, self::$_nullObject, self::$_nullObject, self::$_nullObject, self::$_nullObject, 'civicrm_container');
}
/**
* @param array <CRM_Core_FileSearchInterface> $fileSearches
* @return mixed
......
......@@ -1771,7 +1771,7 @@ class CRM_Utils_System {
* @return bool
*/
public static function isInUpgradeMode() {
$args = explode('/', $_GET['q']);
$args = explode('/', CRM_Utils_Array::value('q', $_GET));
$upgradeInProcess = CRM_Core_Session::singleton()->get('isUpgradePending');
if ((isset($args[1]) && $args[1] == 'upgrade') || $upgradeInProcess) {
return TRUE;
......
......@@ -9,10 +9,14 @@ use Doctrine\Common\Cache\FilesystemCache;
use Doctrine\ORM\EntityManager;
use Doctrine\ORM\Mapping\Driver\AnnotationDriver;
use Doctrine\ORM\Tools\Setup;
use Symfony\Component\Config\ConfigCache;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Dumper\PhpDumper;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\EventDispatcher\ContainerAwareEventDispatcher;
use Symfony\Component\EventDispatcher\DependencyInjection\RegisterListenersPass;
// TODO use Symfony\Component\DependencyInjection\Loader\YamlFileLoader;
......@@ -37,20 +41,85 @@ class Container {
public static function singleton($reset = FALSE) {
if ($reset || self::$singleton === NULL) {
$c = new self();
self::$singleton = $c->createContainer();
self::$singleton = $c->loadContainer();
}
return self::$singleton;
}
/**
* Find a cached container definition or construct a new one.
*
* There are many weird contexts in which Civi initializes (eg different
* variations of multitenancy and different permutations of CMS/CRM bootstrap),
* and hook_container may fire a bit differently in each context. To mitigate
* risk of leaks between environments, we compute a unique envID
* (md5(DB_NAME, HTTP_HOST, SCRIPT_FILENAME, etc)) and use separate caches for
* each (eg "templates_c/CachedCiviContainer.$ENVID.php").
*
* Constants:
* - CIVICRM_CONTAINER_CACHE -- 'always' [default], 'never', 'auto'
* - CIVICRM_DSN
* - CIVICRM_DOMAIN_ID
* - CIVICRM_TEMPLATE_COMPILEDIR
*
* @return ContainerInterface
*/
public function loadContainer() {
// Note: The container's raison d'etre is to manage construction of other
// services. Consequently, we assume a minimal service available -- the classloader
// has been setup, and civicrm.settings.php is loaded, but nothing else works.
$cacheMode = defined('CIVICRM_CONTAINER_CACHE') ? CIVICRM_CONTAINER_CACHE : 'always';
// In pre-installation environments, don't bother with caching.
if (!defined('CIVICRM_TEMPLATE_COMPILEDIR') || !defined('CIVICRM_DSN') || $cacheMode === 'never' || \CRM_Utils_System::isInUpgradeMode()) {
return $this->createContainer();
}
$envId = md5(implode(\CRM_Core_DAO::VALUE_SEPARATOR, array(
defined('CIVICRM_DOMAIN_ID') ? CIVICRM_DOMAIN_ID : 1, // e.g. one database, multi URL
parse_url(CIVICRM_DSN, PHP_URL_PATH), // e.g. one codebase, multi database
\CRM_Utils_Array::value('SCRIPT_FILENAME', $_SERVER, ''), // e.g. CMS vs extern vs installer
\CRM_Utils_Array::value('HTTP_HOST', $_SERVER, ''), // e.g. name-based vhosts
\CRM_Utils_Array::value('SERVER_PORT', $_SERVER, ''), // e.g. port-based vhosts
// Depending on deployment arch, these signals *could* be redundant, but who cares?
)));
$file = CIVICRM_TEMPLATE_COMPILEDIR . "/CachedCiviContainer.{$envId}.php";
$containerConfigCache = new ConfigCache($file, $cacheMode === 'auto');
if (!$containerConfigCache->isFresh()) {
$containerBuilder = $this->createContainer();
$containerBuilder->compile();
$dumper = new PhpDumper($containerBuilder);
$containerConfigCache->write(
$dumper->dump(array('class' => 'CachedCiviContainer')),
$containerBuilder->getResources()
);
}
require_once $file;
$c = new \CachedCiviContainer();
$c->set('service_container', $c);
return $c;
}
/**
* Construct a new container.
*
* @var ContainerBuilder
* @return \Symfony\Component\DependencyInjection\ContainerBuilder
*/
public function createContainer() {
$civicrm_base_path = dirname(dirname(__DIR__));
$container = new ContainerBuilder();
$container->addCompilerPass(new RegisterListenersPass('dispatcher'));
$container->addObjectResource($this);
$container->setParameter('civicrm_base_path', $civicrm_base_path);
$container->set(self::SELF, $this);
//$container->set(self::SELF, $this);
$container->setDefinition(self::SELF, new Definition(
'Civi\Core\Container',
array()
));
// TODO Move configuration to an external file; define caching structure
// if (empty($configDirectories)) {
......@@ -69,36 +138,36 @@ class Container {
// }
$container->setDefinition('lockManager', new Definition(
'\Civi\Core\Lock\LockManager',
'Civi\Core\Lock\LockManager',
array()
))
->setFactoryService(self::SELF)->setFactoryMethod('createLockManager');
$container->setDefinition('angular', new Definition(
'\Civi\Angular\Manager',
'Civi\Angular\Manager',
array()
))
->setFactoryService(self::SELF)->setFactoryMethod('createAngularManager');
$container->setDefinition('dispatcher', new Definition(
'\Symfony\Component\EventDispatcher\EventDispatcher',
array()
'Symfony\Component\EventDispatcher\ContainerAwareEventDispatcher',
array(new Reference('service_container'))
))
->setFactoryService(self::SELF)->setFactoryMethod('createEventDispatcher');
$container->setDefinition('magic_function_provider', new Definition(
'\Civi\API\Provider\MagicFunctionProvider',
'Civi\API\Provider\MagicFunctionProvider',
array()
));
$container->setDefinition('civi_api_kernel', new Definition(
'\Civi\API\Kernel',
'Civi\API\Kernel',
array(new Reference('dispatcher'), new Reference('magic_function_provider'))
))
->setFactoryService(self::SELF)->setFactoryMethod('createApiKernel');
$container->setDefinition('cxn_reg_client', new Definition(
'\Civi\Cxn\Rpc\RegistrationClient',
'Civi\Cxn\Rpc\RegistrationClient',
array()
))
->setFactoryClass('CRM_Cxn_BAO_Cxn')->setFactoryMethod('createRegistrationClient');
......@@ -135,6 +204,8 @@ class Container {
->setFactoryClass($class)->setFactoryMethod('singleton');
}
\CRM_Utils_Hook::container($container);
return $container;
}
......@@ -146,10 +217,11 @@ class Container {
}
/**
* @param ContainerInterface $container
* @return \Symfony\Component\EventDispatcher\EventDispatcher
*/
public function createEventDispatcher() {
$dispatcher = new \Symfony\Component\EventDispatcher\EventDispatcher();
public function createEventDispatcher($container) {
$dispatcher = new ContainerAwareEventDispatcher($container);
$dispatcher->addListener('hook_civicrm_post::Activity', array('\Civi\CCase\Events', 'fireCaseChange'));
$dispatcher->addListener('hook_civicrm_post::Case', array('\Civi\CCase\Events', 'fireCaseChange'));
$dispatcher->addListener('hook_civicrm_caseChange', array('\Civi\CCase\Events', 'delegateToXmlListeners'));
......
......@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
"This file is @generated automatically"
],
"hash": "94145c3d8822e929bea514a67dd54f15",
"hash": "7ae864fa67ed95c56a70091935a19c37",
"packages": [
{
"name": "civicrm/civicrm-cxn-rpc",
......@@ -257,19 +257,67 @@
],
"time": "2012-12-21 11:40:51"
},
{
"name": "symfony/config",
"version": "v2.5.12",
"target-dir": "Symfony/Component/Config",
"source": {
"type": "git",
"url": "https://github.com/symfony/Config.git",
"reference": "c7309e33b719433d5cf3845d0b5b9608609d8c8e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/Config/zipball/c7309e33b719433d5cf3845d0b5b9608609d8c8e",
"reference": "c7309e33b719433d5cf3845d0b5b9608609d8c8e",
"shasum": ""
},
"require": {
"php": ">=5.3.3",
"symfony/filesystem": "~2.3"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.5-dev"
}
},
"autoload": {
"psr-0": {
"Symfony\\Component\\Config\\": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Symfony Community",
"homepage": "http://symfony.com/contributors"
},
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
}
],
"description": "Symfony Config Component",
"homepage": "http://symfony.com",
"time": "2015-01-03 08:01:13"
},
{
"name": "symfony/dependency-injection",
"version": "v2.3.23",
"version": "v2.5.12",
"target-dir": "Symfony/Component/DependencyInjection",
"source": {
"type": "git",
"url": "https://github.com/symfony/DependencyInjection.git",
"reference": "f165ee0e0b3522b5158def22622b2f171a8ecd59"
"reference": "c42aee05b466cc9c66b87ddf7d263402befb6962"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/DependencyInjection/zipball/f165ee0e0b3522b5158def22622b2f171a8ecd59",
"reference": "f165ee0e0b3522b5158def22622b2f171a8ecd59",
"url": "https://api.github.com/repos/symfony/DependencyInjection/zipball/c42aee05b466cc9c66b87ddf7d263402befb6962",
"reference": "c42aee05b466cc9c66b87ddf7d263402befb6962",
"shasum": ""
},
"require": {
......@@ -277,7 +325,8 @@
},
"require-dev": {
"symfony/config": "~2.2",
"symfony/yaml": "~2.0"
"symfony/expression-language": "~2.4,>=2.4.10",
"symfony/yaml": "~2.1"
},
"suggest": {
"symfony/config": "",
......@@ -287,7 +336,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.3-dev"
"dev-master": "2.5-dev"
}
},
"autoload": {
......@@ -311,28 +360,31 @@
],
"description": "Symfony DependencyInjection Component",
"homepage": "http://symfony.com",
"time": "2014-12-02 19:42:47"
"time": "2015-01-25 04:37:39"
},
{
"name": "symfony/event-dispatcher",
"version": "v2.3.23",
"version": "v2.5.12",
"target-dir": "Symfony/Component/EventDispatcher",
"source": {
"type": "git",
"url": "https://github.com/symfony/EventDispatcher.git",
"reference": "36a40695d94e948d7a85347db0b12ba446c400fa"
"reference": "af6eb6a9a1a3b411facfd8e7e3f82a6be7919c04"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/EventDispatcher/zipball/36a40695d94e948d7a85347db0b12ba446c400fa",
"reference": "36a40695d94e948d7a85347db0b12ba446c400fa",
"url": "https://api.github.com/repos/symfony/EventDispatcher/zipball/af6eb6a9a1a3b411facfd8e7e3f82a6be7919c04",
"reference": "af6eb6a9a1a3b411facfd8e7e3f82a6be7919c04",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
},
"require-dev": {
"symfony/dependency-injection": "~2.0"
"psr/log": "~1.0",
"symfony/config": "~2.0,>=2.0.5",
"symfony/dependency-injection": "~2.0,>=2.0.5,<2.6.0",
"symfony/stopwatch": "~2.3"
},
"suggest": {
"symfony/dependency-injection": "",
......@@ -341,7 +393,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.3-dev"
"dev-master": "2.5-dev"
}
},
"autoload": {
......@@ -365,21 +417,70 @@
],
"description": "Symfony EventDispatcher Component",
"homepage": "http://symfony.com",
"time": "2014-11-30 13:33:44"
"time": "2015-01-29 18:20:43"
},
{
"name": "symfony/filesystem",
"version": "v2.7.2",
"source": {
"type": "git",
"url": "https://github.com/symfony/Filesystem.git",
"reference": "2d7b2ddaf3f548f4292df49a99d19c853d43f0b8"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/Filesystem/zipball/2d7b2ddaf3f548f4292df49a99d19c853d43f0b8",
"reference": "2d7b2ddaf3f548f4292df49a99d19c853d43f0b8",
"shasum": ""
},
"require": {
"php": ">=5.3.9"
},
"require-dev": {
"symfony/phpunit-bridge": "~2.7"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.7-dev"
}
},
"autoload": {
"psr-4": {
"Symfony\\Component\\Filesystem\\": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony Filesystem Component",
"homepage": "https://symfony.com",
"time": "2015-07-09 16:07:40"
},
{
"name": "symfony/finder",
"version": "v2.3.23",
"version": "v2.5.12",
"target-dir": "Symfony/Component/Finder",
"source": {
"type": "git",
"url": "https://github.com/symfony/Finder.git",
"reference": "d533aea3400dc463c4d0ba9c3ecf40bd80d49dbd"
"reference": "e527ebf47ff912a45e148b7d0b107b80ec0b3cc2"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/Finder/zipball/d533aea3400dc463c4d0ba9c3ecf40bd80d49dbd",
"reference": "d533aea3400dc463c4d0ba9c3ecf40bd80d49dbd",
"url": "https://api.github.com/repos/symfony/Finder/zipball/e527ebf47ff912a45e148b7d0b107b80ec0b3cc2",
"reference": "e527ebf47ff912a45e148b7d0b107b80ec0b3cc2",
"shasum": ""
},
"require": {
......@@ -388,7 +489,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.3-dev"
"dev-master": "2.5-dev"
}
},
"autoload": {
......@@ -412,33 +513,30 @@
],
"description": "Symfony Finder Component",
"homepage": "http://symfony.com",
"time": "2014-12-02 19:42:47"
"time": "2015-01-03 08:01:13"
},
{
"name": "symfony/process",
"version": "v2.3.28",
"version": "v2.5.12",
"target-dir": "Symfony/Component/Process",
"source": {
"type": "git",
"url": "https://github.com/symfony/Process.git",
"reference": "a8fe947ac58e081f8773e0d160807dcffbff7ed8"
"reference": "00a1308e8b5aec5eba7c8f1708426a78f929be8c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/Process/zipball/a8fe947ac58e081f8773e0d160807dcffbff7ed8",
"reference": "a8fe947ac58e081f8773e0d160807dcffbff7ed8",
"url": "https://api.github.com/repos/symfony/Process/zipball/00a1308e8b5aec5eba7c8f1708426a78f929be8c",
"reference": "00a1308e8b5aec5eba7c8f1708426a78f929be8c",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
},
"require-dev": {
"symfony/phpunit-bridge": "~2.7"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.3-dev"
"dev-master": "2.5-dev"
}
},
"autoload": {
......@@ -452,17 +550,17 @@
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
"name": "Symfony Community",
"homepage": "http://symfony.com/contributors"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
}
],
"description": "Symfony Process Component",
"homepage": "https://symfony.com",
"time": "2015-05-01 14:06:45"
"homepage": "http://symfony.com",
"time": "2015-02-08 07:07:45"
},
{
"name": "totten/ca-config",
......
<?php
define('CIVICRM_TEST', 1);
define('CIVICRM_CONTAINER_CACHE', 'auto');
// This file is loaded on all systems running tests. To override settings on
// your local system, please create "civicrm.settings.local.php" and put
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment