Merge pull request #21575 from agh1/5.42.0-releasenotes-initial
[civicrm-core.git] / CRM / Core / ClassLoader.php
CommitLineData
6a488035
TO
1<?php
2/*
3 +--------------------------------------------------------------------+
bc77d7c0 4 | Copyright CiviCRM LLC. All rights reserved. |
6a488035 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 |
6a488035 9 +--------------------------------------------------------------------+
d25dd0ee 10 */
6a488035
TO
11
12/**
13 *
14 *
15 * @package CRM
ca5cec67 16 * @copyright CiviCRM LLC https://civicrm.org/licensing
6a488035
TO
17 */
18class CRM_Core_ClassLoader {
19
20 /**
21 * We only need one instance of this object. So we use the singleton
22 * pattern and cache the instance in this variable
23 * @var object
6a488035
TO
24 */
25 private static $_singleton = NULL;
26
649d6dd0
TO
27 /**
28 * The classes in CiviTest have ucky, non-standard naming.
29 *
30 * @var array
31 * Array(string $className => string $filePath).
32 */
33 private $civiTestClasses;
34
a0ee3941
EM
35 /**
36 * @param bool $force
37 *
38 * @return object
39 */
00be9182 40 public static function &singleton($force = FALSE) {
6a488035
TO
41 if ($force || self::$_singleton === NULL) {
42 self::$_singleton = new CRM_Core_ClassLoader();
43 }
44 return self::$_singleton;
45 }
46
47 /**
e97c66ff 48 * Has this been registered already.
49 *
50 * @var bool
6a488035
TO
51 */
52 protected $_registered;
53
a0ee3941 54 /**
e97c66ff 55 * Class constructor.
a0ee3941 56 */
6a488035
TO
57 protected function __construct() {
58 $this->_registered = FALSE;
be2fb01f 59 $this->civiTestClasses = [
649d6dd0
TO
60 'CiviCaseTestCase',
61 'CiviDBAssert',
62 'CiviMailUtils',
63 'CiviReportTestCase',
64 'CiviSeleniumTestCase',
649d6dd0
TO
65 'CiviTestSuite',
66 'CiviUnitTestCase',
9e80c052 67 'CiviEndToEndTestCase',
649d6dd0
TO
68 'Contact',
69 'ContributionPage',
70 'Custom',
71 'Event',
72 'Membership',
73 'Participant',
74 'PaypalPro',
be2fb01f 75 ];
6a488035
TO
76 }
77
45b293a2
DS
78 /**
79 * Requires the autoload.php generated by composer
80 *
81 * @return void
82 */
83 protected function requireComposerAutoload() {
d1f60655
DS
84 // We are trying to locate 'vendor/autoload.php'. When installing CiviCRM
85 // manually from the built tarball, that will be two directories up in the
86 // civicrm-core directory. However, if civicrm-core was installed via
87 // composer as a library, that'll be 5 directories up where composer was
88 // run (ex. the Drupal root on a Drupal 8 site).
9f1588f9
DS
89 $civicrm_base_path = dirname(dirname(__DIR__));
90 $top_path = dirname(dirname(dirname(dirname(dirname(__DIR__)))));
45b293a2
DS
91
92 if (file_exists($civicrm_base_path . '/vendor/autoload.php')) {
93 require_once $civicrm_base_path . '/vendor/autoload.php';
94 }
95 elseif (file_exists($top_path . '/vendor/autoload.php')) {
96 require_once $top_path . '/vendor/autoload.php';
97 }
98 }
99
6a488035
TO
100 /**
101 * Registers this instance as an autoloader.
102 *
6a0b768e
TO
103 * @param bool $prepend
104 * Whether to prepend the autoloader or not.
6a488035
TO
105 *
106 * @api
107 */
00be9182 108 public function register($prepend = FALSE) {
6a488035
TO
109 if ($this->_registered) {
110 return;
111 }
fa184193
TO
112 $civicrm_base_path = dirname(dirname(__DIR__));
113
45b293a2 114 $this->requireComposerAutoload();
6a488035
TO
115
116 // we do this to prevent a autoloader errors with joomla / 3rd party packages
117 // use absolute path since we dont know the content of include_path as yet
118 // CRM-11304
fa184193
TO
119 // TODO Remove this autoloader. For civicrm-core and civicrm-packages, the composer autoloader works fine.
120 // Extensions rely on include_path-based autoloading
be2fb01f 121 spl_autoload_register([$this, 'loadClass'], TRUE, $prepend);
c6af44d7 122 $this->initHtmlPurifier($prepend);
6a488035
TO
123
124 $this->_registered = TRUE;
3b8898e8
TO
125 // The ClassLoader runs before the classes are available. Approximate Civi::paths()->get('[civicrm.packages]').
126 if (isset($GLOBALS['civicrm_paths']['civicrm.packages']['path'])) {
127 $packages_path = rtrim($GLOBALS['civicrm_paths']['civicrm.packages']['path'], DIRECTORY_SEPARATOR);
128 }
129 else {
130 $packages_path = implode(DIRECTORY_SEPARATOR, [$civicrm_base_path, 'packages']);
131 }
be2fb01f 132 $include_paths = [
fa184193
TO
133 '.',
134 $civicrm_base_path,
21dfd5f5 135 $packages_path,
6c83ef43
SL
136 // dev/core#2812 Ensure that the database upgrade script can run if dataprocessor extension is enabled. It relies on the legacy custom searches which have been moved into this extension
137 implode(DIRECTORY_SEPARATOR, [$civicrm_base_path, 'ext', 'legacycustomsearches']),
be2fb01f 138 ];
fa184193
TO
139 $include_paths = implode(PATH_SEPARATOR, $include_paths);
140 set_include_path($include_paths . PATH_SEPARATOR . get_include_path());
7991d8ed 141 // @todo Why do we need to load this again?
45b293a2 142 $this->requireComposerAutoload();
6a488035
TO
143 }
144
7a9ab499
EM
145 /**
146 * Initialize HTML purifier class.
147 *
148 * @param string $prepend
149 */
00be9182 150 public function initHtmlPurifier($prepend) {
c6af44d7
C
151 if (class_exists('HTMLPurifier_Bootstrap')) {
152 // HTMLPurifier is already initialized, e.g. by the Drupal module.
153 return;
154 }
155
156 $htmlPurifierPath = $this->getHtmlPurifierPath();
157
158 if (FALSE === $htmlPurifierPath) {
159 // No HTMLPurifier available, e.g. during installation.
160 return;
161 }
162 require_once $htmlPurifierPath;
be2fb01f 163 spl_autoload_register(['HTMLPurifier_Bootstrap', 'autoload'], TRUE, $prepend);
c6af44d7
C
164 }
165
166 /**
167 * @return string|false
168 * Path to the file where the class HTMLPurifier_Bootstrap is defined, or
169 * FALSE, if such a file does not exist.
170 */
171 private function getHtmlPurifierPath() {
172 if (function_exists('libraries_get_path')
173 && ($path = libraries_get_path('htmlpurifier'))
174 && file_exists($file = $path . '/library/HTMLPurifier/Bootstrap.php')
175 ) {
176 // We are in Drupal 7, and the HTMLPurifier module is installed.
177 // Use Drupal's HTMLPurifier path, to avoid conflicts.
178 // @todo Verify that we are really in Drupal 7, and not in some other
179 // environment that happens to provide a 'libraries_get_path()' function.
180 return $file;
181 }
182
183 // we do this to prevent a autoloader errors with joomla / 3rd party packages
184 // Use absolute path, since we don't know the content of include_path yet.
185 // CRM-11304
3b8898e8
TO
186 if (isset($GLOBALS['civicrm_paths']['civicrm.packages']['path'])) {
187 $file = rtrim($GLOBALS['civicrm_paths']['civicrm.packages']['path'], DIRECTORY_SEPARATOR) . '/IDS/vendors/htmlpurifier/HTMLPurifier/Bootstrap.php';
188 }
189 else {
190 $file = dirname(__FILE__) . '/../../packages/IDS/vendors/htmlpurifier/HTMLPurifier/Bootstrap.php';
191 }
c6af44d7
C
192 if (file_exists($file)) {
193 return $file;
194 }
195
196 return FALSE;
197 }
198
a0ee3941
EM
199 /**
200 * @param $class
201 */
00be9182 202 public function loadClass($class) {
fcb47cfd 203 if ($class === 'CiviCRM_API3_Exception' || $class === 'API_Exception') {
eb1afb93
ON
204 //call internal error class api/Exception first
205 // allow api/Exception class call external error class
206 // CiviCRM_API3_Exception
207 require_once 'api/Exception.php';
208 }
6a488035
TO
209 if (
210 // Only load classes that clearly belong to CiviCRM.
8581f9ae 211 // Note: api/v3 does not use classes, but api_v3's test-suite does
dbaa9d7d 212 (0 === strncmp($class, 'CRM_', 4) || 0 === strncmp($class, 'CRMTraits', 9) || 0 === strncmp($class, 'api_v3_', 7) || 0 === strncmp($class, 'WebTest_', 8) || 0 === strncmp($class, 'E2E_', 4)) &&
6a488035
TO
213 // Do not load PHP 5.3 namespaced classes.
214 // (in a future version, maybe)
215 FALSE === strpos($class, '\\')
216 ) {
217 $file = strtr($class, '_', '/') . '.php';
218 // There is some question about the best way to do this.
219 // "require_once" is nice because it's simple and throws
a03a3680
TO
220 // intelligible errors.
221 if (FALSE != stream_resolve_include_path($file)) {
222 require_once $file;
223 }
6a488035 224 }
649d6dd0
TO
225 elseif (in_array($class, $this->civiTestClasses)) {
226 $file = "tests/phpunit/CiviTest/{$class}.php";
227 if (FALSE != stream_resolve_include_path($file)) {
228 require_once $file;
229 }
230 }
231 elseif ($class === 'CiviSeleniumSettings') {
232 if (!empty($GLOBALS['_CV'])) {
233 require_once 'tests/phpunit/CiviTest/CiviSeleniumSettings.auto.php';
234 }
235 elseif (CRM_Utils_File::isIncludable('tests/phpunit/CiviTest/CiviSeleniumSettings.php')) {
236 require_once 'tests/phpunit/CiviTest/CiviSeleniumSettings.php';
237 }
238 }
6a488035 239 }
a03a3680 240
6a488035 241}