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