[BUGFIX] Scheduler BE module fails to load due to autoloader issues
[Packages/TYPO3.CMS.git] / t3lib / class.t3lib_autoloader.php
1 <?php
2 /***************************************************************
3 * Copyright notice
4 *
5 * (c) 2008-2011 Dmitry Dulepov <dmitry@typo3.org>
6 * All rights reserved
7 *
8 * This script is part of the TYPO3 project. The TYPO3 project is
9 * free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * The GNU General Public License can be found at
15 * http://www.gnu.org/copyleft/gpl.html.
16 * A copy is found in the textfile GPL.txt and important notices to the license
17 * from the author is found in LICENSE.txt distributed with these scripts.
18 *
19 *
20 * This script is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 * GNU General Public License for more details.
24 *
25 * This copyright notice MUST APPEAR in all copies of the script!
26 ***************************************************************/
27
28 /**
29 * This class contains TYPO3 autoloader for classes.
30 * It handles:
31 * - The core of TYPO3
32 * - All extensions with an ext_autoload.php file
33 * - All extensions that stick to the 'extbase' like naming convention
34 *
35 * @author Dmitry Dulepov <dmitry@typo3.org>
36 * @author Martin Kutschker <masi@typo3.org>
37 * @author Oliver Hader <oliver@typo3.org>
38 * @author Sebastian Kurf├╝rst <sebastian@typo3.org>
39 * @author Christian Kuhn <lolli@schwarzbu.ch>
40 */
41 class t3lib_autoloader {
42
43 /**
44 * Class name to file mapping. Key: class name. Value: fully qualified file name.
45 *
46 * @var array
47 */
48 protected static $classNameToFileMapping = array();
49
50 /**
51 * Name of cache entry identifier in autoload cache
52 *
53 * @var string
54 */
55 protected static $autoloadCacheIdentifier = NULL;
56
57 /**
58 * The autoloader is static, thus we do not allow instances of this class.
59 */
60 private function __construct() {
61 }
62
63 /**
64 * Installs TYPO3 autoloader, and loads the autoload registry for the core.
65 *
66 * @return boolean TRUE in case of success
67 */
68 public static function registerAutoloader() {
69 self::loadCoreAndExtensionRegistry();
70 @ini_set('unserialize_callback_func', 'spl_autoload_call');
71 return spl_autoload_register('t3lib_autoloader::autoload', TRUE, TRUE);
72 }
73
74 /**
75 * Uninstalls TYPO3 autoloader. This function is for the sake of completeness.
76 * It is never called by the TYPO3 core.
77 *
78 * @return boolean TRUE in case of success
79 */
80 public static function unregisterAutoloader() {
81 return spl_autoload_unregister('t3lib_autoloader::autoload');
82 }
83
84 /**
85 * Autoload function for TYPO3.
86 *
87 * This method looks up class names in the registry
88 * (which contains extensions and core files)
89 *
90 * @param string $className Class name
91 * @return void
92 */
93 public static function autoload($className) {
94 // Use core and extension registry
95 $classPath = self::getClassPathByRegistryLookup($className);
96
97 if ($classPath) {
98 t3lib_div::requireFile($classPath);
99 } else {
100 try {
101 // Regular expression for a valid classname taken from
102 // http://www.php.net/manual/en/language.oop5.basic.php
103 if (preg_match('/^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*$/', $className)) {
104 spl_autoload($className);
105 }
106 } catch (LogicException $exception) {
107 }
108 }
109 }
110
111 /**
112 * Load registry from cache file if available or search
113 * for all loaded extensions and create a cache file
114 *
115 * @return void
116 */
117 protected static function loadCoreAndExtensionRegistry() {
118 $phpCodeCache = $GLOBALS['typo3CacheManager']->getCache('cache_phpcode');
119
120 // Create autoloader cache file if it does not exist yet
121 if (!$phpCodeCache->has(self::getAutoloadCacheIdentifier())) {
122 $classRegistry = self::createCoreAndExtensionRegistry();
123 self::updateRegistryCacheEntry($classRegistry);
124 }
125
126 // Require calculated cache file
127 $mappingArray = $phpCodeCache->requireOnce(self::getAutoloadCacheIdentifier());
128
129 // This can only happen if the autoloader was already registered
130 // in the same call once, the requireOnce of the cache file then
131 // does not give the cached array back. In this case we just read
132 // all cache entries manually again.
133 // This can happen in unit tests and if the cache backend was
134 // switched to NullBackend for example to simplify development
135 if (!is_array($mappingArray)) {
136 $mappingArray = self::createCoreAndExtensionRegistry();
137 }
138
139 self::$classNameToFileMapping = $mappingArray;
140 }
141
142 /**
143 * Get the full path to a class by looking it up in the registry.
144 * If not found, returns NULL.
145 *
146 * @param string $className Class name to find source file of
147 * @return mixed If String: Full name of the file where $className is declared, NULL if no entry is found
148 */
149 protected static function getClassPathByRegistryLookup($className) {
150 $classPath = NULL;
151 $classNameLower = strtolower($className);
152 if (!array_key_exists($classNameLower, self::$classNameToFileMapping)) {
153 self::attemptToLoadRegistryWithNamingConventionForGivenClassName($className);
154 }
155 if (array_key_exists($classNameLower, self::$classNameToFileMapping)) {
156 $classPath = self::$classNameToFileMapping[$classNameLower];
157 }
158 return $classPath;
159 }
160
161 /**
162 * Find all ext_autoload files and merge with core_autoload.
163 *
164 * @return void
165 */
166 protected static function createCoreAndExtensionRegistry() {
167 $classRegistry = require(PATH_t3lib . 'core_autoload.php');
168 // At this point localconf.php was already initialized
169 // we have a current extList and extMgm is also known
170 $loadedExtensions = array_unique(t3lib_div::trimExplode(',', t3lib_extMgm::getEnabledExtensionList(), TRUE));
171 foreach ($loadedExtensions as $extensionKey) {
172 $extensionAutoloadFile = t3lib_extMgm::extPath($extensionKey, 'ext_autoload.php');
173 if (file_exists($extensionAutoloadFile)) {
174 $classRegistry = array_merge($classRegistry, require($extensionAutoloadFile));
175 }
176 }
177 return $classRegistry;
178 }
179
180 /**
181 * Try to load a given class name based on 'extbase' naming convention into the registry.
182 * If the file is found it writes an entry to $classNameToFileMapping and re-caches the
183 * array to the file system to save this lookup for next call.
184 *
185 * @param string $className Class name to find source file of
186 * @return void
187 */
188 protected static function attemptToLoadRegistryWithNamingConventionForGivenClassName($className) {
189 $classNameParts = explode('_', $className, 3);
190 $extensionKey = t3lib_div::camelCaseToLowerCaseUnderscored($classNameParts[1]);
191 if ($extensionKey) {
192 try {
193 // This will throw a BadFunctionCallException if the extension is not loaded
194 $extensionPath = t3lib_extMgm::extPath($extensionKey);
195 $classFilePathAndName = $extensionPath . 'Classes/' . strtr($classNameParts[2], '_', '/') . '.php';
196 if (file_exists($classFilePathAndName)) {
197 self::$classNameToFileMapping[strtolower($className)] = $classFilePathAndName;
198 self::updateRegistryCacheEntry(self::$classNameToFileMapping);
199 }
200 } catch (BadFunctionCallException $exception) {
201 // Catch the exception and do nothing to give
202 // other registered autoloaders a chance to find the file
203 }
204 }
205 }
206
207 /**
208 * Set or update autoloader cache entry
209 *
210 * @param array $registry Current registry entries
211 * @return void
212 */
213 protected static function updateRegistryCacheEntry(array $registry) {
214 $cachedFileContent = 'return array(';
215 foreach ($registry as $className => $classLocation) {
216 $cachedFileContent .= LF . '\'' . $className . '\' => \'' . $classLocation . '\',';
217 }
218 $cachedFileContent .= LF . ');';
219 $GLOBALS['typo3CacheManager']->getCache('cache_phpcode')->set(
220 self::getAutoloadCacheIdentifier(),
221 $cachedFileContent,
222 array('t3lib_autoloader')
223 );
224 }
225
226 /**
227 * Gets the identifier used for caching the registry files.
228 * The identifier depends on whether or not frontend or backend
229 * is called, on the current TYPO3 version and the installation path
230 * of the TYPO3 site (PATH_site).
231 *
232 * In effect, a new registry cache file will be created
233 * when moving to a newer version with possible new core classes
234 * or moving the webroot to another absolute path.
235 *
236 * @return string identifier
237 */
238 protected static function getAutoloadCacheIdentifier() {
239 if (is_null(self::$autoloadCacheIdentifier)) {
240 $frontendOrBackend = TYPO3_MODE === 'FE' ? 'FE' : 'BE';
241 self::$autoloadCacheIdentifier = sha1($frontendOrBackend . TYPO3_version . PATH_site . 'autoload');
242 }
243 return self::$autoloadCacheIdentifier;
244 }
245 }
246 ?>