[TASK] Remove mentiones of register_globals
[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 = array(
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 = array();
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 = 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 = array(
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 } else {
613 throw new \BadMethodCallException('Should be called only if suhosin extension is loaded', 1422634778);
614 }
615 }
616
617 /**
618 * Check suhosin.request.max_vars
619 *
620 * @return Status\StatusInterface
621 */
622 protected function checkSuhosinRequestMaxVars()
623 {
624 $recommendedRequestMaxVars = 400;
625 if ($this->isSuhosinLoadedAndActive()) {
626 $currentRequestMaxVars = ini_get('suhosin.request.max_vars');
627 if ($currentRequestMaxVars < $recommendedRequestMaxVars) {
628 $status = new Status\ErrorStatus();
629 $status->setTitle('PHP suhosin.request.max_vars too low');
630 $status->setMessage(
631 'suhosin.request.max_vars=' . $currentRequestMaxVars . LF .
632 'This setting can lead to lost information if submitting forms with lots of data in TYPO3 CMS' .
633 ' (as the install tool does). It is highly recommended to raise this' .
634 ' to at least ' . $recommendedRequestMaxVars . ':' . LF .
635 'suhosin.request.max_vars=' . $recommendedRequestMaxVars
636 );
637 } else {
638 $status = new Status\OkStatus();
639 $status->setTitle('PHP suhosin.request.max_vars ok');
640 }
641 } else {
642 $status = new Status\InfoStatus();
643 $status->setTitle('Suhosin not loaded');
644 $status->setMessage(
645 'If enabling suhosin, suhosin.request.max_vars' .
646 ' should be set to at least ' . $recommendedRequestMaxVars . ':' . LF .
647 'suhosin.request.max_vars=' . $recommendedRequestMaxVars
648 );
649 }
650 return $status;
651 }
652
653 /**
654 * Check suhosin.request.max_varname_length
655 *
656 * @return Status\StatusInterface
657 */
658 protected function checkSuhosinRequestMaxVarnameLength()
659 {
660 $recommendedRequestMaxVarnameLength = 200;
661 if ($this->isSuhosinLoadedAndActive()) {
662 $currentRequestMaxVarnameLength = ini_get('suhosin.request.max_varname_length');
663 if ($currentRequestMaxVarnameLength < $recommendedRequestMaxVarnameLength) {
664 $status = new Status\ErrorStatus();
665 $status->setTitle('PHP suhosin.request.max_varname_length too low');
666 $status->setMessage(
667 'suhosin.request.max_varname_length=' . $currentRequestMaxVarnameLength . LF .
668 'This setting can lead to lost information if submitting forms with lots of data in TYPO3 CMS' .
669 ' (as the install tool does). It is highly recommended to raise this' .
670 ' to at least ' . $recommendedRequestMaxVarnameLength . ':' . LF .
671 'suhosin.request.max_varname_length=' . $recommendedRequestMaxVarnameLength
672 );
673 } else {
674 $status = new Status\OkStatus();
675 $status->setTitle('PHP suhosin.request.max_varname_length ok');
676 }
677 } else {
678 $status = new Status\InfoStatus();
679 $status->setTitle('Suhosin not loaded');
680 $status->setMessage(
681 'If enabling suhosin, suhosin.request.max_varname_length' .
682 ' should be set to at least ' . $recommendedRequestMaxVarnameLength . ':' . LF .
683 'suhosin.request.max_varname_length=' . $recommendedRequestMaxVarnameLength
684 );
685 }
686 return $status;
687 }
688
689 /**
690 * Check suhosin.post.max_name_length
691 *
692 * @return Status\StatusInterface
693 */
694 protected function checkSuhosinPostMaxNameLength()
695 {
696 $recommendedPostMaxNameLength = 200;
697 if ($this->isSuhosinLoadedAndActive()) {
698 $currentPostMaxNameLength = ini_get('suhosin.post.max_name_length');
699 if ($currentPostMaxNameLength < $recommendedPostMaxNameLength) {
700 $status = new Status\ErrorStatus();
701 $status->setTitle('PHP suhosin.post.max_name_length too low');
702 $status->setMessage(
703 'suhosin.post.max_name_length=' . $currentPostMaxNameLength . LF .
704 'This setting can lead to lost information if submitting forms with lots of data in TYPO3 CMS' .
705 ' (as the install tool does). It is highly recommended to raise this' .
706 ' to at least ' . $recommendedPostMaxNameLength . ':' . LF .
707 'suhosin.post.max_name_length=' . $recommendedPostMaxNameLength
708 );
709 } else {
710 $status = new Status\OkStatus();
711 $status->setTitle('PHP suhosin.post.max_name_length ok');
712 }
713 } else {
714 $status = new Status\InfoStatus();
715 $status->setTitle('Suhosin not loaded');
716 $status->setMessage(
717 'If enabling suhosin, suhosin.post.max_name_length' .
718 ' should be set to at least ' . $recommendedPostMaxNameLength . ':' . LF .
719 'suhosin.post.max_name_length=' . $recommendedPostMaxNameLength
720 );
721 }
722 return $status;
723 }
724
725 /**
726 * Check suhosin.post.max_vars
727 *
728 * @return Status\StatusInterface
729 */
730 protected function checkSuhosinPostMaxVars()
731 {
732 $recommendedPostMaxVars = 400;
733 if ($this->isSuhosinLoadedAndActive()) {
734 $currentPostMaxVars = ini_get('suhosin.post.max_vars');
735 if ($currentPostMaxVars < $recommendedPostMaxVars) {
736 $status = new Status\ErrorStatus();
737 $status->setTitle('PHP suhosin.post.max_vars too low');
738 $status->setMessage(
739 'suhosin.post.max_vars=' . $currentPostMaxVars . LF .
740 'This setting can lead to lost information if submitting forms with lots of data in TYPO3 CMS' .
741 ' (as the install tool does). It is highly recommended to raise this' .
742 ' to at least ' . $recommendedPostMaxVars . ':' . LF .
743 'suhosin.post.max_vars=' . $recommendedPostMaxVars
744 );
745 } else {
746 $status = new Status\OkStatus();
747 $status->setTitle('PHP suhosin.post.max_vars ok');
748 }
749 } else {
750 $status = new Status\InfoStatus();
751 $status->setTitle('Suhosin not loaded');
752 $status->setMessage(
753 'If enabling suhosin, suhosin.post.max_vars' .
754 ' should be set to at least ' . $recommendedPostMaxVars . ':' . LF .
755 'suhosin.post.max_vars=' . $recommendedPostMaxVars
756 );
757 }
758 return $status;
759 }
760
761 /**
762 * Check suhosin.get.max_value_length
763 *
764 * @return Status\StatusInterface
765 */
766 protected function checkSuhosinGetMaxValueLength()
767 {
768 $recommendedGetMaxValueLength = 2000;
769 if ($this->isSuhosinLoadedAndActive()) {
770 $currentGetMaxValueLength = ini_get('suhosin.get.max_value_length');
771 if ($currentGetMaxValueLength < $recommendedGetMaxValueLength) {
772 $status = new Status\ErrorStatus();
773 $status->setTitle('PHP suhosin.get.max_value_length too low');
774 $status->setMessage(
775 'suhosin.get.max_value_length=' . $currentGetMaxValueLength . LF .
776 'This setting can lead to lost information if submitting forms with lots of data in TYPO3 CMS' .
777 ' (as the install tool does). It is highly recommended to raise this' .
778 ' to at least ' . $recommendedGetMaxValueLength . ':' . LF .
779 'suhosin.get.max_value_length=' . $recommendedGetMaxValueLength
780 );
781 } else {
782 $status = new Status\OkStatus();
783 $status->setTitle('PHP suhosin.get.max_value_length ok');
784 }
785 } else {
786 $status = new Status\InfoStatus();
787 $status->setTitle('Suhosin not loaded');
788 $status->setMessage(
789 'If enabling suhosin, suhosin.get.max_value_length' .
790 ' should be set to at least ' . $recommendedGetMaxValueLength . ':' . LF .
791 'suhosin.get.max_value_length=' . $recommendedGetMaxValueLength
792 );
793 }
794 return $status;
795 }
796
797 /**
798 * Check suhosin.get.max_name_length
799 *
800 * @return Status\StatusInterface
801 */
802 protected function checkSuhosinGetMaxNameLength()
803 {
804 $recommendedGetMaxNameLength = 200;
805 if ($this->isSuhosinLoadedAndActive()) {
806 $currentGetMaxNameLength = ini_get('suhosin.get.max_name_length');
807 if ($currentGetMaxNameLength < $recommendedGetMaxNameLength) {
808 $status = new Status\ErrorStatus();
809 $status->setTitle('PHP suhosin.get.max_name_length too low');
810 $status->setMessage(
811 'suhosin.get.max_name_length=' . $currentGetMaxNameLength . LF .
812 'This setting can lead to lost information if submitting forms with lots of data in TYPO3 CMS' .
813 ' (as the install tool does). It is highly recommended to raise this' .
814 ' to at least ' . $recommendedGetMaxNameLength . ':' . LF .
815 'suhosin.get.max_name_length=' . $recommendedGetMaxNameLength
816 );
817 } else {
818 $status = new Status\OkStatus();
819 $status->setTitle('PHP suhosin.get.max_name_length ok');
820 }
821 } else {
822 $status = new Status\InfoStatus();
823 $status->setTitle('Suhosin not loaded');
824 $status->setMessage(
825 'If enabling suhosin, suhosin.get.max_name_length' .
826 ' should be set to at least ' . $recommendedGetMaxNameLength . ':' . LF .
827 'suhosin.get.max_name_length=' . $recommendedGetMaxNameLength
828 );
829 }
830 return $status;
831 }
832
833 /**
834 * Check suhosin.executor.include.whitelist contains phar
835 *
836 * @return Status\StatusInterface
837 */
838 protected function checkSuhosinExecutorIncludeWhiteListContainsPhar()
839 {
840 if ($this->isSuhosinLoadedAndActive()) {
841 $whitelist = (string)ini_get('suhosin.executor.include.whitelist');
842 if (strpos($whitelist, 'phar') === false) {
843 $status = new Status\NoticeStatus();
844 $status->setTitle('PHP suhosin.executor.include.whitelist does not contain phar');
845 $status->setMessage(
846 'suhosin.executor.include.whitelist= ' . $whitelist . LF .
847 '"phar" is currently not a hard requirement of TYPO3 CMS but is nice to have and a possible' .
848 ' requirement in future versions. A useful setting is:' . LF .
849 'suhosin.executor.include.whitelist=phar,vfs'
850 );
851 } else {
852 $status = new Status\OkStatus();
853 $status->setTitle('PHP suhosin.executor.include.whitelist contains phar');
854 }
855 } else {
856 $status = new Status\InfoStatus();
857 $status->setTitle('Suhosin not loaded');
858 $status->setMessage(
859 'If enabling suhosin, a useful setting is:' . LF .
860 'suhosin.executor.include.whitelist=phar,vfs'
861 );
862 }
863 return $status;
864 }
865
866 /**
867 * Check suhosin.executor.include.whitelist contains vfs
868 *
869 * @return Status\StatusInterface
870 */
871 protected function checkSuhosinExecutorIncludeWhiteListContainsVfs()
872 {
873 if ($this->isSuhosinLoadedAndActive()) {
874 $whitelist = (string)ini_get('suhosin.executor.include.whitelist');
875 if (strpos($whitelist, 'vfs') === false) {
876 $status = new Status\WarningStatus();
877 $status->setTitle('PHP suhosin.executor.include.whitelist does not contain vfs');
878 $status->setMessage(
879 'suhosin.executor.include.whitelist= ' . $whitelist . LF .
880 '"vfs" is currently not a hard requirement of TYPO3 CMS but tons of unit tests rely on it.' .
881 ' Furthermore, vfs will likely be a base for an additional compatibility layer in the future.' .
882 ' A useful setting is:' . LF .
883 'suhosin.executor.include.whitelist=phar,vfs'
884 );
885 } else {
886 $status = new Status\OkStatus();
887 $status->setTitle('PHP suhosin.executor.include.whitelist contains vfs');
888 }
889 } else {
890 $status = new Status\InfoStatus();
891 $status->setTitle('Suhosin not loaded');
892 $status->setMessage(
893 'If enabling suhosin, a useful setting is:' . LF .
894 'suhosin.executor.include.whitelist=phar,vfs'
895 );
896 }
897 return $status;
898 }
899
900 /**
901 * Check doc comments can be fetched by reflection
902 *
903 * @return Status\StatusInterface
904 */
905 protected function checkReflectionDocComment()
906 {
907 $testReflection = new \ReflectionMethod(get_class($this), __FUNCTION__);
908 if ($testReflection->getDocComment() === false) {
909 $status = new Status\AlertStatus();
910 $status->setTitle('PHP Doc comment reflection broken');
911 $status->setMessage(
912 'TYPO3 CMS core extensions like extbase and fluid heavily rely on method'
913 . ' comment parsing to fetch annotations and add magic belonging to them.'
914 . ' This does not work in the current environment and so we cannot install'
915 . ' TYPO3 CMS.' . LF
916 . ' Here are some possibilities: ' . LF
917 . '* In Zend OPcache you can disable saving/loading comments. If you are using'
918 . ' Zend OPcache (included since PHP 5.5) then check your php.ini settings for'
919 . ' opcache.save_comments and opcache.load_comments and enable them.' . LF
920 . '* In Zend Optimizer+ you can disable saving comments. If you are using'
921 . ' Zend Optimizer+ then check your php.ini settings for'
922 . ' zend_optimizerplus.save_comments and enable it.' . LF
923 . '* The PHP extension eaccelerator is known to break this if'
924 . ' it is compiled without --with-eaccelerator-doc-comment-inclusion flag.'
925 . ' This compile flag must be specified, otherwise TYPO3 CMS will not work.' . LF
926 . 'For more information take a look in our wiki ' . TYPO3_URL_WIKI_OPCODECACHE . '.'
927 );
928 } else {
929 $status = new Status\OkStatus();
930 $status->setTitle('PHP Doc comment reflection works');
931 }
932 return $status;
933 }
934
935 /**
936 * Checks thread stack size if on windows with apache
937 *
938 * @return Status\StatusInterface
939 */
940 protected function checkWindowsApacheThreadStackSize()
941 {
942 if ($this->isWindowsOs()
943 && substr($_SERVER['SERVER_SOFTWARE'], 0, 6) === 'Apache'
944 ) {
945 $status = new Status\WarningStatus();
946 $status->setTitle('Windows apache thread stack size');
947 $status->setMessage(
948 'This current value cannot be checked by the system, so please ignore this warning if it' .
949 ' is already taken care of: Fluid uses complex regular expressions which require a lot' .
950 ' of stack space during the first processing.' .
951 ' On Windows the default stack size for Apache is a lot smaller than on UNIX.' .
952 ' You can increase the size to 8MB (default on UNIX) by adding the following configuration' .
953 ' to httpd.conf and restarting Apache afterwards:' . LF .
954 '<IfModule mpm_winnt_module>' . LF .
955 'ThreadStackSize 8388608' . LF .
956 '</IfModule>'
957 );
958 } else {
959 $status = new Status\OkStatus();
960 $status->setTitle('Apache ThreadStackSize is not an issue on UNIX systems');
961 }
962 return $status;
963 }
964
965 /**
966 * Check if a specific required PHP extension is loaded
967 *
968 * @param string $extension
969 * @return Status\StatusInterface
970 */
971 protected function checkRequiredPhpExtension($extension)
972 {
973 if (!extension_loaded($extension)) {
974 $status = new Status\ErrorStatus();
975 $status->setTitle('PHP extension ' . $extension . ' not loaded');
976 $status->setMessage(
977 'TYPO3 CMS uses PHP extension ' . $extension . ' but it is not loaded' .
978 ' in your environment. Change your environment to provide this extension.'
979 );
980 } else {
981 $status = new Status\OkStatus();
982 $status->setTitle('PHP extension ' . $extension . ' loaded');
983 }
984 return $status;
985 }
986
987 /**
988 * Check imagecreatetruecolor to verify gdlib works as expected
989 *
990 * @return Status\StatusInterface
991 */
992 protected function checkGdLibTrueColorSupport()
993 {
994 if (function_exists('imagecreatetruecolor')) {
995 $imageResource = @imagecreatetruecolor(50, 100);
996 if (is_resource($imageResource)) {
997 imagedestroy($imageResource);
998 $status = new Status\OkStatus();
999 $status->setTitle('PHP GD library true color works');
1000 } else {
1001 $status = new Status\ErrorStatus();
1002 $status->setTitle('PHP GD library true color support broken');
1003 $status->setMessage(
1004 'GD is loaded, but calling imagecreatetruecolor() fails.' .
1005 ' This must be fixed, TYPO3 CMS won\'t work well otherwise.'
1006 );
1007 }
1008 } else {
1009 $status = new Status\ErrorStatus();
1010 $status->setTitle('PHP GD library true color support missing');
1011 $status->setMessage(
1012 'Gdlib is essential for TYPO3 CMS to work properly.'
1013 );
1014 }
1015 return $status;
1016 }
1017
1018 /**
1019 * Check gif support of GD library
1020 *
1021 * @return Status\StatusInterface
1022 */
1023 protected function checkGdLibGifSupport()
1024 {
1025 if (function_exists('imagecreatefromgif')
1026 && function_exists('imagegif')
1027 && (imagetypes() & IMG_GIF)
1028 ) {
1029 // See http://stackoverflow.com/a/13139830
1030 $imageResource = @imagecreatefromgif('data://image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7');
1031 if (is_resource($imageResource)) {
1032 imagedestroy($imageResource);
1033 $status = new Status\OkStatus();
1034 $status->setTitle('PHP GD library has gif support');
1035 } else {
1036 $status = new Status\ErrorStatus();
1037 $status->setTitle('PHP GD library gif support broken');
1038 $status->setMessage(
1039 'GD is loaded, but calling imagecreatefromgif() fails.' .
1040 ' This must be fixed, TYPO3 CMS won\'t work well otherwise.'
1041 );
1042 }
1043 } else {
1044 $status = new Status\ErrorStatus();
1045 $status->setTitle('PHP GD library gif support missing');
1046 $status->setMessage(
1047 'GD must be compiled with gif support. This is essential for' .
1048 ' TYPO3 CMS to work properly.'
1049 );
1050 }
1051 return $status;
1052 }
1053
1054 /**
1055 * Check jgp support of GD library
1056 *
1057 * @return Status\StatusInterface
1058 */
1059 protected function checkGdLibJpgSupport()
1060 {
1061 if (function_exists('imagecreatefromjpeg')
1062 && function_exists('imagejpeg')
1063 && (imagetypes() & IMG_JPG)
1064 ) {
1065 $status = new Status\OkStatus();
1066 $status->setTitle('PHP GD library has jpg support');
1067 } else {
1068 $status = new Status\ErrorStatus();
1069 $status->setTitle('PHP GD library jpg support missing');
1070 $status->setMessage(
1071 'GD must be compiled with jpg support. This is essential for' .
1072 ' TYPO3 CMS to work properly.'
1073 );
1074 }
1075 return $status;
1076 }
1077
1078 /**
1079 * Check png support of GD library
1080 *
1081 * @return Status\StatusInterface
1082 */
1083 protected function checkGdLibPngSupport()
1084 {
1085 if (function_exists('imagecreatefrompng')
1086 && function_exists('imagepng')
1087 && (imagetypes() & IMG_PNG)
1088 ) {
1089 $imageResource = @imagecreatefrompng('data://image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQMAAAAl21bKAAAAA1BMVEUAAACnej3aAAAAAXRSTlMAQObYZgAAAApJREFUCNdjYAAAAAIAAeIhvDMAAAAASUVORK5CYII');
1090 if (is_resource($imageResource)) {
1091 imagedestroy($imageResource);
1092 $status = new Status\OkStatus();
1093 $status->setTitle('PHP GD library has png support');
1094 } else {
1095 $status = new Status\ErrorStatus();
1096 $status->setTitle('PHP GD library png support broken');
1097 $status->setMessage(
1098 'GD is compiled with png support, but calling imagecreatefrompng() fails.' .
1099 ' Check your environment and fix it, png in GD lib is important' .
1100 ' for TYPO3 CMS to work properly.'
1101 );
1102 }
1103 } else {
1104 $status = new Status\ErrorStatus();
1105 $status->setTitle('PHP GD library png support missing');
1106 $status->setMessage(
1107 'GD must be compiled with png support. This is essential for' .
1108 ' TYPO3 CMS to work properly'
1109 );
1110 }
1111 return $status;
1112 }
1113
1114 /**
1115 * Check gdlib supports freetype
1116 *
1117 * @return Status\StatusInterface
1118 */
1119 protected function checkGdLibFreeTypeSupport()
1120 {
1121 if (function_exists('imagettftext')) {
1122 $status = new Status\OkStatus();
1123 $status->setTitle('PHP GD library has freetype font support');
1124 $status->setMessage(
1125 'There is a difference between the font size setting which the GD' .
1126 ' library should be supplied with. If installation is completed' .
1127 ' a test in the install tool helps to find out the value you need.'
1128 );
1129 } else {
1130 $status = new Status\ErrorStatus();
1131 $status->setTitle('PHP GD library freetype support missing');
1132 $status->setMessage(
1133 'Some core functionality and extension rely on the GD' .
1134 ' to render fonts on images. This support is missing' .
1135 ' in your environment. Install it.'
1136 );
1137 }
1138 return $status;
1139 }
1140
1141 /**
1142 * Helper methods
1143 */
1144
1145 /**
1146 * Validate a given IP address.
1147 *
1148 * @param string $ip IP address to be tested
1149 * @return bool
1150 */
1151 protected function isValidIp($ip)
1152 {
1153 return filter_var($ip, FILTER_VALIDATE_IP) !== false;
1154 }
1155
1156 /**
1157 * Test if this instance runs on windows OS
1158 *
1159 * @return bool TRUE if operating system is windows
1160 */
1161 protected function isWindowsOs()
1162 {
1163 $windowsOs = false;
1164 if (!stristr(PHP_OS, 'darwin') && stristr(PHP_OS, 'win')) {
1165 $windowsOs = true;
1166 }
1167 return $windowsOs;
1168 }
1169
1170 /**
1171 * Helper method to find out if suhosin extension is loaded
1172 *
1173 * @return bool TRUE if suhosin PHP extension is loaded
1174 */
1175 protected function isSuhosinLoadedAndActive()
1176 {
1177 $suhosinLoaded = false;
1178 if (extension_loaded('suhosin')) {
1179 $suhosinInSimulationMode = filter_var(
1180 ini_get('suhosin.simulation'),
1181 FILTER_VALIDATE_BOOLEAN,
1182 array(FILTER_REQUIRE_SCALAR, FILTER_NULL_ON_FAILURE)
1183 );
1184 if (!$suhosinInSimulationMode) {
1185 $suhosinLoaded = true;
1186 }
1187 }
1188 return $suhosinLoaded;
1189 }
1190
1191 /**
1192 * Helper method to explode a string by delimiter and throw away empty values.
1193 * Removes empty values from result array.
1194 *
1195 * @param string $delimiter Delimiter string to explode with
1196 * @param string $string The string to explode
1197 * @return array Exploded values
1198 */
1199 protected function trimExplode($delimiter, $string)
1200 {
1201 $explodedValues = explode($delimiter, $string);
1202 $resultWithPossibleEmptyValues = array_map('trim', $explodedValues);
1203 $result = array();
1204 foreach ($resultWithPossibleEmptyValues as $value) {
1205 if ($value !== '') {
1206 $result[] = $value;
1207 }
1208 }
1209 return $result;
1210 }
1211
1212 /**
1213 * Helper method to get the bytes value from a measurement string like "100k".
1214 *
1215 * @param string $measurement The measurement (e.g. "100k")
1216 * @return int The bytes value (e.g. 102400)
1217 */
1218 protected function getBytesFromSizeMeasurement($measurement)
1219 {
1220 $bytes = doubleval($measurement);
1221 if (stripos($measurement, 'G')) {
1222 $bytes *= 1024 * 1024 * 1024;
1223 } elseif (stripos($measurement, 'M')) {
1224 $bytes *= 1024 * 1024;
1225 } elseif (stripos($measurement, 'K')) {
1226 $bytes *= 1024;
1227 }
1228 return (int)$bytes;
1229 }
1230 }