[TASK] Update php-cs-fixer to 2.5.0
[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 implements CheckInterface
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(): array
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.38';
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.39 2016-06-14'
288 $mainPcreVersionString = explode(' ', $installedPcreVersionString);
289 $mainPcreVersionString = $mainPcreVersionString[0]; // '8.39'
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 = ini_get('max_execution_time');
315 if ($currentMaximumExecutionTime == 0) {
316 $status = new Status\WarningStatus();
317 $status->setTitle('Infinite PHP script execution time');
318 $status->setMessage(
319 'max_execution_time=0' . LF .
320 'While TYPO3 is fine with this, you risk a denial-of-service for your system if for whatever' .
321 ' reason some script hangs in an infinite loop. You are usually on the safe side ' .
322 ' if it is reduced to ' . $recommendedMaximumExecutionTime . ' seconds:' . LF .
323 'max_execution_time=' . $recommendedMaximumExecutionTime
324 );
325 } elseif ($currentMaximumExecutionTime < $minimumMaximumExecutionTime) {
326 $status = new Status\ErrorStatus();
327 $status->setTitle('Low PHP script execution time');
328 $status->setMessage(
329 'max_execution_time=' . $currentMaximumExecutionTime . LF .
330 'Your max_execution_time is too low. Some expensive operations in TYPO3 can take longer than that.' .
331 ' It is recommended to raise the limit to ' . $recommendedMaximumExecutionTime . ' seconds:' . LF .
332 'max_execution_time=' . $recommendedMaximumExecutionTime
333 );
334 } elseif ($currentMaximumExecutionTime < $recommendedMaximumExecutionTime) {
335 $status = new Status\WarningStatus();
336 $status->setTitle('Low PHP script execution time');
337 $status->setMessage(
338 'max_execution_time=' . $currentMaximumExecutionTime . LF .
339 'Your max_execution_time is low. While TYPO3 often runs without problems' .
340 ' with ' . $minimumMaximumExecutionTime . ' seconds,' .
341 ' it may still happen that script execution is stopped before finishing' .
342 ' calculations. You should monitor the system for messages in this area' .
343 ' and maybe raise the limit to ' . $recommendedMaximumExecutionTime . ' seconds:' . LF .
344 'max_execution_time=' . $recommendedMaximumExecutionTime
345 );
346 } else {
347 $status = new Status\OkStatus();
348 $status->setTitle('Maximum PHP script execution time is equal to or more than '
349 . $recommendedMaximumExecutionTime);
350 }
351 return $status;
352 }
353
354 /**
355 * Check for disabled functions
356 *
357 * @return Status\StatusInterface
358 */
359 protected function checkDisableFunctions()
360 {
361 $disabledFunctions = trim(ini_get('disable_functions'));
362
363 // Filter "disable_functions"
364 $disabledFunctionsArray = $this->trimExplode(',', $disabledFunctions);
365
366 // Array with strings to find
367 $findStrings = [
368 // Disabled by default on Ubuntu OS but this is okay since the Core does not use them
369 'pcntl_',
370 ];
371 foreach ($disabledFunctionsArray as $key => $disabledFunction) {
372 foreach ($findStrings as $findString) {
373 if (strpos($disabledFunction, $findString) !== false) {
374 unset($disabledFunctionsArray[$key]);
375 }
376 }
377 }
378
379 if ($disabledFunctions !== '') {
380 if (!empty($disabledFunctionsArray)) {
381 $status = new Status\ErrorStatus();
382 $status->setTitle('Some PHP functions disabled');
383 $status->setMessage(
384 'disable_functions=' . implode(' ', explode(',', $disabledFunctions)) . LF .
385 'These function(s) are disabled. TYPO3 uses some of those, so there might be trouble.' .
386 ' TYPO3 is designed to use the default set of PHP functions plus some common extensions.' .
387 ' Possibly these functions are disabled' .
388 ' due to security considerations and most likely the list would include a function like' .
389 ' exec() which is used by TYPO3 at various places. Depending on which exact functions' .
390 ' are disabled, some parts of the system may just break without further notice.'
391 );
392 } else {
393 $status = new Status\NoticeStatus();
394 $status->setTitle('Some PHP functions currently disabled but OK');
395 $status->setMessage(
396 'disable_functions=' . implode(' ', explode(',', $disabledFunctions)) . LF .
397 'These function(s) are disabled. TYPO3 uses currently none of those, so you are good to go.'
398 );
399 }
400 } else {
401 $status = new Status\OkStatus();
402 $status->setTitle('No disabled PHP functions');
403 }
404 return $status;
405 }
406
407 /**
408 * Verify that mysqli.reconnect is set to 0 in order to avoid improper reconnects
409 *
410 * @return Status\StatusInterface
411 */
412 protected function checkMysqliReconnectSetting()
413 {
414 $currentMysqliReconnectSetting = ini_get('mysqli.reconnect');
415 if ($currentMysqliReconnectSetting === '1') {
416 $status = new Status\ErrorStatus();
417 $status->setTitle('PHP mysqli.reconnect is enabled');
418 $status->setMessage(
419 'mysqli.reconnect=1' . LF .
420 'PHP is configured to automatically reconnect the database connection on disconnection.' . LF .
421 ' Warning: If (e.g. during a long-running task) the connection is dropped and automatically reconnected, ' .
422 ' it may not be reinitialized properly (e.g. charset) and write mangled data to the database!'
423 );
424 } else {
425 $status = new Status\OkStatus();
426 $status->setTitle('PHP mysqli.reconnect is fine');
427 }
428 return $status;
429 }
430
431 /**
432 * Check for doc_root ini setting
433 *
434 * @return Status\StatusInterface
435 */
436 protected function checkDocRoot()
437 {
438 $docRootSetting = trim(ini_get('doc_root'));
439 if ($docRootSetting !== '') {
440 $status = new Status\NoticeStatus();
441 $status->setTitle('doc_root is set');
442 $status->setMessage(
443 'doc_root=' . $docRootSetting . LF .
444 'PHP cannot execute scripts' .
445 ' outside this directory. This setting is seldom used and must correlate' .
446 ' with your actual document root. You might be in trouble if your' .
447 ' TYPO3 CMS core code is linked to some different location.' .
448 ' If that is a problem, the setting must be changed.'
449 );
450 } else {
451 $status = new Status\OkStatus();
452 $status->setTitle('PHP doc_root is not set');
453 }
454 return $status;
455 }
456
457 /**
458 * Check open_basedir
459 *
460 * @return Status\StatusInterface
461 */
462 protected function checkOpenBaseDir()
463 {
464 $openBaseDirSetting = trim(ini_get('open_basedir'));
465 if ($openBaseDirSetting !== '') {
466 $status = new Status\NoticeStatus();
467 $status->setTitle('PHP open_basedir is set');
468 $status->setMessage(
469 'open_basedir = ' . ini_get('open_basedir') . LF .
470 'This restricts TYPO3 to open and include files only in this' .
471 ' path. Please make sure that this does not prevent TYPO3 from running,' .
472 ' if for example your TYPO3 CMS core is linked to a different directory' .
473 ' not included in this path.'
474 );
475 } else {
476 $status = new Status\OkStatus();
477 $status->setTitle('PHP open_basedir is off');
478 }
479 return $status;
480 }
481
482 /**
483 * If xdebug is loaded, the default max_nesting_level of 100 must be raised
484 *
485 * @return Status\StatusInterface
486 */
487 protected function checkXdebugMaxNestingLevel()
488 {
489 if (extension_loaded('xdebug')) {
490 $recommendedMaxNestingLevel = 400;
491 $errorThreshold = 250;
492 $currentMaxNestingLevel = ini_get('xdebug.max_nesting_level');
493 if ($currentMaxNestingLevel < $errorThreshold) {
494 $status = new Status\ErrorStatus();
495 $status->setTitle('PHP xdebug.max_nesting_level is critically low');
496 $status->setMessage(
497 'xdebug.max_nesting_level=' . $currentMaxNestingLevel . LF .
498 'This setting controls the maximum number of nested function calls to protect against' .
499 ' infinite recursion. The current value is too low for TYPO3 CMS and must' .
500 ' be either raised or xdebug has to be unloaded. A value of ' . $recommendedMaxNestingLevel .
501 ' is recommended. Warning: Expect fatal PHP errors in central parts of the CMS' .
502 ' if the value is not raised significantly to:' . LF .
503 'xdebug.max_nesting_level=' . $recommendedMaxNestingLevel
504 );
505 } elseif ($currentMaxNestingLevel < $recommendedMaxNestingLevel) {
506 $status = new Status\WarningStatus();
507 $status->setTitle('PHP xdebug.max_nesting_level is low');
508 $status->setMessage(
509 'xdebug.max_nesting_level=' . $currentMaxNestingLevel . LF .
510 'This setting controls the maximum number of nested function calls to protect against' .
511 ' infinite recursion. The current value is high enough for the TYPO3 CMS core to work' .
512 ' fine, but still some extensions could raise fatal PHP errors if the setting is not' .
513 ' raised further. A value of ' . $recommendedMaxNestingLevel . ' is recommended.' . LF .
514 'xdebug.max_nesting_level=' . $recommendedMaxNestingLevel
515 );
516 } else {
517 $status = new Status\OkStatus();
518 $status->setTitle('PHP xdebug.max_nesting_level ok');
519 }
520 } else {
521 $status = new Status\OkStatus();
522 $status->setTitle('PHP xdebug extension not loaded');
523 }
524 return $status;
525 }
526
527 /**
528 * Check accessibility and functionality of OpenSSL
529 *
530 * @return Status\StatusInterface
531 */
532 protected function checkOpenSslInstalled()
533 {
534 if (extension_loaded('openssl')) {
535 $testKey = @openssl_pkey_new();
536 if (is_resource($testKey)) {
537 openssl_free_key($testKey);
538 $status = new Status\OkStatus();
539 $status->setTitle('PHP OpenSSL extension installed properly');
540 } else {
541 $status = new Status\ErrorStatus();
542 $status->setTitle('PHP OpenSSL extension not working');
543 $status->setMessage(
544 'Something went wrong while trying to create a new private key for testing.' .
545 ' Please check the integration of the PHP OpenSSL extension and if it is installed correctly.'
546 );
547 }
548 } else {
549 $status = new Status\ErrorStatus();
550 $status->setTitle('PHP OpenSSL extension not loaded');
551 $status->setMessage(
552 'OpenSSL is a PHP extension to encrypt/decrypt data between requests.' .
553 ' TYPO3 CMS requires it to be able to encrypt stored passwords to improve the security in the' .
554 ' database layer.'
555 );
556 }
557
558 return $status;
559 }
560
561 /**
562 * Get max_input_vars status
563 *
564 * @return Status\StatusInterface
565 */
566 protected function checkMaxInputVars()
567 {
568 $recommendedMaxInputVars = 1500;
569 $minimumMaxInputVars = 1000;
570 $currentMaxInputVars = ini_get('max_input_vars');
571
572 if ($currentMaxInputVars < $minimumMaxInputVars) {
573 $status = new Status\ErrorStatus();
574 $status->setTitle('PHP max_input_vars too low');
575 $status->setMessage(
576 'max_input_vars=' . $currentMaxInputVars . LF .
577 'This setting can lead to lost information if submitting forms with lots of data in TYPO3 CMS' .
578 ' (as the install tool does). It is highly recommended to raise this' .
579 ' to at least ' . $recommendedMaxInputVars . ':' . LF .
580 'max_input_vars=' . $recommendedMaxInputVars
581 );
582 } elseif ($currentMaxInputVars < $recommendedMaxInputVars) {
583 $status = new Status\WarningStatus();
584 $status->setTitle('PHP max_input_vars very low');
585 $status->setMessage(
586 'max_input_vars=' . $currentMaxInputVars . LF .
587 'This setting can lead to lost information if submitting forms with lots of data in TYPO3 CMS' .
588 ' (as the install tool does). It is highly recommended to raise this' .
589 ' to at least ' . $recommendedMaxInputVars . ':' . LF .
590 'max_input_vars=' . $recommendedMaxInputVars
591 );
592 } else {
593 $status = new Status\OkStatus();
594 $status->setTitle('PHP max_input_vars ok');
595 }
596 return $status;
597 }
598
599 /**
600 * Get suhosin loaded status
601 * Should be called only if suhosin extension is loaded
602 *
603 * @return Status\StatusInterface
604 * @throws \BadMethodCallException
605 */
606 protected function getSuhosinLoadedStatus()
607 {
608 if ($this->isSuhosinLoadedAndActive()) {
609 $status = new Status\OkStatus();
610 $status->setTitle('PHP suhosin extension loaded and active');
611 return $status;
612 }
613 throw new \BadMethodCallException('Should be called only if suhosin extension is loaded', 1422634778);
614 }
615
616 /**
617 * Check suhosin.request.max_vars
618 *
619 * @return Status\StatusInterface
620 */
621 protected function checkSuhosinRequestMaxVars()
622 {
623 $recommendedRequestMaxVars = 400;
624 if ($this->isSuhosinLoadedAndActive()) {
625 $currentRequestMaxVars = ini_get('suhosin.request.max_vars');
626 if ($currentRequestMaxVars < $recommendedRequestMaxVars) {
627 $status = new Status\ErrorStatus();
628 $status->setTitle('PHP suhosin.request.max_vars too low');
629 $status->setMessage(
630 'suhosin.request.max_vars=' . $currentRequestMaxVars . LF .
631 'This setting can lead to lost information if submitting forms with lots of data in TYPO3 CMS' .
632 ' (as the install tool does). It is highly recommended to raise this' .
633 ' to at least ' . $recommendedRequestMaxVars . ':' . LF .
634 'suhosin.request.max_vars=' . $recommendedRequestMaxVars
635 );
636 } else {
637 $status = new Status\OkStatus();
638 $status->setTitle('PHP suhosin.request.max_vars ok');
639 }
640 } else {
641 $status = new Status\InfoStatus();
642 $status->setTitle('Suhosin not loaded');
643 $status->setMessage(
644 'If enabling suhosin, suhosin.request.max_vars' .
645 ' should be set to at least ' . $recommendedRequestMaxVars . ':' . LF .
646 'suhosin.request.max_vars=' . $recommendedRequestMaxVars
647 );
648 }
649 return $status;
650 }
651
652 /**
653 * Check suhosin.request.max_varname_length
654 *
655 * @return Status\StatusInterface
656 */
657 protected function checkSuhosinRequestMaxVarnameLength()
658 {
659 $recommendedRequestMaxVarnameLength = 200;
660 if ($this->isSuhosinLoadedAndActive()) {
661 $currentRequestMaxVarnameLength = ini_get('suhosin.request.max_varname_length');
662 if ($currentRequestMaxVarnameLength < $recommendedRequestMaxVarnameLength) {
663 $status = new Status\ErrorStatus();
664 $status->setTitle('PHP suhosin.request.max_varname_length too low');
665 $status->setMessage(
666 'suhosin.request.max_varname_length=' . $currentRequestMaxVarnameLength . LF .
667 'This setting can lead to lost information if submitting forms with lots of data in TYPO3 CMS' .
668 ' (as the install tool does). It is highly recommended to raise this' .
669 ' to at least ' . $recommendedRequestMaxVarnameLength . ':' . LF .
670 'suhosin.request.max_varname_length=' . $recommendedRequestMaxVarnameLength
671 );
672 } else {
673 $status = new Status\OkStatus();
674 $status->setTitle('PHP suhosin.request.max_varname_length ok');
675 }
676 } else {
677 $status = new Status\InfoStatus();
678 $status->setTitle('Suhosin not loaded');
679 $status->setMessage(
680 'If enabling suhosin, suhosin.request.max_varname_length' .
681 ' should be set to at least ' . $recommendedRequestMaxVarnameLength . ':' . LF .
682 'suhosin.request.max_varname_length=' . $recommendedRequestMaxVarnameLength
683 );
684 }
685 return $status;
686 }
687
688 /**
689 * Check suhosin.post.max_name_length
690 *
691 * @return Status\StatusInterface
692 */
693 protected function checkSuhosinPostMaxNameLength()
694 {
695 $recommendedPostMaxNameLength = 200;
696 if ($this->isSuhosinLoadedAndActive()) {
697 $currentPostMaxNameLength = ini_get('suhosin.post.max_name_length');
698 if ($currentPostMaxNameLength < $recommendedPostMaxNameLength) {
699 $status = new Status\ErrorStatus();
700 $status->setTitle('PHP suhosin.post.max_name_length too low');
701 $status->setMessage(
702 'suhosin.post.max_name_length=' . $currentPostMaxNameLength . LF .
703 'This setting can lead to lost information if submitting forms with lots of data in TYPO3 CMS' .
704 ' (as the install tool does). It is highly recommended to raise this' .
705 ' to at least ' . $recommendedPostMaxNameLength . ':' . LF .
706 'suhosin.post.max_name_length=' . $recommendedPostMaxNameLength
707 );
708 } else {
709 $status = new Status\OkStatus();
710 $status->setTitle('PHP suhosin.post.max_name_length ok');
711 }
712 } else {
713 $status = new Status\InfoStatus();
714 $status->setTitle('Suhosin not loaded');
715 $status->setMessage(
716 'If enabling suhosin, suhosin.post.max_name_length' .
717 ' should be set to at least ' . $recommendedPostMaxNameLength . ':' . LF .
718 'suhosin.post.max_name_length=' . $recommendedPostMaxNameLength
719 );
720 }
721 return $status;
722 }
723
724 /**
725 * Check suhosin.post.max_vars
726 *
727 * @return Status\StatusInterface
728 */
729 protected function checkSuhosinPostMaxVars()
730 {
731 $recommendedPostMaxVars = 400;
732 if ($this->isSuhosinLoadedAndActive()) {
733 $currentPostMaxVars = ini_get('suhosin.post.max_vars');
734 if ($currentPostMaxVars < $recommendedPostMaxVars) {
735 $status = new Status\ErrorStatus();
736 $status->setTitle('PHP suhosin.post.max_vars too low');
737 $status->setMessage(
738 'suhosin.post.max_vars=' . $currentPostMaxVars . LF .
739 'This setting can lead to lost information if submitting forms with lots of data in TYPO3 CMS' .
740 ' (as the install tool does). It is highly recommended to raise this' .
741 ' to at least ' . $recommendedPostMaxVars . ':' . LF .
742 'suhosin.post.max_vars=' . $recommendedPostMaxVars
743 );
744 } else {
745 $status = new Status\OkStatus();
746 $status->setTitle('PHP suhosin.post.max_vars ok');
747 }
748 } else {
749 $status = new Status\InfoStatus();
750 $status->setTitle('Suhosin not loaded');
751 $status->setMessage(
752 'If enabling suhosin, suhosin.post.max_vars' .
753 ' should be set to at least ' . $recommendedPostMaxVars . ':' . LF .
754 'suhosin.post.max_vars=' . $recommendedPostMaxVars
755 );
756 }
757 return $status;
758 }
759
760 /**
761 * Check suhosin.get.max_value_length
762 *
763 * @return Status\StatusInterface
764 */
765 protected function checkSuhosinGetMaxValueLength()
766 {
767 $recommendedGetMaxValueLength = 2000;
768 if ($this->isSuhosinLoadedAndActive()) {
769 $currentGetMaxValueLength = ini_get('suhosin.get.max_value_length');
770 if ($currentGetMaxValueLength < $recommendedGetMaxValueLength) {
771 $status = new Status\ErrorStatus();
772 $status->setTitle('PHP suhosin.get.max_value_length too low');
773 $status->setMessage(
774 'suhosin.get.max_value_length=' . $currentGetMaxValueLength . LF .
775 'This setting can lead to lost information if submitting forms with lots of data in TYPO3 CMS' .
776 ' (as the install tool does). It is highly recommended to raise this' .
777 ' to at least ' . $recommendedGetMaxValueLength . ':' . LF .
778 'suhosin.get.max_value_length=' . $recommendedGetMaxValueLength
779 );
780 } else {
781 $status = new Status\OkStatus();
782 $status->setTitle('PHP suhosin.get.max_value_length ok');
783 }
784 } else {
785 $status = new Status\InfoStatus();
786 $status->setTitle('Suhosin not loaded');
787 $status->setMessage(
788 'If enabling suhosin, suhosin.get.max_value_length' .
789 ' should be set to at least ' . $recommendedGetMaxValueLength . ':' . LF .
790 'suhosin.get.max_value_length=' . $recommendedGetMaxValueLength
791 );
792 }
793 return $status;
794 }
795
796 /**
797 * Check suhosin.get.max_name_length
798 *
799 * @return Status\StatusInterface
800 */
801 protected function checkSuhosinGetMaxNameLength()
802 {
803 $recommendedGetMaxNameLength = 200;
804 if ($this->isSuhosinLoadedAndActive()) {
805 $currentGetMaxNameLength = ini_get('suhosin.get.max_name_length');
806 if ($currentGetMaxNameLength < $recommendedGetMaxNameLength) {
807 $status = new Status\ErrorStatus();
808 $status->setTitle('PHP suhosin.get.max_name_length too low');
809 $status->setMessage(
810 'suhosin.get.max_name_length=' . $currentGetMaxNameLength . LF .
811 'This setting can lead to lost information if submitting forms with lots of data in TYPO3 CMS' .
812 ' (as the install tool does). It is highly recommended to raise this' .
813 ' to at least ' . $recommendedGetMaxNameLength . ':' . LF .
814 'suhosin.get.max_name_length=' . $recommendedGetMaxNameLength
815 );
816 } else {
817 $status = new Status\OkStatus();
818 $status->setTitle('PHP suhosin.get.max_name_length ok');
819 }
820 } else {
821 $status = new Status\InfoStatus();
822 $status->setTitle('Suhosin not loaded');
823 $status->setMessage(
824 'If enabling suhosin, suhosin.get.max_name_length' .
825 ' should be set to at least ' . $recommendedGetMaxNameLength . ':' . LF .
826 'suhosin.get.max_name_length=' . $recommendedGetMaxNameLength
827 );
828 }
829 return $status;
830 }
831
832 /**
833 * Check suhosin.executor.include.whitelist contains phar
834 *
835 * @return Status\StatusInterface
836 */
837 protected function checkSuhosinExecutorIncludeWhiteListContainsPhar()
838 {
839 if ($this->isSuhosinLoadedAndActive()) {
840 $whitelist = (string)ini_get('suhosin.executor.include.whitelist');
841 if (strpos($whitelist, 'phar') === false) {
842 $status = new Status\NoticeStatus();
843 $status->setTitle('PHP suhosin.executor.include.whitelist does not contain phar');
844 $status->setMessage(
845 'suhosin.executor.include.whitelist= ' . $whitelist . LF .
846 '"phar" is currently not a hard requirement of TYPO3 CMS but is nice to have and a possible' .
847 ' requirement in future versions. A useful setting is:' . LF .
848 'suhosin.executor.include.whitelist=phar,vfs'
849 );
850 } else {
851 $status = new Status\OkStatus();
852 $status->setTitle('PHP suhosin.executor.include.whitelist contains phar');
853 }
854 } else {
855 $status = new Status\InfoStatus();
856 $status->setTitle('Suhosin not loaded');
857 $status->setMessage(
858 'If enabling suhosin, a useful setting is:' . LF .
859 'suhosin.executor.include.whitelist=phar,vfs'
860 );
861 }
862 return $status;
863 }
864
865 /**
866 * Check suhosin.executor.include.whitelist contains vfs
867 *
868 * @return Status\StatusInterface
869 */
870 protected function checkSuhosinExecutorIncludeWhiteListContainsVfs()
871 {
872 if ($this->isSuhosinLoadedAndActive()) {
873 $whitelist = (string)ini_get('suhosin.executor.include.whitelist');
874 if (strpos($whitelist, 'vfs') === false) {
875 $status = new Status\WarningStatus();
876 $status->setTitle('PHP suhosin.executor.include.whitelist does not contain vfs');
877 $status->setMessage(
878 'suhosin.executor.include.whitelist= ' . $whitelist . LF .
879 '"vfs" is currently not a hard requirement of TYPO3 CMS but tons of unit tests rely on it.' .
880 ' Furthermore, vfs will likely be a base for an additional compatibility layer in the future.' .
881 ' A useful setting is:' . LF .
882 'suhosin.executor.include.whitelist=phar,vfs'
883 );
884 } else {
885 $status = new Status\OkStatus();
886 $status->setTitle('PHP suhosin.executor.include.whitelist contains vfs');
887 }
888 } else {
889 $status = new Status\InfoStatus();
890 $status->setTitle('Suhosin not loaded');
891 $status->setMessage(
892 'If enabling suhosin, a useful setting is:' . LF .
893 'suhosin.executor.include.whitelist=phar,vfs'
894 );
895 }
896 return $status;
897 }
898
899 /**
900 * Check doc comments can be fetched by reflection
901 *
902 * @return Status\StatusInterface
903 */
904 protected function checkReflectionDocComment()
905 {
906 $testReflection = new \ReflectionMethod(get_class($this), __FUNCTION__);
907 if ($testReflection->getDocComment() === false) {
908 $status = new Status\AlertStatus();
909 $status->setTitle('PHP Doc comment reflection broken');
910 $status->setMessage(
911 'TYPO3 CMS core extensions like extbase and fluid heavily rely on method'
912 . ' comment parsing to fetch annotations and add magic belonging to them.'
913 . ' This does not work in the current environment and so we cannot install'
914 . ' TYPO3 CMS.' . LF
915 . ' Here are some possibilities: ' . LF
916 . '* In Zend OPcache you can disable saving/loading comments. If you are using'
917 . ' Zend OPcache (included since PHP 5.5) then check your php.ini settings for'
918 . ' opcache.save_comments and opcache.load_comments and enable them.' . LF
919 . '* In Zend Optimizer+ you can disable saving comments. If you are using'
920 . ' Zend Optimizer+ then check your php.ini settings for'
921 . ' zend_optimizerplus.save_comments and enable it.' . LF
922 . '* The PHP extension eaccelerator is known to break this if'
923 . ' it is compiled without --with-eaccelerator-doc-comment-inclusion flag.'
924 . ' This compile flag must be specified, otherwise TYPO3 CMS will not work.' . LF
925 . 'For more information take a look in our wiki ' . TYPO3_URL_WIKI_OPCODECACHE . '.'
926 );
927 } else {
928 $status = new Status\OkStatus();
929 $status->setTitle('PHP Doc comment reflection works');
930 }
931 return $status;
932 }
933
934 /**
935 * Checks thread stack size if on windows with apache
936 *
937 * @return Status\StatusInterface
938 */
939 protected function checkWindowsApacheThreadStackSize()
940 {
941 if ($this->isWindowsOs()
942 && substr($_SERVER['SERVER_SOFTWARE'], 0, 6) === 'Apache'
943 ) {
944 $status = new Status\WarningStatus();
945 $status->setTitle('Windows apache thread stack size');
946 $status->setMessage(
947 'This current value cannot be checked by the system, so please ignore this warning if it' .
948 ' is already taken care of: Fluid uses complex regular expressions which require a lot' .
949 ' of stack space during the first processing.' .
950 ' On Windows the default stack size for Apache is a lot smaller than on UNIX.' .
951 ' You can increase the size to 8MB (default on UNIX) by adding the following configuration' .
952 ' to httpd.conf and restarting Apache afterwards:' . LF .
953 '&lt;IfModule mpm_winnt_module&gt;' . LF .
954 'ThreadStackSize 8388608' . LF .
955 '&lt;/IfModule&gt;'
956 );
957 } else {
958 $status = new Status\OkStatus();
959 $status->setTitle('Apache ThreadStackSize is not an issue on UNIX systems');
960 }
961 return $status;
962 }
963
964 /**
965 * Check if a specific required PHP extension is loaded
966 *
967 * @param string $extension
968 * @return Status\StatusInterface
969 */
970 protected function checkRequiredPhpExtension($extension)
971 {
972 if (!extension_loaded($extension)) {
973 $status = new Status\ErrorStatus();
974 $status->setTitle('PHP extension ' . $extension . ' not loaded');
975 $status->setMessage(
976 'TYPO3 CMS uses PHP extension ' . $extension . ' but it is not loaded' .
977 ' in your environment. Change your environment to provide this extension.'
978 );
979 } else {
980 $status = new Status\OkStatus();
981 $status->setTitle('PHP extension ' . $extension . ' loaded');
982 }
983 return $status;
984 }
985
986 /**
987 * Check imagecreatetruecolor to verify gdlib works as expected
988 *
989 * @return Status\StatusInterface
990 */
991 protected function checkGdLibTrueColorSupport()
992 {
993 if (function_exists('imagecreatetruecolor')) {
994 $imageResource = @imagecreatetruecolor(50, 100);
995 if (is_resource($imageResource)) {
996 imagedestroy($imageResource);
997 $status = new Status\OkStatus();
998 $status->setTitle('PHP GD library true color works');
999 } else {
1000 $status = new Status\ErrorStatus();
1001 $status->setTitle('PHP GD library true color support broken');
1002 $status->setMessage(
1003 'GD is loaded, but calling imagecreatetruecolor() fails.' .
1004 ' This must be fixed, TYPO3 CMS won\'t work well otherwise.'
1005 );
1006 }
1007 } else {
1008 $status = new Status\ErrorStatus();
1009 $status->setTitle('PHP GD library true color support missing');
1010 $status->setMessage(
1011 'Gdlib is essential for TYPO3 CMS to work properly.'
1012 );
1013 }
1014 return $status;
1015 }
1016
1017 /**
1018 * Check gif support of GD library
1019 *
1020 * @return Status\StatusInterface
1021 */
1022 protected function checkGdLibGifSupport()
1023 {
1024 if (function_exists('imagecreatefromgif')
1025 && function_exists('imagegif')
1026 && (imagetypes() & IMG_GIF)
1027 ) {
1028 // See http://stackoverflow.com/a/13139830
1029 $imageResource = @imagecreatefromgif('data://image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7');
1030 if (is_resource($imageResource)) {
1031 imagedestroy($imageResource);
1032 $status = new Status\OkStatus();
1033 $status->setTitle('PHP GD library has gif support');
1034 } else {
1035 $status = new Status\ErrorStatus();
1036 $status->setTitle('PHP GD library gif support broken');
1037 $status->setMessage(
1038 'GD is loaded, but calling imagecreatefromgif() fails.' .
1039 ' This must be fixed, TYPO3 CMS won\'t work well otherwise.'
1040 );
1041 }
1042 } else {
1043 $status = new Status\ErrorStatus();
1044 $status->setTitle('PHP GD library gif support missing');
1045 $status->setMessage(
1046 'GD must be compiled with gif support. This is essential for' .
1047 ' TYPO3 CMS to work properly.'
1048 );
1049 }
1050 return $status;
1051 }
1052
1053 /**
1054 * Check jgp support of GD library
1055 *
1056 * @return Status\StatusInterface
1057 */
1058 protected function checkGdLibJpgSupport()
1059 {
1060 if (function_exists('imagecreatefromjpeg')
1061 && function_exists('imagejpeg')
1062 && (imagetypes() & IMG_JPG)
1063 ) {
1064 $status = new Status\OkStatus();
1065 $status->setTitle('PHP GD library has jpg support');
1066 } else {
1067 $status = new Status\ErrorStatus();
1068 $status->setTitle('PHP GD library jpg support missing');
1069 $status->setMessage(
1070 'GD must be compiled with jpg support. This is essential for' .
1071 ' TYPO3 CMS to work properly.'
1072 );
1073 }
1074 return $status;
1075 }
1076
1077 /**
1078 * Check png support of GD library
1079 *
1080 * @return Status\StatusInterface
1081 */
1082 protected function checkGdLibPngSupport()
1083 {
1084 if (function_exists('imagecreatefrompng')
1085 && function_exists('imagepng')
1086 && (imagetypes() & IMG_PNG)
1087 ) {
1088 $imageResource = @imagecreatefrompng('data://image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQMAAAAl21bKAAAAA1BMVEUAAACnej3aAAAAAXRSTlMAQObYZgAAAApJREFUCNdjYAAAAAIAAeIhvDMAAAAASUVORK5CYII');
1089 if (is_resource($imageResource)) {
1090 imagedestroy($imageResource);
1091 $status = new Status\OkStatus();
1092 $status->setTitle('PHP GD library has png support');
1093 } else {
1094 $status = new Status\ErrorStatus();
1095 $status->setTitle('PHP GD library png support broken');
1096 $status->setMessage(
1097 'GD is compiled with png support, but calling imagecreatefrompng() fails.' .
1098 ' Check your environment and fix it, png in GD lib is important' .
1099 ' for TYPO3 CMS to work properly.'
1100 );
1101 }
1102 } else {
1103 $status = new Status\ErrorStatus();
1104 $status->setTitle('PHP GD library png support missing');
1105 $status->setMessage(
1106 'GD must be compiled with png support. This is essential for' .
1107 ' TYPO3 CMS to work properly'
1108 );
1109 }
1110 return $status;
1111 }
1112
1113 /**
1114 * Check gdlib supports freetype
1115 *
1116 * @return Status\StatusInterface
1117 */
1118 protected function checkGdLibFreeTypeSupport()
1119 {
1120 if (function_exists('imagettftext')) {
1121 $status = new Status\OkStatus();
1122 $status->setTitle('PHP GD library has freetype font support');
1123 $status->setMessage(
1124 'There is a difference between the font size setting which the GD' .
1125 ' library should be supplied with. If installation is completed' .
1126 ' a test in the install tool helps to find out the value you need.'
1127 );
1128 } else {
1129 $status = new Status\ErrorStatus();
1130 $status->setTitle('PHP GD library freetype support missing');
1131 $status->setMessage(
1132 'Some core functionality and extension rely on the GD' .
1133 ' to render fonts on images. This support is missing' .
1134 ' in your environment. Install it.'
1135 );
1136 }
1137 return $status;
1138 }
1139
1140 /**
1141 * Helper methods
1142 */
1143
1144 /**
1145 * Validate a given IP address.
1146 *
1147 * @param string $ip IP address to be tested
1148 * @return bool
1149 */
1150 protected function isValidIp($ip)
1151 {
1152 return filter_var($ip, FILTER_VALIDATE_IP) !== false;
1153 }
1154
1155 /**
1156 * Test if this instance runs on windows OS
1157 *
1158 * @return bool TRUE if operating system is windows
1159 */
1160 protected function isWindowsOs()
1161 {
1162 $windowsOs = false;
1163 if (!stristr(PHP_OS, 'darwin') && stristr(PHP_OS, 'win')) {
1164 $windowsOs = true;
1165 }
1166 return $windowsOs;
1167 }
1168
1169 /**
1170 * Helper method to find out if suhosin extension is loaded
1171 *
1172 * @return bool TRUE if suhosin PHP extension is loaded
1173 */
1174 protected function isSuhosinLoadedAndActive()
1175 {
1176 $suhosinLoaded = false;
1177 if (extension_loaded('suhosin')) {
1178 $suhosinInSimulationMode = filter_var(
1179 ini_get('suhosin.simulation'),
1180 FILTER_VALIDATE_BOOLEAN,
1181 [FILTER_REQUIRE_SCALAR, FILTER_NULL_ON_FAILURE]
1182 );
1183 if (!$suhosinInSimulationMode) {
1184 $suhosinLoaded = true;
1185 }
1186 }
1187 return $suhosinLoaded;
1188 }
1189
1190 /**
1191 * Helper method to explode a string by delimiter and throw away empty values.
1192 * Removes empty values from result array.
1193 *
1194 * @param string $delimiter Delimiter string to explode with
1195 * @param string $string The string to explode
1196 * @return array Exploded values
1197 */
1198 protected function trimExplode($delimiter, $string)
1199 {
1200 $explodedValues = explode($delimiter, $string);
1201 $resultWithPossibleEmptyValues = array_map('trim', $explodedValues);
1202 $result = [];
1203 foreach ($resultWithPossibleEmptyValues as $value) {
1204 if ($value !== '') {
1205 $result[] = $value;
1206 }
1207 }
1208 return $result;
1209 }
1210
1211 /**
1212 * Helper method to get the bytes value from a measurement string like "100k".
1213 *
1214 * @param string $measurement The measurement (e.g. "100k")
1215 * @return int The bytes value (e.g. 102400)
1216 */
1217 protected function getBytesFromSizeMeasurement($measurement)
1218 {
1219 $bytes = (float)$measurement;
1220 if (stripos($measurement, 'G')) {
1221 $bytes *= 1024 * 1024 * 1024;
1222 } elseif (stripos($measurement, 'M')) {
1223 $bytes *= 1024 * 1024;
1224 } elseif (stripos($measurement, 'K')) {
1225 $bytes *= 1024;
1226 }
1227 return (int)$bytes;
1228 }
1229 }