[BUGFIX] Output correct max upload size
[Packages/TYPO3.CMS.git] / typo3 / sysext / install / Classes / SystemEnvironment / Check.php
1 <?php
2 namespace TYPO3\CMS\Install\SystemEnvironment;
3
4 /*
5 * This file is part of the TYPO3 CMS project.
6 *
7 * It is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU General Public License, either version 2
9 * of the License, or any later version.
10 *
11 * For the full copyright and license information, please read the
12 * LICENSE.txt file that was distributed with this source code.
13 *
14 * The TYPO3 project - inspiring people to share!
15 */
16
17 use TYPO3\CMS\Install\Status;
18
19 /**
20 * Check system environment status
21 *
22 * This class is a hardcoded requirement check of the underlying
23 * server and PHP system.
24 *
25 * The class *must not* check for any TYPO3 specific things like
26 * specific configuration values or directories. It should not fail
27 * if there is no TYPO3 at all.
28 *
29 * The only core code used is the class loader
30 *
31 * This class is instantiated as the *very first* class during
32 * installation. It is meant to be *standalone* und must not have
33 * any requirements, except the status classes. It must be possible
34 * to run this script separated from the rest of the core, without
35 * dependencies.
36 *
37 * This means especially:
38 * * No hooks or anything like that
39 * * No usage of *any* TYPO3 code like GeneralUtility
40 * * No require of anything but the status classes
41 * * No localization
42 *
43 * The status messages and title *must not* include HTML, use plain
44 * text only. The return values of this class are not bound to HTML
45 * and can be used in different scopes (eg. as json array).
46 */
47 class Check implements CheckInterface
48 {
49 /**
50 * @var array List of required PHP extensions
51 */
52 protected $requiredPhpExtensions = [
53 'filter',
54 'gd',
55 'hash',
56 'json',
57 'mysqli',
58 'openssl',
59 'session',
60 'SPL',
61 'standard',
62 'xml',
63 'zip',
64 'zlib',
65 ];
66
67 /**
68 * @var string[]
69 */
70 protected $suggestedPhpExtensions = [
71 'fileinfo' => 'This extension is used for proper file type detection in the File Abstraction Layer.',
72 ];
73
74 /**
75 * Get all status information as array with status objects
76 *
77 * @return array<\TYPO3\CMS\Install\Status\StatusInterface>
78 */
79 public function getStatus(): array
80 {
81 $status = [];
82 $status[] = $this->checkCurrentDirectoryIsInIncludePath();
83 $status[] = $this->checkFileUploadEnabled();
84 $status[] = $this->checkPostUploadSizeIsHigherOrEqualMaximumFileUploadSize();
85 $status[] = $this->checkMemorySettings();
86 $status[] = $this->checkPhpVersion();
87 $status[] = $this->checkMaxExecutionTime();
88 $status[] = $this->checkDisableFunctions();
89 $status[] = $this->checkMysqliReconnectSetting();
90 $status[] = $this->checkDocRoot();
91 $status[] = $this->checkOpenBaseDir();
92 $status[] = $this->checkXdebugMaxNestingLevel();
93 $status[] = $this->checkOpenSslInstalled();
94
95 $status[] = $this->checkMaxInputVars();
96 $status[] = $this->checkReflectionDocComment();
97 $status[] = $this->checkWindowsApacheThreadStackSize();
98
99 foreach ($this->requiredPhpExtensions as $extension) {
100 $status[] = $this->checkPhpExtension($extension);
101 }
102
103 foreach ($this->suggestedPhpExtensions as $extension => $purpose) {
104 $status[] = $this->checkPhpExtension($extension, false, $purpose);
105 }
106
107 $status[] = $this->checkPcreVersion();
108 $status[] = $this->checkGdLibTrueColorSupport();
109 $status[] = $this->checkGdLibGifSupport();
110 $status[] = $this->checkGdLibJpgSupport();
111 $status[] = $this->checkGdLibPngSupport();
112 $status[] = $this->checkGdLibFreeTypeSupport();
113
114 return $status;
115 }
116
117 /**
118 * Checks if current directory (.) is in PHP include path
119 *
120 * @return Status\StatusInterface
121 */
122 protected function checkCurrentDirectoryIsInIncludePath()
123 {
124 $includePath = ini_get('include_path');
125 $delimiter = $this->isWindowsOs() ? ';' : ':';
126 $pathArray = $this->trimExplode($delimiter, $includePath);
127 if (!in_array('.', $pathArray)) {
128 $status = new Status\WarningStatus();
129 $status->setTitle('Current directory (./) is not within PHP include path');
130 $status->setMessage(
131 'include_path = ' . implode(' ', $pathArray) . LF .
132 'Normally the current path \'.\' is included in the' .
133 ' include_path of PHP. Although TYPO3 does not rely on this,' .
134 ' it is an unusual setting that may introduce problems for' .
135 ' some extensions.'
136 );
137 } else {
138 $status = new Status\OkStatus();
139 $status->setTitle('Current directory (./) is within PHP include path.');
140 }
141 return $status;
142 }
143
144 /**
145 * Check if file uploads are enabled in PHP
146 *
147 * @return Status\StatusInterface
148 */
149 protected function checkFileUploadEnabled()
150 {
151 if (!ini_get('file_uploads')) {
152 $status = new Status\ErrorStatus();
153 $status->setTitle('File uploads not allowed in PHP');
154 $status->setMessage(
155 'file_uploads=' . ini_get('file_uploads') . LF .
156 'TYPO3 uses the ability to upload files from the browser in various cases.' .
157 ' If this flag is disabled in PHP, you won\'t be able to upload files.' .
158 ' But it doesn\'t end here, because not only are files not accepted by' .
159 ' the server - ALL content in the forms are discarded and therefore' .
160 ' nothing at all will be editable if you don\'t set this flag!'
161 );
162 } else {
163 $status = new Status\OkStatus();
164 $status->setTitle('File uploads allowed in PHP');
165 }
166 return $status;
167 }
168
169 /**
170 * Check maximum post upload size correlates with maximum file upload
171 *
172 * @return Status\StatusInterface
173 */
174 protected function checkPostUploadSizeIsHigherOrEqualMaximumFileUploadSize()
175 {
176 $maximumUploadFilesize = $this->getBytesFromSizeMeasurement(ini_get('upload_max_filesize'));
177 $maximumPostSize = $this->getBytesFromSizeMeasurement(ini_get('post_max_size'));
178 if ($maximumPostSize > 0 && $maximumPostSize < $maximumUploadFilesize) {
179 $status = new Status\ErrorStatus();
180 $status->setTitle('Maximum size for POST requests is smaller than maximum upload filesize in PHP');
181 $status->setMessage(
182 'upload_max_filesize=' . ini_get('upload_max_filesize') . LF .
183 'post_max_size=' . ini_get('post_max_size') . LF .
184 'You have defined a maximum size for file uploads in PHP which' .
185 ' exceeds the allowed size for POST requests. Therefore the' .
186 ' file uploads can also not be larger than ' . ini_get('post_max_size') . '.'
187 );
188 } else {
189 $status = new Status\OkStatus();
190 $status->setTitle('Maximum post upload size correlates with maximum upload file size in PHP');
191 $status->setMessage('The maximum size for file uploads is actually set to ' . ini_get('upload_max_filesize'));
192 }
193 return $status;
194 }
195
196 /**
197 * Check memory settings
198 *
199 * @return Status\StatusInterface
200 */
201 protected function checkMemorySettings()
202 {
203 $minimumMemoryLimit = 64;
204 $recommendedMemoryLimit = 128;
205 $memoryLimit = $this->getBytesFromSizeMeasurement(ini_get('memory_limit'));
206 if ($memoryLimit <= 0) {
207 $status = new Status\WarningStatus();
208 $status->setTitle('Unlimited memory limit for PHP');
209 $status->setMessage(
210 'PHP is configured not to limit memory usage at all. This is a risk' .
211 ' and should be avoided in production setup. In general it\'s best practice to limit this.' .
212 ' To be safe, set a limit in PHP, but with a minimum of ' . $recommendedMemoryLimit . 'MB:' . LF .
213 'memory_limit=' . $recommendedMemoryLimit . 'M'
214 );
215 } elseif ($memoryLimit < 1024 * 1024 * $minimumMemoryLimit) {
216 $status = new Status\ErrorStatus();
217 $status->setTitle('PHP Memory limit below ' . $minimumMemoryLimit . 'MB');
218 $status->setMessage(
219 'memory_limit=' . ini_get('memory_limit') . LF .
220 'Your system is configured to enforce a memory limit for PHP scripts lower than ' .
221 $minimumMemoryLimit . 'MB. It is required to raise the limit.' .
222 ' We recommend a minimum PHP memory limit of ' . $recommendedMemoryLimit . 'MB:' . LF .
223 'memory_limit=' . $recommendedMemoryLimit . 'M'
224 );
225 } elseif ($memoryLimit < 1024 * 1024 * $recommendedMemoryLimit) {
226 $status = new Status\WarningStatus();
227 $status->setTitle('PHP Memory limit below ' . $recommendedMemoryLimit . 'MB');
228 $status->setMessage(
229 'memory_limit=' . ini_get('memory_limit') . LF .
230 'Your system is configured to enforce a memory limit for PHP scripts lower than ' .
231 $recommendedMemoryLimit . 'MB.' .
232 ' A slim TYPO3 instance without many extensions will probably work, but you should monitor your' .
233 ' system for "allowed memory size of X bytes exhausted" messages, especially if using the backend.' .
234 ' To be on the safe side,' . ' we recommend a minimum PHP memory limit of ' .
235 $recommendedMemoryLimit . 'MB:' . LF .
236 'memory_limit=' . $recommendedMemoryLimit . 'M'
237 );
238 } else {
239 $status = new Status\OkStatus();
240 $status->setTitle('PHP Memory limit is equal to or more than ' . $recommendedMemoryLimit . 'MB');
241 }
242 return $status;
243 }
244
245 /**
246 * Check minimum PHP version
247 *
248 * @return Status\StatusInterface
249 */
250 protected function checkPhpVersion()
251 {
252 $minimumPhpVersion = '7.0.0';
253 $currentPhpVersion = phpversion();
254 if (version_compare($currentPhpVersion, $minimumPhpVersion) < 0) {
255 $status = new Status\ErrorStatus();
256 $status->setTitle('PHP version too low');
257 $status->setMessage(
258 'Your PHP version ' . $currentPhpVersion . ' is too old. TYPO3 CMS does not run' .
259 ' with this version. Update to at least PHP ' . $minimumPhpVersion
260 );
261 } else {
262 $status = new Status\OkStatus();
263 $status->setTitle('PHP version is fine');
264 }
265 return $status;
266 }
267
268 /**
269 * Check PRCE module is loaded and minimum version
270 *
271 * @return Status\StatusInterface
272 */
273 protected function checkPcreVersion()
274 {
275 $minimumPcreVersion = '8.38';
276 if (!extension_loaded('pcre')) {
277 $status = new Status\ErrorStatus();
278 $status->setTitle('PHP extension pcre not loaded');
279 $status->setMessage(
280 'TYPO3 CMS uses PHP extension pcre but it is not loaded' .
281 ' in your environment. Change your environment to provide this extension' .
282 ' in with minimum version ' . $minimumPcreVersion . '.'
283 );
284 } else {
285 $installedPcreVersionString = trim(PCRE_VERSION); // '8.39 2016-06-14'
286 $mainPcreVersionString = explode(' ', $installedPcreVersionString);
287 $mainPcreVersionString = $mainPcreVersionString[0]; // '8.39'
288 if (version_compare($mainPcreVersionString, $minimumPcreVersion) < 0) {
289 $status = new Status\ErrorStatus();
290 $status->setTitle('PCRE version too low');
291 $status->setMessage(
292 'Your PCRE version ' . PCRE_VERSION . ' is too old. TYPO3 CMS may trigger PHP segmentantion' .
293 ' faults with this version. Update to at least PCRE ' . $minimumPcreVersion
294 );
295 } else {
296 $status = new Status\OkStatus();
297 $status->setTitle('PHP extension PCRE is loaded and version is fine');
298 }
299 }
300 return $status;
301 }
302
303 /**
304 * Check maximum execution time
305 *
306 * @return Status\StatusInterface
307 */
308 protected function checkMaxExecutionTime()
309 {
310 $minimumMaximumExecutionTime = 30;
311 $recommendedMaximumExecutionTime = 240;
312 $currentMaximumExecutionTime = ini_get('max_execution_time');
313 if ($currentMaximumExecutionTime == 0) {
314 $status = new Status\WarningStatus();
315 $status->setTitle('Infinite PHP script execution time');
316 $status->setMessage(
317 'max_execution_time=0' . LF .
318 'While TYPO3 is fine with this, you risk a denial-of-service for your system if for whatever' .
319 ' reason some script hangs in an infinite loop. You are usually on the safe side ' .
320 ' if it is reduced to ' . $recommendedMaximumExecutionTime . ' seconds:' . LF .
321 'max_execution_time=' . $recommendedMaximumExecutionTime
322 );
323 } elseif ($currentMaximumExecutionTime < $minimumMaximumExecutionTime) {
324 $status = new Status\ErrorStatus();
325 $status->setTitle('Low PHP script execution time');
326 $status->setMessage(
327 'max_execution_time=' . $currentMaximumExecutionTime . LF .
328 'Your max_execution_time is too low. Some expensive operations in TYPO3 can take longer than that.' .
329 ' It is recommended to raise the limit to ' . $recommendedMaximumExecutionTime . ' seconds:' . LF .
330 'max_execution_time=' . $recommendedMaximumExecutionTime
331 );
332 } elseif ($currentMaximumExecutionTime < $recommendedMaximumExecutionTime) {
333 $status = new Status\WarningStatus();
334 $status->setTitle('Low PHP script execution time');
335 $status->setMessage(
336 'max_execution_time=' . $currentMaximumExecutionTime . LF .
337 'Your max_execution_time is low. While TYPO3 often runs without problems' .
338 ' with ' . $minimumMaximumExecutionTime . ' seconds,' .
339 ' it may still happen that script execution is stopped before finishing' .
340 ' calculations. You should monitor the system for messages in this area' .
341 ' and maybe raise the limit to ' . $recommendedMaximumExecutionTime . ' seconds:' . LF .
342 'max_execution_time=' . $recommendedMaximumExecutionTime
343 );
344 } else {
345 $status = new Status\OkStatus();
346 $status->setTitle('Maximum PHP script execution time is equal to or more than '
347 . $recommendedMaximumExecutionTime);
348 }
349 return $status;
350 }
351
352 /**
353 * Check for disabled functions
354 *
355 * @return Status\StatusInterface
356 */
357 protected function checkDisableFunctions()
358 {
359 $disabledFunctions = trim(ini_get('disable_functions'));
360
361 // Filter "disable_functions"
362 $disabledFunctionsArray = $this->trimExplode(',', $disabledFunctions);
363
364 // Array with strings to find
365 $findStrings = [
366 // Disabled by default on Ubuntu OS but this is okay since the Core does not use them
367 'pcntl_',
368 ];
369 foreach ($disabledFunctionsArray as $key => $disabledFunction) {
370 foreach ($findStrings as $findString) {
371 if (strpos($disabledFunction, $findString) !== false) {
372 unset($disabledFunctionsArray[$key]);
373 }
374 }
375 }
376
377 if ($disabledFunctions !== '') {
378 if (!empty($disabledFunctionsArray)) {
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 } else {
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 }
398 } else {
399 $status = new Status\OkStatus();
400 $status->setTitle('No disabled PHP functions');
401 }
402 return $status;
403 }
404
405 /**
406 * Verify that mysqli.reconnect is set to 0 in order to avoid improper reconnects
407 *
408 * @return Status\StatusInterface
409 */
410 protected function checkMysqliReconnectSetting()
411 {
412 $currentMysqliReconnectSetting = ini_get('mysqli.reconnect');
413 if ($currentMysqliReconnectSetting === '1') {
414 $status = new Status\ErrorStatus();
415 $status->setTitle('PHP mysqli.reconnect is enabled');
416 $status->setMessage(
417 'mysqli.reconnect=1' . LF .
418 'PHP is configured to automatically reconnect the database connection on disconnection.' . LF .
419 ' Warning: If (e.g. during a long-running task) the connection is dropped and automatically reconnected, ' .
420 ' it may not be reinitialized properly (e.g. charset) and write mangled data to the database!'
421 );
422 } else {
423 $status = new Status\OkStatus();
424 $status->setTitle('PHP mysqli.reconnect is fine');
425 }
426 return $status;
427 }
428
429 /**
430 * Check for doc_root ini setting
431 *
432 * @return Status\StatusInterface
433 */
434 protected function checkDocRoot()
435 {
436 $docRootSetting = trim(ini_get('doc_root'));
437 if ($docRootSetting !== '') {
438 $status = new Status\NoticeStatus();
439 $status->setTitle('doc_root is set');
440 $status->setMessage(
441 'doc_root=' . $docRootSetting . LF .
442 'PHP cannot execute scripts' .
443 ' outside this directory. This setting is seldom used and must correlate' .
444 ' with your actual document root. You might be in trouble if your' .
445 ' TYPO3 CMS core code is linked to some different location.' .
446 ' If that is a problem, the setting must be changed.'
447 );
448 } else {
449 $status = new Status\OkStatus();
450 $status->setTitle('PHP doc_root is not set');
451 }
452 return $status;
453 }
454
455 /**
456 * Check open_basedir
457 *
458 * @return Status\StatusInterface
459 */
460 protected function checkOpenBaseDir()
461 {
462 $openBaseDirSetting = trim(ini_get('open_basedir'));
463 if ($openBaseDirSetting !== '') {
464 $status = new Status\NoticeStatus();
465 $status->setTitle('PHP open_basedir is set');
466 $status->setMessage(
467 'open_basedir = ' . ini_get('open_basedir') . LF .
468 'This restricts TYPO3 to open and include files only in this' .
469 ' path. Please make sure that this does not prevent TYPO3 from running,' .
470 ' if for example your TYPO3 CMS core is linked to a different directory' .
471 ' not included in this path.'
472 );
473 } else {
474 $status = new Status\OkStatus();
475 $status->setTitle('PHP open_basedir is off');
476 }
477 return $status;
478 }
479
480 /**
481 * If xdebug is loaded, the default max_nesting_level of 100 must be raised
482 *
483 * @return Status\StatusInterface
484 */
485 protected function checkXdebugMaxNestingLevel()
486 {
487 if (extension_loaded('xdebug')) {
488 $recommendedMaxNestingLevel = 400;
489 $errorThreshold = 250;
490 $currentMaxNestingLevel = ini_get('xdebug.max_nesting_level');
491 if ($currentMaxNestingLevel < $errorThreshold) {
492 $status = new Status\ErrorStatus();
493 $status->setTitle('PHP xdebug.max_nesting_level is critically 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 has to be unloaded. A value of ' . $recommendedMaxNestingLevel .
499 ' is recommended. Warning: Expect fatal PHP errors in central parts of the CMS' .
500 ' if the value is not raised significantly to:' . LF .
501 'xdebug.max_nesting_level=' . $recommendedMaxNestingLevel
502 );
503 } elseif ($currentMaxNestingLevel < $recommendedMaxNestingLevel) {
504 $status = new Status\WarningStatus();
505 $status->setTitle('PHP xdebug.max_nesting_level is low');
506 $status->setMessage(
507 'xdebug.max_nesting_level=' . $currentMaxNestingLevel . LF .
508 'This setting controls the maximum number of nested function calls to protect against' .
509 ' infinite recursion. The current value is high enough for the TYPO3 CMS core to work' .
510 ' fine, but still some extensions could raise fatal PHP errors if the setting is not' .
511 ' raised further. A value of ' . $recommendedMaxNestingLevel . ' is recommended.' . LF .
512 'xdebug.max_nesting_level=' . $recommendedMaxNestingLevel
513 );
514 } else {
515 $status = new Status\OkStatus();
516 $status->setTitle('PHP xdebug.max_nesting_level ok');
517 }
518 } else {
519 $status = new Status\OkStatus();
520 $status->setTitle('PHP xdebug extension not loaded');
521 }
522 return $status;
523 }
524
525 /**
526 * Check accessibility and functionality of OpenSSL
527 *
528 * @return Status\StatusInterface
529 */
530 protected function checkOpenSslInstalled()
531 {
532 if (extension_loaded('openssl')) {
533 $testKey = @openssl_pkey_new();
534 if (is_resource($testKey)) {
535 openssl_free_key($testKey);
536 $status = new Status\OkStatus();
537 $status->setTitle('PHP OpenSSL extension installed properly');
538 } else {
539 $status = new Status\ErrorStatus();
540 $status->setTitle('PHP OpenSSL extension not working');
541 $status->setMessage(
542 'Something went wrong while trying to create a new private key for testing.' .
543 ' Please check the integration of the PHP OpenSSL extension and if it is installed correctly.'
544 );
545 }
546 } else {
547 $status = new Status\ErrorStatus();
548 $status->setTitle('PHP OpenSSL extension not loaded');
549 $status->setMessage(
550 'OpenSSL is a PHP extension to encrypt/decrypt data between requests.' .
551 ' TYPO3 CMS requires it to be able to encrypt stored passwords to improve the security in the' .
552 ' database layer.'
553 );
554 }
555
556 return $status;
557 }
558
559 /**
560 * Get max_input_vars status
561 *
562 * @return Status\StatusInterface
563 */
564 protected function checkMaxInputVars()
565 {
566 $recommendedMaxInputVars = 1500;
567 $minimumMaxInputVars = 1000;
568 $currentMaxInputVars = ini_get('max_input_vars');
569
570 if ($currentMaxInputVars < $minimumMaxInputVars) {
571 $status = new Status\ErrorStatus();
572 $status->setTitle('PHP max_input_vars too low');
573 $status->setMessage(
574 'max_input_vars=' . $currentMaxInputVars . LF .
575 'This setting can lead to lost information if submitting forms with lots of data in TYPO3 CMS' .
576 ' (as the install tool does). It is highly recommended to raise this' .
577 ' to at least ' . $recommendedMaxInputVars . ':' . LF .
578 'max_input_vars=' . $recommendedMaxInputVars
579 );
580 } elseif ($currentMaxInputVars < $recommendedMaxInputVars) {
581 $status = new Status\WarningStatus();
582 $status->setTitle('PHP max_input_vars very low');
583 $status->setMessage(
584 'max_input_vars=' . $currentMaxInputVars . LF .
585 'This setting can lead to lost information if submitting forms with lots of data in TYPO3 CMS' .
586 ' (as the install tool does). It is highly recommended to raise this' .
587 ' to at least ' . $recommendedMaxInputVars . ':' . LF .
588 'max_input_vars=' . $recommendedMaxInputVars
589 );
590 } else {
591 $status = new Status\OkStatus();
592 $status->setTitle('PHP max_input_vars ok');
593 }
594 return $status;
595 }
596
597 /**
598 * Check doc comments can be fetched by reflection
599 *
600 * @return Status\StatusInterface
601 */
602 protected function checkReflectionDocComment()
603 {
604 $testReflection = new \ReflectionMethod(get_class($this), __FUNCTION__);
605 if ($testReflection->getDocComment() === false) {
606 $status = new Status\AlertStatus();
607 $status->setTitle('PHP Doc comment reflection broken');
608 $status->setMessage(
609 'TYPO3 CMS core extensions like extbase and fluid heavily rely on method'
610 . ' comment parsing to fetch annotations and add magic belonging to them.'
611 . ' This does not work in the current environment and so we cannot install'
612 . ' TYPO3 CMS.' . LF
613 . ' Here are some possibilities: ' . LF
614 . '* In Zend OPcache you can disable saving/loading comments. If you are using'
615 . ' Zend OPcache (included since PHP 5.5) then check your php.ini settings for'
616 . ' opcache.save_comments and opcache.load_comments and enable them.' . LF
617 . '* In Zend Optimizer+ you can disable saving comments. If you are using'
618 . ' Zend Optimizer+ then check your php.ini settings for'
619 . ' zend_optimizerplus.save_comments and enable it.' . LF
620 . '* The PHP extension eaccelerator is known to break this if'
621 . ' it is compiled without --with-eaccelerator-doc-comment-inclusion flag.'
622 . ' This compile flag must be specified, otherwise TYPO3 CMS will not work.' . LF
623 . 'For more information take a look in our wiki ' . TYPO3_URL_WIKI_OPCODECACHE . '.'
624 );
625 } else {
626 $status = new Status\OkStatus();
627 $status->setTitle('PHP Doc comment reflection works');
628 }
629 return $status;
630 }
631
632 /**
633 * Checks thread stack size if on windows with apache
634 *
635 * @return Status\StatusInterface
636 */
637 protected function checkWindowsApacheThreadStackSize()
638 {
639 if ($this->isWindowsOs()
640 && substr($_SERVER['SERVER_SOFTWARE'], 0, 6) === 'Apache'
641 ) {
642 $status = new Status\WarningStatus();
643 $status->setTitle('Windows apache thread stack size');
644 $status->setMessage(
645 'This current value cannot be checked by the system, so please ignore this warning if it' .
646 ' is already taken care of: Fluid uses complex regular expressions which require a lot' .
647 ' of stack space during the first processing.' .
648 ' On Windows the default stack size for Apache is a lot smaller than on UNIX.' .
649 ' You can increase the size to 8MB (default on UNIX) by adding the following configuration' .
650 ' to httpd.conf and restarting Apache afterwards:' . LF .
651 '&lt;IfModule mpm_winnt_module&gt;' . LF .
652 'ThreadStackSize 8388608' . LF .
653 '&lt;/IfModule&gt;'
654 );
655 } else {
656 $status = new Status\OkStatus();
657 $status->setTitle('Apache ThreadStackSize is not an issue on UNIX systems');
658 }
659 return $status;
660 }
661
662 /**
663 * Checks if a specific PHP extension is loaded.
664 *
665 * @param string $extension
666 * @param bool $required
667 * @param string $purpose
668 * @return Status\StatusInterface
669 */
670 protected function checkPhpExtension(string $extension, bool $required = true, string $purpose = '')
671 {
672 if (!extension_loaded($extension)) {
673 $status = $required ? new Status\ErrorStatus() : new Status\WarningStatus();
674 $status->setTitle('PHP extension ' . $extension . ' not loaded');
675 $status->setMessage(
676 'TYPO3 uses the PHP extension "' . $extension . '" but it is not loaded'
677 . ' in your environment. Change your environment to provide this extension. '
678 . $purpose
679 );
680 } else {
681 $status = new Status\OkStatus();
682 $status->setTitle('PHP extension "' . $extension . '" loaded');
683 }
684 return $status;
685 }
686
687 /**
688 * Check imagecreatetruecolor to verify gdlib works as expected
689 *
690 * @return Status\StatusInterface
691 */
692 protected function checkGdLibTrueColorSupport()
693 {
694 if (function_exists('imagecreatetruecolor')) {
695 $imageResource = @imagecreatetruecolor(50, 100);
696 if (is_resource($imageResource)) {
697 imagedestroy($imageResource);
698 $status = new Status\OkStatus();
699 $status->setTitle('PHP GD library true color works');
700 } else {
701 $status = new Status\ErrorStatus();
702 $status->setTitle('PHP GD library true color support broken');
703 $status->setMessage(
704 'GD is loaded, but calling imagecreatetruecolor() fails.' .
705 ' This must be fixed, TYPO3 CMS won\'t work well otherwise.'
706 );
707 }
708 } else {
709 $status = new Status\ErrorStatus();
710 $status->setTitle('PHP GD library true color support missing');
711 $status->setMessage(
712 'Gdlib is essential for TYPO3 CMS to work properly.'
713 );
714 }
715 return $status;
716 }
717
718 /**
719 * Check gif support of GD library
720 *
721 * @return Status\StatusInterface
722 */
723 protected function checkGdLibGifSupport()
724 {
725 if (function_exists('imagecreatefromgif')
726 && function_exists('imagegif')
727 && (imagetypes() & IMG_GIF)
728 ) {
729 // See http://stackoverflow.com/a/13139830
730 $imageResource = @imagecreatefromgif('data://image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7');
731 if (is_resource($imageResource)) {
732 imagedestroy($imageResource);
733 $status = new Status\OkStatus();
734 $status->setTitle('PHP GD library has gif support');
735 } else {
736 $status = new Status\ErrorStatus();
737 $status->setTitle('PHP GD library gif support broken');
738 $status->setMessage(
739 'GD is loaded, but calling imagecreatefromgif() fails.' .
740 ' This must be fixed, TYPO3 CMS won\'t work well otherwise.'
741 );
742 }
743 } else {
744 $status = new Status\ErrorStatus();
745 $status->setTitle('PHP GD library gif support missing');
746 $status->setMessage(
747 'GD must be compiled with gif support. This is essential for' .
748 ' TYPO3 CMS to work properly.'
749 );
750 }
751 return $status;
752 }
753
754 /**
755 * Check jgp support of GD library
756 *
757 * @return Status\StatusInterface
758 */
759 protected function checkGdLibJpgSupport()
760 {
761 if (function_exists('imagecreatefromjpeg')
762 && function_exists('imagejpeg')
763 && (imagetypes() & IMG_JPG)
764 ) {
765 $status = new Status\OkStatus();
766 $status->setTitle('PHP GD library has jpg support');
767 } else {
768 $status = new Status\ErrorStatus();
769 $status->setTitle('PHP GD library jpg support missing');
770 $status->setMessage(
771 'GD must be compiled with jpg support. This is essential for' .
772 ' TYPO3 CMS to work properly.'
773 );
774 }
775 return $status;
776 }
777
778 /**
779 * Check png support of GD library
780 *
781 * @return Status\StatusInterface
782 */
783 protected function checkGdLibPngSupport()
784 {
785 if (function_exists('imagecreatefrompng')
786 && function_exists('imagepng')
787 && (imagetypes() & IMG_PNG)
788 ) {
789 $imageResource = @imagecreatefrompng('data://image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQMAAAAl21bKAAAAA1BMVEUAAACnej3aAAAAAXRSTlMAQObYZgAAAApJREFUCNdjYAAAAAIAAeIhvDMAAAAASUVORK5CYII');
790 if (is_resource($imageResource)) {
791 imagedestroy($imageResource);
792 $status = new Status\OkStatus();
793 $status->setTitle('PHP GD library has png support');
794 } else {
795 $status = new Status\ErrorStatus();
796 $status->setTitle('PHP GD library png support broken');
797 $status->setMessage(
798 'GD is compiled with png support, but calling imagecreatefrompng() fails.' .
799 ' Check your environment and fix it, png in GD lib is important' .
800 ' for TYPO3 CMS to work properly.'
801 );
802 }
803 } else {
804 $status = new Status\ErrorStatus();
805 $status->setTitle('PHP GD library png support missing');
806 $status->setMessage(
807 'GD must be compiled with png support. This is essential for' .
808 ' TYPO3 CMS to work properly'
809 );
810 }
811 return $status;
812 }
813
814 /**
815 * Check gdlib supports freetype
816 *
817 * @return Status\StatusInterface
818 */
819 protected function checkGdLibFreeTypeSupport()
820 {
821 if (function_exists('imagettftext')) {
822 $status = new Status\OkStatus();
823 $status->setTitle('PHP GD library has freetype font support');
824 $status->setMessage(
825 'There is a difference between the font size setting which the GD' .
826 ' library should be supplied with. If installation is completed' .
827 ' a test in the install tool helps to find out the value you need.'
828 );
829 } else {
830 $status = new Status\ErrorStatus();
831 $status->setTitle('PHP GD library freetype support missing');
832 $status->setMessage(
833 'Some core functionality and extension rely on the GD' .
834 ' to render fonts on images. This support is missing' .
835 ' in your environment. Install it.'
836 );
837 }
838 return $status;
839 }
840
841 /**
842 * Helper methods
843 */
844
845 /**
846 * Validate a given IP address.
847 *
848 * @param string $ip IP address to be tested
849 * @return bool
850 */
851 protected function isValidIp($ip)
852 {
853 return filter_var($ip, FILTER_VALIDATE_IP) !== false;
854 }
855
856 /**
857 * Test if this instance runs on windows OS
858 *
859 * @return bool TRUE if operating system is windows
860 */
861 protected function isWindowsOs()
862 {
863 $windowsOs = false;
864 if (!stristr(PHP_OS, 'darwin') && stristr(PHP_OS, 'win')) {
865 $windowsOs = true;
866 }
867 return $windowsOs;
868 }
869
870 /**
871 * Helper method to explode a string by delimiter and throw away empty values.
872 * Removes empty values from result array.
873 *
874 * @param string $delimiter Delimiter string to explode with
875 * @param string $string The string to explode
876 * @return array Exploded values
877 */
878 protected function trimExplode($delimiter, $string)
879 {
880 $explodedValues = explode($delimiter, $string);
881 $resultWithPossibleEmptyValues = array_map('trim', $explodedValues);
882 $result = [];
883 foreach ($resultWithPossibleEmptyValues as $value) {
884 if ($value !== '') {
885 $result[] = $value;
886 }
887 }
888 return $result;
889 }
890
891 /**
892 * Helper method to get the bytes value from a measurement string like "100k".
893 *
894 * @param string $measurement The measurement (e.g. "100k")
895 * @return int The bytes value (e.g. 102400)
896 */
897 protected function getBytesFromSizeMeasurement($measurement)
898 {
899 $bytes = (float)$measurement;
900 if (stripos($measurement, 'G')) {
901 $bytes *= 1024 * 1024 * 1024;
902 } elseif (stripos($measurement, 'M')) {
903 $bytes *= 1024 * 1024;
904 } elseif (stripos($measurement, 'K')) {
905 $bytes *= 1024;
906 }
907 return (int)$bytes;
908 }
909 }