Merge pull request #14667 from hoegrammer/master
[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 * $Id$
18 *
19 */
20class CRM_Core_ClassLoader {
21
22 /**
23 * We only need one instance of this object. So we use the singleton
24 * pattern and cache the instance in this variable
25 * @var object
6a488035
TO
26 */
27 private static $_singleton = NULL;
28
649d6dd0
TO
29 /**
30 * The classes in CiviTest have ucky, non-standard naming.
31 *
32 * @var array
33 * Array(string $className => string $filePath).
34 */
35 private $civiTestClasses;
36
a0ee3941
EM
37 /**
38 * @param bool $force
39 *
40 * @return object
41 */
00be9182 42 public static function &singleton($force = FALSE) {
6a488035
TO
43 if ($force || self::$_singleton === NULL) {
44 self::$_singleton = new CRM_Core_ClassLoader();
45 }
46 return self::$_singleton;
47 }
48
49 /**
e97c66ff 50 * Has this been registered already.
51 *
52 * @var bool
6a488035
TO
53 */
54 protected $_registered;
55
a0ee3941 56 /**
e97c66ff 57 * Class constructor.
a0ee3941 58 */
6a488035
TO
59 protected function __construct() {
60 $this->_registered = FALSE;
be2fb01f 61 $this->civiTestClasses = [
649d6dd0
TO
62 'CiviCaseTestCase',
63 'CiviDBAssert',
64 'CiviMailUtils',
65 'CiviReportTestCase',
66 'CiviSeleniumTestCase',
649d6dd0
TO
67 'CiviTestSuite',
68 'CiviUnitTestCase',
9e80c052 69 'CiviEndToEndTestCase',
649d6dd0
TO
70 'Contact',
71 'ContributionPage',
72 'Custom',
73 'Event',
74 'Membership',
75 'Participant',
76 'PaypalPro',
be2fb01f 77 ];
6a488035
TO
78 }
79
45b293a2
DS
80 /**
81 * Requires the autoload.php generated by composer
82 *
83 * @return void
84 */
85 protected function requireComposerAutoload() {
d1f60655
DS
86 // We are trying to locate 'vendor/autoload.php'. When installing CiviCRM
87 // manually from the built tarball, that will be two directories up in the
88 // civicrm-core directory. However, if civicrm-core was installed via
89 // composer as a library, that'll be 5 directories up where composer was
90 // run (ex. the Drupal root on a Drupal 8 site).
9f1588f9
DS
91 $civicrm_base_path = dirname(dirname(__DIR__));
92 $top_path = dirname(dirname(dirname(dirname(dirname(__DIR__)))));
45b293a2
DS
93
94 if (file_exists($civicrm_base_path . '/vendor/autoload.php')) {
95 require_once $civicrm_base_path . '/vendor/autoload.php';
96 }
97 elseif (file_exists($top_path . '/vendor/autoload.php')) {
98 require_once $top_path . '/vendor/autoload.php';
99 }
100 }
101
6a488035
TO
102 /**
103 * Registers this instance as an autoloader.
104 *
6a0b768e
TO
105 * @param bool $prepend
106 * Whether to prepend the autoloader or not.
6a488035
TO
107 *
108 * @api
109 */
00be9182 110 public function register($prepend = FALSE) {
6a488035
TO
111 if ($this->_registered) {
112 return;
113 }
fa184193
TO
114 $civicrm_base_path = dirname(dirname(__DIR__));
115
45b293a2 116 $this->requireComposerAutoload();
6a488035
TO
117
118 // we do this to prevent a autoloader errors with joomla / 3rd party packages
119 // use absolute path since we dont know the content of include_path as yet
120 // CRM-11304
fa184193
TO
121 // TODO Remove this autoloader. For civicrm-core and civicrm-packages, the composer autoloader works fine.
122 // Extensions rely on include_path-based autoloading
be2fb01f 123 spl_autoload_register([$this, 'loadClass'], TRUE, $prepend);
c6af44d7 124 $this->initHtmlPurifier($prepend);
6a488035
TO
125
126 $this->_registered = TRUE;
3b8898e8
TO
127 // The ClassLoader runs before the classes are available. Approximate Civi::paths()->get('[civicrm.packages]').
128 if (isset($GLOBALS['civicrm_paths']['civicrm.packages']['path'])) {
129 $packages_path = rtrim($GLOBALS['civicrm_paths']['civicrm.packages']['path'], DIRECTORY_SEPARATOR);
130 }
131 else {
132 $packages_path = implode(DIRECTORY_SEPARATOR, [$civicrm_base_path, 'packages']);
133 }
be2fb01f 134 $include_paths = [
fa184193
TO
135 '.',
136 $civicrm_base_path,
21dfd5f5 137 $packages_path,
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) {
eb1afb93
ON
203 if ($class === 'CiviCRM_API3_Exception') {
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}