3 +--------------------------------------------------------------------+
4 | Copyright CiviCRM LLC. All rights reserved. |
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 +--------------------------------------------------------------------+
13 * The MixinScanner scans the list of actives extensions and their required mixins.
15 class CRM_Extension_MixinScanner
{
18 * @var CRM_Extension_Mapper
23 * @var CRM_Extension_Manager
29 * A list of base-paths which are implicitly supported by 'include' directives.
30 * Sorted with the longest paths first.
32 protected $relativeBases;
35 * CRM_Extension_ClassLoader constructor.
36 * @param \CRM_Extension_Mapper|NULL $mapper
37 * @param \CRM_Extension_Manager|NULL $manager
38 * @param bool $relativize
39 * Whether to store paths in relative form.
40 * Enabling this may slow-down scanning a bit, and it has no benefit when for on-demand loaders.
41 * However, if the loader is cached, then it may make for smaller, more portable cache-file.
43 public function __construct(?\CRM_Extension_Mapper
$mapper = NULL, \CRM_Extension_Manager
$manager = NULL, $relativize = TRUE) {
44 $this->mapper
= $mapper ?
: CRM_Extension_System
::singleton()->getMapper();
45 $this->manager
= $manager ?
: CRM_Extension_System
::singleton()->getManager();
47 $this->relativeBases
= [Civi
::paths()->getVariable('civicrm.root', 'path')];
48 // Previous drafts used `relativeBases=explode(include_path)`. However, this produces unstable results
49 // when flip through the phases of the lifecycle - because the include_path changes throughout the lifecycle.
50 usort($this->relativeBases
, function($a, $b) {
51 return strlen($b) - strlen($a);
55 $this->relativeBases
= NULL;
60 * @return \CRM_Extension_MixinLoader
62 public function createLoader() {
63 $l = new CRM_Extension_MixinLoader();
65 foreach ($this->getInstalledKeys() as $key) {
67 $path = $this->mapper
->keyToBasePath($key);
68 $l->addMixInfo($this->createMixInfo($path . DIRECTORY_SEPARATOR
. CRM_Extension_Info
::FILENAME
));
69 $l->addFunctionFiles($this->findFunctionFiles("$path/mixin/*@*.mixin.php"));
70 $l->addFunctionFiles($this->findFunctionFiles("$path/mixin/*@*/mixin.php"), TRUE);
72 catch (CRM_Extension_Exception_ParseException
$e) {
73 error_log(sprintf('MixinScanner: Failed to read extension (%s)', $key));
77 $l->addFunctionFiles($this->findFunctionFiles(Civi
::paths()->getPath('[civicrm.root]/mixin/*@*.mixin.php')));
78 $l->addFunctionFiles($this->findFunctionFiles(Civi
::paths()->getPath('[civicrm.root]/mixin/*@*/mixin.php')), TRUE);
86 private function getInstalledKeys() {
89 $statuses = $this->manager
->getStatuses();
91 foreach ($statuses as $key => $status) {
92 if ($status === CRM_Extension_Manager
::STATUS_INSTALLED
) {
101 * @param string $infoFile
102 * Path to the 'info.xml' file
103 * @return \CRM_Extension_MixInfo
104 * @throws \CRM_Extension_Exception_ParseException
106 private function createMixInfo(string $infoFile) {
107 $info = CRM_Extension_Info
::loadFromFile($infoFile);
108 $instance = new CRM_Extension_MixInfo();
109 $instance->longName
= $info->key
;
110 $instance->shortName
= $info->file
;
111 $instance->path
= rtrim(dirname($infoFile), '/' . DIRECTORY_SEPARATOR
);
112 $instance->mixins
= $info->mixins
;
117 * @param string $globPat
119 * Ex: ['mix/xml-menu-autoload@1.0.mixin.php']
121 private function findFunctionFiles($globPat) {
122 $useRel = $this->relativeBases
!== NULL;
124 $funcFiles = (array) glob($globPat);
126 foreach ($funcFiles as $shimFile) {
127 $shimFileRel = $useRel ?
$this->relativize($shimFile) : $shimFile;
128 $result[] = $shimFileRel;
134 * Convert the absolute $file to an expression that is supported by 'include'.
136 * @param string $file
139 private function relativize($file) {
140 foreach ($this->relativeBases
as $relativeBase) {
141 if (CRM_Utils_File
::isChildPath($relativeBase, $file)) {
142 return ltrim(CRM_Utils_File
::relativize($file, $relativeBase), '/' . DIRECTORY_SEPARATOR
);