2d670a2da28265d2c7c0071f78ea874b301437cd
[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 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, 'Ux_')
82 || GeneralUtility::isFirstPartOfStr($className, 'user_')
83 || GeneralUtility::isFirstPartOfStr($className, 'User_')
84 ) {
85 // If class in question starts with one of the allowed old prefixes
86 static::checkClassCacheEntryAndRequire($classPath);
87 } else {
88 // Do nothing for system extensions or external libraries.
89 // They are already using the proper type hints or do not use them at all.
90 static::requireClassFile($classPath);
91 }
92 }
93
94 /**
95 * Require class file from cache and create if it doesn't exist yet
96 *
97 * @param $classPath
98 * @return void
99 */
100 static protected function checkClassCacheEntryAndRequire($classPath) {
101 $cacheIdentifier = static::getClassPathCacheIdentifier($classPath);
102 /** @var $phpCodeCache \TYPO3\CMS\Core\Cache\Frontend\PhpFrontend */
103 $phpCodeCache = $GLOBALS['typo3CacheManager']->getCache('cache_core');
104 if (!$phpCodeCache->has($cacheIdentifier)) {
105 $classCode = static::rewriteMethodTypeHintsFromClassPath($classPath);
106 $phpCodeCache->set($cacheIdentifier, $classCode, array(), 0);
107 }
108 $phpCodeCache->requireOnce($cacheIdentifier);
109 }
110
111 /**
112 * Generates the cache identifier from the relative class path and the files sha1 hash
113 *
114 * @static
115 * @param string $classPath
116 * @return string
117 */
118 static protected function getClassPathCacheIdentifier($classPath) {
119 // The relative class path is part of the cache identifier
120 $relativeClassPath = (GeneralUtility::isFirstPartOfStr($classPath, PATH_site)) ? substr($classPath, strlen(PATH_site)) : $classPath;
121 $fileExtension = strrchr($classPath, '.');
122 $fileNameWithoutExtension = substr(basename($classPath), 0, strlen($fileExtension) * -1);
123 // The class content has to be part of the identifier too
124 // otherwise the old class files get loaded from cache
125 $fileSha1 = sha1_file($classPath);
126 $cacheIdentifier = 'ClassLoader_' . $fileNameWithoutExtension . '_' . substr(sha1($fileSha1 . '|' . $relativeClassPath), 0, 20);
127 // Clean up identifier to be a valid cache entry identifier
128 $cacheIdentifier = preg_replace('/[^a-zA-Z0-9_%\-&]/i', '_', $cacheIdentifier);
129 return $cacheIdentifier;
130 }
131
132 /**
133 * Loads the class path and rewrites the type hints
134 *
135 * @static
136 * @param string $classPath
137 * @return string rewritten php code
138 */
139 static protected function rewriteMethodTypeHintsFromClassPath($classPath) {
140 $pcreBacktrackLimitOriginal = ini_get('pcre.backtrack_limit');
141 $classAliasMap = static::$aliasToClassNameMapping;
142 $fileContent = static::getClassFileContent($classPath);
143 $fileLength = strlen($fileContent);
144 $hasReplacements = FALSE;
145 // when the class file is bigger than the original pcre backtrace limit increase the limit
146 if ($pcreBacktrackLimitOriginal < $fileLength) {
147 ini_set('pcre.backtrack_limit', $fileLength);
148 }
149 $fileContent = preg_replace_callback(
150 '/function[ \t]+([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)\s*\((.*?\$.*?)\)(\s*[{;])/ims',
151 function($matches) use($classAliasMap, &$hasReplacements) {
152 if (isset($matches[1]) && isset($matches[2])) {
153 list($functionName, $argumentList) = array_slice($matches, 1, 2);
154 $arguments = explode(',', $argumentList);
155 $arguments = array_map('trim', $arguments);
156 $arguments = preg_replace_callback('/([\\a-z0-9_]+\s+)?((\s*[&]*\s*\$[a-z0-9_]+)(\s*=\s*.+)?)/ims', function($argumentMatches) use($classAliasMap, &$hasReplacements) {
157 if (isset($argumentMatches[1]) && isset($argumentMatches[2])) {
158 $typeHint = strtolower(ltrim(trim($argumentMatches[1]), '\\'));
159 if (isset($classAliasMap[$typeHint])) {
160 $hasReplacements = TRUE;
161 return '\\' . $classAliasMap[$typeHint] . ' ' . $argumentMatches[2];
162 }
163 }
164 return $argumentMatches[0];
165 }, $arguments);
166 return 'function ' . $functionName . '(' . implode(', ', $arguments) . ')' . $matches[3];
167 }
168 return $matches[0];
169 }, $fileContent);
170 $fileContent = preg_replace(array(
171 '/^\s*<\?php/',
172 '/\?>\s*$/'
173 ), '', $fileContent);
174 if ($pcreBacktrackLimitOriginal < $fileLength) {
175 ini_set('pcre.backtrack_limit', $pcreBacktrackLimitOriginal);
176 }
177
178 if (!$hasReplacements) {
179 $fileContent = 'require_once \'' . $classPath . '\';';
180 }
181
182 return $fileContent;
183 }
184
185 /**
186 * Wrapper method to be able to mock in unit tests
187 *
188 * @param string $classPath
189 */
190 protected static function requireClassFile($classPath) {
191 GeneralUtility::requireOnce($classPath);
192 }
193
194 /**
195 * Wrapper method to be able to mock in unit tests
196 *
197 * @param string $classPath
198 * @return string
199 */
200 protected static function getClassFileContent($classPath) {
201 return file_get_contents($classPath);
202 }
203
204 }
205
206 ?>