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