Merge pull request #17470 from colemanw/array
[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,
be2fb01f 136 ];
fa184193
TO
137 $include_paths = implode(PATH_SEPARATOR, $include_paths);
138 set_include_path($include_paths . PATH_SEPARATOR . get_include_path());
7991d8ed 139 // @todo Why do we need to load this again?
45b293a2 140 $this->requireComposerAutoload();
6a488035
TO
141 }
142
7a9ab499
EM
143 /**
144 * Initialize HTML purifier class.
145 *
146 * @param string $prepend
147 */
00be9182 148 public function initHtmlPurifier($prepend) {
c6af44d7
C
149 if (class_exists('HTMLPurifier_Bootstrap')) {
150 // HTMLPurifier is already initialized, e.g. by the Drupal module.
151 return;
152 }
153
154 $htmlPurifierPath = $this->getHtmlPurifierPath();
155
156 if (FALSE === $htmlPurifierPath) {
157 // No HTMLPurifier available, e.g. during installation.
158 return;
159 }
160 require_once $htmlPurifierPath;
be2fb01f 161 spl_autoload_register(['HTMLPurifier_Bootstrap', 'autoload'], TRUE, $prepend);
c6af44d7
C
162 }
163
164 /**
165 * @return string|false
166 * Path to the file where the class HTMLPurifier_Bootstrap is defined, or
167 * FALSE, if such a file does not exist.
168 */
169 private function getHtmlPurifierPath() {
170 if (function_exists('libraries_get_path')
171 && ($path = libraries_get_path('htmlpurifier'))
172 && file_exists($file = $path . '/library/HTMLPurifier/Bootstrap.php')
173 ) {
174 // We are in Drupal 7, and the HTMLPurifier module is installed.
175 // Use Drupal's HTMLPurifier path, to avoid conflicts.
176 // @todo Verify that we are really in Drupal 7, and not in some other
177 // environment that happens to provide a 'libraries_get_path()' function.
178 return $file;
179 }
180
181 // we do this to prevent a autoloader errors with joomla / 3rd party packages
182 // Use absolute path, since we don't know the content of include_path yet.
183 // CRM-11304
3b8898e8
TO
184 if (isset($GLOBALS['civicrm_paths']['civicrm.packages']['path'])) {
185 $file = rtrim($GLOBALS['civicrm_paths']['civicrm.packages']['path'], DIRECTORY_SEPARATOR) . '/IDS/vendors/htmlpurifier/HTMLPurifier/Bootstrap.php';
186 }
187 else {
188 $file = dirname(__FILE__) . '/../../packages/IDS/vendors/htmlpurifier/HTMLPurifier/Bootstrap.php';
189 }
c6af44d7
C
190 if (file_exists($file)) {
191 return $file;
192 }
193
194 return FALSE;
195 }
196
a0ee3941
EM
197 /**
198 * @param $class
199 */
00be9182 200 public function loadClass($class) {
eb1afb93
ON
201 if ($class === 'CiviCRM_API3_Exception') {
202 //call internal error class api/Exception first
203 // allow api/Exception class call external error class
204 // CiviCRM_API3_Exception
205 require_once 'api/Exception.php';
206 }
6a488035
TO
207 if (
208 // Only load classes that clearly belong to CiviCRM.
8581f9ae 209 // Note: api/v3 does not use classes, but api_v3's test-suite does
dbaa9d7d 210 (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
211 // Do not load PHP 5.3 namespaced classes.
212 // (in a future version, maybe)
213 FALSE === strpos($class, '\\')
214 ) {
215 $file = strtr($class, '_', '/') . '.php';
216 // There is some question about the best way to do this.
217 // "require_once" is nice because it's simple and throws
a03a3680
TO
218 // intelligible errors.
219 if (FALSE != stream_resolve_include_path($file)) {
220 require_once $file;
221 }
6a488035 222 }
649d6dd0
TO
223 elseif (in_array($class, $this->civiTestClasses)) {
224 $file = "tests/phpunit/CiviTest/{$class}.php";
225 if (FALSE != stream_resolve_include_path($file)) {
226 require_once $file;
227 }
228 }
229 elseif ($class === 'CiviSeleniumSettings') {
230 if (!empty($GLOBALS['_CV'])) {
231 require_once 'tests/phpunit/CiviTest/CiviSeleniumSettings.auto.php';
232 }
233 elseif (CRM_Utils_File::isIncludable('tests/phpunit/CiviTest/CiviSeleniumSettings.php')) {
234 require_once 'tests/phpunit/CiviTest/CiviSeleniumSettings.php';
235 }
236 }
6a488035 237 }
a03a3680 238
6a488035 239}