Commit | Line | Data |
---|---|---|
fa184193 TO |
1 | <?php |
2 | namespace Civi\Core; | |
46bcf597 | 3 | |
10760fa1 | 4 | use Civi\Core\Lock\LockManager; |
fa184193 TO |
5 | use Doctrine\Common\Annotations\AnnotationReader; |
6 | use Doctrine\Common\Annotations\AnnotationRegistry; | |
7 | use Doctrine\Common\Annotations\FileCacheReader; | |
8 | use Doctrine\Common\Cache\FilesystemCache; | |
9 | use Doctrine\ORM\EntityManager; | |
10 | use Doctrine\ORM\Mapping\Driver\AnnotationDriver; | |
11 | use Doctrine\ORM\Tools\Setup; | |
40787e18 | 12 | use Symfony\Component\Config\ConfigCache; |
fa184193 | 13 | use Symfony\Component\DependencyInjection\ContainerBuilder; |
c8074a93 | 14 | use Symfony\Component\DependencyInjection\ContainerInterface; |
fa184193 | 15 | use Symfony\Component\DependencyInjection\Definition; |
40787e18 | 16 | use Symfony\Component\DependencyInjection\Dumper\PhpDumper; |
fa184193 | 17 | use Symfony\Component\DependencyInjection\Reference; |
40787e18 TO |
18 | use Symfony\Component\EventDispatcher\ContainerAwareEventDispatcher; |
19 | use Symfony\Component\EventDispatcher\DependencyInjection\RegisterListenersPass; | |
fa184193 TO |
20 | |
21 | // TODO use Symfony\Component\DependencyInjection\Loader\YamlFileLoader; | |
22 | ||
6550386a EM |
23 | /** |
24 | * Class Container | |
25 | * @package Civi\Core | |
26 | */ | |
fa184193 TO |
27 | class Container { |
28 | ||
29 | const SELF = 'civi_container_factory'; | |
30 | ||
31 | /** | |
32 | * @var ContainerBuilder | |
33 | */ | |
34 | private static $singleton; | |
35 | ||
36 | /** | |
04855556 TO |
37 | * @param bool $reset |
38 | * Whether to forcibly rebuild the entire container. | |
fa184193 TO |
39 | * @return \Symfony\Component\DependencyInjection\TaggedContainerInterface |
40 | */ | |
378e2654 TO |
41 | public static function singleton($reset = FALSE) { |
42 | if ($reset || self::$singleton === NULL) { | |
fa184193 | 43 | $c = new self(); |
40787e18 | 44 | self::$singleton = $c->loadContainer(); |
fa184193 TO |
45 | } |
46 | return self::$singleton; | |
47 | } | |
48 | ||
49 | /** | |
40787e18 TO |
50 | * Find a cached container definition or construct a new one. |
51 | * | |
52 | * There are many weird contexts in which Civi initializes (eg different | |
53 | * variations of multitenancy and different permutations of CMS/CRM bootstrap), | |
54 | * and hook_container may fire a bit differently in each context. To mitigate | |
55 | * risk of leaks between environments, we compute a unique envID | |
56 | * (md5(DB_NAME, HTTP_HOST, SCRIPT_FILENAME, etc)) and use separate caches for | |
57 | * each (eg "templates_c/CachedCiviContainer.$ENVID.php"). | |
58 | * | |
59 | * Constants: | |
60 | * - CIVICRM_CONTAINER_CACHE -- 'always' [default], 'never', 'auto' | |
61 | * - CIVICRM_DSN | |
62 | * - CIVICRM_DOMAIN_ID | |
63 | * - CIVICRM_TEMPLATE_COMPILEDIR | |
64 | * | |
65 | * @return ContainerInterface | |
66 | */ | |
67 | public function loadContainer() { | |
68 | // Note: The container's raison d'etre is to manage construction of other | |
69 | // services. Consequently, we assume a minimal service available -- the classloader | |
70 | // has been setup, and civicrm.settings.php is loaded, but nothing else works. | |
71 | ||
72 | $cacheMode = defined('CIVICRM_CONTAINER_CACHE') ? CIVICRM_CONTAINER_CACHE : 'always'; | |
73 | ||
74 | // In pre-installation environments, don't bother with caching. | |
75 | if (!defined('CIVICRM_TEMPLATE_COMPILEDIR') || !defined('CIVICRM_DSN') || $cacheMode === 'never' || \CRM_Utils_System::isInUpgradeMode()) { | |
76 | return $this->createContainer(); | |
77 | } | |
78 | ||
79 | $envId = md5(implode(\CRM_Core_DAO::VALUE_SEPARATOR, array( | |
80 | defined('CIVICRM_DOMAIN_ID') ? CIVICRM_DOMAIN_ID : 1, // e.g. one database, multi URL | |
81 | parse_url(CIVICRM_DSN, PHP_URL_PATH), // e.g. one codebase, multi database | |
82 | \CRM_Utils_Array::value('SCRIPT_FILENAME', $_SERVER, ''), // e.g. CMS vs extern vs installer | |
83 | \CRM_Utils_Array::value('HTTP_HOST', $_SERVER, ''), // e.g. name-based vhosts | |
84 | \CRM_Utils_Array::value('SERVER_PORT', $_SERVER, ''), // e.g. port-based vhosts | |
85 | // Depending on deployment arch, these signals *could* be redundant, but who cares? | |
86 | ))); | |
87 | $file = CIVICRM_TEMPLATE_COMPILEDIR . "/CachedCiviContainer.{$envId}.php"; | |
88 | $containerConfigCache = new ConfigCache($file, $cacheMode === 'auto'); | |
89 | ||
90 | if (!$containerConfigCache->isFresh()) { | |
91 | $containerBuilder = $this->createContainer(); | |
92 | $containerBuilder->compile(); | |
93 | $dumper = new PhpDumper($containerBuilder); | |
94 | $containerConfigCache->write( | |
95 | $dumper->dump(array('class' => 'CachedCiviContainer')), | |
96 | $containerBuilder->getResources() | |
97 | ); | |
98 | } | |
99 | ||
100 | require_once $file; | |
101 | $c = new \CachedCiviContainer(); | |
102 | $c->set('service_container', $c); | |
103 | return $c; | |
104 | } | |
105 | ||
106 | /** | |
107 | * Construct a new container. | |
108 | * | |
fa184193 | 109 | * @var ContainerBuilder |
77b97be7 | 110 | * @return \Symfony\Component\DependencyInjection\ContainerBuilder |
fa184193 TO |
111 | */ |
112 | public function createContainer() { | |
113 | $civicrm_base_path = dirname(dirname(__DIR__)); | |
114 | $container = new ContainerBuilder(); | |
40787e18 TO |
115 | $container->addCompilerPass(new RegisterListenersPass('dispatcher')); |
116 | $container->addObjectResource($this); | |
fa184193 | 117 | $container->setParameter('civicrm_base_path', $civicrm_base_path); |
40787e18 TO |
118 | //$container->set(self::SELF, $this); |
119 | $container->setDefinition(self::SELF, new Definition( | |
120 | 'Civi\Core\Container', | |
121 | array() | |
122 | )); | |
fa184193 | 123 | |
505d8b83 TO |
124 | // TODO Move configuration to an external file; define caching structure |
125 | // if (empty($configDirectories)) { | |
126 | // throw new \Exception(__CLASS__ . ': Missing required properties (civicrmRoot, configDirectories)'); | |
127 | // } | |
128 | // $locator = new FileLocator($configDirectories); | |
129 | // $loaderResolver = new LoaderResolver(array( | |
130 | // new YamlFileLoader($container, $locator) | |
131 | // )); | |
132 | // $delegatingLoader = new DelegatingLoader($loaderResolver); | |
133 | // foreach (array('services.yml') as $file) { | |
134 | // $yamlUserFiles = $locator->locate($file, NULL, FALSE); | |
135 | // foreach ($yamlUserFiles as $file) { | |
136 | // $delegatingLoader->load($file); | |
137 | // } | |
138 | // } | |
fa184193 | 139 | |
10760fa1 | 140 | $container->setDefinition('lockManager', new Definition( |
40787e18 | 141 | 'Civi\Core\Lock\LockManager', |
10760fa1 TO |
142 | array() |
143 | )) | |
144 | ->setFactoryService(self::SELF)->setFactoryMethod('createLockManager'); | |
145 | ||
16072ce1 | 146 | $container->setDefinition('angular', new Definition( |
40787e18 | 147 | 'Civi\Angular\Manager', |
16072ce1 TO |
148 | array() |
149 | )) | |
150 | ->setFactoryService(self::SELF)->setFactoryMethod('createAngularManager'); | |
151 | ||
fa184193 | 152 | $container->setDefinition('dispatcher', new Definition( |
40787e18 TO |
153 | 'Symfony\Component\EventDispatcher\ContainerAwareEventDispatcher', |
154 | array(new Reference('service_container')) | |
fa184193 TO |
155 | )) |
156 | ->setFactoryService(self::SELF)->setFactoryMethod('createEventDispatcher'); | |
157 | ||
c65db512 | 158 | $container->setDefinition('magic_function_provider', new Definition( |
40787e18 | 159 | 'Civi\API\Provider\MagicFunctionProvider', |
c65db512 TO |
160 | array() |
161 | )); | |
162 | ||
0f643fb2 | 163 | $container->setDefinition('civi_api_kernel', new Definition( |
40787e18 | 164 | 'Civi\API\Kernel', |
c65db512 | 165 | array(new Reference('dispatcher'), new Reference('magic_function_provider')) |
0f643fb2 TO |
166 | )) |
167 | ->setFactoryService(self::SELF)->setFactoryMethod('createApiKernel'); | |
168 | ||
7b4bbb34 | 169 | $container->setDefinition('cxn_reg_client', new Definition( |
40787e18 | 170 | 'Civi\Cxn\Rpc\RegistrationClient', |
7b4bbb34 TO |
171 | array() |
172 | )) | |
9ae2d27b | 173 | ->setFactoryClass('CRM_Cxn_BAO_Cxn')->setFactoryMethod('createRegistrationClient'); |
7b4bbb34 | 174 | |
6e5ad5ee TO |
175 | $container->setDefinition('psr_log', new Definition('CRM_Core_Error_Log', array())); |
176 | ||
a4704404 TO |
177 | foreach (array('settings', 'js_strings', 'community_messages') as $cacheName) { |
178 | $container->setDefinition("cache.{$cacheName}", new Definition( | |
179 | 'CRM_Utils_Cache_Interface', | |
180 | array( | |
181 | array( | |
182 | 'name' => $cacheName, | |
a944a143 | 183 | 'type' => array('*memory*', 'SqlGroup', 'ArrayCache'), |
a4704404 TO |
184 | ), |
185 | ) | |
186 | ))->setFactoryClass('CRM_Utils_Cache')->setFactoryMethod('create'); | |
187 | } | |
3a84c0ab TO |
188 | |
189 | $container->setDefinition('settings_manager', new Definition( | |
190 | 'Civi\Core\SettingsManager', | |
191 | array(new Reference('cache.settings')) | |
192 | )); | |
193 | ||
247eb841 TO |
194 | $container->setDefinition('pear_mail', new Definition('Mail')) |
195 | ->setFactoryClass('CRM_Utils_Mail')->setFactoryMethod('createMailer'); | |
196 | ||
c8074a93 TO |
197 | // Expose legacy singletons as services in the container. |
198 | $singletons = array( | |
199 | 'resources' => 'CRM_Core_Resources', | |
200 | 'httpClient' => 'CRM_Utils_HttpClient', | |
7b5937fe | 201 | 'cache.default' => 'CRM_Utils_Cache', |
c8074a93 TO |
202 | // Maybe? 'config' => 'CRM_Core_Config', |
203 | // Maybe? 'smarty' => 'CRM_Core_Smarty', | |
204 | ); | |
205 | foreach ($singletons as $name => $class) { | |
206 | $container->setDefinition($name, new Definition( | |
207 | $class | |
208 | )) | |
209 | ->setFactoryClass($class)->setFactoryMethod('singleton'); | |
210 | } | |
211 | ||
40787e18 TO |
212 | \CRM_Utils_Hook::container($container); |
213 | ||
fa184193 TO |
214 | return $container; |
215 | } | |
216 | ||
16072ce1 TO |
217 | /** |
218 | * @return \Civi\Angular\Manager | |
219 | */ | |
220 | public function createAngularManager() { | |
221 | return new \Civi\Angular\Manager(\CRM_Core_Resources::singleton()); | |
222 | } | |
223 | ||
fa184193 | 224 | /** |
40787e18 | 225 | * @param ContainerInterface $container |
fa184193 TO |
226 | * @return \Symfony\Component\EventDispatcher\EventDispatcher |
227 | */ | |
40787e18 TO |
228 | public function createEventDispatcher($container) { |
229 | $dispatcher = new ContainerAwareEventDispatcher($container); | |
708d8fa2 | 230 | $dispatcher->addListener('hook_civicrm_post::Activity', array('\Civi\CCase\Events', 'fireCaseChange')); |
753657ed | 231 | $dispatcher->addListener('hook_civicrm_post::Case', array('\Civi\CCase\Events', 'fireCaseChange')); |
708d8fa2 | 232 | $dispatcher->addListener('hook_civicrm_caseChange', array('\Civi\CCase\Events', 'delegateToXmlListeners')); |
b019b130 | 233 | $dispatcher->addListener('hook_civicrm_caseChange', array('\Civi\CCase\SequenceListener', 'onCaseChange_static')); |
8498c2b7 | 234 | $dispatcher->addListener('DAO::post-insert', array('\CRM_Core_BAO_RecurringEntity', 'triggerInsert')); |
235 | $dispatcher->addListener('DAO::post-update', array('\CRM_Core_BAO_RecurringEntity', 'triggerUpdate')); | |
236 | $dispatcher->addListener('DAO::post-delete', array('\CRM_Core_BAO_RecurringEntity', 'triggerDelete')); | |
46bcf597 | 237 | $dispatcher->addListener('hook_civicrm_unhandled_exception', array( |
9ae2d27b TO |
238 | 'CRM_Core_LegacyErrorHandler', |
239 | 'handleException', | |
240 | )); | |
fa184193 TO |
241 | return $dispatcher; |
242 | } | |
0f643fb2 | 243 | |
10760fa1 TO |
244 | /** |
245 | * @return LockManager | |
246 | */ | |
247 | public function createLockManager() { | |
248 | // Ideally, downstream implementers could override any definitions in | |
249 | // the container. For now, we'll make-do with some define()s. | |
250 | $lm = new LockManager(); | |
251 | $lm | |
252 | ->register('/^cache\./', defined('CIVICRM_CACHE_LOCK') ? CIVICRM_CACHE_LOCK : array('CRM_Core_Lock', 'createScopedLock')) | |
253 | ->register('/^data\./', defined('CIVICRM_DATA_LOCK') ? CIVICRM_DATA_LOCK : array('CRM_Core_Lock', 'createScopedLock')) | |
254 | ->register('/^worker\.mailing\.send\./', defined('CIVICRM_WORK_LOCK') ? CIVICRM_WORK_LOCK : array('CRM_Core_Lock', 'createCivimailLock')) | |
255 | ->register('/^worker\./', defined('CIVICRM_WORK_LOCK') ? CIVICRM_WORK_LOCK : array('CRM_Core_Lock', 'createScopedLock')); | |
256 | ||
257 | // Registrations may use complex resolver expressions, but (as a micro-optimization) | |
258 | // the default factory is specified as an array. | |
259 | ||
260 | return $lm; | |
261 | } | |
262 | ||
0f643fb2 TO |
263 | /** |
264 | * @param \Symfony\Component\EventDispatcher\EventDispatcher $dispatcher | |
2a6da8d7 EM |
265 | * @param $magicFunctionProvider |
266 | * | |
0f643fb2 TO |
267 | * @return \Civi\API\Kernel |
268 | */ | |
c65db512 | 269 | public function createApiKernel($dispatcher, $magicFunctionProvider) { |
0a946de2 | 270 | $dispatcher->addSubscriber(new \Civi\API\Subscriber\ChainSubscriber()); |
b55bc593 | 271 | $dispatcher->addSubscriber(new \Civi\API\Subscriber\TransactionSubscriber()); |
bace5cd9 | 272 | $dispatcher->addSubscriber(new \Civi\API\Subscriber\I18nSubscriber()); |
c65db512 | 273 | $dispatcher->addSubscriber($magicFunctionProvider); |
d0c9daa4 | 274 | $dispatcher->addSubscriber(new \Civi\API\Subscriber\PermissionCheck()); |
dcef11bd | 275 | $dispatcher->addSubscriber(new \Civi\API\Subscriber\APIv3SchemaAdapter()); |
6d3bdc98 TO |
276 | $dispatcher->addSubscriber(new \Civi\API\Subscriber\WrapperAdapter(array( |
277 | \CRM_Utils_API_HTMLInputCoder::singleton(), | |
278 | \CRM_Utils_API_NullOutputCoder::singleton(), | |
279 | \CRM_Utils_API_ReloadOption::singleton(), | |
280 | \CRM_Utils_API_MatchOption::singleton(), | |
281 | ))); | |
0661f62b | 282 | $dispatcher->addSubscriber(new \Civi\API\Subscriber\XDebugSubscriber()); |
82376c19 TO |
283 | $kernel = new \Civi\API\Kernel($dispatcher); |
284 | ||
285 | $reflectionProvider = new \Civi\API\Provider\ReflectionProvider($kernel); | |
286 | $dispatcher->addSubscriber($reflectionProvider); | |
287 | ||
56154d36 TO |
288 | $dispatcher->addSubscriber(new \Civi\API\Subscriber\DynamicFKAuthorization( |
289 | $kernel, | |
290 | 'Attachment', | |
291 | array('create', 'get', 'delete'), | |
2e37a19f | 292 | // Given a file ID, determine the entity+table it's attached to. |
56154d36 TO |
293 | 'SELECT if(cf.id,1,0) as is_valid, cef.entity_table, cef.entity_id |
294 | FROM civicrm_file cf | |
295 | LEFT JOIN civicrm_entity_file cef ON cf.id = cef.file_id | |
296 | WHERE cf.id = %1', | |
29468114 TO |
297 | // Get a list of custom fields (field_name,table_name,extends) |
298 | 'SELECT concat("custom_",fld.id) as field_name, | |
299 | grp.table_name as table_name, | |
300 | grp.extends as extends | |
301 | FROM civicrm_custom_field fld | |
302 | INNER JOIN civicrm_custom_group grp ON fld.custom_group_id = grp.id | |
303 | WHERE fld.data_type = "File" | |
304 | ', | |
e3e66815 | 305 | array('civicrm_activity', 'civicrm_mailing', 'civicrm_contact', 'civicrm_grant') |
56154d36 TO |
306 | )); |
307 | ||
82376c19 TO |
308 | $kernel->setApiProviders(array( |
309 | $reflectionProvider, | |
310 | $magicFunctionProvider, | |
311 | )); | |
312 | ||
0f643fb2 TO |
313 | return $kernel; |
314 | } | |
96025800 | 315 | |
fa184193 | 316 | } |