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