[TASK] Re-work/simplify copyright header in PHP files - Part 2
[Packages/TYPO3.CMS.git] / typo3 / sysext / install / Classes / Controller / Action / Ajax / ExtensionCompatibilityTester.php
1 <?php
2 namespace TYPO3\CMS\Install\Controller\Action\Ajax;
3
4 /**
5 * This file is part of the TYPO3 CMS project.
6 *
7 * It is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU General Public License, either version 2
9 * of the License, or any later version.
10 *
11 * For the full copyright and license information, please read the
12 * LICENSE.txt file that was distributed with this source code.
13 *
14 * The TYPO3 project - inspiring people to share!
15 */
16
17 use TYPO3\CMS\Core\Utility;
18
19 /**
20 * Load Extensions
21 *
22 * The idea is to load ext_localconf and ext_tables of extensions one-by-one
23 * until one of those files throws a fatal. The javascript will then recognise
24 * the fatal and initiates another run that will leave out the fataling extension
25 * to check the rest.
26 */
27 class ExtensionCompatibilityTester extends AbstractAjaxAction {
28
29 /**
30 * Store extension loading protocol
31 *
32 * @var string
33 */
34 protected $protocolFile = '';
35
36 /**
37 * Store errors that occured during checks.
38 *
39 * @var string
40 */
41 protected $errorProtocolFile = '';
42
43 /**
44 * Define whether to log errors to file or not.
45 *
46 * @var boolean
47 */
48 protected $logError = FALSE;
49
50 /**
51 * Construct this class
52 * set default protocol file location
53 */
54 public function __construct() {
55 $this->protocolFile = PATH_site . 'typo3temp/ExtensionCompatibilityTester.txt';
56 $this->errorProtocolFile = PATH_site . 'typo3temp/ExtensionCompatibilityTesterErrors.json';
57 }
58
59 /**
60 * Main entry point for checking extensions to load,
61 * setting up the checks (deleting protocol), and returning
62 * OK if process run through without errors
63 *
64 * @return string "OK" if process ran through without errors
65 */
66 protected function executeAction() {
67 register_shutdown_function(array($this, 'logError'));
68 $getVars = Utility\GeneralUtility::_GET('install');
69 if (isset($getVars['extensionCompatibilityTester']) && isset($getVars['extensionCompatibilityTester']['forceCheck']) && ($getVars['extensionCompatibilityTester']['forceCheck'] == 1)) {
70 $this->deleteProtocolFile();
71 }
72 $this->tryToLoadExtLocalconfAndExtTablesOfExtensions($this->getExtensionsToLoad());
73 return 'OK';
74 }
75
76 /**
77 * Delete the protocol files if they exist
78 *
79 * @return void
80 */
81 protected function deleteProtocolFile() {
82 if (file_exists($this->protocolFile)) {
83 unlink($this->protocolFile);
84 }
85 if (file_exists($this->errorProtocolFile)) {
86 unlink($this->errorProtocolFile);
87 }
88 }
89
90 /**
91 * Get extensions that should be loaded.
92 * Fills the TYPO3_LOADED_EXT array.
93 * Only considers local extensions
94 *
95 * @return array
96 */
97 protected function getExtensionsToLoad() {
98 $extensionsToLoad = array();
99 $extensionsToExclude = $this->getExtensionsToExclude();
100 foreach ($GLOBALS['TYPO3_LOADED_EXT'] as $key => $extension) {
101 if (!in_array($key, $extensionsToExclude)) {
102 $extensionsToLoad[$key] = $extension;
103 }
104 }
105 return $extensionsToLoad;
106 }
107
108 /**
109 * Gets extensions already known to be incompatible
110 * This class is recursively called, and this method is needed
111 * to not run into the same errors twice.
112 *
113 * @return array
114 */
115 protected function getExtensionsToExclude() {
116 $exclude = Utility\GeneralUtility::getUrl($this->protocolFile);
117 return Utility\GeneralUtility::trimExplode(',', (string)$exclude);
118 }
119
120 /**
121 * Tries to load the ext_localconf and ext_tables files of all non-core extensions
122 * Writes current extension name to file and deletes it again when inclusion was
123 * successful.
124 *
125 * @param array $extensions
126 * @return void
127 */
128 protected function tryToLoadExtLocalconfAndExtTablesOfExtensions(array $extensions) {
129 foreach ($extensions as $extensionKey => $extension) {
130 $this->writeCurrentExtensionToFile($extensionKey);
131 $this->loadExtLocalconfForExtension($extensionKey, $extension);
132 $this->removeCurrentExtensionFromFile($extensionKey);
133 }
134 Utility\ExtensionManagementUtility::loadBaseTca(FALSE);
135 foreach ($extensions as $extensionKey => $extension) {
136 $this->writeCurrentExtensionToFile($extensionKey);
137 $this->loadExtTablesForExtension($extensionKey, $extension);
138 $this->removeCurrentExtensionFromFile($extensionKey);
139 }
140 }
141
142 /**
143 * Loads ext_tables.php for a single extension. Method is a modified copy of
144 * the original bootstrap method.
145 *
146 * @param string $extensionKey
147 * @param \ArrayAccess $extension
148 * @return void
149 */
150 protected function loadExtTablesForExtension($extensionKey, array $extension) {
151 // In general it is recommended to not rely on it to be globally defined in that
152 // scope, but we can not prohibit this without breaking backwards compatibility
153 global $T3_SERVICES, $T3_VAR, $TYPO3_CONF_VARS;
154 global $TBE_MODULES, $TBE_MODULES_EXT, $TCA;
155 global $PAGES_TYPES, $TBE_STYLES, $FILEICONS;
156 global $_EXTKEY;
157 // Load each ext_tables.php file of loaded extensions
158 $_EXTKEY = $extensionKey;
159 if (isset($extension['ext_tables.php']) && $extension['ext_tables.php']) {
160 // $_EXTKEY and $_EXTCONF are available in ext_tables.php
161 // and are explicitly set in cached file as well
162 $_EXTCONF = $GLOBALS['TYPO3_CONF_VARS']['EXT']['extConf'][$_EXTKEY];
163 require $extension['ext_tables.php'];
164 Utility\ExtensionManagementUtility::loadNewTcaColumnsConfigFiles();
165 }
166 }
167
168 /**
169 * Loads ext_localconf.php for a single extension. Method is a modified copy of
170 * the original bootstrap method.
171 *
172 * @param string $extensionKey
173 * @param \ArrayAccess $extension
174 * @return void
175 */
176 protected function loadExtLocalconfForExtension($extensionKey, array $extension) {
177 // This is the main array meant to be manipulated in the ext_localconf.php files
178 // In general it is recommended to not rely on it to be globally defined in that
179 // scope but to use $GLOBALS['TYPO3_CONF_VARS'] instead.
180 // Nevertheless we define it here as global for backwards compatibility.
181 global $TYPO3_CONF_VARS;
182 $_EXTKEY = $extensionKey;
183 if (isset($extension['ext_localconf.php']) && $extension['ext_localconf.php']) {
184 // $_EXTKEY and $_EXTCONF are available in ext_localconf.php
185 // and are explicitly set in cached file as well
186 $_EXTCONF = $GLOBALS['TYPO3_CONF_VARS']['EXT']['extConf'][$_EXTKEY];
187 require $extension['ext_localconf.php'];
188 }
189 }
190
191 /**
192 * Writes $extensionKey to the protocol file by adding it comma separated at
193 * the end of the file.
194 *
195 * @param string $extensionKey
196 * @return void
197 */
198 protected function writeCurrentExtensionToFile($extensionKey) {
199 $incompatibleExtensions = array_filter(Utility\GeneralUtility::trimExplode(',', (string)Utility\GeneralUtility::getUrl($this->protocolFile)));
200 $incompatibleExtensions = array_merge($incompatibleExtensions, array($extensionKey));
201 Utility\GeneralUtility::writeFile($this->protocolFile, implode(', ', $incompatibleExtensions));
202 $this->logError = TRUE;
203 }
204
205 /**
206 * Removes $extensionKey from protocol file.
207 *
208 * @param string $extensionKey
209 * @return void
210 */
211 protected function removeCurrentExtensionFromFile($extensionKey) {
212 $extensionsInFile = array_filter(Utility\GeneralUtility::trimExplode(',', (string)Utility\GeneralUtility::getUrl($this->protocolFile)));
213 $extensionsByKey = array_flip($extensionsInFile);
214 unset($extensionsByKey[$extensionKey]);
215 $extensionsForFile = array_flip($extensionsByKey);
216 Utility\GeneralUtility::writeFile($this->protocolFile, implode(', ', $extensionsForFile));
217 $this->logError = FALSE;
218 }
219
220 /**
221 * Log last occured error for logging.
222 *
223 * @return void
224 */
225 public function logError() {
226 // Logging is disabled.
227 if (!$this->logError) {
228 return;
229 }
230
231 // Fetch existing errors, add last one and write to file again.
232 $lastError = error_get_last();
233 $errors = array();
234
235 if (file_exists($this->errorProtocolFile)) {
236 $errors = json_decode(Utility\GeneralUtility::getUrl($this->errorProtocolFile));
237 }
238 switch ($lastError['type']) {
239 case E_ERROR:
240 $lastError['type'] = 'E_ERROR';
241 break;
242 case E_WARNING:
243 $lastError['type'] = 'E_WARNING';
244 break;
245 case E_PARSE:
246 $lastError['type'] = 'E_PARSE';
247 break;
248 case E_NOTICE:
249 $lastError['type'] = 'E_NOTICE';
250 break;
251 case E_NOTICE:
252 $lastError['type'] = 'E_NOTICE';
253 break;
254 }
255 $errors[] = $lastError;
256
257 Utility\GeneralUtility::writeFile($this->errorProtocolFile, json_encode($errors));
258 }
259 }