Merge pull request #14280 from seamuslee001/dev_core_975
[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 /**
66 * @var bool TRUE if previously registered
67 */
68 protected $_registered;
69
a0ee3941 70 /**
a0ee3941 71 */
6a488035
TO
72 protected function __construct() {
73 $this->_registered = FALSE;
be2fb01f 74 $this->civiTestClasses = [
649d6dd0
TO
75 'CiviCaseTestCase',
76 'CiviDBAssert',
77 'CiviMailUtils',
78 'CiviReportTestCase',
79 'CiviSeleniumTestCase',
649d6dd0
TO
80 'CiviTestSuite',
81 'CiviUnitTestCase',
9e80c052 82 'CiviEndToEndTestCase',
649d6dd0
TO
83 'Contact',
84 'ContributionPage',
85 'Custom',
86 'Event',
87 'Membership',
88 'Participant',
89 'PaypalPro',
be2fb01f 90 ];
6a488035
TO
91 }
92
45b293a2
DS
93 /**
94 * Requires the autoload.php generated by composer
95 *
96 * @return void
97 */
98 protected function requireComposerAutoload() {
d1f60655
DS
99 // We are trying to locate 'vendor/autoload.php'. When installing CiviCRM
100 // manually from the built tarball, that will be two directories up in the
101 // civicrm-core directory. However, if civicrm-core was installed via
102 // composer as a library, that'll be 5 directories up where composer was
103 // run (ex. the Drupal root on a Drupal 8 site).
9f1588f9
DS
104 $civicrm_base_path = dirname(dirname(__DIR__));
105 $top_path = dirname(dirname(dirname(dirname(dirname(__DIR__)))));
45b293a2
DS
106
107 if (file_exists($civicrm_base_path . '/vendor/autoload.php')) {
108 require_once $civicrm_base_path . '/vendor/autoload.php';
109 }
110 elseif (file_exists($top_path . '/vendor/autoload.php')) {
111 require_once $top_path . '/vendor/autoload.php';
112 }
113 }
114
6a488035
TO
115 /**
116 * Registers this instance as an autoloader.
117 *
6a0b768e
TO
118 * @param bool $prepend
119 * Whether to prepend the autoloader or not.
6a488035
TO
120 *
121 * @api
122 */
00be9182 123 public function register($prepend = FALSE) {
6a488035
TO
124 if ($this->_registered) {
125 return;
126 }
fa184193
TO
127 $civicrm_base_path = dirname(dirname(__DIR__));
128
45b293a2 129 $this->requireComposerAutoload();
6a488035
TO
130
131 // we do this to prevent a autoloader errors with joomla / 3rd party packages
132 // use absolute path since we dont know the content of include_path as yet
133 // CRM-11304
fa184193
TO
134 // TODO Remove this autoloader. For civicrm-core and civicrm-packages, the composer autoloader works fine.
135 // Extensions rely on include_path-based autoloading
be2fb01f 136 spl_autoload_register([$this, 'loadClass'], TRUE, $prepend);
c6af44d7 137 $this->initHtmlPurifier($prepend);
6a488035
TO
138
139 $this->_registered = TRUE;
be2fb01f
CW
140 $packages_path = implode(DIRECTORY_SEPARATOR, [$civicrm_base_path, 'packages']);
141 $include_paths = [
fa184193
TO
142 '.',
143 $civicrm_base_path,
21dfd5f5 144 $packages_path,
be2fb01f 145 ];
fa184193
TO
146 $include_paths = implode(PATH_SEPARATOR, $include_paths);
147 set_include_path($include_paths . PATH_SEPARATOR . get_include_path());
7991d8ed 148 // @todo Why do we need to load this again?
45b293a2 149 $this->requireComposerAutoload();
6a488035
TO
150 }
151
7a9ab499
EM
152 /**
153 * Initialize HTML purifier class.
154 *
155 * @param string $prepend
156 */
00be9182 157 public function initHtmlPurifier($prepend) {
c6af44d7
C
158 if (class_exists('HTMLPurifier_Bootstrap')) {
159 // HTMLPurifier is already initialized, e.g. by the Drupal module.
160 return;
161 }
162
163 $htmlPurifierPath = $this->getHtmlPurifierPath();
164
165 if (FALSE === $htmlPurifierPath) {
166 // No HTMLPurifier available, e.g. during installation.
167 return;
168 }
169 require_once $htmlPurifierPath;
be2fb01f 170 spl_autoload_register(['HTMLPurifier_Bootstrap', 'autoload'], TRUE, $prepend);
c6af44d7
C
171 }
172
173 /**
174 * @return string|false
175 * Path to the file where the class HTMLPurifier_Bootstrap is defined, or
176 * FALSE, if such a file does not exist.
177 */
178 private function getHtmlPurifierPath() {
179 if (function_exists('libraries_get_path')
180 && ($path = libraries_get_path('htmlpurifier'))
181 && file_exists($file = $path . '/library/HTMLPurifier/Bootstrap.php')
182 ) {
183 // We are in Drupal 7, and the HTMLPurifier module is installed.
184 // Use Drupal's HTMLPurifier path, to avoid conflicts.
185 // @todo Verify that we are really in Drupal 7, and not in some other
186 // environment that happens to provide a 'libraries_get_path()' function.
187 return $file;
188 }
189
190 // we do this to prevent a autoloader errors with joomla / 3rd party packages
191 // Use absolute path, since we don't know the content of include_path yet.
192 // CRM-11304
1efcf255 193 $file = dirname(__FILE__) . '/../../packages/IDS/vendors/htmlpurifer/HTMLPurifier/Bootstrap.php';
c6af44d7
C
194 if (file_exists($file)) {
195 return $file;
196 }
197
198 return FALSE;
199 }
200
a0ee3941
EM
201 /**
202 * @param $class
203 */
00be9182 204 public function loadClass($class) {
6a488035
TO
205 if (
206 // Only load classes that clearly belong to CiviCRM.
8581f9ae 207 // Note: api/v3 does not use classes, but api_v3's test-suite does
dbaa9d7d 208 (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
209 // Do not load PHP 5.3 namespaced classes.
210 // (in a future version, maybe)
211 FALSE === strpos($class, '\\')
212 ) {
213 $file = strtr($class, '_', '/') . '.php';
214 // There is some question about the best way to do this.
215 // "require_once" is nice because it's simple and throws
a03a3680
TO
216 // intelligible errors.
217 if (FALSE != stream_resolve_include_path($file)) {
218 require_once $file;
219 }
6a488035 220 }
649d6dd0
TO
221 elseif (in_array($class, $this->civiTestClasses)) {
222 $file = "tests/phpunit/CiviTest/{$class}.php";
223 if (FALSE != stream_resolve_include_path($file)) {
224 require_once $file;
225 }
226 }
227 elseif ($class === 'CiviSeleniumSettings') {
228 if (!empty($GLOBALS['_CV'])) {
229 require_once 'tests/phpunit/CiviTest/CiviSeleniumSettings.auto.php';
230 }
231 elseif (CRM_Utils_File::isIncludable('tests/phpunit/CiviTest/CiviSeleniumSettings.php')) {
232 require_once 'tests/phpunit/CiviTest/CiviSeleniumSettings.php';
233 }
234 }
6a488035 235 }
a03a3680 236
6a488035 237}