Merge pull request #19840 from MikeyMJCO/patch-7
[civicrm-core.git] / CRM / Extension / ClassLoader.php
CommitLineData
4025c773
TO
1<?php
2/*
3 +--------------------------------------------------------------------+
bc77d7c0 4 | Copyright CiviCRM LLC. All rights reserved. |
4025c773 5 | |
bc77d7c0
TO
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 |
4025c773
TO
9 +--------------------------------------------------------------------+
10 */
11
12/**
016d95d3 13 * Class CRM_Extension_ClassLoader
4025c773
TO
14 */
15class CRM_Extension_ClassLoader {
16
a76a800b
TO
17 /**
18 * List of class-loader features that are valid in this version of Civi.
19 *
20 * This may be useful for some extensions which enable/disable polyfills based on environment.
21 */
22 const FEATURES = ',psr0,psr4,';
23
4025c773
TO
24 /**
25 * @var CRM_Extension_Mapper
26 */
27 protected $mapper;
28
29 /**
30 * @var CRM_Extension_Container_Interface
31 */
32 protected $container;
33
34 /**
35 * @var CRM_Extension_Manager
36 */
37 protected $manager;
38
39 /**
40 * @var \Composer\Autoload\ClassLoader
41 */
42 protected $loader;
43
44 /**
45 * CRM_Extension_ClassLoader constructor.
46 * @param \CRM_Extension_Mapper $mapper
47 * @param \CRM_Extension_Container_Interface $container
48 * @param \CRM_Extension_Manager $manager
49 */
50 public function __construct(\CRM_Extension_Mapper $mapper, \CRM_Extension_Container_Interface $container, \CRM_Extension_Manager $manager) {
51 $this->mapper = $mapper;
52 $this->container = $container;
53 $this->manager = $manager;
54 }
55
56 public function __destruct() {
85c7eb67 57 $this->unregister();
4025c773
TO
58 }
59
60 /**
61 * Registers this instance as an autoloader.
14069c56 62 * @return CRM_Extension_ClassLoader
4025c773
TO
63 */
64 public function register() {
65 // In pre-installation environments, don't bother with caching.
6f50d29c 66 if (!defined('CIVICRM_DSN') || defined('CIVICRM_TEST') || \CRM_Utils_System::isInUpgradeMode()) {
d1f58c6b
TO
67 $this->loader = $this->buildClassLoader();
68 return $this->loader->register();
4025c773
TO
69 }
70
85c7eb67 71 $file = $this->getCacheFile();
4025c773 72 if (file_exists($file)) {
d1f58c6b 73 $this->loader = require $file;
4025c773
TO
74 }
75 else {
d1f58c6b
TO
76 $this->loader = $this->buildClassLoader();
77 $ser = serialize($this->loader);
4025c773
TO
78 file_put_contents($file,
79 sprintf("<?php\nreturn unserialize(%s);", var_export($ser, 1))
80 );
81 }
d1f58c6b 82 return $this->loader->register();
4025c773
TO
83 }
84
85 /**
86 * @return \Composer\Autoload\ClassLoader
87 * @throws \CRM_Extension_Exception
88 * @throws \Exception
89 */
90 public function buildClassLoader() {
91 $loader = new \Composer\Autoload\ClassLoader();
92
93 $statuses = $this->manager->getStatuses();
94 foreach ($statuses as $key => $status) {
95 if ($status !== CRM_Extension_Manager::STATUS_INSTALLED) {
96 continue;
97 }
d1f58c6b 98 self::loadExtension($loader, $this->mapper->keyToInfo($key), $this->mapper->keyToBasePath($key));
4025c773
TO
99 }
100
101 return $loader;
102 }
103
85c7eb67
TO
104 public function unregister() {
105 if ($this->loader) {
106 $this->loader->unregister();
107 $this->loader = NULL;
108 }
109 }
110
111 public function refresh() {
112 $this->unregister();
113 $file = $this->getCacheFile();
114 if (file_exists($file)) {
115 unlink($file);
116 }
117 $this->register();
118 }
119
d1f58c6b
TO
120 /**
121 * Add a newly installed extension to the active classloader.
122 *
123 * NOTE: This is intended for use by CRM/Extension subsystem during installation.
124 *
125 * @param \CRM_Extension_Info $info
126 * @param string $path
127 */
128 public function installExtension(CRM_Extension_Info $info, string $path): void {
129 $file = $this->getCacheFile();
130 if (file_exists($file)) {
131 unlink($file);
132 }
133 if ($this->loader) {
134 self::loadExtension($this->loader, $info, $path);
135 }
136 }
137
138 /**
139 * Read the extension metadata configure a classloader.
140 *
141 * @param \Composer\Autoload\ClassLoader $loader
142 * @param \CRM_Extension_Info $info
143 * @param string $path
144 */
145 private static function loadExtension(\Composer\Autoload\ClassLoader $loader, CRM_Extension_Info $info, string $path): void {
146 if (!empty($info->classloader)) {
147 foreach ($info->classloader as $mapping) {
148 switch ($mapping['type']) {
149 case 'psr0':
150 $loader->add($mapping['prefix'], CRM_Utils_File::addTrailingSlash($path . '/' . $mapping['path']));
151 break;
152
153 case 'psr4':
154 $loader->addPsr4($mapping['prefix'], $path . '/' . $mapping['path']);
155 break;
156 }
157 }
158 }
159 }
160
85c7eb67
TO
161 /**
162 * @return string
163 */
164 protected function getCacheFile() {
165 $envId = \CRM_Core_Config_Runtime::getId();
6f50d29c 166 $file = \Civi::paths()->getPath("[civicrm.compile]/CachedExtLoader.{$envId}.php");
85c7eb67
TO
167 return $file;
168 }
169
4025c773 170}