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