68fcf16dc5ffb83980e371d7323b2480258bb4a3
[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\Install\Status;
18
19 /**
20 * Check system environment status
21 *
22 * This class is a hardcoded requirement check of the underlying
23 * server and PHP system.
24 *
25 * The class *must not* check for any TYPO3 specific things like
26 * specific configuration values or directories. It should not fail
27 * if there is no TYPO3 at all.
28 *
29 * The only core code used is the class loader
30 *
31 * This class is instantiated as the *very first* class during
32 * installation. It is meant to be *standalone* und must not have
33 * any requirements, except the status classes. It must be possible
34 * to run this script separated from the rest of the core, without
35 * dependencies.
36 *
37 * This means especially:
38 * * No hooks or anything like that
39 * * No usage of *any* TYPO3 code like GeneralUtility
40 * * No require of anything but the status classes
41 * * No localization
42 *
43 * The status messages and title *must not* include HTML, use plain
44 * text only. The return values of this class are not bound to HTML
45 * and can be used in different scopes (eg. as json array).
46 */
47 class Check
48 {
49 /**
50 * @var array List of required PHP extensions
51 */
52 protected $requiredPhpExtensions = [
53 'filter',
54 'gd',
55 'hash',
56 'json',
57 'mysqli',
58 'openssl',
59 'session',
60 'soap',
61 'SPL',
62 'standard',
63 'xml',
64 'zip',
65 'zlib',
66 ];
67
68 /**
69 * Get all status information as array with status objects
70 *
71 * @return array<\TYPO3\CMS\Install\Status\StatusInterface>
72 */
73 public function getStatus()
74 {
75 $status = [];
76 $status[] = $this->checkCurrentDirectoryIsInIncludePath();
77 $status[] = $this->checkFileUploadEnabled();
78 $status[] = $this->checkPostUploadSizeIsHigherOrEqualMaximumFileUploadSize();
79 $status[] = $this->checkMemorySettings();
80 $status[] = $this->checkPhpVersion();
81 $status[] = $this->checkMaxExecutionTime();
82 $status[] = $this->checkDisableFunctions();
83 $status[] = $this->checkMysqliReconnectSetting();
84 $status[] = $this->checkDocRoot();
85 $status[] = $this->checkOpenBaseDir();
86 $status[] = $this->checkXdebugMaxNestingLevel();
87 $status[] = $this->checkOpenSslInstalled();
88
89 if ($this->isSuhosinLoadedAndActive()) {
90 $status[] = $this->getSuhosinLoadedStatus();
91 $status[] = $this->checkSuhosinRequestMaxVars();
92 $status[] = $this->checkSuhosinRequestMaxVarnameLength();
93 $status[] = $this->checkSuhosinPostMaxNameLength();
94 $status[] = $this->checkSuhosinPostMaxVars();
95 $status[] = $this->checkSuhosinGetMaxNameLength();
96 $status[] = $this->checkSuhosinGetMaxValueLength();
97 $status[] = $this->checkSuhosinExecutorIncludeWhiteListContainsPhar();
98 $status[] = $this->checkSuhosinExecutorIncludeWhiteListContainsVfs();
99 }
100
101 $status[] = $this->checkMaxInputVars();
102 $status[] = $this->checkReflectionDocComment();
103 $status[] = $this->checkWindowsApacheThreadStackSize();
104
105 foreach ($this->requiredPhpExtensions as $extension) {
106 $status[] = $this->checkRequiredPhpExtension($extension);
107 }
108
109 $status[] = $this->checkPcreVersion();
110 $status[] = $this->checkGdLibTrueColorSupport();
111 $status[] = $this->checkGdLibGifSupport();
112 $status[] = $this->checkGdLibJpgSupport();
113 $status[] = $this->checkGdLibPngSupport();
114 $status[] = $this->checkGdLibFreeTypeSupport();
115
116 return $status;
117 }
118
119 /**
120 * Checks if current directory (.) is in PHP include path
121 *
122 * @return Status\StatusInterface
123 */
124 protected function checkCurrentDirectoryIsInIncludePath()
125 {
126 $includePath = ini_get('include_path');
127 $delimiter = $this->isWindowsOs() ? ';' : ':';
128 $pathArray = $this->trimExplode($delimiter, $includePath);
129 if (!in_array('.', $pathArray)) {
130 $status = new Status\WarningStatus();
131 $status->setTitle('Current directory (./) is not within PHP include path');
132 $status->setMessage(
133 'include_path = ' . implode(' ', $pathArray) . LF .
134 'Normally the current path \'.\' is included in the' .
135 ' include_path of PHP. Although TYPO3 does not rely on this,' .
136 ' it is an unusual setting that may introduce problems for' .
137 ' some extensions.'
138 );
139 } else {
140 $status = new Status\OkStatus();
141 $status->setTitle('Current directory (./) is within PHP include path.');
142 }
143 return $status;
144 }
145
146 /**
147 * Check if file uploads are enabled in PHP
148 *
149 * @return Status\StatusInterface
150 */
151 protected function checkFileUploadEnabled()
152 {
153 if (!ini_get('file_uploads')) {
154 $status = new Status\ErrorStatus();
155 $status->setTitle('File uploads not allowed in PHP');
156 $status->setMessage(
157 'file_uploads=' . ini_get('file_uploads') . LF .
158 'TYPO3 uses the ability to upload files from the browser in various cases.' .
159 ' If this flag is disabled in PHP, you won\'t be able to upload files.' .
160 ' But it doesn\'t end here, because not only are files not accepted by' .
161 ' the server - ALL content in the forms are discarded and therefore' .
162 ' nothing at all will be editable if you don\'t set this flag!'
163 );
164 } else {
165 $status = new Status\OkStatus();
166 $status->setTitle('File uploads allowed in PHP');
167 }
168 return $status;
169 }
170
171 /**
172 * Check maximum post upload size correlates with maximum file upload
173 *
174 * @return Status\StatusInterface
175 */
176 protected function checkPostUploadSizeIsHigherOrEqualMaximumFileUploadSize()
177 {
178 $maximumUploadFilesize = $this->getBytesFromSizeMeasurement(ini_get('upload_max_filesize'));
179 $maximumPostSize = $this->getBytesFromSizeMeasurement(ini_get('post_max_size'));
180 if ($maximumPostSize > 0 && $maximumPostSize < $maximumUploadFilesize) {
181 $status = new Status\ErrorStatus();
182 $status->setTitle('Maximum size for POST requests is smaller than maximum upload filesize in PHP');
183 $status->setMessage(
184 'upload_max_filesize=' . ini_get('upload_max_filesize') . LF .
185 'post_max_size=' . ini_get('post_max_size') . LF .
186 'You have defined a maximum size for file uploads in PHP which' .
187 ' exceeds the allowed size for POST requests. Therefore the' .
188 ' file uploads can also not be larger than ' . ini_get('post_max_size') . '.'
189 );
190 } else {
191 $status = new Status\OkStatus();
192 $status->setTitle('Maximum post upload size correlates with maximum upload file size in PHP');
193 $status->setMessage('The maximum size for file uploads is actually set to ' . ini_get('post_max_size'));
194 }
195 return $status;
196 }
197
198 /**
199 * Check memory settings
200 *
201 * @return Status\StatusInterface
202 */
203 protected function checkMemorySettings()
204 {
205 $minimumMemoryLimit = 64;
206 $recommendedMemoryLimit = 128;
207 $memoryLimit = $this->getBytesFromSizeMeasurement(ini_get('memory_limit'));
208 if ($memoryLimit <= 0) {
209 $status = new Status\WarningStatus();
210 $status->setTitle('Unlimited memory limit for PHP');
211 $status->setMessage(
212 'PHP is configured not to limit memory usage at all. This is a risk' .
213 ' and should be avoided in production setup. In general it\'s best practice to limit this.' .
214 ' To be safe, set a limit in PHP, but with a minimum of ' . $recommendedMemoryLimit . 'MB:' . LF .
215 'memory_limit=' . $recommendedMemoryLimit . 'M'
216 );
217 } elseif ($memoryLimit < 1024 * 1024 * $minimumMemoryLimit) {
218 $status = new Status\ErrorStatus();
219 $status->setTitle('PHP Memory limit below ' . $minimumMemoryLimit . 'MB');
220 $status->setMessage(
221 'memory_limit=' . ini_get('memory_limit') . LF .
222 'Your system is configured to enforce a memory limit for PHP scripts lower than ' .
223 $minimumMemoryLimit . 'MB. It is required to raise the limit.' .
224 ' We recommend a minimum PHP memory limit of ' . $recommendedMemoryLimit . 'MB:' . LF .
225 'memory_limit=' . $recommendedMemoryLimit . 'M'
226 );
227 } elseif ($memoryLimit < 1024 * 1024 * $recommendedMemoryLimit) {
228 $status = new Status\WarningStatus();
229 $status->setTitle('PHP Memory limit below ' . $recommendedMemoryLimit . 'MB');
230 $status->setMessage(
231 'memory_limit=' . ini_get('memory_limit') . LF .
232 'Your system is configured to enforce a memory limit for PHP scripts lower than ' .
233 $recommendedMemoryLimit . 'MB.' .
234 ' A slim TYPO3 instance without many extensions will probably work, but you should monitor your' .
235 ' system for "allowed memory size of X bytes exhausted" messages, especially if using the backend.' .
236 ' To be on the safe side,' . ' we recommend a minimum PHP memory limit of ' .
237 $recommendedMemoryLimit . 'MB:' . LF .
238 'memory_limit=' . $recommendedMemoryLimit . 'M'
239 );
240 } else {
241 $status = new Status\OkStatus();
242 $status->setTitle('PHP Memory limit is equal to or more than ' . $recommendedMemoryLimit . 'MB');
243 }
244 return $status;
245 }
246
247 /**
248 * Check minimum PHP version
249 *
250 * @return Status\StatusInterface
251 */
252 protected function checkPhpVersion()
253 {
254 $minimumPhpVersion = '7.0.0';
255 $currentPhpVersion = phpversion();
256 if (version_compare($currentPhpVersion, $minimumPhpVersion) < 0) {
257 $status = new Status\ErrorStatus();
258 $status->setTitle('PHP version too low');
259 $status->setMessage(
260 'Your PHP version ' . $currentPhpVersion . ' is too old. TYPO3 CMS does not run' .
261 ' with this version. Update to at least PHP ' . $minimumPhpVersion
262 );
263 } else {
264 $status = new Status\OkStatus();
265 $status->setTitle('PHP version is fine');
266 }
267 return $status;
268 }
269
270 /**
271 * Check PRCE module is loaded and minimum version
272 *
273 * @return Status\StatusInterface
274 */
275 protected function checkPcreVersion()
276 {
277 $minimumPcreVersion = '8.30';
278 if (!extension_loaded('pcre')) {
279 $status = new Status\ErrorStatus();
280 $status->setTitle('PHP extension pcre not loaded');
281 $status->setMessage(
282 'TYPO3 CMS uses PHP extension pcre but it is not loaded' .
283 ' in your environment. Change your environment to provide this extension' .
284 ' in with minimum version ' . $minimumPcreVersion . '.'
285 );
286 } else {
287 $installedPcreVersionString = trim(PCRE_VERSION); // '8.31 2012-07-06'
288 $mainPcreVersionString = explode(' ', $installedPcreVersionString);
289 $mainPcreVersionString = $mainPcreVersionString[0]; // '8.31'
290 if (version_compare($mainPcreVersionString, $minimumPcreVersion) < 0) {
291 $status = new Status\ErrorStatus();
292 $status->setTitle('PCRE version too low');
293 $status->setMessage(
294 'Your PCRE version ' . PCRE_VERSION . ' is too old. TYPO3 CMS may trigger PHP segmentantion' .
295 ' faults with this version. Update to at least PCRE ' . $minimumPcreVersion
296 );
297 } else {
298 $status = new Status\OkStatus();
299 $status->setTitle('PHP extension PCRE is loaded and version is fine');
300 }
301 }
302 return $status;
303 }
304
305 /**
306 * Check maximum execution time
307 *
308 * @return Status\StatusInterface
309 */
310 protected function checkMaxExecutionTime()
311 {
312 $minimumMaximumExecutionTime = 30;
313 $recommendedMaximumExecutionTime = 240;
314 $currentMaximumExecutionTime = (int)ini_get('max_execution_time');
315 if (PHP_SAPI === 'cli' && $currentMaximumExecutionTime === 0) {
316 $status = new Status\NoticeStatus();
317 $status->setTitle('Infinite PHP script execution time detected, which is admissible on the CLI');
318 } elseif ($currentMaximumExecutionTime === 0) {
319 $status = new Status\WarningStatus();
320 $status->setTitle('Infinite PHP script execution time');
321 $status->setMessage(
322 'max_execution_time=0' . LF .
323 'While TYPO3 is fine with this, you risk a denial-of-service for your system if for whatever' .
324 ' reason some script hangs in an infinite loop. You are usually on the safe side ' .
325 ' if it is reduced to ' . $recommendedMaximumExecutionTime . ' seconds:' . LF .
326 'max_execution_time=' . $recommendedMaximumExecutionTime
327 );
328 } elseif ($currentMaximumExecutionTime < $minimumMaximumExecutionTime) {
329 $status = new Status\ErrorStatus();
330 $status->setTitle('Low PHP script execution time');
331 $status->setMessage(
332 'max_execution_time=' . $currentMaximumExecutionTime . LF .
333 'Your max_execution_time is too low. Some expensive operations in TYPO3 can take longer than that.' .
334 ' It is recommended to raise the limit to ' . $recommendedMaximumExecutionTime . ' seconds:' . LF .
335 'max_execution_time=' . $recommendedMaximumExecutionTime
336 );
337 } elseif ($currentMaximumExecutionTime < $recommendedMaximumExecutionTime) {
338 $status = new Status\WarningStatus();
339 $status->setTitle('Low PHP script execution time');
340 $status->setMessage(
341 'max_execution_time=' . $currentMaximumExecutionTime . LF .
342 'Your max_execution_time is low. While TYPO3 often runs without problems' .
343 ' with ' . $minimumMaximumExecutionTime . ' seconds,' .
344 ' it may still happen that script execution is stopped before finishing' .
345 ' calculations. You should monitor the system for messages in this area' .
346 ' and maybe raise the limit to ' . $recommendedMaximumExecutionTime . ' seconds:' . LF .
347 'max_execution_time=' . $recommendedMaximumExecutionTime
348 );
349 } else {
350 $status = new Status\OkStatus();
351 $status->setTitle('Maximum PHP script execution time is equal to or more than '
352 . $recommendedMaximumExecutionTime);
353 }
354
355 return $status;
356 }
357
358 /**
359 * Check for disabled functions
360 *
361 * @return Status\StatusInterface
362 */
363 protected function checkDisableFunctions()
364 {
365 $disabledFunctions = trim(ini_get('disable_functions'));
366
367 // Filter "disable_functions"
368 $disabledFunctionsArray = $this->trimExplode(',', $disabledFunctions);
369
370 // Array with strings to find
371 $findStrings = [
372 // Disabled by default on Ubuntu OS but this is okay since the Core does not use them
373 'pcntl_',
374 ];
375 foreach ($disabledFunctionsArray as $key => $disabledFunction) {
376 foreach ($findStrings as $findString) {
377 if (strpos($disabledFunction, $findString) !== false) {
378 unset($disabledFunctionsArray[$key]);
379 }
380 }
381 }
382
383 if ($disabledFunctions !== '') {
384 if (!empty($disabledFunctionsArray)) {
385 $status = new Status\ErrorStatus();
386 $status->setTitle('Some PHP functions disabled');
387 $status->setMessage(
388 'disable_functions=' . implode(' ', explode(',', $disabledFunctions)) . LF .
389 'These function(s) are disabled. TYPO3 uses some of those, so there might be trouble.' .
390 ' TYPO3 is designed to use the default set of PHP functions plus some common extensions.' .
391 ' Possibly these functions are disabled' .
392 ' due to security considerations and most likely the list would include a function like' .
393 ' exec() which is used by TYPO3 at various places. Depending on which exact functions' .
394 ' are disabled, some parts of the system may just break without further notice.'
395 );
396 } else {
397 $status = new Status\NoticeStatus();
398 $status->setTitle('Some PHP functions currently disabled but OK');
399 $status->setMessage(
400 'disable_functions=' . implode(' ', explode(',', $disabledFunctions)) . LF .
401 'These function(s) are disabled. TYPO3 uses currently none of those, so you are good to go.'
402 );
403 }
404 } else {
405 $status = new Status\OkStatus();
406 $status->setTitle('No disabled PHP functions');
407 }
408 return $status;
409 }
410
411 /**
412 * Verify that mysqli.reconnect is set to 0 in order to avoid improper reconnects
413 *
414 * @return Status\StatusInterface
415 */
416 protected function checkMysqliReconnectSetting()
417 {
418 $currentMysqliReconnectSetting = ini_get('mysqli.reconnect');
419 if ($currentMysqliReconnectSetting === '1') {
420 $status = new Status\ErrorStatus();
421 $status->setTitle('PHP mysqli.reconnect is enabled');
422 $status->setMessage(
423 'mysqli.reconnect=1' . LF .
424 'PHP is configured to automatically reconnect the database connection on disconnection.' . LF .
425 ' Warning: If (e.g. during a long-running task) the connection is dropped and automatically reconnected, ' .
426 ' it may not be reinitialized properly (e.g. charset) and write mangled data to the database!'
427 );
428 } else {
429 $status = new Status\OkStatus();
430 $status->setTitle('PHP mysqli.reconnect is fine');
431 }
432 return $status;
433 }
434
435 /**
436 * Check for doc_root ini setting
437 *
438 * @return Status\StatusInterface
439 */
440 protected function checkDocRoot()
441 {
442 $docRootSetting = trim(ini_get('doc_root'));
443 if ($docRootSetting !== '') {
444 $status = new Status\NoticeStatus();
445 $status->setTitle('doc_root is set');
446 $status->setMessage(
447 'doc_root=' . $docRootSetting . LF .
448 'PHP cannot execute scripts' .
449 ' outside this directory. This setting is seldom used and must correlate' .
450 ' with your actual document root. You might be in trouble if your' .
451 ' TYPO3 CMS core code is linked to some different location.' .
452 ' If that is a problem, the setting must be changed.'
453 );
454 } else {
455 $status = new Status\OkStatus();
456 $status->setTitle('PHP doc_root is not set');
457 }
458 return $status;
459 }
460
461 /**
462 * Check open_basedir
463 *
464 * @return Status\StatusInterface
465 */
466 protected function checkOpenBaseDir()
467 {
468 $openBaseDirSetting = trim(ini_get('open_basedir'));
469 if ($openBaseDirSetting !== '') {
470 $status = new Status\NoticeStatus();
471 $status->setTitle('PHP open_basedir is set');
472 $status->setMessage(
473 'open_basedir = ' . ini_get('open_basedir') . LF .
474 'This restricts TYPO3 to open and include files only in this' .
475 ' path. Please make sure that this does not prevent TYPO3 from running,' .
476 ' if for example your TYPO3 CMS core is linked to a different directory' .
477 ' not included in this path.'
478 );
479 } else {
480 $status = new Status\OkStatus();
481 $status->setTitle('PHP open_basedir is off');
482 }
483 return $status;
484 }
485
486 /**
487 * If xdebug is loaded, the default max_nesting_level of 100 must be raised
488 *
489 * @return Status\StatusInterface
490 */
491 protected function checkXdebugMaxNestingLevel()
492 {
493 if (extension_loaded('xdebug')) {
494 $recommendedMaxNestingLevel = 400;
495 $errorThreshold = 250;
496 $currentMaxNestingLevel = ini_get('xdebug.max_nesting_level');
497 if ($currentMaxNestingLevel < $errorThreshold) {
498 $status = new Status\ErrorStatus();
499 $status->setTitle('PHP xdebug.max_nesting_level is critically low');
500 $status->setMessage(
501 'xdebug.max_nesting_level=' . $currentMaxNestingLevel . LF .
502 'This setting controls the maximum number of nested function calls to protect against' .
503 ' infinite recursion. The current value is too low for TYPO3 CMS and must' .
504 ' be either raised or xdebug has to be unloaded. A value of ' . $recommendedMaxNestingLevel .
505 ' is recommended. Warning: Expect fatal PHP errors in central parts of the CMS' .
506 ' if the value is not raised significantly to:' . LF .
507 'xdebug.max_nesting_level=' . $recommendedMaxNestingLevel
508 );
509 } elseif ($currentMaxNestingLevel < $recommendedMaxNestingLevel) {
510 $status = new Status\WarningStatus();
511 $status->setTitle('PHP xdebug.max_nesting_level is low');
512 $status->setMessage(
513 'xdebug.max_nesting_level=' . $currentMaxNestingLevel . LF .
514 'This setting controls the maximum number of nested function calls to protect against' .
515 ' infinite recursion. The current value is high enough for the TYPO3 CMS core to work' .
516 ' fine, but still some extensions could raise fatal PHP errors if the setting is not' .
517 ' raised further. A value of ' . $recommendedMaxNestingLevel . ' is recommended.' . LF .
518 'xdebug.max_nesting_level=' . $recommendedMaxNestingLevel
519 );
520 } else {
521 $status = new Status\OkStatus();
522 $status->setTitle('PHP xdebug.max_nesting_level ok');
523 }
524 } else {
525 $status = new Status\OkStatus();
526 $status->setTitle('PHP xdebug extension not loaded');
527 }
528 return $status;
529 }
530
531 /**
532 * Check accessibility and functionality of OpenSSL
533 *
534 * @return Status\StatusInterface
535 */
536 protected function checkOpenSslInstalled()
537 {
538 if (extension_loaded('openssl')) {
539 $testKey = @openssl_pkey_new();
540 if (is_resource($testKey)) {
541 openssl_free_key($testKey);
542 $status = new Status\OkStatus();
543 $status->setTitle('PHP OpenSSL extension installed properly');
544 } else {
545 $status = new Status\ErrorStatus();
546 $status->setTitle('PHP OpenSSL extension not working');
547 $status->setMessage(
548 'Something went wrong while trying to create a new private key for testing.' .
549 ' Please check the integration of the PHP OpenSSL extension and if it is installed correctly.'
550 );
551 }
552 } else {
553 $status = new Status\ErrorStatus();
554 $status->setTitle('PHP OpenSSL extension not loaded');
555 $status->setMessage(
556 'OpenSSL is a PHP extension to encrypt/decrypt data between requests.' .
557 ' TYPO3 CMS requires it to be able to encrypt stored passwords to improve the security in the' .
558 ' database layer.'
559 );
560 }
561
562 return $status;
563 }
564
565 /**
566 * Get max_input_vars status
567 *
568 * @return Status\StatusInterface
569 */
570 protected function checkMaxInputVars()
571 {
572 $recommendedMaxInputVars = 1500;
573 $minimumMaxInputVars = 1000;
574 $currentMaxInputVars = ini_get('max_input_vars');
575
576 if ($currentMaxInputVars < $minimumMaxInputVars) {
577 $status = new Status\ErrorStatus();
578 $status->setTitle('PHP max_input_vars too low');
579 $status->setMessage(
580 'max_input_vars=' . $currentMaxInputVars . LF .
581 'This setting can lead to lost information if submitting forms with lots of data in TYPO3 CMS' .
582 ' (as the install tool does). It is highly recommended to raise this' .
583 ' to at least ' . $recommendedMaxInputVars . ':' . LF .
584 'max_input_vars=' . $recommendedMaxInputVars
585 );
586 } elseif ($currentMaxInputVars < $recommendedMaxInputVars) {
587 $status = new Status\WarningStatus();
588 $status->setTitle('PHP max_input_vars very low');
589 $status->setMessage(
590 'max_input_vars=' . $currentMaxInputVars . LF .
591 'This setting can lead to lost information if submitting forms with lots of data in TYPO3 CMS' .
592 ' (as the install tool does). It is highly recommended to raise this' .
593 ' to at least ' . $recommendedMaxInputVars . ':' . LF .
594 'max_input_vars=' . $recommendedMaxInputVars
595 );
596 } else {
597 $status = new Status\OkStatus();
598 $status->setTitle('PHP max_input_vars ok');
599 }
600 return $status;
601 }
602
603 /**
604 * Get suhosin loaded status
605 * Should be called only if suhosin extension is loaded
606 *
607 * @return Status\StatusInterface
608 * @throws \BadMethodCallException
609 */
610 protected function getSuhosinLoadedStatus()
611 {
612 if ($this->isSuhosinLoadedAndActive()) {
613 $status = new Status\OkStatus();
614 $status->setTitle('PHP suhosin extension loaded and active');
615 return $status;
616 } else {
617 throw new \BadMethodCallException('Should be called only if suhosin extension is loaded', 1422634778);
618 }
619 }
620
621 /**
622 * Check suhosin.request.max_vars
623 *
624 * @return Status\StatusInterface
625 */
626 protected function checkSuhosinRequestMaxVars()
627 {
628 $recommendedRequestMaxVars = 400;
629 if ($this->isSuhosinLoadedAndActive()) {
630 $currentRequestMaxVars = ini_get('suhosin.request.max_vars');
631 if ($currentRequestMaxVars < $recommendedRequestMaxVars) {
632 $status = new Status\ErrorStatus();
633 $status->setTitle('PHP suhosin.request.max_vars too low');
634 $status->setMessage(
635 'suhosin.request.max_vars=' . $currentRequestMaxVars . LF .
636 'This setting can lead to lost information if submitting forms with lots of data in TYPO3 CMS' .
637 ' (as the install tool does). It is highly recommended to raise this' .
638 ' to at least ' . $recommendedRequestMaxVars . ':' . LF .
639 'suhosin.request.max_vars=' . $recommendedRequestMaxVars
640 );
641 } else {
642 $status = new Status\OkStatus();
643 $status->setTitle('PHP suhosin.request.max_vars ok');
644 }
645 } else {
646 $status = new Status\InfoStatus();
647 $status->setTitle('Suhosin not loaded');
648 $status->setMessage(
649 'If enabling suhosin, suhosin.request.max_vars' .
650 ' should be set to at least ' . $recommendedRequestMaxVars . ':' . LF .
651 'suhosin.request.max_vars=' . $recommendedRequestMaxVars
652 );
653 }
654 return $status;
655 }
656
657 /**
658 * Check suhosin.request.max_varname_length
659 *
660 * @return Status\StatusInterface
661 */
662 protected function checkSuhosinRequestMaxVarnameLength()
663 {
664 $recommendedRequestMaxVarnameLength = 200;
665 if ($this->isSuhosinLoadedAndActive()) {
666 $currentRequestMaxVarnameLength = ini_get('suhosin.request.max_varname_length');
667 if ($currentRequestMaxVarnameLength < $recommendedRequestMaxVarnameLength) {
668 $status = new Status\ErrorStatus();
669 $status->setTitle('PHP suhosin.request.max_varname_length too low');
670 $status->setMessage(
671 'suhosin.request.max_varname_length=' . $currentRequestMaxVarnameLength . 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 ' . $recommendedRequestMaxVarnameLength . ':' . LF .
675 'suhosin.request.max_varname_length=' . $recommendedRequestMaxVarnameLength
676 );
677 } else {
678 $status = new Status\OkStatus();
679 $status->setTitle('PHP suhosin.request.max_varname_length ok');
680 }
681 } else {
682 $status = new Status\InfoStatus();
683 $status->setTitle('Suhosin not loaded');
684 $status->setMessage(
685 'If enabling suhosin, suhosin.request.max_varname_length' .
686 ' should be set to at least ' . $recommendedRequestMaxVarnameLength . ':' . LF .
687 'suhosin.request.max_varname_length=' . $recommendedRequestMaxVarnameLength
688 );
689 }
690 return $status;
691 }
692
693 /**
694 * Check suhosin.post.max_name_length
695 *
696 * @return Status\StatusInterface
697 */
698 protected function checkSuhosinPostMaxNameLength()
699 {
700 $recommendedPostMaxNameLength = 200;
701 if ($this->isSuhosinLoadedAndActive()) {
702 $currentPostMaxNameLength = ini_get('suhosin.post.max_name_length');
703 if ($currentPostMaxNameLength < $recommendedPostMaxNameLength) {
704 $status = new Status\ErrorStatus();
705 $status->setTitle('PHP suhosin.post.max_name_length too low');
706 $status->setMessage(
707 'suhosin.post.max_name_length=' . $currentPostMaxNameLength . LF .
708 'This setting can lead to lost information if submitting forms with lots of data in TYPO3 CMS' .
709 ' (as the install tool does). It is highly recommended to raise this' .
710 ' to at least ' . $recommendedPostMaxNameLength . ':' . LF .
711 'suhosin.post.max_name_length=' . $recommendedPostMaxNameLength
712 );
713 } else {
714 $status = new Status\OkStatus();
715 $status->setTitle('PHP suhosin.post.max_name_length ok');
716 }
717 } else {
718 $status = new Status\InfoStatus();
719 $status->setTitle('Suhosin not loaded');
720 $status->setMessage(
721 'If enabling suhosin, suhosin.post.max_name_length' .
722 ' should be set to at least ' . $recommendedPostMaxNameLength . ':' . LF .
723 'suhosin.post.max_name_length=' . $recommendedPostMaxNameLength
724 );
725 }
726 return $status;
727 }
728
729 /**
730 * Check suhosin.post.max_vars
731 *
732 * @return Status\StatusInterface
733 */
734 protected function checkSuhosinPostMaxVars()
735 {
736 $recommendedPostMaxVars = 400;
737 if ($this->isSuhosinLoadedAndActive()) {
738 $currentPostMaxVars = ini_get('suhosin.post.max_vars');
739 if ($currentPostMaxVars < $recommendedPostMaxVars) {
740 $status = new Status\ErrorStatus();
741 $status->setTitle('PHP suhosin.post.max_vars too low');
742 $status->setMessage(
743 'suhosin.post.max_vars=' . $currentPostMaxVars . LF .
744 'This setting can lead to lost information if submitting forms with lots of data in TYPO3 CMS' .
745 ' (as the install tool does). It is highly recommended to raise this' .
746 ' to at least ' . $recommendedPostMaxVars . ':' . LF .
747 'suhosin.post.max_vars=' . $recommendedPostMaxVars
748 );
749 } else {
750 $status = new Status\OkStatus();
751 $status->setTitle('PHP suhosin.post.max_vars ok');
752 }
753 } else {
754 $status = new Status\InfoStatus();
755 $status->setTitle('Suhosin not loaded');
756 $status->setMessage(
757 'If enabling suhosin, suhosin.post.max_vars' .
758 ' should be set to at least ' . $recommendedPostMaxVars . ':' . LF .
759 'suhosin.post.max_vars=' . $recommendedPostMaxVars
760 );
761 }
762 return $status;
763 }
764
765 /**
766 * Check suhosin.get.max_value_length
767 *
768 * @return Status\StatusInterface
769 */
770 protected function checkSuhosinGetMaxValueLength()
771 {
772 $recommendedGetMaxValueLength = 2000;
773 if ($this->isSuhosinLoadedAndActive()) {
774 $currentGetMaxValueLength = ini_get('suhosin.get.max_value_length');
775 if ($currentGetMaxValueLength < $recommendedGetMaxValueLength) {
776 $status = new Status\ErrorStatus();
777 $status->setTitle('PHP suhosin.get.max_value_length too low');
778 $status->setMessage(
779 'suhosin.get.max_value_length=' . $currentGetMaxValueLength . LF .
780 'This setting can lead to lost information if submitting forms with lots of data in TYPO3 CMS' .
781 ' (as the install tool does). It is highly recommended to raise this' .
782 ' to at least ' . $recommendedGetMaxValueLength . ':' . LF .
783 'suhosin.get.max_value_length=' . $recommendedGetMaxValueLength
784 );
785 } else {
786 $status = new Status\OkStatus();
787 $status->setTitle('PHP suhosin.get.max_value_length ok');
788 }
789 } else {
790 $status = new Status\InfoStatus();
791 $status->setTitle('Suhosin not loaded');
792 $status->setMessage(
793 'If enabling suhosin, suhosin.get.max_value_length' .
794 ' should be set to at least ' . $recommendedGetMaxValueLength . ':' . LF .
795 'suhosin.get.max_value_length=' . $recommendedGetMaxValueLength
796 );
797 }
798 return $status;
799 }
800
801 /**
802 * Check suhosin.get.max_name_length
803 *
804 * @return Status\StatusInterface
805 */
806 protected function checkSuhosinGetMaxNameLength()
807 {
808 $recommendedGetMaxNameLength = 200;
809 if ($this->isSuhosinLoadedAndActive()) {
810 $currentGetMaxNameLength = ini_get('suhosin.get.max_name_length');
811 if ($currentGetMaxNameLength < $recommendedGetMaxNameLength) {
812 $status = new Status\ErrorStatus();
813 $status->setTitle('PHP suhosin.get.max_name_length too low');
814 $status->setMessage(
815 'suhosin.get.max_name_length=' . $currentGetMaxNameLength . LF .
816 'This setting can lead to lost information if submitting forms with lots of data in TYPO3 CMS' .
817 ' (as the install tool does). It is highly recommended to raise this' .
818 ' to at least ' . $recommendedGetMaxNameLength . ':' . LF .
819 'suhosin.get.max_name_length=' . $recommendedGetMaxNameLength
820 );
821 } else {
822 $status = new Status\OkStatus();
823 $status->setTitle('PHP suhosin.get.max_name_length ok');
824 }
825 } else {
826 $status = new Status\InfoStatus();
827 $status->setTitle('Suhosin not loaded');
828 $status->setMessage(
829 'If enabling suhosin, suhosin.get.max_name_length' .
830 ' should be set to at least ' . $recommendedGetMaxNameLength . ':' . LF .
831 'suhosin.get.max_name_length=' . $recommendedGetMaxNameLength
832 );
833 }
834 return $status;
835 }
836
837 /**
838 * Check suhosin.executor.include.whitelist contains phar
839 *
840 * @return Status\StatusInterface
841 */
842 protected function checkSuhosinExecutorIncludeWhiteListContainsPhar()
843 {
844 if ($this->isSuhosinLoadedAndActive()) {
845 $whitelist = (string)ini_get('suhosin.executor.include.whitelist');
846 if (strpos($whitelist, 'phar') === false) {
847 $status = new Status\NoticeStatus();
848 $status->setTitle('PHP suhosin.executor.include.whitelist does not contain phar');
849 $status->setMessage(
850 'suhosin.executor.include.whitelist= ' . $whitelist . LF .
851 '"phar" is currently not a hard requirement of TYPO3 CMS but is nice to have and a possible' .
852 ' requirement in future versions. A useful setting is:' . LF .
853 'suhosin.executor.include.whitelist=phar,vfs'
854 );
855 } else {
856 $status = new Status\OkStatus();
857 $status->setTitle('PHP suhosin.executor.include.whitelist contains phar');
858 }
859 } else {
860 $status = new Status\InfoStatus();
861 $status->setTitle('Suhosin not loaded');
862 $status->setMessage(
863 'If enabling suhosin, a useful setting is:' . LF .
864 'suhosin.executor.include.whitelist=phar,vfs'
865 );
866 }
867 return $status;
868 }
869
870 /**
871 * Check suhosin.executor.include.whitelist contains vfs
872 *
873 * @return Status\StatusInterface
874 */
875 protected function checkSuhosinExecutorIncludeWhiteListContainsVfs()
876 {
877 if ($this->isSuhosinLoadedAndActive()) {
878 $whitelist = (string)ini_get('suhosin.executor.include.whitelist');
879 if (strpos($whitelist, 'vfs') === false) {
880 $status = new Status\WarningStatus();
881 $status->setTitle('PHP suhosin.executor.include.whitelist does not contain vfs');
882 $status->setMessage(
883 'suhosin.executor.include.whitelist= ' . $whitelist . LF .
884 '"vfs" is currently not a hard requirement of TYPO3 CMS but tons of unit tests rely on it.' .
885 ' Furthermore, vfs will likely be a base for an additional compatibility layer in the future.' .
886 ' A useful setting is:' . LF .
887 'suhosin.executor.include.whitelist=phar,vfs'
888 );
889 } else {
890 $status = new Status\OkStatus();
891 $status->setTitle('PHP suhosin.executor.include.whitelist contains vfs');
892 }
893 } else {
894 $status = new Status\InfoStatus();
895 $status->setTitle('Suhosin not loaded');
896 $status->setMessage(
897 'If enabling suhosin, a useful setting is:' . LF .
898 'suhosin.executor.include.whitelist=phar,vfs'
899 );
900 }
901 return $status;
902 }
903
904 /**
905 * Check doc comments can be fetched by reflection
906 *
907 * @return Status\StatusInterface
908 */
909 protected function checkReflectionDocComment()
910 {
911 $testReflection = new \ReflectionMethod(get_class($this), __FUNCTION__);
912 if ($testReflection->getDocComment() === false) {
913 $status = new Status\AlertStatus();
914 $status->setTitle('PHP Doc comment reflection broken');
915 $status->setMessage(
916 'TYPO3 CMS core extensions like extbase and fluid heavily rely on method'
917 . ' comment parsing to fetch annotations and add magic belonging to them.'
918 . ' This does not work in the current environment and so we cannot install'
919 . ' TYPO3 CMS.' . LF
920 . ' Here are some possibilities: ' . LF
921 . '* In Zend OPcache you can disable saving/loading comments. If you are using'
922 . ' Zend OPcache (included since PHP 5.5) then check your php.ini settings for'
923 . ' opcache.save_comments and opcache.load_comments and enable them.' . LF
924 . '* In Zend Optimizer+ you can disable saving comments. If you are using'
925 . ' Zend Optimizer+ then check your php.ini settings for'
926 . ' zend_optimizerplus.save_comments and enable it.' . LF
927 . '* The PHP extension eaccelerator is known to break this if'
928 . ' it is compiled without --with-eaccelerator-doc-comment-inclusion flag.'
929 . ' This compile flag must be specified, otherwise TYPO3 CMS will not work.' . LF
930 . 'For more information take a look in our wiki ' . TYPO3_URL_WIKI_OPCODECACHE . '.'
931 );
932 } else {
933 $status = new Status\OkStatus();
934 $status->setTitle('PHP Doc comment reflection works');
935 }
936 return $status;
937 }
938
939 /**
940 * Checks thread stack size if on windows with apache
941 *
942 * @return Status\StatusInterface
943 */
944 protected function checkWindowsApacheThreadStackSize()
945 {
946 if ($this->isWindowsOs()
947 && substr($_SERVER['SERVER_SOFTWARE'], 0, 6) === 'Apache'
948 ) {
949 $status = new Status\WarningStatus();
950 $status->setTitle('Windows apache thread stack size');
951 $status->setMessage(
952 'This current value cannot be checked by the system, so please ignore this warning if it' .
953 ' is already taken care of: Fluid uses complex regular expressions which require a lot' .
954 ' of stack space during the first processing.' .
955 ' On Windows the default stack size for Apache is a lot smaller than on UNIX.' .
956 ' You can increase the size to 8MB (default on UNIX) by adding the following configuration' .
957 ' to httpd.conf and restarting Apache afterwards:' . LF .
958 '<IfModule mpm_winnt_module>' . LF .
959 'ThreadStackSize 8388608' . LF .
960 '</IfModule>'
961 );
962 } else {
963 $status = new Status\OkStatus();
964 $status->setTitle('Apache ThreadStackSize is not an issue on UNIX systems');
965 }
966 return $status;
967 }
968
969 /**
970 * Check if a specific required PHP extension is loaded
971 *
972 * @param string $extension
973 * @return Status\StatusInterface
974 */
975 protected function checkRequiredPhpExtension($extension)
976 {
977 if (!extension_loaded($extension)) {
978 $status = new Status\ErrorStatus();
979 $status->setTitle('PHP extension ' . $extension . ' not loaded');
980 $status->setMessage(
981 'TYPO3 CMS uses PHP extension ' . $extension . ' but it is not loaded' .
982 ' in your environment. Change your environment to provide this extension.'
983 );
984 } else {
985 $status = new Status\OkStatus();
986 $status->setTitle('PHP extension ' . $extension . ' loaded');
987 }
988 return $status;
989 }
990
991 /**
992 * Check imagecreatetruecolor to verify gdlib works as expected
993 *
994 * @return Status\StatusInterface
995 */
996 protected function checkGdLibTrueColorSupport()
997 {
998 if (function_exists('imagecreatetruecolor')) {
999 $imageResource = @imagecreatetruecolor(50, 100);
1000 if (is_resource($imageResource)) {
1001 imagedestroy($imageResource);
1002 $status = new Status\OkStatus();
1003 $status->setTitle('PHP GD library true color works');
1004 } else {
1005 $status = new Status\ErrorStatus();
1006 $status->setTitle('PHP GD library true color support broken');
1007 $status->setMessage(
1008 'GD is loaded, but calling imagecreatetruecolor() fails.' .
1009 ' This must be fixed, TYPO3 CMS won\'t work well otherwise.'
1010 );
1011 }
1012 } else {
1013 $status = new Status\ErrorStatus();
1014 $status->setTitle('PHP GD library true color support missing');
1015 $status->setMessage(
1016 'Gdlib is essential for TYPO3 CMS to work properly.'
1017 );
1018 }
1019 return $status;
1020 }
1021
1022 /**
1023 * Check gif support of GD library
1024 *
1025 * @return Status\StatusInterface
1026 */
1027 protected function checkGdLibGifSupport()
1028 {
1029 if (function_exists('imagecreatefromgif')
1030 && function_exists('imagegif')
1031 && (imagetypes() & IMG_GIF)
1032 ) {
1033 // See http://stackoverflow.com/a/13139830
1034 $imageResource = @imagecreatefromgif('data://image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7');
1035 if (is_resource($imageResource)) {
1036 imagedestroy($imageResource);
1037 $status = new Status\OkStatus();
1038 $status->setTitle('PHP GD library has gif support');
1039 } else {
1040 $status = new Status\ErrorStatus();
1041 $status->setTitle('PHP GD library gif support broken');
1042 $status->setMessage(
1043 'GD is loaded, but calling imagecreatefromgif() fails.' .
1044 ' This must be fixed, TYPO3 CMS won\'t work well otherwise.'
1045 );
1046 }
1047 } else {
1048 $status = new Status\ErrorStatus();
1049 $status->setTitle('PHP GD library gif support missing');
1050 $status->setMessage(
1051 'GD must be compiled with gif support. This is essential for' .
1052 ' TYPO3 CMS to work properly.'
1053 );
1054 }
1055 return $status;
1056 }
1057
1058 /**
1059 * Check jgp support of GD library
1060 *
1061 * @return Status\StatusInterface
1062 */
1063 protected function checkGdLibJpgSupport()
1064 {
1065 if (function_exists('imagecreatefromjpeg')
1066 && function_exists('imagejpeg')
1067 && (imagetypes() & IMG_JPG)
1068 ) {
1069 $status = new Status\OkStatus();
1070 $status->setTitle('PHP GD library has jpg support');
1071 } else {
1072 $status = new Status\ErrorStatus();
1073 $status->setTitle('PHP GD library jpg support missing');
1074 $status->setMessage(
1075 'GD must be compiled with jpg support. This is essential for' .
1076 ' TYPO3 CMS to work properly.'
1077 );
1078 }
1079 return $status;
1080 }
1081
1082 /**
1083 * Check png support of GD library
1084 *
1085 * @return Status\StatusInterface
1086 */
1087 protected function checkGdLibPngSupport()
1088 {
1089 if (function_exists('imagecreatefrompng')
1090 && function_exists('imagepng')
1091 && (imagetypes() & IMG_PNG)
1092 ) {
1093 $imageResource = @imagecreatefrompng('data://image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQMAAAAl21bKAAAAA1BMVEUAAACnej3aAAAAAXRSTlMAQObYZgAAAApJREFUCNdjYAAAAAIAAeIhvDMAAAAASUVORK5CYII');
1094 if (is_resource($imageResource)) {
1095 imagedestroy($imageResource);
1096 $status = new Status\OkStatus();
1097 $status->setTitle('PHP GD library has png support');
1098 } else {
1099 $status = new Status\ErrorStatus();
1100 $status->setTitle('PHP GD library png support broken');
1101 $status->setMessage(
1102 'GD is compiled with png support, but calling imagecreatefrompng() fails.' .
1103 ' Check your environment and fix it, png in GD lib is important' .
1104 ' for TYPO3 CMS to work properly.'
1105 );
1106 }
1107 } else {
1108 $status = new Status\ErrorStatus();
1109 $status->setTitle('PHP GD library png support missing');
1110 $status->setMessage(
1111 'GD must be compiled with png support. This is essential for' .
1112 ' TYPO3 CMS to work properly'
1113 );
1114 }
1115 return $status;
1116 }
1117
1118 /**
1119 * Check gdlib supports freetype
1120 *
1121 * @return Status\StatusInterface
1122 */
1123 protected function checkGdLibFreeTypeSupport()
1124 {
1125 if (function_exists('imagettftext')) {
1126 $status = new Status\OkStatus();
1127 $status->setTitle('PHP GD library has freetype font support');
1128 $status->setMessage(
1129 'There is a difference between the font size setting which the GD' .
1130 ' library should be supplied with. If installation is completed' .
1131 ' a test in the install tool helps to find out the value you need.'
1132 );
1133 } else {
1134 $status = new Status\ErrorStatus();
1135 $status->setTitle('PHP GD library freetype support missing');
1136 $status->setMessage(
1137 'Some core functionality and extension rely on the GD' .
1138 ' to render fonts on images. This support is missing' .
1139 ' in your environment. Install it.'
1140 );
1141 }
1142 return $status;
1143 }
1144
1145 /**
1146 * Helper methods
1147 */
1148
1149 /**
1150 * Validate a given IP address.
1151 *
1152 * @param string $ip IP address to be tested
1153 * @return bool
1154 */
1155 protected function isValidIp($ip)
1156 {
1157 return filter_var($ip, FILTER_VALIDATE_IP) !== false;
1158 }
1159
1160 /**
1161 * Test if this instance runs on windows OS
1162 *
1163 * @return bool TRUE if operating system is windows
1164 */
1165 protected function isWindowsOs()
1166 {
1167 $windowsOs = false;
1168 if (!stristr(PHP_OS, 'darwin') && stristr(PHP_OS, 'win')) {
1169 $windowsOs = true;
1170 }
1171 return $windowsOs;
1172 }
1173
1174 /**
1175 * Helper method to find out if suhosin extension is loaded
1176 *
1177 * @return bool TRUE if suhosin PHP extension is loaded
1178 */
1179 protected function isSuhosinLoadedAndActive()
1180 {
1181 $suhosinLoaded = false;
1182 if (extension_loaded('suhosin')) {
1183 $suhosinInSimulationMode = filter_var(
1184 ini_get('suhosin.simulation'),
1185 FILTER_VALIDATE_BOOLEAN,
1186 [FILTER_REQUIRE_SCALAR, FILTER_NULL_ON_FAILURE]
1187 );
1188 if (!$suhosinInSimulationMode) {
1189 $suhosinLoaded = true;
1190 }
1191 }
1192 return $suhosinLoaded;
1193 }
1194
1195 /**
1196 * Helper method to explode a string by delimiter and throw away empty values.
1197 * Removes empty values from result array.
1198 *
1199 * @param string $delimiter Delimiter string to explode with
1200 * @param string $string The string to explode
1201 * @return array Exploded values
1202 */
1203 protected function trimExplode($delimiter, $string)
1204 {
1205 $explodedValues = explode($delimiter, $string);
1206 $resultWithPossibleEmptyValues = array_map('trim', $explodedValues);
1207 $result = [];
1208 foreach ($resultWithPossibleEmptyValues as $value) {
1209 if ($value !== '') {
1210 $result[] = $value;
1211 }
1212 }
1213 return $result;
1214 }
1215
1216 /**
1217 * Helper method to get the bytes value from a measurement string like "100k".
1218 *
1219 * @param string $measurement The measurement (e.g. "100k")
1220 * @return int The bytes value (e.g. 102400)
1221 */
1222 protected function getBytesFromSizeMeasurement($measurement)
1223 {
1224 $bytes = (float)$measurement;
1225 if (stripos($measurement, 'G')) {
1226 $bytes *= 1024 * 1024 * 1024;
1227 } elseif (stripos($measurement, 'M')) {
1228 $bytes *= 1024 * 1024;
1229 } elseif (stripos($measurement, 'K')) {
1230 $bytes *= 1024;
1231 }
1232 return (int)$bytes;
1233 }
1234 }