Commit | Line | Data |
---|---|---|
fa184193 TO |
1 | <?php |
2 | namespace Civi\Core; | |
46bcf597 | 3 | |
c8d84653 | 4 | use Civi\Core\Event\EventScanner; |
10760fa1 | 5 | use Civi\Core\Lock\LockManager; |
40787e18 | 6 | use Symfony\Component\Config\ConfigCache; |
fa184193 TO |
7 | use Symfony\Component\DependencyInjection\ContainerBuilder; |
8 | use Symfony\Component\DependencyInjection\Definition; | |
40787e18 | 9 | use Symfony\Component\DependencyInjection\Dumper\PhpDumper; |
fa184193 | 10 | use Symfony\Component\DependencyInjection\Reference; |
40787e18 | 11 | use Symfony\Component\EventDispatcher\DependencyInjection\RegisterListenersPass; |
fa184193 TO |
12 | |
13 | // TODO use Symfony\Component\DependencyInjection\Loader\YamlFileLoader; | |
14 | ||
6550386a EM |
15 | /** |
16 | * Class Container | |
17 | * @package Civi\Core | |
18 | */ | |
fa184193 TO |
19 | class Container { |
20 | ||
21 | const SELF = 'civi_container_factory'; | |
22 | ||
fa184193 | 23 | /** |
04855556 TO |
24 | * @param bool $reset |
25 | * Whether to forcibly rebuild the entire container. | |
fa184193 TO |
26 | * @return \Symfony\Component\DependencyInjection\TaggedContainerInterface |
27 | */ | |
378e2654 | 28 | public static function singleton($reset = FALSE) { |
7f835399 TO |
29 | if ($reset || !isset(\Civi::$statics[__CLASS__]['container'])) { |
30 | self::boot(TRUE); | |
fa184193 | 31 | } |
7f835399 | 32 | return \Civi::$statics[__CLASS__]['container']; |
fa184193 TO |
33 | } |
34 | ||
35 | /** | |
40787e18 TO |
36 | * Find a cached container definition or construct a new one. |
37 | * | |
38 | * There are many weird contexts in which Civi initializes (eg different | |
39 | * variations of multitenancy and different permutations of CMS/CRM bootstrap), | |
40 | * and hook_container may fire a bit differently in each context. To mitigate | |
41 | * risk of leaks between environments, we compute a unique envID | |
42 | * (md5(DB_NAME, HTTP_HOST, SCRIPT_FILENAME, etc)) and use separate caches for | |
43 | * each (eg "templates_c/CachedCiviContainer.$ENVID.php"). | |
44 | * | |
45 | * Constants: | |
46 | * - CIVICRM_CONTAINER_CACHE -- 'always' [default], 'never', 'auto' | |
47 | * - CIVICRM_DSN | |
48 | * - CIVICRM_DOMAIN_ID | |
40787e18 | 49 | * |
34f3bbd9 | 50 | * @return \Symfony\Component\DependencyInjection\ContainerInterface |
40787e18 TO |
51 | */ |
52 | public function loadContainer() { | |
53 | // Note: The container's raison d'etre is to manage construction of other | |
54 | // services. Consequently, we assume a minimal service available -- the classloader | |
55 | // has been setup, and civicrm.settings.php is loaded, but nothing else works. | |
56 | ||
5497e016 | 57 | $cacheMode = defined('CIVICRM_CONTAINER_CACHE') ? CIVICRM_CONTAINER_CACHE : 'auto'; |
40787e18 TO |
58 | |
59 | // In pre-installation environments, don't bother with caching. | |
43a2114e | 60 | if (!defined('CIVICRM_DSN') || defined('CIVICRM_TEST') || CIVICRM_UF === 'UnitTests' || $cacheMode === 'never' || \CRM_Utils_System::isInUpgradeMode()) { |
12e01332 TO |
61 | $containerBuilder = $this->createContainer(); |
62 | $containerBuilder->compile(); | |
63 | return $containerBuilder; | |
40787e18 TO |
64 | } |
65 | ||
83617886 | 66 | $envId = \CRM_Core_Config_Runtime::getId(); |
6f50d29c | 67 | $file = \Civi::paths()->getPath("[civicrm.compile]/CachedCiviContainer.{$envId}.php"); |
40787e18 | 68 | $containerConfigCache = new ConfigCache($file, $cacheMode === 'auto'); |
40787e18 TO |
69 | if (!$containerConfigCache->isFresh()) { |
70 | $containerBuilder = $this->createContainer(); | |
71 | $containerBuilder->compile(); | |
72 | $dumper = new PhpDumper($containerBuilder); | |
73 | $containerConfigCache->write( | |
c64f69d9 | 74 | $dumper->dump(['class' => 'CachedCiviContainer']), |
40787e18 TO |
75 | $containerBuilder->getResources() |
76 | ); | |
77 | } | |
78 | ||
79 | require_once $file; | |
80 | $c = new \CachedCiviContainer(); | |
40787e18 TO |
81 | return $c; |
82 | } | |
83 | ||
84 | /** | |
85 | * Construct a new container. | |
86 | * | |
34f3bbd9 | 87 | * @var \Symfony\Component\DependencyInjection\ContainerBuilder |
77b97be7 | 88 | * @return \Symfony\Component\DependencyInjection\ContainerBuilder |
fa184193 TO |
89 | */ |
90 | public function createContainer() { | |
91 | $civicrm_base_path = dirname(dirname(__DIR__)); | |
92 | $container = new ContainerBuilder(); | |
40787e18 TO |
93 | $container->addCompilerPass(new RegisterListenersPass('dispatcher')); |
94 | $container->addObjectResource($this); | |
fa184193 | 95 | $container->setParameter('civicrm_base_path', $civicrm_base_path); |
40787e18 | 96 | //$container->set(self::SELF, $this); |
762dc04d TO |
97 | |
98 | $container->addResource(new \Symfony\Component\Config\Resource\FileResource(__FILE__)); | |
99 | ||
40787e18 TO |
100 | $container->setDefinition(self::SELF, new Definition( |
101 | 'Civi\Core\Container', | |
c64f69d9 | 102 | [] |
40787e18 | 103 | )); |
fa184193 | 104 | |
505d8b83 TO |
105 | // TODO Move configuration to an external file; define caching structure |
106 | // if (empty($configDirectories)) { | |
107 | // throw new \Exception(__CLASS__ . ': Missing required properties (civicrmRoot, configDirectories)'); | |
108 | // } | |
109 | // $locator = new FileLocator($configDirectories); | |
110 | // $loaderResolver = new LoaderResolver(array( | |
111 | // new YamlFileLoader($container, $locator) | |
112 | // )); | |
113 | // $delegatingLoader = new DelegatingLoader($loaderResolver); | |
114 | // foreach (array('services.yml') as $file) { | |
115 | // $yamlUserFiles = $locator->locate($file, NULL, FALSE); | |
116 | // foreach ($yamlUserFiles as $file) { | |
117 | // $delegatingLoader->load($file); | |
118 | // } | |
119 | // } | |
fa184193 | 120 | |
16072ce1 | 121 | $container->setDefinition('angular', new Definition( |
40787e18 | 122 | 'Civi\Angular\Manager', |
c64f69d9 | 123 | [] |
16072ce1 | 124 | )) |
3c006cb8 | 125 | ->setFactory([new Reference(self::SELF), 'createAngularManager'])->setPublic(TRUE); |
16072ce1 | 126 | |
9bd30577 CW |
127 | $container->setDefinition('angularjs.loader', new Definition('Civi\Angular\AngularLoader', [])) |
128 | ->setPublic(TRUE); | |
129 | ||
fa184193 | 130 | $container->setDefinition('dispatcher', new Definition( |
762dc04d | 131 | 'Civi\Core\CiviEventDispatcher', |
42ccedc7 | 132 | [] |
fa184193 | 133 | )) |
3c006cb8 | 134 | ->setFactory([new Reference(self::SELF), 'createEventDispatcher'])->setPublic(TRUE); |
fa184193 | 135 | |
c65db512 | 136 | $container->setDefinition('magic_function_provider', new Definition( |
40787e18 | 137 | 'Civi\API\Provider\MagicFunctionProvider', |
c64f69d9 | 138 | [] |
3c006cb8 | 139 | ))->setPublic(TRUE); |
c65db512 | 140 | |
0f643fb2 | 141 | $container->setDefinition('civi_api_kernel', new Definition( |
40787e18 | 142 | 'Civi\API\Kernel', |
c64f69d9 | 143 | [new Reference('dispatcher'), new Reference('magic_function_provider')] |
0f643fb2 | 144 | )) |
3c006cb8 | 145 | ->setFactory([new Reference(self::SELF), 'createApiKernel'])->setPublic(TRUE); |
0f643fb2 | 146 | |
7b4bbb34 | 147 | $container->setDefinition('cxn_reg_client', new Definition( |
40787e18 | 148 | 'Civi\Cxn\Rpc\RegistrationClient', |
c64f69d9 | 149 | [] |
7b4bbb34 | 150 | )) |
3c006cb8 | 151 | ->setFactory('CRM_Cxn_BAO_Cxn::createRegistrationClient')->setPublic(TRUE); |
7b4bbb34 | 152 | |
3c006cb8 | 153 | $container->setDefinition('psr_log', new Definition('CRM_Core_Error_Log', []))->setPublic(TRUE); |
c213eb51 TO |
154 | $container->setDefinition('psr_log_manager', new Definition('Civi\Core\LogManager', []))->setPublic(TRUE); |
155 | // With the default log-manager, you may overload a channel by defining a service, e.g. | |
156 | // $container->setDefinition('log.ipn', new Definition('CRM_Core_Error_Log', []))->setPublic(TRUE); | |
6e5ad5ee | 157 | |
c64f69d9 | 158 | $basicCaches = [ |
7a19d718 TO |
159 | 'js_strings' => 'js_strings', |
160 | 'community_messages' => 'community_messages', | |
b1fc1ab0 | 161 | 'checks' => 'checks', |
19707a63 | 162 | 'session' => 'CiviCRM Session', |
90cdaa0e | 163 | 'long' => 'long', |
0136044e | 164 | 'groups' => 'contact groups', |
96689db3 | 165 | 'navigation' => 'navigation', |
792fce0b | 166 | 'customData' => 'custom data', |
9cdf85c1 | 167 | 'fields' => 'contact fields', |
81c4c148 | 168 | 'contactTypes' => 'contactTypes', |
eb151aab | 169 | 'metadata' => 'metadata', |
c64f69d9 | 170 | ]; |
a9538f25 TO |
171 | $verSuffixCaches = ['metadata']; |
172 | $verSuffix = '_' . preg_replace(';[^0-9a-z_];', '_', \CRM_Utils_System::version()); | |
7a19d718 | 173 | foreach ($basicCaches as $cacheSvc => $cacheGrp) { |
023bcf83 | 174 | $definitionParams = [ |
a9538f25 | 175 | 'name' => $cacheGrp . (in_array($cacheGrp, $verSuffixCaches) ? $verSuffix : ''), |
023bcf83 SL |
176 | 'type' => ['*memory*', 'SqlGroup', 'ArrayCache'], |
177 | ]; | |
178 | // For Caches that we don't really care about the ttl for and/or maybe accessed | |
179 | // fairly often we use the fastArrayDecorator which improves reads and writes, these | |
180 | // caches should also not have concurrency risk. | |
eb151aab | 181 | $fastArrayCaches = ['groups', 'navigation', 'customData', 'fields', 'contactTypes', 'metadata']; |
023bcf83 SL |
182 | if (in_array($cacheSvc, $fastArrayCaches)) { |
183 | $definitionParams['withArray'] = 'fast'; | |
184 | } | |
7a19d718 | 185 | $container->setDefinition("cache.{$cacheSvc}", new Definition( |
a4704404 | 186 | 'CRM_Utils_Cache_Interface', |
023bcf83 | 187 | [$definitionParams] |
3c006cb8 | 188 | ))->setFactory('CRM_Utils_Cache::create')->setPublic(TRUE); |
a4704404 | 189 | } |
3a84c0ab | 190 | |
da4effce SL |
191 | // PrevNextCache cannot use memory or array cache at the moment because the |
192 | // Code in CRM_Core_BAO_PrevNextCache assumes that this cache is sql backed. | |
193 | $container->setDefinition("cache.prevNextCache", new Definition( | |
194 | 'CRM_Utils_Cache_Interface', | |
195 | [ | |
196 | [ | |
197 | 'name' => 'CiviCRM Search PrevNextCache', | |
198 | 'type' => ['SqlGroup'], | |
199 | ], | |
200 | ] | |
3c006cb8 | 201 | ))->setFactory('CRM_Utils_Cache::create')->setPublic(TRUE); |
da4effce | 202 | |
4ed867e0 TO |
203 | $container->setDefinition('sql_triggers', new Definition( |
204 | 'Civi\Core\SqlTriggers', | |
c64f69d9 | 205 | [] |
3c006cb8 | 206 | ))->setPublic(TRUE); |
4ed867e0 | 207 | |
87e3fe24 TO |
208 | $container->setDefinition('asset_builder', new Definition( |
209 | 'Civi\Core\AssetBuilder', | |
c64f69d9 | 210 | [] |
3c006cb8 | 211 | ))->setPublic(TRUE); |
87e3fe24 | 212 | |
d89d2545 TO |
213 | $container->setDefinition('themes', new Definition( |
214 | 'Civi\Core\Themes', | |
215 | [] | |
3c006cb8 | 216 | ))->setPublic(TRUE); |
386fe6c2 EM |
217 | |
218 | $container->setDefinition('format', new Definition( | |
219 | '\Civi\Core\Format', | |
220 | [] | |
221 | ))->setPublic(TRUE); | |
d89d2545 | 222 | |
6e7adedc | 223 | $container->setDefinition('bundle.bootstrap3', new Definition('CRM_Core_Resources_Bundle', ['bootstrap3'])) |
3c86501c | 224 | ->setFactory('CRM_Core_Resources_Common::createBootstrap3Bundle')->setPublic(TRUE); |
6e7adedc | 225 | |
b2d8361e | 226 | $container->setDefinition('bundle.coreStyles', new Definition('CRM_Core_Resources_Bundle', ['coreStyles'])) |
3c86501c | 227 | ->setFactory('CRM_Core_Resources_Common::createStyleBundle')->setPublic(TRUE); |
b2d8361e TO |
228 | |
229 | $container->setDefinition('bundle.coreResources', new Definition('CRM_Core_Resources_Bundle', ['coreResources'])) | |
3c86501c | 230 | ->setFactory('CRM_Core_Resources_Common::createFullBundle')->setPublic(TRUE); |
b2d8361e | 231 | |
247eb841 | 232 | $container->setDefinition('pear_mail', new Definition('Mail')) |
3c86501c | 233 | ->setFactory('CRM_Utils_Mail::createMailer')->setPublic(TRUE); |
247eb841 | 234 | |
7dfe650d | 235 | $container->setDefinition('crypto.registry', new Definition('Civi\Crypto\CryptoRegistry')) |
23fa0118 | 236 | ->setFactory('Civi\Crypto\CryptoRegistry::createDefaultRegistry')->setPublic(TRUE); |
281eacd8 | 237 | |
7c5110c3 TO |
238 | $container->setDefinition('crypto.token', new Definition('Civi\Crypto\CryptoToken', [])) |
239 | ->setPublic(TRUE); | |
8d3452c4 | 240 | |
9c976d32 TO |
241 | $container->setDefinition('crypto.jwt', new Definition('Civi\Crypto\CryptoJwt', [])) |
242 | ->setPublic(TRUE); | |
243 | ||
7f835399 | 244 | if (empty(\Civi::$statics[__CLASS__]['boot'])) { |
272dfdc3 | 245 | throw new \RuntimeException('Cannot initialize container. Boot services are undefined.'); |
7f835399 TO |
246 | } |
247 | foreach (\Civi::$statics[__CLASS__]['boot'] as $bootService => $def) { | |
765daeac | 248 | $container->setDefinition($bootService, new Definition())->setSynthetic(TRUE)->setPublic(TRUE); |
83617886 TO |
249 | } |
250 | ||
c8074a93 | 251 | // Expose legacy singletons as services in the container. |
c64f69d9 | 252 | $singletons = [ |
c8074a93 | 253 | 'httpClient' => 'CRM_Utils_HttpClient', |
7b5937fe | 254 | 'cache.default' => 'CRM_Utils_Cache', |
a7c57397 | 255 | 'i18n' => 'CRM_Core_I18n', |
c8074a93 TO |
256 | // Maybe? 'config' => 'CRM_Core_Config', |
257 | // Maybe? 'smarty' => 'CRM_Core_Smarty', | |
c64f69d9 | 258 | ]; |
c8074a93 TO |
259 | foreach ($singletons as $name => $class) { |
260 | $container->setDefinition($name, new Definition( | |
261 | $class | |
262 | )) | |
3c006cb8 | 263 | ->setFactory([$class, 'singleton'])->setPublic(TRUE); |
c8074a93 | 264 | } |
e5c6b3cd | 265 | $container->setAlias('cache.short', 'cache.default')->setPublic(TRUE); |
c8074a93 | 266 | |
88d93265 | 267 | $container->setDefinition('civi.pipe', new Definition( |
25804d7a | 268 | 'Civi\Pipe\PipeSession', |
1e88220a | 269 | [] |
88d93265 | 270 | ))->setPublic(TRUE)->setShared(FALSE); |
1e88220a | 271 | |
223ba025 TO |
272 | $container->setDefinition('resources', new Definition( |
273 | 'CRM_Core_Resources', | |
274 | [new Reference('service_container')] | |
3c006cb8 | 275 | ))->setFactory([new Reference(self::SELF), 'createResources'])->setPublic(TRUE); |
223ba025 | 276 | |
bbcf0f46 TO |
277 | $container->setDefinition('resources.js_strings', new Definition( |
278 | 'CRM_Core_Resources_Strings', | |
279 | [new Reference('cache.js_strings')] | |
280 | ))->setPublic(TRUE); | |
281 | ||
780fd0e3 | 282 | $container->setDefinition('prevnext', new Definition( |
99098349 TO |
283 | 'CRM_Core_PrevNextCache_Interface', |
284 | [new Reference('service_container')] | |
3c006cb8 | 285 | ))->setFactory([new Reference(self::SELF), 'createPrevNextCache'])->setPublic(TRUE); |
99098349 TO |
286 | |
287 | $container->setDefinition('prevnext.driver.sql', new Definition( | |
780fd0e3 TO |
288 | 'CRM_Core_PrevNextCache_Sql', |
289 | [] | |
3c006cb8 | 290 | ))->setPublic(TRUE); |
780fd0e3 | 291 | |
751f3d98 TO |
292 | $container->setDefinition('prevnext.driver.redis', new Definition( |
293 | 'CRM_Core_PrevNextCache_Redis', | |
294 | [new Reference('cache_config')] | |
3c006cb8 | 295 | ))->setPublic(TRUE); |
751f3d98 TO |
296 | |
297 | $container->setDefinition('cache_config', new Definition('ArrayObject')) | |
3c006cb8 | 298 | ->setFactory([new Reference(self::SELF), 'createCacheConfig'])->setPublic(TRUE); |
751f3d98 | 299 | |
e0a667ad TO |
300 | $container->setDefinition('civi.activity.triggers', new Definition( |
301 | 'Civi\Core\SqlTrigger\TimestampTriggers', | |
c64f69d9 | 302 | ['civicrm_activity', 'Activity'] |
3c006cb8 | 303 | ))->addTag('kernel.event_listener', ['event' => 'hook_civicrm_triggerInfo', 'method' => 'onTriggerInfo'])->setPublic(TRUE); |
e0a667ad TO |
304 | |
305 | $container->setDefinition('civi.case.triggers', new Definition( | |
306 | 'Civi\Core\SqlTrigger\TimestampTriggers', | |
c64f69d9 | 307 | ['civicrm_case', 'Case'] |
3c006cb8 | 308 | ))->addTag('kernel.event_listener', ['event' => 'hook_civicrm_triggerInfo', 'method' => 'onTriggerInfo'])->setPublic(TRUE); |
e0a667ad | 309 | |
b21ffed7 TO |
310 | $container->setDefinition('civi.case.staticTriggers', new Definition( |
311 | 'Civi\Core\SqlTrigger\StaticTriggers', | |
c64f69d9 CW |
312 | [ |
313 | [ | |
314 | [ | |
315 | 'upgrade_check' => ['table' => 'civicrm_case', 'column' => 'modified_date'], | |
b21ffed7 TO |
316 | 'table' => 'civicrm_case_activity', |
317 | 'when' => 'AFTER', | |
c64f69d9 | 318 | 'event' => ['INSERT'], |
f0401a44 | 319 | 'sql' => "UPDATE civicrm_case SET modified_date = CURRENT_TIMESTAMP WHERE id = NEW.case_id;", |
c64f69d9 CW |
320 | ], |
321 | [ | |
322 | 'upgrade_check' => ['table' => 'civicrm_case', 'column' => 'modified_date'], | |
b21ffed7 TO |
323 | 'table' => 'civicrm_activity', |
324 | 'when' => 'BEFORE', | |
c64f69d9 | 325 | 'event' => ['UPDATE', 'DELETE'], |
f0401a44 | 326 | 'sql' => "UPDATE civicrm_case SET modified_date = CURRENT_TIMESTAMP WHERE id IN (SELECT ca.case_id FROM civicrm_case_activity ca WHERE ca.activity_id = OLD.id);", |
c64f69d9 CW |
327 | ], |
328 | ], | |
329 | ] | |
b21ffed7 | 330 | )) |
3c006cb8 | 331 | ->addTag('kernel.event_listener', ['event' => 'hook_civicrm_triggerInfo', 'method' => 'onTriggerInfo'])->setPublic(TRUE); |
b21ffed7 | 332 | |
43ceab3f TO |
333 | $container->setDefinition('civi_token_compat', new Definition( |
334 | 'Civi\Token\TokenCompatSubscriber', | |
c64f69d9 | 335 | [] |
3c006cb8 | 336 | ))->addTag('kernel.event_subscriber')->setPublic(TRUE); |
56df2d06 | 337 | $container->setDefinition("crm_mailing_action_tokens", new Definition( |
0f4031da | 338 | 'CRM_Mailing_ActionTokens', |
c64f69d9 | 339 | [] |
3c006cb8 | 340 | ))->addTag('kernel.event_subscriber')->setPublic(TRUE); |
43ceab3f | 341 | |
a7f29bf4 | 342 | foreach (['Activity', 'Contact', 'Contribute', 'Event', 'Mailing', 'Member', 'Case'] as $comp) { |
0f4031da | 343 | $container->setDefinition('crm_' . strtolower($comp) . '_tokens', new Definition( |
46f5566c | 344 | "CRM_{$comp}_Tokens", |
c64f69d9 | 345 | [] |
3c006cb8 | 346 | ))->addTag('kernel.event_subscriber')->setPublic(TRUE); |
46f5566c | 347 | } |
fa75f064 TO |
348 | $container->setDefinition('civi_token_impliedcontext', new Definition( |
349 | 'Civi\Token\ImpliedContextSubscriber', | |
350 | [] | |
351 | ))->addTag('kernel.event_subscriber')->setPublic(TRUE); | |
b7472bd6 EM |
352 | $container->setDefinition('crm_participant_tokens', new Definition( |
353 | 'CRM_Event_ParticipantTokens', | |
354 | [] | |
355 | ))->addTag('kernel.event_subscriber')->setPublic(TRUE); | |
0f4031da EM |
356 | $container->setDefinition('crm_contribution_recur_tokens', new Definition( |
357 | 'CRM_Contribute_RecurTokens', | |
358 | [] | |
359 | ))->addTag('kernel.event_subscriber')->setPublic(TRUE); | |
3c78698e EM |
360 | $container->setDefinition('crm_domain_tokens', new Definition( |
361 | 'CRM_Core_DomainTokens', | |
362 | [] | |
363 | ))->addTag('kernel.event_subscriber')->setPublic(TRUE); | |
50a23755 | 364 | |
c8d84653 TO |
365 | $dispatcherDefn = $container->getDefinition('dispatcher'); |
366 | foreach (\CRM_Core_DAO_AllCoreTables::getBaoClasses() as $baoEntity => $baoClass) { | |
367 | $listenerMap = EventScanner::findListeners($baoClass, $baoEntity); | |
368 | if ($listenerMap) { | |
369 | $file = (new \ReflectionClass($baoClass))->getFileName(); | |
370 | $container->addResource(new \Symfony\Component\Config\Resource\FileResource($file)); | |
371 | $dispatcherDefn->addMethodCall('addListenerMap', [$baoClass, $listenerMap]); | |
372 | } | |
373 | } | |
374 | ||
caaeea39 | 375 | \CRM_Api4_Services::hook_container($container); |
aa4343bc | 376 | |
40787e18 TO |
377 | \CRM_Utils_Hook::container($container); |
378 | ||
fa184193 TO |
379 | return $container; |
380 | } | |
381 | ||
16072ce1 TO |
382 | /** |
383 | * @return \Civi\Angular\Manager | |
384 | */ | |
385 | public function createAngularManager() { | |
386 | return new \Civi\Angular\Manager(\CRM_Core_Resources::singleton()); | |
387 | } | |
388 | ||
fa184193 | 389 | /** |
3c006cb8 | 390 | * @return \Symfony\Component\EventDispatcher\EventDispatcher |
fa184193 | 391 | */ |
42ccedc7 TO |
392 | public function createEventDispatcher() { |
393 | // Continue building on the original dispatcher created during bootstrap. | |
9bd30577 | 394 | /** @var CiviEventDispatcher $dispatcher */ |
42ccedc7 | 395 | $dispatcher = static::getBootService('dispatcher.boot'); |
ecb0ae5d | 396 | |
cc69f300 TO |
397 | // Sometimes, you have a generic event ('hook_pre') and wish to fire more targeted aliases ('hook_pre::MyEntity') to allow shorter subscriber lists. |
398 | $aliasEvent = function($eventName, $fieldName) { | |
399 | return function($e) use ($eventName, $fieldName) { | |
400 | \Civi::dispatcher()->dispatch($eventName . "::" . $e->{$fieldName}, $e); | |
401 | }; | |
402 | }; | |
4bf92107 TO |
403 | $aliasMethodEvent = function($eventName, $methodName) { |
404 | return function($e) use ($eventName, $methodName) { | |
405 | \Civi::dispatcher()->dispatch($eventName . "::" . $e->{$methodName}(), $e); | |
406 | }; | |
407 | }; | |
408 | ||
409 | $dispatcher->addListener('civi.api4.validate', $aliasMethodEvent('civi.api4.validate', 'getEntityName'), 100); | |
af4cccf7 | 410 | $dispatcher->addListener('civi.api4.authorizeRecord', $aliasMethodEvent('civi.api4.authorizeRecord', 'getEntityName'), 100); |
304fc59f | 411 | $dispatcher->addListener('civi.api4.entityTypes', ['\Civi\Api4\Provider\CustomEntityProvider', 'addCustomEntities'], 100); |
cc69f300 | 412 | |
1d440215 TO |
413 | $dispatcher->addListener('civi.core.install', ['\Civi\Core\InstallationCanary', 'check']); |
414 | $dispatcher->addListener('civi.core.install', ['\Civi\Core\DatabaseInitializer', 'initialize']); | |
415 | $dispatcher->addListener('civi.core.install', ['\Civi\Core\LocalizationInitializer', 'initialize']); | |
74effac4 | 416 | $dispatcher->addListener('hook_civicrm_post', ['\CRM_Core_Transaction', 'addPostCommit'], -1000); |
cc69f300 | 417 | $dispatcher->addListener('hook_civicrm_pre', $aliasEvent('hook_civicrm_pre', 'entity'), 100); |
9d4c4ffd | 418 | $dispatcher->addListener('civi.dao.preDelete', ['\CRM_Core_BAO_EntityTag', 'preDeleteOtherEntity']); |
cc69f300 | 419 | $dispatcher->addListener('hook_civicrm_post', $aliasEvent('hook_civicrm_post', 'entity'), 100); |
c64f69d9 CW |
420 | $dispatcher->addListener('hook_civicrm_post::Activity', ['\Civi\CCase\Events', 'fireCaseChange']); |
421 | $dispatcher->addListener('hook_civicrm_post::Case', ['\Civi\CCase\Events', 'fireCaseChange']); | |
422 | $dispatcher->addListener('hook_civicrm_caseChange', ['\Civi\CCase\Events', 'delegateToXmlListeners']); | |
423 | $dispatcher->addListener('hook_civicrm_caseChange', ['\Civi\CCase\SequenceListener', 'onCaseChange_static']); | |
f1aeb0ba | 424 | $dispatcher->addListener('hook_civicrm_cryptoRotateKey', ['\Civi\Crypto\RotateKeys', 'rotateSmtp']); |
c64f69d9 | 425 | $dispatcher->addListener('hook_civicrm_eventDefs', ['\Civi\Core\CiviEventInspector', 'findBuiltInEvents']); |
0d0031a0 | 426 | // TODO We need a better code-convention for metadata about non-hook events. |
c64f69d9 CW |
427 | $dispatcher->addListener('hook_civicrm_eventDefs', ['\Civi\API\Events', 'hookEventDefs']); |
428 | $dispatcher->addListener('hook_civicrm_eventDefs', ['\Civi\Core\Event\SystemInstallEvent', 'hookEventDefs']); | |
429 | $dispatcher->addListener('hook_civicrm_buildAsset', ['\Civi\Angular\Page\Modules', 'buildAngularModules']); | |
9bd30577 | 430 | $dispatcher->addListenerService('civi.region.render', ['angularjs.loader', 'onRegionRender']); |
c64f69d9 CW |
431 | $dispatcher->addListener('hook_civicrm_buildAsset', ['\CRM_Utils_VisualBundle', 'buildAssetJs']); |
432 | $dispatcher->addListener('hook_civicrm_buildAsset', ['\CRM_Utils_VisualBundle', 'buildAssetCss']); | |
f22fb451 | 433 | $dispatcher->addListener('hook_civicrm_buildAsset', ['\CRM_Core_Resources', 'renderMenubarStylesheet']); |
2daeb956 | 434 | $dispatcher->addListener('hook_civicrm_buildAsset', ['\CRM_Core_Resources', 'renderL10nJs']); |
303017a1 | 435 | $dispatcher->addListener('hook_civicrm_coreResourceList', ['\CRM_Utils_System', 'appendCoreResources']); |
62c20d1e | 436 | $dispatcher->addListener('hook_civicrm_getAssetUrl', ['\CRM_Utils_System', 'alterAssetUrl']); |
28e28c36 | 437 | $dispatcher->addListener('hook_civicrm_alterExternUrl', ['\CRM_Utils_System', 'migrateExternUrl'], 1000); |
54043949 CW |
438 | // Not a BAO class so it can't implement hookInterface |
439 | $dispatcher->addListener('hook_civicrm_post', ['CRM_Utils_Recent', 'on_hook_civicrm_post']); | |
29541977 TO |
440 | $dispatcher->addListener('hook_civicrm_permissionList', ['CRM_Core_Permission_List', 'findConstPermissions'], 975); |
441 | $dispatcher->addListener('hook_civicrm_permissionList', ['CRM_Core_Permission_List', 'findCiviPermissions'], 950); | |
442 | $dispatcher->addListener('hook_civicrm_permissionList', ['CRM_Core_Permission_List', 'findCmsPermissions'], 925); | |
443 | ||
d357f225 | 444 | $dispatcher->addListener('hook_civicrm_postSave_civicrm_domain', ['\CRM_Core_BAO_Domain', 'onPostSave']); |
c64f69d9 | 445 | $dispatcher->addListener('hook_civicrm_unhandled_exception', [ |
9ae2d27b TO |
446 | 'CRM_Core_LegacyErrorHandler', |
447 | 'handleException', | |
c64f69d9 | 448 | ], -200); |
bc2feeb1 TO |
449 | $dispatcher->addListener('civi.actionSchedule.getMappings', ['CRM_Activity_ActionMapping', 'onRegisterActionMappings']); |
450 | $dispatcher->addListener('civi.actionSchedule.getMappings', ['CRM_Contact_ActionMapping', 'onRegisterActionMappings']); | |
451 | $dispatcher->addListener('civi.actionSchedule.getMappings', ['CRM_Contribute_ActionMapping_ByPage', 'onRegisterActionMappings']); | |
452 | $dispatcher->addListener('civi.actionSchedule.getMappings', ['CRM_Contribute_ActionMapping_ByType', 'onRegisterActionMappings']); | |
453 | $dispatcher->addListener('civi.actionSchedule.getMappings', ['CRM_Event_ActionMapping', 'onRegisterActionMappings']); | |
454 | $dispatcher->addListener('civi.actionSchedule.getMappings', ['CRM_Member_ActionMapping', 'onRegisterActionMappings']); | |
46f5566c | 455 | |
fa184193 TO |
456 | return $dispatcher; |
457 | } | |
0f643fb2 | 458 | |
10760fa1 | 459 | /** |
34f3bbd9 | 460 | * @return \Civi\Core\Lock\LockManager |
10760fa1 | 461 | */ |
83617886 | 462 | public static function createLockManager() { |
10760fa1 TO |
463 | // Ideally, downstream implementers could override any definitions in |
464 | // the container. For now, we'll make-do with some define()s. | |
465 | $lm = new LockManager(); | |
466 | $lm | |
c64f69d9 CW |
467 | ->register('/^cache\./', defined('CIVICRM_CACHE_LOCK') ? CIVICRM_CACHE_LOCK : ['CRM_Core_Lock', 'createScopedLock']) |
468 | ->register('/^data\./', defined('CIVICRM_DATA_LOCK') ? CIVICRM_DATA_LOCK : ['CRM_Core_Lock', 'createScopedLock']) | |
469 | ->register('/^worker\.mailing\.send\./', defined('CIVICRM_WORK_LOCK') ? CIVICRM_WORK_LOCK : ['CRM_Core_Lock', 'createCivimailLock']) | |
470 | ->register('/^worker\./', defined('CIVICRM_WORK_LOCK') ? CIVICRM_WORK_LOCK : ['CRM_Core_Lock', 'createScopedLock']); | |
10760fa1 TO |
471 | |
472 | // Registrations may use complex resolver expressions, but (as a micro-optimization) | |
473 | // the default factory is specified as an array. | |
474 | ||
475 | return $lm; | |
476 | } | |
477 | ||
0f643fb2 TO |
478 | /** |
479 | * @param \Symfony\Component\EventDispatcher\EventDispatcher $dispatcher | |
2a6da8d7 EM |
480 | * @param $magicFunctionProvider |
481 | * | |
0f643fb2 TO |
482 | * @return \Civi\API\Kernel |
483 | */ | |
c65db512 | 484 | public function createApiKernel($dispatcher, $magicFunctionProvider) { |
0a946de2 | 485 | $dispatcher->addSubscriber(new \Civi\API\Subscriber\ChainSubscriber()); |
b55bc593 | 486 | $dispatcher->addSubscriber(new \Civi\API\Subscriber\TransactionSubscriber()); |
bace5cd9 | 487 | $dispatcher->addSubscriber(new \Civi\API\Subscriber\I18nSubscriber()); |
c65db512 | 488 | $dispatcher->addSubscriber($magicFunctionProvider); |
d0c9daa4 | 489 | $dispatcher->addSubscriber(new \Civi\API\Subscriber\PermissionCheck()); |
dcef11bd | 490 | $dispatcher->addSubscriber(new \Civi\API\Subscriber\APIv3SchemaAdapter()); |
c64f69d9 | 491 | $dispatcher->addSubscriber(new \Civi\API\Subscriber\WrapperAdapter([ |
6d3bdc98 TO |
492 | \CRM_Utils_API_HTMLInputCoder::singleton(), |
493 | \CRM_Utils_API_NullOutputCoder::singleton(), | |
494 | \CRM_Utils_API_ReloadOption::singleton(), | |
495 | \CRM_Utils_API_MatchOption::singleton(), | |
c64f69d9 | 496 | ])); |
2aafb0fc | 497 | $dispatcher->addSubscriber(new \Civi\API\Subscriber\DebugSubscriber()); |
82376c19 TO |
498 | $kernel = new \Civi\API\Kernel($dispatcher); |
499 | ||
500 | $reflectionProvider = new \Civi\API\Provider\ReflectionProvider($kernel); | |
501 | $dispatcher->addSubscriber($reflectionProvider); | |
502 | ||
56154d36 TO |
503 | $dispatcher->addSubscriber(new \Civi\API\Subscriber\DynamicFKAuthorization( |
504 | $kernel, | |
505 | 'Attachment', | |
c64f69d9 | 506 | ['create', 'get', 'delete'], |
2e37a19f | 507 | // Given a file ID, determine the entity+table it's attached to. |
56154d36 TO |
508 | 'SELECT if(cf.id,1,0) as is_valid, cef.entity_table, cef.entity_id |
509 | FROM civicrm_file cf | |
510 | LEFT JOIN civicrm_entity_file cef ON cf.id = cef.file_id | |
511 | WHERE cf.id = %1', | |
29468114 TO |
512 | // Get a list of custom fields (field_name,table_name,extends) |
513 | 'SELECT concat("custom_",fld.id) as field_name, | |
514 | grp.table_name as table_name, | |
515 | grp.extends as extends | |
516 | FROM civicrm_custom_field fld | |
517 | INNER JOIN civicrm_custom_group grp ON fld.custom_group_id = grp.id | |
518 | WHERE fld.data_type = "File" | |
09871447 | 519 | ' |
56154d36 TO |
520 | )); |
521 | ||
c64f69d9 | 522 | $kernel->setApiProviders([ |
82376c19 TO |
523 | $reflectionProvider, |
524 | $magicFunctionProvider, | |
c64f69d9 | 525 | ]); |
82376c19 | 526 | |
0f643fb2 TO |
527 | return $kernel; |
528 | } | |
96025800 | 529 | |
223ba025 | 530 | /** |
34f3bbd9 | 531 | * @param \Symfony\Component\DependencyInjection\ContainerInterface $container |
223ba025 TO |
532 | * @return \CRM_Core_Resources |
533 | */ | |
534 | public static function createResources($container) { | |
535 | $sys = \CRM_Extension_System::singleton(); | |
536 | return new \CRM_Core_Resources( | |
537 | $sys->getMapper(), | |
bbcf0f46 | 538 | new \CRM_Core_Resources_Strings($container->get('cache.js_strings')), |
223ba025 TO |
539 | \CRM_Core_Config::isUpgradeMode() ? NULL : 'resCacheCode' |
540 | ); | |
541 | } | |
542 | ||
99098349 | 543 | /** |
34f3bbd9 | 544 | * @param \Symfony\Component\DependencyInjection\ContainerInterface $container |
99098349 TO |
545 | * @return \CRM_Core_PrevNextCache_Interface |
546 | */ | |
547 | public static function createPrevNextCache($container) { | |
e28bf654 | 548 | $setting = \Civi::settings()->get('prevNextBackend'); |
272dfdc3 | 549 | if (!$setting || $setting === 'default') { |
e28bf654 TO |
550 | $cacheDriver = \CRM_Utils_Cache::getCacheDriver(); |
551 | $service = 'prevnext.driver.' . strtolower($cacheDriver); | |
272dfdc3 | 552 | return $container->has($service) |
e28bf654 TO |
553 | ? $container->get($service) |
554 | : $container->get('prevnext.driver.sql'); | |
555 | } | |
272dfdc3 | 556 | return $container->get('prevnext.driver.' . $setting); |
99098349 TO |
557 | } |
558 | ||
272dfdc3 | 559 | /** |
560 | * @return \ArrayObject | |
561 | */ | |
751f3d98 TO |
562 | public static function createCacheConfig() { |
563 | $driver = \CRM_Utils_Cache::getCacheDriver(); | |
564 | $settings = \CRM_Utils_Cache::getCacheSettings($driver); | |
565 | $settings['driver'] = $driver; | |
566 | return new \ArrayObject($settings); | |
567 | } | |
568 | ||
83617886 TO |
569 | /** |
570 | * Get a list of boot services. | |
571 | * | |
572 | * These are services which must be setup *before* the container can operate. | |
573 | * | |
7f835399 | 574 | * @param bool $loadFromDB |
83617886 TO |
575 | * @throws \CRM_Core_Exception |
576 | */ | |
7f835399 | 577 | public static function boot($loadFromDB) { |
56eafc21 | 578 | // Array(string $serviceId => object $serviceInstance). |
c64f69d9 | 579 | $bootServices = []; |
7f835399 | 580 | \Civi::$statics[__CLASS__]['boot'] = &$bootServices; |
d4330c62 | 581 | |
56eafc21 | 582 | $bootServices['runtime'] = $runtime = new \CRM_Core_Config_Runtime(); |
7f835399 | 583 | $runtime->initialize($loadFromDB); |
d4330c62 | 584 | |
b1db062e H |
585 | $bootServices['paths'] = new \Civi\Core\Paths(); |
586 | ||
42ccedc7 | 587 | $bootServices['dispatcher.boot'] = new CiviEventDispatcher(); |
27e05c5c TO |
588 | |
589 | // Quality control: There should be no pre-boot hooks because they make it harder to understand/support/refactor. | |
590 | // If a pre-boot hook sneaks in, we'll raise an error. | |
591 | $bootDispatchPolicy = [ | |
592 | '/^hook_/' => 'not-ready', | |
593 | '/^civi\./' => 'run', | |
594 | ]; | |
595 | $mainDispatchPolicy = \CRM_Core_Config::isUpgradeMode() ? \CRM_Upgrade_DispatchPolicy::get('upgrade.main') : NULL; | |
596 | $bootServices['dispatcher.boot']->setDispatchPolicy($bootDispatchPolicy); | |
42ccedc7 | 597 | |
7f835399 | 598 | $class = $runtime->userFrameworkClass; |
56eafc21 | 599 | $bootServices['userSystem'] = $userSystem = new $class(); |
7f835399 TO |
600 | $userSystem->initialize(); |
601 | ||
602 | $userPermissionClass = 'CRM_Core_Permission_' . $runtime->userFramework; | |
56eafc21 | 603 | $bootServices['userPermissionClass'] = new $userPermissionClass(); |
7f835399 | 604 | |
c64f69d9 | 605 | $bootServices['cache.settings'] = \CRM_Utils_Cache::create([ |
56eafc21 | 606 | 'name' => 'settings', |
c64f69d9 CW |
607 | 'type' => ['*memory*', 'SqlGroup', 'ArrayCache'], |
608 | ]); | |
7f835399 | 609 | |
56eafc21 | 610 | $bootServices['settings_manager'] = new \Civi\Core\SettingsManager($bootServices['cache.settings']); |
7f835399 | 611 | |
56eafc21 | 612 | $bootServices['lockManager'] = self::createLockManager(); |
7f835399 TO |
613 | |
614 | if ($loadFromDB && $runtime->dsn) { | |
3a036b15 | 615 | \CRM_Core_DAO::init($runtime->dsn); |
edbcbd96 | 616 | \CRM_Utils_Hook::singleton(TRUE); |
7f835399 | 617 | \CRM_Extension_System::singleton(TRUE); |
15e47dfc | 618 | \CRM_Extension_System::singleton()->getClassLoader()->register(); |
8aff4734 | 619 | \CRM_Extension_System::singleton()->getMixinLoader()->run(); |
ebd1137d | 620 | \CRM_Utils_Hook::singleton()->commonBuildModuleList('civicrm_boot'); |
27e05c5c | 621 | $bootServices['dispatcher.boot']->setDispatchPolicy($mainDispatchPolicy); |
7f835399 | 622 | |
1b81ed50 | 623 | $runtime->includeCustomPath(); |
624 | ||
7f835399 | 625 | $c = new self(); |
56eafc21 TO |
626 | $container = $c->loadContainer(); |
627 | foreach ($bootServices as $name => $obj) { | |
628 | $container->set($name, $obj); | |
629 | } | |
630 | \Civi::$statics[__CLASS__]['container'] = $container; | |
acccf150 TO |
631 | // Ensure all container-based serivces have a chance to add their listeners. |
632 | // Without this, it's a matter of happenstance (dependent upon particular page-request/configuration/etc). | |
e103005b | 633 | $container->get('dispatcher'); |
634 | ||
83617886 | 635 | } |
27e05c5c TO |
636 | else { |
637 | $bootServices['dispatcher.boot']->setDispatchPolicy($mainDispatchPolicy); | |
638 | } | |
83617886 TO |
639 | } |
640 | ||
272dfdc3 | 641 | /** |
642 | * @param string $name | |
643 | * | |
644 | * @return mixed | |
645 | */ | |
83617886 | 646 | public static function getBootService($name) { |
56eafc21 | 647 | return \Civi::$statics[__CLASS__]['boot'][$name]; |
83617886 TO |
648 | } |
649 | ||
4d8e83b6 TO |
650 | /** |
651 | * Determine whether the container services are available. | |
652 | * | |
653 | * @return bool | |
654 | */ | |
655 | public static function isContainerBooted() { | |
656 | return isset(\Civi::$statics[__CLASS__]['container']); | |
657 | } | |
658 | ||
fa184193 | 659 | } |