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