[BUGFIX] Remove early return in PageTreeDataProvider
[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 return spl_autoload_register('t3lib_autoloader::autoload', TRUE, TRUE);
71 }
72
73 /**
74 * Uninstalls TYPO3 autoloader. This function is for the sake of completeness.
75 * It is never called by the TYPO3 core.
76 *
77 * @return boolean TRUE in case of success
78 */
79 public static function unregisterAutoloader() {
80 return spl_autoload_unregister('t3lib_autoloader::autoload');
81 }
82
83 /**
84 * Autoload function for TYPO3.
85 *
86 * This method looks up class names in the registry
87 * (which contains extensions and core files)
88 *
89 * @param string $className Class name
90 * @return void
91 */
92 public static function autoload($className) {
93 // Use core and extension registry
94 $classPath = self::getClassPathByRegistryLookup($className);
95
96 if ($classPath) {
97 t3lib_div::requireFile($classPath);
98 } else {
99 try {
100 // Regular expression for a valid classname taken from
101 // http://www.php.net/manual/en/language.oop5.basic.php
102 if (preg_match('/^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*$/', $className)) {
103 spl_autoload($className);
104 }
105 } catch (LogicException $exception) {
106 }
107 }
108 }
109
110 /**
111 * Load registry from cache file if available or search
112 * for all loaded extensions and create a cache file
113 *
114 * @return void
115 */
116 protected static function loadCoreAndExtensionRegistry() {
117 $phpCodeCache = $GLOBALS['typo3CacheManager']->getCache('cache_phpcode');
118
119 // Create autoloader cache file if it does not exist yet
120 if (!$phpCodeCache->has(self::getAutoloadCacheIdentifier())) {
121 $classRegistry = self::createCoreAndExtensionRegistry();
122 self::updateRegistryCacheEntry($classRegistry);
123 }
124
125 // Require calculated cache file
126 $mappingArray = $phpCodeCache->requireOnce(self::getAutoloadCacheIdentifier());
127
128 // This can only happen if the autoloader was already registered
129 // in the same call once, the requireOnce of the cache file then
130 // does not give the cached array back. In this case we just read
131 // all cache entries manually again.
132 // This can happen in unit tests and if the cache backend was
133 // switched to NullBackend for example to simplify development
134 if (!is_array($mappingArray)) {
135 $mappingArray = self::createCoreAndExtensionRegistry();
136 }
137
138 self::$classNameToFileMapping = $mappingArray;
139 }
140
141 /**
142 * Get the full path to a class by looking it up in the registry.
143 * If not found, returns NULL.
144 *
145 * @param string $className Class name to find source file of
146 * @return mixed If String: Full name of the file where $className is declared, NULL if no entry is found
147 */
148 protected static function getClassPathByRegistryLookup($className) {
149 $classPath = NULL;
150 $classNameLower = strtolower($className);
151 if (!array_key_exists($classNameLower, self::$classNameToFileMapping)) {
152 self::attemptToLoadRegistryWithNamingConventionForGivenClassName($className);
153 }
154 if (array_key_exists($classNameLower, self::$classNameToFileMapping)) {
155 $classPath = self::$classNameToFileMapping[$classNameLower];
156 }
157 return $classPath;
158 }
159
160 /**
161 * Find all ext_autoload files and merge with core_autoload.
162 *
163 * @return void
164 */
165 protected static function createCoreAndExtensionRegistry() {
166 $classRegistry = require(PATH_t3lib . 'core_autoload.php');
167 // At this point localconf.php was already initialized
168 // we have a current extList and extMgm is also known
169 $loadedExtensions = array_unique(t3lib_div::trimExplode(',', t3lib_extMgm::getEnabledExtensionList(), TRUE));
170 foreach ($loadedExtensions as $extensionKey) {
171 $extensionAutoloadFile = t3lib_extMgm::extPath($extensionKey, 'ext_autoload.php');
172 if (file_exists($extensionAutoloadFile)) {
173 $classRegistry = array_merge($classRegistry, require($extensionAutoloadFile));
174 }
175 }
176 return $classRegistry;
177 }
178
179 /**
180 * Try to load a given class name based on 'extbase' naming convention into the registry.
181 * If the file is found it writes an entry to $classNameToFileMapping and re-caches the
182 * array to the file system to save this lookup for next call.
183 *
184 * @param string $className Class name to find source file of
185 * @return void
186 */
187 protected static function attemptToLoadRegistryWithNamingConventionForGivenClassName($className) {
188 $classNameParts = explode('_', $className, 3);
189 $extensionKey = t3lib_div::camelCaseToLowerCaseUnderscored($classNameParts[1]);
190 if ($extensionKey) {
191 try {
192 // This will throw a BadFunctionCallException if the extension is not loaded
193 $extensionPath = t3lib_extMgm::extPath($extensionKey);
194 $classFilePathAndName = $extensionPath . 'Classes/' . strtr($classNameParts[2], '_', '/') . '.php';
195 if (file_exists($classFilePathAndName)) {
196 self::$classNameToFileMapping[strtolower($className)] = $classFilePathAndName;
197 self::updateRegistryCacheEntry(self::$classNameToFileMapping);
198 }
199 } catch (BadFunctionCallException $exception) {
200 // Catch the exception and do nothing to give
201 // other registered autoloaders a chance to find the file
202 }
203 }
204 }
205
206 /**
207 * Set or update autoloader cache entry
208 *
209 * @param array $registry Current registry entries
210 * @return void
211 */
212 protected static function updateRegistryCacheEntry(array $registry) {
213 $cachedFileContent = 'return array(';
214 foreach ($registry as $className => $classLocation) {
215 $cachedFileContent .= LF . '\'' . $className . '\' => \'' . $classLocation . '\',';
216 }
217 $cachedFileContent .= LF . ');';
218 $GLOBALS['typo3CacheManager']->getCache('cache_phpcode')->set(
219 self::getAutoloadCacheIdentifier(),
220 $cachedFileContent,
221 array('t3lib_autoloader')
222 );
223 }
224
225 /**
226 * Gets the identifier used for caching the registry files.
227 * The identifier depends on whether or not frontend or backend
228 * is called, on the current TYPO3 version and the installation path
229 * of the TYPO3 site (PATH_site).
230 *
231 * In effect, a new registry cache file will be created
232 * when moving to a newer version with possible new core classes
233 * or moving the webroot to another absolute path.
234 *
235 * @return string identifier
236 */
237 protected static function getAutoloadCacheIdentifier() {
238 if (is_null(self::$autoloadCacheIdentifier)) {
239 $frontendOrBackend = TYPO3_MODE === 'FE' ? 'FE' : 'BE';
240 self::$autoloadCacheIdentifier = sha1($frontendOrBackend . TYPO3_version . PATH_site . 'autoload');
241 }
242 return self::$autoloadCacheIdentifier;
243 }
244 }
245 ?>