bdf391938f8e456c4a79fc340a1585b071760b4c
[Packages/TYPO3.CMS.git] / typo3 / sysext / core / Classes / Compatibility / CompatbilityClassLoaderPhpBelow50307.php
1 <?php
2 namespace TYPO3\CMS\Core\Compatibility;
3 use \TYPO3\CMS\Core\Utility\GeneralUtility;
4
5 /***************************************************************
6 * Copyright notice
7 *
8 * (c) 2012-2013 Thomas Maroschik <tmaroschik@dfau.de>
9 * All rights reserved
10 *
11 * This script is part of the TYPO3 project. The TYPO3 project is
12 * free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
16 *
17 * The GNU General Public License can be found at
18 * http://www.gnu.org/copyleft/gpl.html.
19 * A copy is found in the textfile GPL.txt and important notices to the license
20 * from the author is found in LICENSE.txt distributed with these scripts.
21 *
22 *
23 * This script is distributed in the hope that it will be useful,
24 * but WITHOUT ANY WARRANTY; without even the implied warranty of
25 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
26 * GNU General Public License for more details.
27 *
28 * This copyright notice MUST APPEAR in all copies of the script!
29 ***************************************************************/
30
31 /**
32 * This is a compatibility layer for systems running PHP < 5.3.7
33 * It rewrites the type hints in method definitions so that they are identical to the
34 * core interface definition
35 *
36 * @author Thomas Maroschik <tmaroschik@dfau.de>
37 */
38 class CompatbilityClassLoaderPhpBelow50307 extends \TYPO3\CMS\Core\Core\ClassLoader {
39
40 /**
41 * Contains the class loaders class name
42 *
43 * @var string
44 */
45 static protected $className = __CLASS__;
46
47 /**
48 * Installs TYPO3 autoloader, and loads the autoload registry for the core.
49 *
50 * @return boolean TRUE in case of success
51 */
52 static public function registerAutoloader() {
53 return parent::registerAutoloader();
54 }
55
56 /**
57 * Unload TYPO3 autoloader and write any additional classes
58 * found during the script run to the cache file.
59 *
60 * This method is called during shutdown of the framework.
61 *
62 * @return boolean TRUE in case of success
63 */
64 static public function unregisterAutoloader() {
65 return parent::unregisterAutoloader();
66 }
67
68 /**
69 * Require the class file and rewrite non sysext files transparently
70 *
71 * @static
72 * @param string $classPath
73 * @param string $className
74 * @return void
75 */
76 static public function requireClassFileOnce($classPath, $className) {
77 if (
78 GeneralUtility::isFirstPartOfStr($className, 'tx_')
79 || GeneralUtility::isFirstPartOfStr($className, 'Tx_')
80 || GeneralUtility::isFirstPartOfStr($className, 'ux_')
81 || GeneralUtility::isFirstPartOfStr($className, 'user_')
82 || GeneralUtility::isFirstPartOfStr($className, 'User_')
83 ) {
84 // If class in question starts with one of the allowed old prefixes
85 static::checkClassCacheEntryAndRequire($classPath);
86 } else {
87 // Do nothing for system extensions or external libraries.
88 // They are already using the proper type hints or do not use them at all.
89 static::requireClassFile($classPath);
90 }
91 }
92
93 /**
94 * Require class file from cache and create if it doesn't exist yet
95 *
96 * @param $classPath
97 * @return void
98 */
99 static protected function checkClassCacheEntryAndRequire($classPath) {
100 $cacheIdentifier = static::getClassPathCacheIdentifier($classPath);
101 /** @var $phpCodeCache \TYPO3\CMS\Core\Cache\Frontend\PhpFrontend */
102 $phpCodeCache = $GLOBALS['typo3CacheManager']->getCache('cache_core');
103 if (!$phpCodeCache->has($cacheIdentifier)) {
104 $classCode = static::rewriteMethodTypeHintsFromClassPath($classPath);
105 $phpCodeCache->set($cacheIdentifier, $classCode, array(), 0);
106 }
107 $phpCodeCache->requireOnce($cacheIdentifier);
108 }
109
110 /**
111 * Generates the cache identifier from the relative class path and the files sha1 hash
112 *
113 * @static
114 * @param string $classPath
115 * @return string
116 */
117 static protected function getClassPathCacheIdentifier($classPath) {
118 // The relative class path is part of the cache identifier
119 $relativeClassPath = (GeneralUtility::isFirstPartOfStr($classPath, PATH_site)) ? substr($classPath, strlen(PATH_site)) : $classPath;
120 $fileExtension = strrchr($classPath, '.');
121 $fileNameWithoutExtension = substr(basename($classPath), 0, strlen($fileExtension) * -1);
122 // The class content has to be part of the identifier too
123 // otherwise the old class files get loaded from cache
124 $fileSha1 = sha1_file($classPath);
125 $cacheIdentifier = 'ClassLoader_' . $fileNameWithoutExtension . '_' . substr(sha1($fileSha1 . '|' . $relativeClassPath), 0, 20);
126 // Clean up identifier to be a valid cache entry identifier
127 $cacheIdentifier = preg_replace('/[^a-zA-Z0-9_%\-&]/i', '_', $cacheIdentifier);
128 return $cacheIdentifier;
129 }
130
131 /**
132 * Loads the class path and rewrites the type hints
133 *
134 * @static
135 * @param string $classPath
136 * @return string rewritten php code
137 */
138 static protected function rewriteMethodTypeHintsFromClassPath($classPath) {
139 $pcreBacktrackLimitOriginal = ini_get('pcre.backtrack_limit');
140 $classAliasMap = static::$aliasToClassNameMapping;
141 $fileContent = static::getClassFileContent($classPath);
142 $fileLength = strlen($fileContent);
143 $hasReplacements = FALSE;
144 // when the class file is bigger than the original pcre backtrace limit increase the limit
145 if ($pcreBacktrackLimitOriginal < $fileLength) {
146 ini_set('pcre.backtrack_limit', $fileLength);
147 }
148 $fileContent = preg_replace_callback(
149 '/function[ \t]+([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)\s*\((.*?\$.*?)\)(\s*[{;])/ims',
150 function($matches) use($classAliasMap, &$hasReplacements) {
151 if (isset($matches[1]) && isset($matches[2])) {
152 list($functionName, $argumentList) = array_slice($matches, 1, 2);
153 $arguments = explode(',', $argumentList);
154 $arguments = array_map('trim', $arguments);
155 $arguments = preg_replace_callback('/([\\a-z0-9_]+\s+)?((\s*[&]*\s*\$[a-z0-9_]+)(\s*=\s*.+)?)/ims', function($argumentMatches) use($classAliasMap, &$hasReplacements) {
156 if (isset($argumentMatches[1]) && isset($argumentMatches[2])) {
157 $typeHint = strtolower(ltrim(trim($argumentMatches[1]), '\\'));
158 if (isset($classAliasMap[$typeHint])) {
159 $hasReplacements = TRUE;
160 return '\\' . $classAliasMap[$typeHint] . ' ' . $argumentMatches[2];
161 }
162 }
163 return $argumentMatches[0];
164 }, $arguments);
165 return 'function ' . $functionName . '(' . implode(', ', $arguments) . ')' . $matches[3];
166 }
167 return $matches[0];
168 }, $fileContent);
169 $fileContent = preg_replace(array(
170 '/^\s*<\?php/',
171 '/\?>\s*$/'
172 ), '', $fileContent);
173 if ($pcreBacktrackLimitOriginal < $fileLength) {
174 ini_set('pcre.backtrack_limit', $pcreBacktrackLimitOriginal);
175 }
176
177 if (!$hasReplacements) {
178 $fileContent = 'require_once \'' . $classPath . '\';';
179 }
180
181 return $fileContent;
182 }
183
184 /**
185 * Wrapper method to be able to mock in unit tests
186 *
187 * @param string $classPath
188 */
189 protected static function requireClassFile($classPath) {
190 GeneralUtility::requireOnce($classPath);
191 }
192
193 /**
194 * Wrapper method to be able to mock in unit tests
195 *
196 * @param string $classPath
197 * @return string
198 */
199 protected static function getClassFileContent($classPath) {
200 return file_get_contents($classPath);
201 }
202
203 }
204
205 ?>