(REF) Mixins - Move more aspects of scanning from MixinLoader to MixinScanner
[civicrm-core.git] / CRM / Extension / MixinLoader.php
1 <?php
2 /*
3 +--------------------------------------------------------------------+
4 | Copyright CiviCRM LLC. All rights reserved. |
5 | |
6 | This work is published under the GNU AGPLv3 license with some |
7 | permitted exceptions and without any warranty. For full license |
8 | and copyright information, see https://civicrm.org/licensing |
9 +--------------------------------------------------------------------+
10 */
11
12 /**
13 * The MixinLoader tracks a list of extensions and mixins.
14 */
15 class CRM_Extension_MixinLoader {
16
17 /**
18 * Load all extensions and call their respective function-files.
19 *
20 * @throws \CRM_Core_Exception
21 */
22 public function run(CRM_Extension_BootCache $bootCache, array $liveFuncFiles, array $mixInfos): void {
23 // == WIP ==
24 //
25 //Do mixins run strictly once (during boot)? Or could they run twice? Or incrementally? Some edge-cases:
26 // - Mixins should make changes via dispatcher() and container(). If there's a Civi::reset(), then these things go away. We'll need to
27 // re-register. (Example scenario: unit-testing)
28 // - Mixins register for every active module. If a new module is enabled, then we haven't had a chance to run on the new extension.
29 // - Mixins register for every active module. If an old module is disabled, then there may be old listeners/services lingering.
30 if (!isset(\Civi::$statics[__CLASS__]['done'])) {
31 \Civi::$statics[__CLASS__]['done'] = [];
32 }
33 $done = &\Civi::$statics[__CLASS__]['done'];
34
35 // Read each live func-file once, even if there's some kind of Civi::reset(). This avoids hard-crash where the func-file registers a PHP class/function/interface.
36 // Granted, PHP symbols require care to avoid conflicts between `mymixin@1.0` and `mymixin@2.0` -- but you can deal with that. For minor-versions, you're
37 // safe because we deduplicate.
38 static $funcsByFile = [];
39 foreach ($liveFuncFiles as $verExpr => $file) {
40 if (!isset($funcsByFile[$file])) {
41 $func = include_once $file;
42 if (is_callable($func)) {
43 $funcsByFile[$file] = $func;
44 }
45 else {
46 error_log(sprintf('MixinLoader: Received invalid callback from \"%s\"', $file));
47 }
48 }
49 }
50
51 foreach ($mixInfos as $ext) {
52 /** @var \CRM_Extension_MixInfo $ext */
53 foreach ($ext->mixins as $verExpr) {
54 $doneId = $ext->longName . '::' . $verExpr;
55 if (isset($done[$doneId])) {
56 continue;
57 }
58 if (isset($funcsByFile[$liveFuncFiles[$verExpr]])) {
59 call_user_func($funcsByFile[$liveFuncFiles[$verExpr]], $ext, $bootCache);
60 $done[$doneId] = 1;
61 }
62 else {
63 error_log(sprintf('MixinLoader: Failed to load "%s" for extension "%s"', $verExpr, $ext->longName));
64 }
65 }
66 }
67 }
68
69 }