c7e528cd1c17ec67c1b0c7465a9c98db61785b3c
[Packages/TYPO3.CMS.git] / typo3 / sysext / install / Classes / SystemEnvironment / Check.php
1 <?php
2 namespace TYPO3\CMS\Install\SystemEnvironment;
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\Service\OpcodeCacheService;
18 use TYPO3\CMS\Core\Utility\GeneralUtility;
19 use TYPO3\CMS\Install\Status;
20
21 /**
22 * Check system environment status
23 *
24 * This class is a hardcoded requirement check of the underlying
25 * server and PHP system.
26 *
27 * The class *must not* check for any TYPO3 specific things like
28 * specific configuration values or directories. It should not fail
29 * if there is no TYPO3 at all.
30 *
31 * The only core code used is the class loader
32 *
33 * This class is instantiated as the *very first* class during
34 * installation. It is meant to be *standalone* und must not have
35 * any requirements, except the status classes. It must be possible
36 * to run this script separated from the rest of the core, without
37 * dependencies.
38 *
39 * This means especially:
40 * * No hooks or anything like that
41 * * No usage of *any* TYPO3 code like GeneralUtility
42 * * No require of anything but the status classes
43 * * No localization
44 *
45 * The status messages and title *must not* include HTML, use plain
46 * text only. The return values of this class are not bound to HTML
47 * and can be used in different scopes (eg. as json array).
48 */
49 class Check
50 {
51 /**
52 * @var array List of required PHP extensions
53 */
54 protected $requiredPhpExtensions = array(
55 'fileinfo',
56 'filter',
57 'gd',
58 'hash',
59 'json',
60 'mysqli',
61 'openssl',
62 'session',
63 'soap',
64 'SPL',
65 'standard',
66 'xml',
67 'zip',
68 'zlib',
69 );
70
71 /**
72 * Get all status information as array with status objects
73 *
74 * @return array<\TYPO3\CMS\Install\Status\StatusInterface>
75 */
76 public function getStatus()
77 {
78 $statusArray = array();
79 $statusArray[] = $this->checkCurrentDirectoryIsInIncludePath();
80 $statusArray[] = $this->checkTrustedHostPattern();
81 $statusArray[] = $this->checkFileUploadEnabled();
82 $statusArray[] = $this->checkPostUploadSizeIsHigherOrEqualMaximumFileUploadSize();
83 $statusArray[] = $this->checkMemorySettings();
84 $statusArray[] = $this->checkPhpVersion();
85 $statusArray[] = $this->checkMaxExecutionTime();
86 $statusArray[] = $this->checkDisableFunctions();
87 $statusArray[] = $this->checkDownloadsPossible();
88 $statusArray[] = $this->checkMysqliReconnectSetting();
89 $statusArray[] = $this->checkAlwaysPopulateRawPostDataSetting();
90 $statusArray[] = $this->checkDocRoot();
91 $statusArray[] = $this->checkOpenBaseDir();
92 $statusArray[] = $this->checkXdebugMaxNestingLevel();
93 $statusArray[] = $this->checkOpenSslInstalled();
94 if ($this->isSuhosinLoadedAndActive()) {
95 $statusArray[] = $this->getSuhosinLoadedStatus();
96 $statusArray[] = $this->checkSuhosinRequestMaxVars();
97 $statusArray[] = $this->checkSuhosinRequestMaxVarnameLength();
98 $statusArray[] = $this->checkSuhosinPostMaxNameLength();
99 $statusArray[] = $this->checkSuhosinPostMaxVars();
100 $statusArray[] = $this->checkSuhosinGetMaxNameLength();
101 $statusArray[] = $this->checkSuhosinGetMaxValueLength();
102 $statusArray[] = $this->checkSuhosinExecutorIncludeWhiteListContainsPhar();
103 $statusArray[] = $this->checkSuhosinExecutorIncludeWhiteListContainsVfs();
104 }
105 $statusArray[] = $this->checkMaxInputVars();
106 $statusArray[] = $this->checkSomePhpOpcodeCacheIsLoaded();
107 $statusArray[] = $this->checkReflectionDocComment();
108 $statusArray[] = $this->checkSystemLocale();
109 $statusArray[] = $this->checkLocaleWithUTF8filesystem();
110 $statusArray[] = $this->checkWindowsApacheThreadStackSize();
111 foreach ($this->requiredPhpExtensions as $extension) {
112 $statusArray[] = $this->checkRequiredPhpExtension($extension);
113 }
114 $statusArray[] = $this->checkPcreVersion();
115 $statusArray[] = $this->checkGdLibTrueColorSupport();
116 $statusArray[] = $this->checkGdLibGifSupport();
117 $statusArray[] = $this->checkGdLibJpgSupport();
118 $statusArray[] = $this->checkGdLibPngSupport();
119 $statusArray[] = $this->checkGdLibFreeTypeSupport();
120 $statusArray[] = $this->checkRegisterGlobals();
121 $statusArray[] = $this->checkLibXmlBug();
122 $statusArray[] = $this->isTrueTypeFontWorking();
123 return $statusArray;
124 }
125
126 /**
127 * Checks if current directory (.) is in PHP include path
128 *
129 * @return Status\StatusInterface
130 */
131 protected function checkCurrentDirectoryIsInIncludePath()
132 {
133 $includePath = ini_get('include_path');
134 $delimiter = $this->isWindowsOs() ? ';' : ':';
135 $pathArray = $this->trimExplode($delimiter, $includePath);
136 if (!in_array('.', $pathArray)) {
137 $status = new Status\WarningStatus();
138 $status->setTitle('Current directory (./) is not within PHP include path');
139 $status->setMessage(
140 'include_path = ' . implode(' ', $pathArray) . LF .
141 'Normally the current path \'.\' is included in the' .
142 ' include_path of PHP. Although TYPO3 does not rely on this,' .
143 ' it is an unusual setting that may introduce problems for' .
144 ' some extensions.'
145 );
146 } else {
147 $status = new Status\OkStatus();
148 $status->setTitle('Current directory (./) is within PHP include path.');
149 }
150 return $status;
151 }
152
153 /**
154 * Checks the status of the trusted hosts pattern check
155 *
156 * @return Status\StatusInterface
157 */
158 protected function checkTrustedHostPattern()
159 {
160 if ($GLOBALS['TYPO3_CONF_VARS']['SYS']['trustedHostsPattern'] === GeneralUtility::ENV_TRUSTED_HOSTS_PATTERN_ALLOW_ALL) {
161 $status = new Status\WarningStatus();
162 $status->setTitle('Trusted hosts pattern is insecure');
163 $status->setMessage('Trusted hosts pattern is configured to allow all header values. Check the pattern defined in Install Tool -> All configuration -> System -> trustedHostsPattern and adapt it to expected host value(s).');
164 } else {
165 if (GeneralUtility::hostHeaderValueMatchesTrustedHostsPattern($_SERVER['HTTP_HOST'])) {
166 $status = new Status\OkStatus();
167 $status->setTitle('Trusted hosts pattern is configured to allow current host value.');
168 } else {
169 $status = new Status\ErrorStatus();
170 $status->setTitle('Trusted hosts pattern mismatch');
171 $status->setMessage('The trusted hosts pattern will be configured to allow all header values. This is because your $SERVER_NAME is "' . htmlspecialchars($_SERVER['SERVER_NAME']) . '" while your HTTP_HOST is "' . htmlspecialchars($_SERVER['HTTP_HOST']) . '". Check the pattern defined in Install Tool -> All configuration -> System -> trustedHostsPattern and adapt it to expected host value(s).');
172 }
173 }
174
175 return $status;
176 }
177
178 /**
179 * Check if file uploads are enabled in PHP
180 *
181 * @return Status\StatusInterface
182 */
183 protected function checkFileUploadEnabled()
184 {
185 if (!ini_get('file_uploads')) {
186 $status = new Status\ErrorStatus();
187 $status->setTitle('File uploads not allowed in PHP');
188 $status->setMessage(
189 'file_uploads=' . ini_get('file_uploads') . LF .
190 'TYPO3 uses the ability to upload files from the browser in various cases.' .
191 ' If this flag is disabled in PHP, you won\'t be able to upload files.' .
192 ' But it doesn\'t end here, because not only are files not accepted by' .
193 ' the server - ALL content in the forms are discarded and therefore' .
194 ' nothing at all will be editable if you don\'t set this flag!'
195 );
196 } else {
197 $status = new Status\OkStatus();
198 $status->setTitle('File uploads allowed in PHP');
199 }
200 return $status;
201 }
202
203 /**
204 * Check maximum post upload size correlates with maximum file upload
205 *
206 * @return Status\StatusInterface
207 */
208 protected function checkPostUploadSizeIsHigherOrEqualMaximumFileUploadSize()
209 {
210 $maximumUploadFilesize = $this->getBytesFromSizeMeasurement(ini_get('upload_max_filesize'));
211 $maximumPostSize = $this->getBytesFromSizeMeasurement(ini_get('post_max_size'));
212 if ($maximumPostSize > 0 && $maximumPostSize < $maximumUploadFilesize) {
213 $status = new Status\ErrorStatus();
214 $status->setTitle('Maximum size for POST requests is smaller than maximum upload filesize in PHP');
215 $status->setMessage(
216 'upload_max_filesize=' . ini_get('upload_max_filesize') . LF .
217 'post_max_size=' . ini_get('post_max_size') . LF .
218 'You have defined a maximum size for file uploads in PHP which' .
219 ' exceeds the allowed size for POST requests. Therefore the' .
220 ' file uploads can also not be larger than ' . ini_get('post_max_size') . '.'
221 );
222 } else {
223 $status = new Status\OkStatus();
224 $status->setTitle('Maximum post upload size correlates with maximum upload file size in PHP');
225 }
226 return $status;
227 }
228
229 /**
230 * Check memory settings
231 *
232 * @return Status\StatusInterface
233 */
234 protected function checkMemorySettings()
235 {
236 $minimumMemoryLimit = 32;
237 $recommendedMemoryLimit = 64;
238 $memoryLimit = $this->getBytesFromSizeMeasurement(ini_get('memory_limit'));
239 if ($memoryLimit <= 0) {
240 $status = new Status\WarningStatus();
241 $status->setTitle('Unlimited memory limit for PHP');
242 $status->setMessage(
243 'PHP is configured not to limit memory usage at all. This is a risk' .
244 ' and should be avoided in production setup. In general it\'s best practice to limit this.' .
245 ' To be safe, set a limit in PHP, but with a minimum of ' . $recommendedMemoryLimit . 'MB:' . LF .
246 'memory_limit=' . $recommendedMemoryLimit . 'M'
247 );
248 } elseif ($memoryLimit < 1024 * 1024 * $minimumMemoryLimit) {
249 $status = new Status\ErrorStatus();
250 $status->setTitle('PHP Memory limit below ' . $minimumMemoryLimit . 'MB');
251 $status->setMessage(
252 'memory_limit=' . ini_get('memory_limit') . LF .
253 'Your system is configured to enforce a memory limit for PHP scripts lower than ' .
254 $minimumMemoryLimit . 'MB. It is required to raise the limit.' .
255 ' We recommend a minimum PHP memory limit of ' . $recommendedMemoryLimit . 'MB:' . LF .
256 'memory_limit=' . $recommendedMemoryLimit . 'M'
257 );
258 } elseif ($memoryLimit < 1024 * 1024 * $recommendedMemoryLimit) {
259 $status = new Status\WarningStatus();
260 $status->setTitle('PHP Memory limit below ' . $recommendedMemoryLimit . 'MB');
261 $status->setMessage(
262 'memory_limit=' . ini_get('memory_limit') . LF .
263 'Your system is configured to enforce a memory limit for PHP scripts lower than ' .
264 $recommendedMemoryLimit . 'MB.' .
265 ' A slim TYPO3 instance without many extensions will probably work, but you should monitor your' .
266 ' system for "allowed memory size of X bytes exhausted" messages, especially if using the backend.' .
267 ' To be on the safe side,' . ' we recommend a minimum PHP memory limit of ' .
268 $recommendedMemoryLimit . 'MB:' . LF .
269 'memory_limit=' . $recommendedMemoryLimit . 'M'
270 );
271 } else {
272 $status = new Status\OkStatus();
273 $status->setTitle('PHP Memory limit is equal to or more than ' . $recommendedMemoryLimit . 'MB');
274 }
275 return $status;
276 }
277
278 /**
279 * Check minimum PHP version
280 *
281 * @return Status\StatusInterface
282 */
283 protected function checkPhpVersion()
284 {
285 $minimumPhpVersion = '5.5.0';
286 $currentPhpVersion = phpversion();
287 if (version_compare($currentPhpVersion, $minimumPhpVersion) < 0) {
288 $status = new Status\ErrorStatus();
289 $status->setTitle('PHP version too low');
290 $status->setMessage(
291 'Your PHP version ' . $currentPhpVersion . ' is too old. TYPO3 CMS does not run' .
292 ' with this version. Update to at least PHP ' . $minimumPhpVersion
293 );
294 } else {
295 $status = new Status\OkStatus();
296 $status->setTitle('PHP version is fine');
297 }
298 return $status;
299 }
300
301 /**
302 * Check PRCE module is loaded and minimum version
303 *
304 * @return Status\StatusInterface
305 */
306 protected function checkPcreVersion()
307 {
308 $minimumPcreVersion = '8.30';
309 if (!extension_loaded('pcre')) {
310 $status = new Status\ErrorStatus();
311 $status->setTitle('PHP extension pcre not loaded');
312 $status->setMessage(
313 'TYPO3 CMS uses PHP extension pcre but it is not loaded' .
314 ' in your environment. Change your environment to provide this extension' .
315 ' in with minimum version ' . $minimumPcreVersion . '.'
316 );
317 } else {
318 $installedPcreVersionString = trim(PCRE_VERSION); // '8.31 2012-07-06'
319 $mainPcreVersionString = explode(' ', $installedPcreVersionString);
320 $mainPcreVersionString = $mainPcreVersionString[0]; // '8.31'
321 if (version_compare($mainPcreVersionString, $minimumPcreVersion) < 0) {
322 $status = new Status\ErrorStatus();
323 $status->setTitle('PCRE version too low');
324 $status->setMessage(
325 'Your PCRE version ' . PCRE_VERSION . ' is too old. TYPO3 CMS may trigger PHP segmentantion' .
326 ' faults with this version. Update to at least PCRE ' . $minimumPcreVersion
327 );
328 } else {
329 $status = new Status\OkStatus();
330 $status->setTitle('PHP extension PCRE is loaded and version is fine');
331 }
332 }
333 return $status;
334 }
335
336 /**
337 * Check maximum execution time
338 *
339 * @return Status\StatusInterface
340 */
341 protected function checkMaxExecutionTime()
342 {
343 $minimumMaximumExecutionTime = 30;
344 $recommendedMaximumExecutionTime = 240;
345 $currentMaximumExecutionTime = ini_get('max_execution_time');
346 if ($currentMaximumExecutionTime == 0) {
347 $status = new Status\WarningStatus();
348 $status->setTitle('Infinite PHP script execution time');
349 $status->setMessage(
350 'max_execution_time=0' . LF .
351 'While TYPO3 is fine with this, you risk a denial-of-service for your system if for whatever' .
352 ' reason some script hangs in an infinite loop. You are usually on the safe side ' .
353 ' if it is reduced to ' . $recommendedMaximumExecutionTime . ' seconds:' . LF .
354 'max_execution_time=' . $recommendedMaximumExecutionTime
355 );
356 } elseif ($currentMaximumExecutionTime < $minimumMaximumExecutionTime) {
357 $status = new Status\ErrorStatus();
358 $status->setTitle('Low PHP script execution time');
359 $status->setMessage(
360 'max_execution_time=' . $currentMaximumExecutionTime . LF .
361 'Your max_execution_time is too low. Some expensive operations in TYPO3 can take longer than that.' .
362 ' It is recommended to raise the limit to ' . $recommendedMaximumExecutionTime . ' seconds:' . LF .
363 'max_execution_time=' . $recommendedMaximumExecutionTime
364 );
365 } elseif ($currentMaximumExecutionTime < $recommendedMaximumExecutionTime) {
366 $status = new Status\WarningStatus();
367 $status->setTitle('Low PHP script execution time');
368 $status->setMessage(
369 'max_execution_time=' . $currentMaximumExecutionTime . LF .
370 'Your max_execution_time is low. While TYPO3 often runs without problems' .
371 ' with ' . $minimumMaximumExecutionTime . ' seconds,' .
372 ' it may still happen that script execution is stopped before finishing' .
373 ' calculations. You should monitor the system for messages in this area' .
374 ' and maybe raise the limit to ' . $recommendedMaximumExecutionTime . ' seconds:' . LF .
375 'max_execution_time=' . $recommendedMaximumExecutionTime
376 );
377 } else {
378 $status = new Status\OkStatus();
379 $status->setTitle('Maximum PHP script execution time is equal to or more than '
380 . $recommendedMaximumExecutionTime);
381 }
382 return $status;
383 }
384
385 /**
386 * Check for disabled functions
387 *
388 * @return Status\StatusInterface
389 */
390 protected function checkDisableFunctions()
391 {
392 $disabledFunctions = trim(ini_get('disable_functions'));
393
394 // Filter "disable_functions"
395 $disabledFunctionsArray = $this->trimExplode(',', $disabledFunctions);
396
397 // Array with strings to find
398 $findStrings = array(
399 // Disabled by default on Ubuntu OS but this is okay since the Core does not use them
400 'pcntl_',
401 );
402 foreach ($disabledFunctionsArray as $key => $disabledFunction) {
403 foreach ($findStrings as $findString) {
404 if (strpos($disabledFunction, $findString) !== false) {
405 unset($disabledFunctionsArray[$key]);
406 }
407 }
408 }
409
410 if ($disabledFunctions !== '') {
411 if (!empty($disabledFunctionsArray)) {
412 $status = new Status\ErrorStatus();
413 $status->setTitle('Some PHP functions disabled');
414 $status->setMessage(
415 'disable_functions=' . implode(' ', explode(',', $disabledFunctions)) . LF .
416 'These function(s) are disabled. TYPO3 uses some of those, so there might be trouble.' .
417 ' TYPO3 is designed to use the default set of PHP functions plus some common extensions.' .
418 ' Possibly these functions are disabled' .
419 ' due to security considerations and most likely the list would include a function like' .
420 ' exec() which is used by TYPO3 at various places. Depending on which exact functions' .
421 ' are disabled, some parts of the system may just break without further notice.'
422 );
423 } else {
424 $status = new Status\NoticeStatus();
425 $status->setTitle('Some PHP functions currently disabled but OK');
426 $status->setMessage(
427 'disable_functions=' . implode(' ', explode(',', $disabledFunctions)) . LF .
428 'These function(s) are disabled. TYPO3 uses currently none of those, so you are good to go.'
429 );
430 }
431 } else {
432 $status = new Status\OkStatus();
433 $status->setTitle('No disabled PHP functions');
434 }
435 return $status;
436 }
437
438 /**
439 * Check if it is possible to download external data (e.g. TER)
440 * Either allow_url_fopen must be enabled or curl must be used
441 *
442 * @return Status\OkStatus|Status\WarningStatus
443 */
444 protected function checkDownloadsPossible()
445 {
446 $allowUrlFopen = (bool)ini_get('allow_url_fopen');
447 $curlEnabled = !empty($GLOBALS['TYPO3_CONF_VARS']['SYS']['curlUse']);
448 if ($allowUrlFopen || $curlEnabled) {
449 $status = new Status\OkStatus();
450 $status->setTitle('Fetching external URLs is allowed');
451 } else {
452 $status = new Status\WarningStatus();
453 $status->setTitle('Fetching external URLs is not allowed');
454 $status->setMessage(
455 'Either enable PHP runtime setting "allow_url_fopen"' . LF . 'or enable curl by setting [SYS][curlUse] accordingly.'
456 );
457 }
458 return $status;
459 }
460
461 /**
462 * Verify that mysqli.reconnect is set to 0 in order to avoid improper reconnects
463 *
464 * @return Status\StatusInterface
465 */
466 protected function checkMysqliReconnectSetting()
467 {
468 $currentMysqliReconnectSetting = ini_get('mysqli.reconnect');
469 if ($currentMysqliReconnectSetting === '1') {
470 $status = new Status\ErrorStatus();
471 $status->setTitle('PHP mysqli.reconnect is enabled');
472 $status->setMessage(
473 'mysqli.reconnect=1' . LF .
474 'PHP is configured to automatically reconnect the database connection on disconnection.' . LF .
475 ' Warning: If (e.g. during a long-running task) the connection is dropped and automatically reconnected, ' .
476 ' it may not be reinitialized properly (e.g. charset) and write mangled data to the database!'
477 );
478 } else {
479 $status = new Status\OkStatus();
480 $status->setTitle('PHP mysqli.reconnect is fine');
481 }
482 return $status;
483 }
484
485 /**
486 * Check that always_populate_raw_post_data has been set to -1 on PHP 5.6 or newer
487 *
488 * @return Status\StatusInterface
489 */
490 protected function checkAlwaysPopulateRawPostDataSetting()
491 {
492 $minimumPhpVersion = '5.6.0';
493 $maximumPhpVersion = '7.0.0';
494 $currentPhpVersion = phpversion();
495 $currentAlwaysPopulaterRawPostDataSetting = ini_get('always_populate_raw_post_data');
496 if (version_compare($currentPhpVersion, $maximumPhpVersion) >= 0) {
497 $status = new Status\OkStatus();
498 $status->setTitle('PHP always_populate_raw_post_data is removed as of PHP 7.0 or newer');
499 } elseif (version_compare($currentPhpVersion, $minimumPhpVersion) >= 0 && $currentAlwaysPopulaterRawPostDataSetting !== '-1') {
500 $status = new Status\ErrorStatus();
501 $status->setTitle('PHP always_populate_raw_post_data is deprecated');
502 $status->setMessage(
503 'always_populate_raw_post_data=' . $currentAlwaysPopulaterRawPostDataSetting . LF .
504 'PHP is configured to automatically populate $HTTP_RAW_POST_DATA.' . LF .
505 ' Warning: Expect fatal errors in central parts of the CMS' .
506 ' if the value is not changed to:' . LF .
507 'always_populate_raw_post_data=-1'
508 );
509 } else {
510 $status = new Status\OkStatus();
511 $status->setTitle('PHP always_populate_raw_post_data is fine');
512 }
513 return $status;
514 }
515
516 /**
517 * Check for doc_root ini setting
518 *
519 * @return Status\StatusInterface
520 */
521 protected function checkDocRoot()
522 {
523 $docRootSetting = trim(ini_get('doc_root'));
524 if ($docRootSetting !== '') {
525 $status = new Status\NoticeStatus();
526 $status->setTitle('doc_root is set');
527 $status->setMessage(
528 'doc_root=' . $docRootSetting . LF .
529 'PHP cannot execute scripts' .
530 ' outside this directory. This setting is seldom used and must correlate' .
531 ' with your actual document root. You might be in trouble if your' .
532 ' TYPO3 CMS core code is linked to some different location.' .
533 ' If that is a problem, the setting must be changed.'
534 );
535 } else {
536 $status = new Status\OkStatus();
537 $status->setTitle('PHP doc_root is not set');
538 }
539 return $status;
540 }
541
542 /**
543 * Check open_basedir
544 *
545 * @return Status\StatusInterface
546 */
547 protected function checkOpenBaseDir()
548 {
549 $openBaseDirSetting = trim(ini_get('open_basedir'));
550 if ($openBaseDirSetting !== '') {
551 $status = new Status\NoticeStatus();
552 $status->setTitle('PHP open_basedir is set');
553 $status->setMessage(
554 'open_basedir = ' . ini_get('open_basedir') . LF .
555 'This restricts TYPO3 to open and include files only in this' .
556 ' path. Please make sure that this does not prevent TYPO3 from running,' .
557 ' if for example your TYPO3 CMS core is linked to a different directory' .
558 ' not included in this path.'
559 );
560 } else {
561 $status = new Status\OkStatus();
562 $status->setTitle('PHP open_basedir is off');
563 }
564 return $status;
565 }
566
567 /**
568 * If xdebug is loaded, the default max_nesting_level of 100 must be raised
569 *
570 * @return Status\StatusInterface
571 */
572 protected function checkXdebugMaxNestingLevel()
573 {
574 if (extension_loaded('xdebug')) {
575 $recommendedMaxNestingLevel = 400;
576 $errorThreshold = 250;
577 $currentMaxNestingLevel = ini_get('xdebug.max_nesting_level');
578 if ($currentMaxNestingLevel < $errorThreshold) {
579 $status = new Status\ErrorStatus();
580 $status->setTitle('PHP xdebug.max_nesting_level is critically low');
581 $status->setMessage(
582 'xdebug.max_nesting_level=' . $currentMaxNestingLevel . LF .
583 'This setting controls the maximum number of nested function calls to protect against' .
584 ' infinite recursion. The current value is too low for TYPO3 CMS and must' .
585 ' be either raised or xdebug has to be unloaded. A value of ' . $recommendedMaxNestingLevel .
586 ' is recommended. Warning: Expect fatal PHP errors in central parts of the CMS' .
587 ' if the value is not raised significantly to:' . LF .
588 'xdebug.max_nesting_level=' . $recommendedMaxNestingLevel
589 );
590 } elseif ($currentMaxNestingLevel < $recommendedMaxNestingLevel) {
591 $status = new Status\WarningStatus();
592 $status->setTitle('PHP xdebug.max_nesting_level is low');
593 $status->setMessage(
594 'xdebug.max_nesting_level=' . $currentMaxNestingLevel . LF .
595 'This setting controls the maximum number of nested function calls to protect against' .
596 ' infinite recursion. The current value is high enough for the TYPO3 CMS core to work' .
597 ' fine, but still some extensions could raise fatal PHP errors if the setting is not' .
598 ' raised further. A value of ' . $recommendedMaxNestingLevel . ' is recommended.' . LF .
599 'xdebug.max_nesting_level=' . $recommendedMaxNestingLevel
600 );
601 } else {
602 $status = new Status\OkStatus();
603 $status->setTitle('PHP xdebug.max_nesting_level ok');
604 }
605 } else {
606 $status = new Status\OkStatus();
607 $status->setTitle('PHP xdebug extension not loaded');
608 }
609 return $status;
610 }
611
612 /**
613 * Check accessibility and functionality of OpenSSL
614 *
615 * @return Status\StatusInterface
616 */
617 protected function checkOpenSslInstalled()
618 {
619 if (extension_loaded('openssl')) {
620 $testKey = @openssl_pkey_new();
621 if (is_resource($testKey)) {
622 openssl_free_key($testKey);
623 $status = new Status\OkStatus();
624 $status->setTitle('PHP OpenSSL extension installed properly');
625 } else {
626 $status = new Status\ErrorStatus();
627 $status->setTitle('PHP OpenSSL extension not working');
628 $status->setMessage(
629 'Something went wrong while trying to create a new private key for testing.' .
630 ' Please check the integration of the PHP OpenSSL extension and if it is installed correctly.'
631 );
632 }
633 } else {
634 $status = new Status\ErrorStatus();
635 $status->setTitle('PHP OpenSSL extension not loaded');
636 $status->setMessage(
637 'OpenSSL is a PHP extension to encrypt/decrypt data between requests.' .
638 ' TYPO3 CMS requires it to be able to encrypt stored passwords to improve the security in the' .
639 ' database layer.'
640 );
641 }
642
643 return $status;
644 }
645
646 /**
647 * Get max_input_vars status
648 *
649 * @return Status\StatusInterface
650 */
651 protected function checkMaxInputVars()
652 {
653 $recommendedMaxInputVars = 1500;
654 $minimumMaxInputVars = 1000;
655 $currentMaxInputVars = ini_get('max_input_vars');
656
657 if ($currentMaxInputVars < $minimumMaxInputVars) {
658 $status = new Status\ErrorStatus();
659 $status->setTitle('PHP max_input_vars too low');
660 $status->setMessage(
661 'max_input_vars=' . $currentMaxInputVars . LF .
662 'This setting can lead to lost information if submitting forms with lots of data in TYPO3 CMS' .
663 ' (as the install tool does). It is highly recommended to raise this' .
664 ' to at least ' . $recommendedMaxInputVars . ':' . LF .
665 'max_input_vars=' . $recommendedMaxInputVars
666 );
667 } elseif ($currentMaxInputVars < $recommendedMaxInputVars) {
668 $status = new Status\WarningStatus();
669 $status->setTitle('PHP max_input_vars very low');
670 $status->setMessage(
671 'max_input_vars=' . $currentMaxInputVars . LF .
672 'This setting can lead to lost information if submitting forms with lots of data in TYPO3 CMS' .
673 ' (as the install tool does). It is highly recommended to raise this' .
674 ' to at least ' . $recommendedMaxInputVars . ':' . LF .
675 'max_input_vars=' . $recommendedMaxInputVars
676 );
677 } else {
678 $status = new Status\OkStatus();
679 $status->setTitle('PHP max_input_vars ok');
680 }
681 return $status;
682 }
683
684 /**
685 * Get suhosin loaded status
686 * Should be called only if suhosin extension is loaded
687 *
688 * @return Status\StatusInterface
689 * @throws \BadMethodCallException
690 */
691 protected function getSuhosinLoadedStatus()
692 {
693 if ($this->isSuhosinLoadedAndActive()) {
694 $status = new Status\OkStatus();
695 $status->setTitle('PHP suhosin extension loaded and active');
696 return $status;
697 } else {
698 throw new \BadMethodCallException('Should be called only if suhosin extension is loaded', 1422634778);
699 }
700 }
701
702 /**
703 * Check suhosin.request.max_vars
704 *
705 * @return Status\StatusInterface
706 */
707 protected function checkSuhosinRequestMaxVars()
708 {
709 $recommendedRequestMaxVars = 400;
710 if ($this->isSuhosinLoadedAndActive()) {
711 $currentRequestMaxVars = ini_get('suhosin.request.max_vars');
712 if ($currentRequestMaxVars < $recommendedRequestMaxVars) {
713 $status = new Status\ErrorStatus();
714 $status->setTitle('PHP suhosin.request.max_vars too low');
715 $status->setMessage(
716 'suhosin.request.max_vars=' . $currentRequestMaxVars . LF .
717 'This setting can lead to lost information if submitting forms with lots of data in TYPO3 CMS' .
718 ' (as the install tool does). It is highly recommended to raise this' .
719 ' to at least ' . $recommendedRequestMaxVars . ':' . LF .
720 'suhosin.request.max_vars=' . $recommendedRequestMaxVars
721 );
722 } else {
723 $status = new Status\OkStatus();
724 $status->setTitle('PHP suhosin.request.max_vars ok');
725 }
726 } else {
727 $status = new Status\InfoStatus();
728 $status->setTitle('Suhosin not loaded');
729 $status->setMessage(
730 'If enabling suhosin, suhosin.request.max_vars' .
731 ' should be set to at least ' . $recommendedRequestMaxVars . ':' . LF .
732 'suhosin.request.max_vars=' . $recommendedRequestMaxVars
733 );
734 }
735 return $status;
736 }
737
738 /**
739 * Check suhosin.request.max_varname_length
740 *
741 * @return Status\StatusInterface
742 */
743 protected function checkSuhosinRequestMaxVarnameLength()
744 {
745 $recommendedRequestMaxVarnameLength = 200;
746 if ($this->isSuhosinLoadedAndActive()) {
747 $currentRequestMaxVarnameLength = ini_get('suhosin.request.max_varname_length');
748 if ($currentRequestMaxVarnameLength < $recommendedRequestMaxVarnameLength) {
749 $status = new Status\ErrorStatus();
750 $status->setTitle('PHP suhosin.request.max_varname_length too low');
751 $status->setMessage(
752 'suhosin.request.max_varname_length=' . $currentRequestMaxVarnameLength . LF .
753 'This setting can lead to lost information if submitting forms with lots of data in TYPO3 CMS' .
754 ' (as the install tool does). It is highly recommended to raise this' .
755 ' to at least ' . $recommendedRequestMaxVarnameLength . ':' . LF .
756 'suhosin.request.max_varname_length=' . $recommendedRequestMaxVarnameLength
757 );
758 } else {
759 $status = new Status\OkStatus();
760 $status->setTitle('PHP suhosin.request.max_varname_length ok');
761 }
762 } else {
763 $status = new Status\InfoStatus();
764 $status->setTitle('Suhosin not loaded');
765 $status->setMessage(
766 'If enabling suhosin, suhosin.request.max_varname_length' .
767 ' should be set to at least ' . $recommendedRequestMaxVarnameLength . ':' . LF .
768 'suhosin.request.max_varname_length=' . $recommendedRequestMaxVarnameLength
769 );
770 }
771 return $status;
772 }
773
774 /**
775 * Check suhosin.post.max_name_length
776 *
777 * @return Status\StatusInterface
778 */
779 protected function checkSuhosinPostMaxNameLength()
780 {
781 $recommendedPostMaxNameLength = 200;
782 if ($this->isSuhosinLoadedAndActive()) {
783 $currentPostMaxNameLength = ini_get('suhosin.post.max_name_length');
784 if ($currentPostMaxNameLength < $recommendedPostMaxNameLength) {
785 $status = new Status\ErrorStatus();
786 $status->setTitle('PHP suhosin.post.max_name_length too low');
787 $status->setMessage(
788 'suhosin.post.max_name_length=' . $currentPostMaxNameLength . LF .
789 'This setting can lead to lost information if submitting forms with lots of data in TYPO3 CMS' .
790 ' (as the install tool does). It is highly recommended to raise this' .
791 ' to at least ' . $recommendedPostMaxNameLength . ':' . LF .
792 'suhosin.post.max_name_length=' . $recommendedPostMaxNameLength
793 );
794 } else {
795 $status = new Status\OkStatus();
796 $status->setTitle('PHP suhosin.post.max_name_length ok');
797 }
798 } else {
799 $status = new Status\InfoStatus();
800 $status->setTitle('Suhosin not loaded');
801 $status->setMessage(
802 'If enabling suhosin, suhosin.post.max_name_length' .
803 ' should be set to at least ' . $recommendedPostMaxNameLength . ':' . LF .
804 'suhosin.post.max_name_length=' . $recommendedPostMaxNameLength
805 );
806 }
807 return $status;
808 }
809
810 /**
811 * Check suhosin.post.max_vars
812 *
813 * @return Status\StatusInterface
814 */
815 protected function checkSuhosinPostMaxVars()
816 {
817 $recommendedPostMaxVars = 400;
818 if ($this->isSuhosinLoadedAndActive()) {
819 $currentPostMaxVars = ini_get('suhosin.post.max_vars');
820 if ($currentPostMaxVars < $recommendedPostMaxVars) {
821 $status = new Status\ErrorStatus();
822 $status->setTitle('PHP suhosin.post.max_vars too low');
823 $status->setMessage(
824 'suhosin.post.max_vars=' . $currentPostMaxVars . LF .
825 'This setting can lead to lost information if submitting forms with lots of data in TYPO3 CMS' .
826 ' (as the install tool does). It is highly recommended to raise this' .
827 ' to at least ' . $recommendedPostMaxVars . ':' . LF .
828 'suhosin.post.max_vars=' . $recommendedPostMaxVars
829 );
830 } else {
831 $status = new Status\OkStatus();
832 $status->setTitle('PHP suhosin.post.max_vars ok');
833 }
834 } else {
835 $status = new Status\InfoStatus();
836 $status->setTitle('Suhosin not loaded');
837 $status->setMessage(
838 'If enabling suhosin, suhosin.post.max_vars' .
839 ' should be set to at least ' . $recommendedPostMaxVars . ':' . LF .
840 'suhosin.post.max_vars=' . $recommendedPostMaxVars
841 );
842 }
843 return $status;
844 }
845
846 /**
847 * Check suhosin.get.max_value_length
848 *
849 * @return Status\StatusInterface
850 */
851 protected function checkSuhosinGetMaxValueLength()
852 {
853 $recommendedGetMaxValueLength = 2000;
854 if ($this->isSuhosinLoadedAndActive()) {
855 $currentGetMaxValueLength = ini_get('suhosin.get.max_value_length');
856 if ($currentGetMaxValueLength < $recommendedGetMaxValueLength) {
857 $status = new Status\ErrorStatus();
858 $status->setTitle('PHP suhosin.get.max_value_length too low');
859 $status->setMessage(
860 'suhosin.get.max_value_length=' . $currentGetMaxValueLength . LF .
861 'This setting can lead to lost information if submitting forms with lots of data in TYPO3 CMS' .
862 ' (as the install tool does). It is highly recommended to raise this' .
863 ' to at least ' . $recommendedGetMaxValueLength . ':' . LF .
864 'suhosin.get.max_value_length=' . $recommendedGetMaxValueLength
865 );
866 } else {
867 $status = new Status\OkStatus();
868 $status->setTitle('PHP suhosin.get.max_value_length ok');
869 }
870 } else {
871 $status = new Status\InfoStatus();
872 $status->setTitle('Suhosin not loaded');
873 $status->setMessage(
874 'If enabling suhosin, suhosin.get.max_value_length' .
875 ' should be set to at least ' . $recommendedGetMaxValueLength . ':' . LF .
876 'suhosin.get.max_value_length=' . $recommendedGetMaxValueLength
877 );
878 }
879 return $status;
880 }
881
882 /**
883 * Check suhosin.get.max_name_length
884 *
885 * @return Status\StatusInterface
886 */
887 protected function checkSuhosinGetMaxNameLength()
888 {
889 $recommendedGetMaxNameLength = 200;
890 if ($this->isSuhosinLoadedAndActive()) {
891 $currentGetMaxNameLength = ini_get('suhosin.get.max_name_length');
892 if ($currentGetMaxNameLength < $recommendedGetMaxNameLength) {
893 $status = new Status\ErrorStatus();
894 $status->setTitle('PHP suhosin.get.max_name_length too low');
895 $status->setMessage(
896 'suhosin.get.max_name_length=' . $currentGetMaxNameLength . LF .
897 'This setting can lead to lost information if submitting forms with lots of data in TYPO3 CMS' .
898 ' (as the install tool does). It is highly recommended to raise this' .
899 ' to at least ' . $recommendedGetMaxNameLength . ':' . LF .
900 'suhosin.get.max_name_length=' . $recommendedGetMaxNameLength
901 );
902 } else {
903 $status = new Status\OkStatus();
904 $status->setTitle('PHP suhosin.get.max_name_length ok');
905 }
906 } else {
907 $status = new Status\InfoStatus();
908 $status->setTitle('Suhosin not loaded');
909 $status->setMessage(
910 'If enabling suhosin, suhosin.get.max_name_length' .
911 ' should be set to at least ' . $recommendedGetMaxNameLength . ':' . LF .
912 'suhosin.get.max_name_length=' . $recommendedGetMaxNameLength
913 );
914 }
915 return $status;
916 }
917
918 /**
919 * Check suhosin.executor.include.whitelist contains phar
920 *
921 * @return Status\StatusInterface
922 */
923 protected function checkSuhosinExecutorIncludeWhiteListContainsPhar()
924 {
925 if ($this->isSuhosinLoadedAndActive()) {
926 $whitelist = (string)ini_get('suhosin.executor.include.whitelist');
927 if (strpos($whitelist, 'phar') === false) {
928 $status = new Status\NoticeStatus();
929 $status->setTitle('PHP suhosin.executor.include.whitelist does not contain phar');
930 $status->setMessage(
931 'suhosin.executor.include.whitelist= ' . $whitelist . LF .
932 '"phar" is currently not a hard requirement of TYPO3 CMS but is nice to have and a possible' .
933 ' requirement in future versions. A useful setting is:' . LF .
934 'suhosin.executor.include.whitelist=phar,vfs'
935 );
936 } else {
937 $status = new Status\OkStatus();
938 $status->setTitle('PHP suhosin.executor.include.whitelist contains phar');
939 }
940 } else {
941 $status = new Status\InfoStatus();
942 $status->setTitle('Suhosin not loaded');
943 $status->setMessage(
944 'If enabling suhosin, a useful setting is:' . LF .
945 'suhosin.executor.include.whitelist=phar,vfs'
946 );
947 }
948 return $status;
949 }
950
951 /**
952 * Check suhosin.executor.include.whitelist contains vfs
953 *
954 * @return Status\StatusInterface
955 */
956 protected function checkSuhosinExecutorIncludeWhiteListContainsVfs()
957 {
958 if ($this->isSuhosinLoadedAndActive()) {
959 $whitelist = (string)ini_get('suhosin.executor.include.whitelist');
960 if (strpos($whitelist, 'vfs') === false) {
961 $status = new Status\WarningStatus();
962 $status->setTitle('PHP suhosin.executor.include.whitelist does not contain vfs');
963 $status->setMessage(
964 'suhosin.executor.include.whitelist= ' . $whitelist . LF .
965 '"vfs" is currently not a hard requirement of TYPO3 CMS but tons of unit tests rely on it.' .
966 ' Furthermore, vfs will likely be a base for an additional compatibility layer in the future.' .
967 ' A useful setting is:' . LF .
968 'suhosin.executor.include.whitelist=phar,vfs'
969 );
970 } else {
971 $status = new Status\OkStatus();
972 $status->setTitle('PHP suhosin.executor.include.whitelist contains vfs');
973 }
974 } else {
975 $status = new Status\InfoStatus();
976 $status->setTitle('Suhosin not loaded');
977 $status->setMessage(
978 'If enabling suhosin, a useful setting is:' . LF .
979 'suhosin.executor.include.whitelist=phar,vfs'
980 );
981 }
982 return $status;
983 }
984
985 /**
986 * Check if some opcode cache is loaded
987 *
988 * @return Status\StatusInterface
989 */
990 protected function checkSomePhpOpcodeCacheIsLoaded()
991 {
992 // Link to our wiki page, so we can update opcode cache issue information independent of TYPO3 CMS releases.
993 $wikiLink = 'For more information take a look in our wiki ' . TYPO3_URL_WIKI_OPCODECACHE . '.';
994 $opcodeCaches = GeneralUtility::makeInstance(OpcodeCacheService::class)->getAllActive();
995 if (empty($opcodeCaches)) {
996 // Set status to notice. It needs to be notice so email won't be triggered.
997 $status = new Status\NoticeStatus();
998 $status->setTitle('No PHP opcode cache loaded');
999 $status->setMessage(
1000 'PHP opcode caches hold a compiled version of executed PHP scripts in' .
1001 ' memory and do not require to recompile a script each time it is accessed.' .
1002 ' This can be a massive performance improvement and can reduce the load on a' .
1003 ' server in general. A parse time reduction by factor three for fully cached' .
1004 ' pages can be achieved easily if using an opcode cache.' .
1005 LF . $wikiLink
1006 );
1007 } else {
1008 $status = new Status\OkStatus();
1009 $message = '';
1010
1011 foreach ($opcodeCaches as $opcodeCache => $properties) {
1012 $message .= 'Name: ' . $opcodeCache . ' Version: ' . $properties['version'];
1013 $message .= LF;
1014
1015 if ($properties['error']) {
1016 // Set status to error if not already set
1017 if ($status->getSeverity() !== 'error') {
1018 $status = new Status\ErrorStatus();
1019 }
1020 $message .= ' This opcode cache is marked as malfunctioning by the TYPO3 CMS Team.';
1021 } elseif ($properties['canInvalidate']) {
1022 $message .= ' This opcode cache should work correctly and has good performance.';
1023 } else {
1024 // Set status to notice if not already error set. It needs to be notice so email won't be triggered.
1025 if ($status->getSeverity() !== 'error' || $status->getSeverity() !== 'warning') {
1026 $status = new Status\NoticeStatus();
1027 }
1028 $message .= ' This opcode cache may work correctly but has medium performance.';
1029 }
1030 $message .= LF;
1031 }
1032
1033 $message .= $wikiLink;
1034
1035 // Set title of status depending on serverity
1036 switch ($status->getSeverity()) {
1037 case 'error':
1038 $status->setTitle('A possibly malfunctioning PHP opcode cache is loaded');
1039 break;
1040 case 'warning':
1041 $status->setTitle('A PHP opcode cache is loaded which may cause problems');
1042 break;
1043 case 'ok':
1044 default:
1045 $status->setTitle('A PHP opcode cache is loaded');
1046 break;
1047 }
1048 $status->setMessage($message);
1049 }
1050 return $status;
1051 }
1052
1053 /**
1054 * Check doc comments can be fetched by reflection
1055 *
1056 * @return Status\StatusInterface
1057 */
1058 protected function checkReflectionDocComment()
1059 {
1060 $testReflection = new \ReflectionMethod(get_class($this), __FUNCTION__);
1061 if ($testReflection->getDocComment() === false) {
1062 $status = new Status\AlertStatus();
1063 $status->setTitle('PHP Doc comment reflection broken');
1064 $status->setMessage(
1065 'TYPO3 CMS core extensions like extbase and fluid heavily rely on method'
1066 . ' comment parsing to fetch annotations and add magic belonging to them.'
1067 . ' This does not work in the current environment and so we cannot install'
1068 . ' TYPO3 CMS.' . LF
1069 . ' Here are some possibilities: ' . LF
1070 . '* In Zend OPcache you can disable saving/loading comments. If you are using'
1071 . ' Zend OPcache (included since PHP 5.5) then check your php.ini settings for'
1072 . ' opcache.save_comments and opcache.load_comments and enable them.' . LF
1073 . '* In Zend Optimizer+ you can disable saving comments. If you are using'
1074 . ' Zend Optimizer+ then check your php.ini settings for'
1075 . ' zend_optimizerplus.save_comments and enable it.' . LF
1076 . '* The PHP extension eaccelerator is known to break this if'
1077 . ' it is compiled without --with-eaccelerator-doc-comment-inclusion flag.'
1078 . ' This compile flag must be specified, otherwise TYPO3 CMS will not work.' . LF
1079 . 'For more information take a look in our wiki ' . TYPO3_URL_WIKI_OPCODECACHE . '.'
1080 );
1081 } else {
1082 $status = new Status\OkStatus();
1083 $status->setTitle('PHP Doc comment reflection works');
1084 }
1085 return $status;
1086 }
1087
1088 /**
1089 * Check if systemLocale setting is correct (locale exists in the OS)
1090 *
1091 * @return Status\StatusInterface
1092 */
1093 protected function checkSystemLocale()
1094 {
1095 $currentLocale = setlocale(LC_CTYPE, 0);
1096
1097 // On Windows an empty locale value uses the regional settings from the Control Panel
1098 if ($GLOBALS['TYPO3_CONF_VARS']['SYS']['systemLocale'] === '' && TYPO3_OS !== 'WIN') {
1099 $status = new Status\InfoStatus();
1100 $status->setTitle('Empty systemLocale setting');
1101 $status->setMessage(
1102 '$GLOBALS[TYPO3_CONF_VARS][SYS][systemLocale] is not set. This is fine as long as no UTF-8' .
1103 ' file system is used.'
1104 );
1105 } elseif (setlocale(LC_CTYPE, $GLOBALS['TYPO3_CONF_VARS']['SYS']['systemLocale']) === false) {
1106 $status = new Status\ErrorStatus();
1107 $status->setTitle('Incorrect systemLocale setting');
1108 $status->setMessage(
1109 'Current value of the $GLOBALS[TYPO3_CONF_VARS][SYS][systemLocale] is incorrect. A locale with' .
1110 ' this name doesn\'t exist in the operating system.'
1111 );
1112 setlocale(LC_CTYPE, $currentLocale);
1113 } else {
1114 $status = new Status\OkStatus();
1115 $status->setTitle('System locale is correct');
1116 }
1117
1118 return $status;
1119 }
1120
1121 /**
1122 * Checks whether we can use file names with UTF-8 characters.
1123 * Configured system locale must support UTF-8 when UTF8filesystem is set
1124 *
1125 * @return Status\StatusInterface
1126 */
1127 protected function checkLocaleWithUTF8filesystem()
1128 {
1129 if ($GLOBALS['TYPO3_CONF_VARS']['SYS']['UTF8filesystem']) {
1130 // On Windows an empty local value uses the regional settings from the Control Panel
1131 if ($GLOBALS['TYPO3_CONF_VARS']['SYS']['systemLocale'] === '' && TYPO3_OS !== 'WIN') {
1132 $status = new Status\ErrorStatus();
1133 $status->setTitle('System locale not set on UTF-8 file system');
1134 $status->setMessage(
1135 '$GLOBALS[TYPO3_CONF_VARS][SYS][UTF8filesystem] is set, but $GLOBALS[TYPO3_CONF_VARS][SYS][systemLocale]' .
1136 ' is empty. Make sure a valid locale which supports UTF-8 is set.'
1137 );
1138 } else {
1139 $testString = 'ÖöĄĆŻĘĆćążąęó.jpg';
1140 $currentLocale = setlocale(LC_CTYPE, 0);
1141 $quote = TYPO3_OS === 'WIN' ? '"' : '\'';
1142
1143 setlocale(LC_CTYPE, $GLOBALS['TYPO3_CONF_VARS']['SYS']['systemLocale']);
1144
1145 if (escapeshellarg($testString) === $quote . $testString . $quote) {
1146 $status = new Status\OkStatus();
1147 $status->setTitle('File names with UTF-8 characters can be used.');
1148 } else {
1149 $status = new Status\ErrorStatus();
1150 $status->setTitle('System locale setting doesn\'t support UTF-8 file names.');
1151 $status->setMessage(
1152 'Please check your $GLOBALS[TYPO3_CONF_VARS][SYS][systemLocale] setting.'
1153 );
1154 }
1155
1156 setlocale(LC_CTYPE, $currentLocale);
1157 }
1158 } else {
1159 $status = new Status\OkStatus();
1160 $status->setTitle('Skipping test, as UTF8filesystem is not enabled.');
1161 }
1162
1163 return $status;
1164 }
1165
1166 /**
1167 * Checks thread stack size if on windows with apache
1168 *
1169 * @return Status\StatusInterface
1170 */
1171 protected function checkWindowsApacheThreadStackSize()
1172 {
1173 if ($this->isWindowsOs()
1174 && substr($_SERVER['SERVER_SOFTWARE'], 0, 6) === 'Apache'
1175 ) {
1176 $status = new Status\WarningStatus();
1177 $status->setTitle('Windows apache thread stack size');
1178 $status->setMessage(
1179 'This current value cannot be checked by the system, so please ignore this warning if it' .
1180 ' is already taken care of: Fluid uses complex regular expressions which require a lot' .
1181 ' of stack space during the first processing.' .
1182 ' On Windows the default stack size for Apache is a lot smaller than on UNIX.' .
1183 ' You can increase the size to 8MB (default on UNIX) by adding the following configuration' .
1184 ' to httpd.conf and restarting Apache afterwards:' . LF .
1185 '<IfModule mpm_winnt_module>' . LF .
1186 'ThreadStackSize 8388608' . LF .
1187 '</IfModule>'
1188 );
1189 } else {
1190 $status = new Status\OkStatus();
1191 $status->setTitle('Apache ThreadStackSize is not an issue on UNIX systems');
1192 }
1193 return $status;
1194 }
1195
1196 /**
1197 * Check if a specific required PHP extension is loaded
1198 *
1199 * @param string $extension
1200 * @return Status\StatusInterface
1201 */
1202 protected function checkRequiredPhpExtension($extension)
1203 {
1204 if (!extension_loaded($extension)) {
1205 $status = new Status\ErrorStatus();
1206 $status->setTitle('PHP extension ' . $extension . ' not loaded');
1207 $status->setMessage(
1208 'TYPO3 CMS uses PHP extension ' . $extension . ' but it is not loaded' .
1209 ' in your environment. Change your environment to provide this extension.'
1210 );
1211 } else {
1212 $status = new Status\OkStatus();
1213 $status->setTitle('PHP extension ' . $extension . ' loaded');
1214 }
1215 return $status;
1216 }
1217
1218 /**
1219 * Check imagecreatetruecolor to verify gdlib works as expected
1220 *
1221 * @return Status\StatusInterface
1222 */
1223 protected function checkGdLibTrueColorSupport()
1224 {
1225 if (function_exists('imagecreatetruecolor')) {
1226 $imageResource = @imagecreatetruecolor(50, 100);
1227 if (is_resource($imageResource)) {
1228 imagedestroy($imageResource);
1229 $status = new Status\OkStatus();
1230 $status->setTitle('PHP GD library true color works');
1231 } else {
1232 $status = new Status\ErrorStatus();
1233 $status->setTitle('PHP GD library true color support broken');
1234 $status->setMessage(
1235 'GD is loaded, but calling imagecreatetruecolor() fails.' .
1236 ' This must be fixed, TYPO3 CMS won\'t work well otherwise.'
1237 );
1238 }
1239 } else {
1240 $status = new Status\ErrorStatus();
1241 $status->setTitle('PHP GD library true color support missing');
1242 $status->setMessage(
1243 'Gdlib is essential for TYPO3 CMS to work properly.'
1244 );
1245 }
1246 return $status;
1247 }
1248
1249 /**
1250 * Check gif support of GD library
1251 *
1252 * @return Status\StatusInterface
1253 */
1254 protected function checkGdLibGifSupport()
1255 {
1256 if (function_exists('imagecreatefromgif')
1257 && function_exists('imagegif')
1258 && (imagetypes() & IMG_GIF)
1259 ) {
1260 $imageResource = @imagecreatefromgif(__DIR__ . '/../../Resources/Public/Images/TestInput/Test.gif');
1261 if (is_resource($imageResource)) {
1262 imagedestroy($imageResource);
1263 $status = new Status\OkStatus();
1264 $status->setTitle('PHP GD library has gif support');
1265 } else {
1266 $status = new Status\ErrorStatus();
1267 $status->setTitle('PHP GD library gif support broken');
1268 $status->setMessage(
1269 'GD is loaded, but calling imagecreatefromgif() fails.' .
1270 ' This must be fixed, TYPO3 CMS won\'t work well otherwise.'
1271 );
1272 }
1273 } else {
1274 $status = new Status\ErrorStatus();
1275 $status->setTitle('PHP GD library gif support missing');
1276 $status->setMessage(
1277 'GD must be compiled with gif support. This is essential for' .
1278 ' TYPO3 CMS to work properly.'
1279 );
1280 }
1281 return $status;
1282 }
1283
1284 /**
1285 * Check jgp support of GD library
1286 *
1287 * @return Status\StatusInterface
1288 */
1289 protected function checkGdLibJpgSupport()
1290 {
1291 if (function_exists('imagecreatefromjpeg')
1292 && function_exists('imagejpeg')
1293 && (imagetypes() & IMG_JPG)
1294 ) {
1295 $status = new Status\OkStatus();
1296 $status->setTitle('PHP GD library has jpg support');
1297 } else {
1298 $status = new Status\ErrorStatus();
1299 $status->setTitle('PHP GD library jpg support missing');
1300 $status->setMessage(
1301 'GD must be compiled with jpg support. This is essential for' .
1302 ' TYPO3 CMS to work properly.'
1303 );
1304 }
1305 return $status;
1306 }
1307
1308 /**
1309 * Check png support of GD library
1310 *
1311 * @return Status\StatusInterface
1312 */
1313 protected function checkGdLibPngSupport()
1314 {
1315 if (function_exists('imagecreatefrompng')
1316 && function_exists('imagepng')
1317 && (imagetypes() & IMG_PNG)
1318 ) {
1319 $imageResource = @imagecreatefrompng(__DIR__ . '/../../Resources/Public/Images/TestInput/Test.png');
1320 if (is_resource($imageResource)) {
1321 imagedestroy($imageResource);
1322 $status = new Status\OkStatus();
1323 $status->setTitle('PHP GD library has png support');
1324 } else {
1325 $status = new Status\ErrorStatus();
1326 $status->setTitle('PHP GD library png support broken');
1327 $status->setMessage(
1328 'GD is compiled with png support, but calling imagecreatefrompng() fails.' .
1329 ' Check your environment and fix it, png in GD lib is important' .
1330 ' for TYPO3 CMS to work properly.'
1331 );
1332 }
1333 } else {
1334 $status = new Status\ErrorStatus();
1335 $status->setTitle('PHP GD library png support missing');
1336 $status->setMessage(
1337 'GD must be compiled with png support. This is essential for' .
1338 ' TYPO3 CMS to work properly'
1339 );
1340 }
1341 return $status;
1342 }
1343
1344 /**
1345 * Check gdlib supports freetype
1346 *
1347 * @return Status\StatusInterface
1348 */
1349 protected function checkGdLibFreeTypeSupport()
1350 {
1351 if (function_exists('imagettftext')) {
1352 $status = new Status\OkStatus();
1353 $status->setTitle('PHP GD library has freetype font support');
1354 $status->setMessage(
1355 'There is a difference between the font size setting which the GD' .
1356 ' library should be supplied with. If installation is completed' .
1357 ' a test in the install tool helps to find out the value you need.'
1358 );
1359 } else {
1360 $status = new Status\ErrorStatus();
1361 $status->setTitle('PHP GD library freetype support missing');
1362 $status->setMessage(
1363 'Some core functionality and extension rely on the GD' .
1364 ' to render fonts on images. This support is missing' .
1365 ' in your environment. Install it.'
1366 );
1367 }
1368 return $status;
1369 }
1370
1371 /**
1372 * Create true type font test image
1373 *
1374 * @return Status\StatusInterface
1375 */
1376 protected function isTrueTypeFontWorking()
1377 {
1378 if (function_exists('imageftbbox')) {
1379 // 20 Pixels at 96 DPI
1380 $fontSize = (20 / 96 * 72);
1381 $textDimensions = @imageftbbox(
1382 $fontSize,
1383 0,
1384 __DIR__ . '/../../Resources/Private/Font/vera.ttf',
1385 'Testing true type support'
1386 );
1387 $fontBoxWidth = $textDimensions[2] - $textDimensions[0];
1388 if ($fontBoxWidth < 300 && $fontBoxWidth > 200) {
1389 $status = new Status\OkStatus();
1390 $status->setTitle('FreeType True Type Font DPI');
1391 $status->setMessage('Fonts are rendered by FreeType library. ' .
1392 'We need to ensure that the final dimensions are as expected. ' .
1393 'This server renderes fonts based on 96 DPI correctly');
1394 } else {
1395 $status = new Status\NoticeStatus();
1396 $status->setTitle('FreeType True Type Font DPI');
1397 $status->setMessage('Fonts are rendered by FreeType library. ' .
1398 'This server does not render fonts as expected. ' .
1399 'Please check your FreeType 2 module.');
1400 }
1401 } else {
1402 $status = new Status\ErrorStatus();
1403 $status->setTitle('PHP GD library freetype2 support missing');
1404 $status->setMessage(
1405 'The core relies on GD library compiled into PHP with freetype2' .
1406 ' support. This is missing on your system. Please install it.'
1407 );
1408 }
1409
1410 return $status;
1411 }
1412
1413 /**
1414 * Check register globals
1415 *
1416 * @return Status\StatusInterface
1417 */
1418 protected function checkRegisterGlobals()
1419 {
1420 $registerGlobalsEnabled = filter_var(
1421 ini_get('register_globals'),
1422 FILTER_VALIDATE_BOOLEAN,
1423 array(FILTER_REQUIRE_SCALAR, FILTER_NULL_ON_FAILURE)
1424 );
1425 if ($registerGlobalsEnabled === true) {
1426 $status = new Status\ErrorStatus();
1427 $status->setTitle('PHP register globals on');
1428 $status->setMessage(
1429 'register_globals=' . ini_get('register_globals') . LF .
1430 'TYPO3 requires PHP setting "register_globals" set to off.' .
1431 ' This ancient PHP setting is a big security problem and should' .
1432 ' never be enabled:' . LF .
1433 'register_globals=Off'
1434 );
1435 } else {
1436 $status = new Status\OkStatus();
1437 $status->setTitle('PHP register globals off');
1438 }
1439 return $status;
1440 }
1441
1442 /**
1443 * Check for bug in libxml
1444 *
1445 * @return Status\StatusInterface
1446 */
1447 protected function checkLibXmlBug()
1448 {
1449 $sampleArray = array('Test>><<Data');
1450
1451 $xmlContent = '<numIndex index="0">Test&gt;&gt;&lt;&lt;Data</numIndex>' . LF;
1452
1453 $xml = \TYPO3\CMS\Core\Utility\GeneralUtility::array2xml($sampleArray, '', -1);
1454
1455 if ($xmlContent !== $xml) {
1456 $status = new Status\ErrorStatus();
1457 $status->setTitle('PHP libxml bug present');
1458 $status->setMessage(
1459 'Some hosts have problems saving ">><<" in a flexform.' .
1460 ' To fix this, enable [BE][flexformForceCDATA] in' .
1461 ' All Configuration.'
1462 );
1463 } else {
1464 $status = new Status\OkStatus();
1465 $status->setTitle('PHP libxml bug not present');
1466 }
1467 return $status;
1468 }
1469
1470 /**
1471 * Helper methods
1472 */
1473
1474 /**
1475 * Validate a given IP address.
1476 *
1477 * @param string $ip IP address to be tested
1478 * @return bool
1479 */
1480 protected function isValidIp($ip)
1481 {
1482 return filter_var($ip, FILTER_VALIDATE_IP) !== false;
1483 }
1484
1485 /**
1486 * Test if this instance runs on windows OS
1487 *
1488 * @return bool TRUE if operating system is windows
1489 */
1490 protected function isWindowsOs()
1491 {
1492 $windowsOs = false;
1493 if (!stristr(PHP_OS, 'darwin') && stristr(PHP_OS, 'win')) {
1494 $windowsOs = true;
1495 }
1496 return $windowsOs;
1497 }
1498
1499 /**
1500 * Helper method to find out if suhosin extension is loaded
1501 *
1502 * @return bool TRUE if suhosin PHP extension is loaded
1503 */
1504 protected function isSuhosinLoadedAndActive()
1505 {
1506 $suhosinLoaded = false;
1507 if (extension_loaded('suhosin')) {
1508 $suhosinInSimulationMode = filter_var(
1509 ini_get('suhosin.simulation'),
1510 FILTER_VALIDATE_BOOLEAN,
1511 array(FILTER_REQUIRE_SCALAR, FILTER_NULL_ON_FAILURE)
1512 );
1513 if (!$suhosinInSimulationMode) {
1514 $suhosinLoaded = true;
1515 }
1516 }
1517 return $suhosinLoaded;
1518 }
1519
1520 /**
1521 * Helper method to explode a string by delimiter and throw away empty values.
1522 * Removes empty values from result array.
1523 *
1524 * @param string $delimiter Delimiter string to explode with
1525 * @param string $string The string to explode
1526 * @return array Exploded values
1527 */
1528 protected function trimExplode($delimiter, $string)
1529 {
1530 $explodedValues = explode($delimiter, $string);
1531 $resultWithPossibleEmptyValues = array_map('trim', $explodedValues);
1532 $result = array();
1533 foreach ($resultWithPossibleEmptyValues as $value) {
1534 if ($value !== '') {
1535 $result[] = $value;
1536 }
1537 }
1538 return $result;
1539 }
1540
1541 /**
1542 * Helper method to get the bytes value from a measurement string like "100k".
1543 *
1544 * @param string $measurement The measurement (e.g. "100k")
1545 * @return int The bytes value (e.g. 102400)
1546 */
1547 protected function getBytesFromSizeMeasurement($measurement)
1548 {
1549 $bytes = doubleval($measurement);
1550 if (stripos($measurement, 'G')) {
1551 $bytes *= 1024 * 1024 * 1024;
1552 } elseif (stripos($measurement, 'M')) {
1553 $bytes *= 1024 * 1024;
1554 } elseif (stripos($measurement, 'K')) {
1555 $bytes *= 1024;
1556 }
1557 return (int)$bytes;
1558 }
1559 }