X-Git-Url: https://vcs.fsf.org/?a=blobdiff_plain;f=Civi%2FCore%2FContainer.php;h=fa7fe74994879e346d770ab0aec30db7a323b137;hb=50a2375554b2a33cacebbe3822ff3c84f60fb9e9;hp=71679e9aff0a263be86391ef8991bbeebe4fc0eb;hpb=01cfa0fdb7ff05c39e18bac68fbf0fd11dcb8e0f;p=civicrm-core.git diff --git a/Civi/Core/Container.php b/Civi/Core/Container.php index 71679e9aff..fa7fe74994 100644 --- a/Civi/Core/Container.php +++ b/Civi/Core/Container.php @@ -1,6 +1,7 @@ createContainer(); + if ($reset || !isset(\Civi::$statics[__CLASS__]['container'])) { + self::boot(TRUE); + } + return \Civi::$statics[__CLASS__]['container']; + } + + /** + * 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 = \CRM_Core_Config_Runtime::getId(); + $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() + ); } - return self::$singleton; + + 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)) { @@ -68,32 +124,66 @@ class Container { // } $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', + array() + )) + ->setFactoryClass('CRM_Cxn_BAO_Cxn')->setFactoryMethod('createRegistrationClient'); + + $container->setDefinition('psr_log', new Definition('CRM_Core_Error_Log', array())); + + foreach (array('js_strings', 'community_messages') as $cacheName) { + $container->setDefinition("cache.{$cacheName}", new Definition( + 'CRM_Utils_Cache_Interface', + array( + array( + 'name' => $cacheName, + 'type' => array('*memory*', 'SqlGroup', 'ArrayCache'), + ), + ) + ))->setFactoryClass('CRM_Utils_Cache')->setFactoryMethod('create'); + } + + $container->setDefinition('pear_mail', new Definition('Mail')) + ->setFactoryClass('CRM_Utils_Mail')->setFactoryMethod('createMailer'); + + if (empty(\Civi::$statics[__CLASS__]['boot'])) { + throw new \RuntimeException("Cannot initialize container. Boot services are undefined."); + } + foreach (\Civi::$statics[__CLASS__]['boot'] as $bootService => $def) { + $container->setDefinition($bootService, new Definition($def['class'], array($bootService))) + ->setFactoryClass(__CLASS__) + ->setFactoryMethod('getBootService'); + } + // Expose legacy singletons as services in the container. $singletons = array( 'resources' => 'CRM_Core_Resources', 'httpClient' => 'CRM_Utils_HttpClient', + 'cache.default' => 'CRM_Utils_Cache', + 'i18n' => 'CRM_Core_I18n', // Maybe? 'config' => 'CRM_Core_Config', // Maybe? 'smarty' => 'CRM_Core_Smarty', ); @@ -104,6 +194,18 @@ class Container { ->setFactoryClass($class)->setFactoryMethod('singleton'); } + $container->setDefinition('civi_token_compat', new Definition( + 'Civi\Token\TokenCompatSubscriber', + array() + ))->addTag('kernel.event_subscriber'); + + $container->setDefinition('actionscheduletmp', new Definition( + 'CRM_Core_ActionScheduleTmp', + array() + ))->addTag('kernel.event_subscriber'); + + \CRM_Utils_Hook::container($container); + return $container; } @@ -115,10 +217,11 @@ class Container { } /** - * @return \Symfony\Component\EventDispatcher\EventDispatcher + * @param ContainerInterface $container + * @return \Symfony\Component\EventDispatcher\ContainerAwareEventDispatcher */ - 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')); @@ -127,12 +230,31 @@ class Container { $dispatcher->addListener('DAO::post-update', array('\CRM_Core_BAO_RecurringEntity', 'triggerUpdate')); $dispatcher->addListener('DAO::post-delete', array('\CRM_Core_BAO_RecurringEntity', 'triggerDelete')); $dispatcher->addListener('hook_civicrm_unhandled_exception', array( - 'CRM_Core_LegacyErrorHandler', - 'handleException', - )); + 'CRM_Core_LegacyErrorHandler', + 'handleException', + )); return $dispatcher; } + /** + * @return LockManager + */ + public static function createLockManager() { + // Ideally, downstream implementers could override any definitions in + // the container. For now, we'll make-do with some define()s. + $lm = new LockManager(); + $lm + ->register('/^cache\./', defined('CIVICRM_CACHE_LOCK') ? CIVICRM_CACHE_LOCK : array('CRM_Core_Lock', 'createScopedLock')) + ->register('/^data\./', defined('CIVICRM_DATA_LOCK') ? CIVICRM_DATA_LOCK : array('CRM_Core_Lock', 'createScopedLock')) + ->register('/^worker\.mailing\.send\./', defined('CIVICRM_WORK_LOCK') ? CIVICRM_WORK_LOCK : array('CRM_Core_Lock', 'createCivimailLock')) + ->register('/^worker\./', defined('CIVICRM_WORK_LOCK') ? CIVICRM_WORK_LOCK : array('CRM_Core_Lock', 'createScopedLock')); + + // Registrations may use complex resolver expressions, but (as a micro-optimization) + // the default factory is specified as an array. + + return $lm; + } + /** * @param \Symfony\Component\EventDispatcher\EventDispatcher $dispatcher * @param $magicFunctionProvider @@ -186,4 +308,75 @@ class Container { return $kernel; } + /** + * Get a list of boot services. + * + * These are services which must be setup *before* the container can operate. + * + * @param bool $loadFromDB + * @throws \CRM_Core_Exception + */ + public static function boot($loadFromDB) { + $bootServices = array(); + \Civi::$statics[__CLASS__]['boot'] = &$bootServices; + + $bootServices['runtime'] = array( + 'class' => 'CRM_Core_Config_Runtime', + 'obj' => ($runtime = new \CRM_Core_Config_Runtime()), + ); + $runtime->initialize($loadFromDB); + + if ($loadFromDB && $runtime->dsn) { + \CRM_Core_DAO::init($runtime->dsn); + } + + $bootServices['paths'] = array( + 'class' => 'Civi\Core\Paths', + 'obj' => new \Civi\Core\Paths(), + ); + + $class = $runtime->userFrameworkClass; + $bootServices['userSystem'] = array( + 'class' => 'CRM_Utils_Cache_Interface', + 'obj' => ($userSystem = new $class()), + ); + $userSystem->initialize(); + + $userPermissionClass = 'CRM_Core_Permission_' . $runtime->userFramework; + $bootServices['userPermissionClass'] = array( + // Ugh, silly name. + 'class' => 'CRM_Core_Permission_Base', + 'obj' => new $userPermissionClass(), + ); + + $bootServices['cache.settings'] = array( + 'class' => 'CRM_Utils_Cache_Interface', + 'obj' => \CRM_Utils_Cache::create(array( + 'name' => 'settings', + 'type' => array('*memory*', 'SqlGroup', 'ArrayCache'), + )), + ); + + $bootServices['settings_manager'] = array( + 'class' => 'Civi\Core\SettingsManager', + 'obj' => new \Civi\Core\SettingsManager($bootServices['cache.settings']['obj']), + ); + + $bootServices['lockManager'] = array( + 'class' => 'Civi\Core\Lock\LockManager', + 'obj' => self::createLockManager(), + ); + + if ($loadFromDB && $runtime->dsn) { + \CRM_Extension_System::singleton(TRUE); + + $c = new self(); + \Civi::$statics[__CLASS__]['container'] = $c->loadContainer(); + } + } + + public static function getBootService($name) { + return \Civi::$statics[__CLASS__]['boot'][$name]['obj']; + } + }