*/ class Check { /** * @var array List of required PHP extensions */ protected $requiredPhpExtensions = array( 'fileinfo', 'filter', 'gd', 'hash', 'json', 'mysqli', 'openssl', 'pcre', 'session', 'soap', 'SPL', 'standard', 'xml', 'zip', 'zlib', ); /** * Get all status information as array with status objects * * @return array<\TYPO3\CMS\Install\Status\StatusInterface> */ public function getStatus() { $statusArray = array(); $statusArray[] = $this->checkCurrentDirectoryIsInIncludePath(); $statusArray[] = $this->checkFileUploadEnabled(); $statusArray[] = $this->checkMaximumFileUploadSize(); $statusArray[] = $this->checkPostUploadSizeIsHigherOrEqualMaximumFileUploadSize(); $statusArray[] = $this->checkMemorySettings(); $statusArray[] = $this->checkPhpVersion(); $statusArray[] = $this->checkMaxExecutionTime(); $statusArray[] = $this->checkDisableFunctions(); $statusArray[] = $this->checkDownloadsPossible(); $statusArray[] = $this->checkSafeMode(); $statusArray[] = $this->checkDocRoot(); $statusArray[] = $this->checkOpenBaseDir(); $statusArray[] = $this->checkXdebugMaxNestingLevel(); $statusArray[] = $this->checkOpenSslInstalled(); $statusArray[] = $this->checkSuhosinLoaded(); $statusArray[] = $this->checkSuhosinRequestMaxVars(); $statusArray[] = $this->checkSuhosinRequestMaxVarnameLength(); $statusArray[] = $this->checkSuhosinPostMaxNameLength(); $statusArray[] = $this->checkSuhosinPostMaxVars(); $statusArray[] = $this->checkSuhosinGetMaxNameLength(); $statusArray[] = $this->checkSuhosinGetMaxValueLength(); $statusArray[] = $this->checkSuhosinExecutorIncludeWhitelistContainsPhar(); $statusArray[] = $this->checkSuhosinExecutorIncludeWhitelistContainsVfs(); $statusArray[] = $this->checkSomePhpOpcodeCacheIsLoaded(); $statusArray[] = $this->checkReflectionDocComment(); $statusArray[] = $this->checkSystemLocale(); $statusArray[] = $this->checkLocaleWithUTF8filesystem(); $statusArray[] = $this->checkWindowsApacheThreadStackSize(); foreach ($this->requiredPhpExtensions as $extension) { $statusArray[] = $this->checkRequiredPhpExtension($extension); } $statusArray[] = $this->checkGdLibTrueColorSupport(); $statusArray[] = $this->checkGdLibGifSupport(); $statusArray[] = $this->checkGdLibJpgSupport(); $statusArray[] = $this->checkGdLibPngSupport(); $statusArray[] = $this->checkGdLibFreeTypeSupport(); $statusArray[] = $this->checkPhpMagicQuotes(); $statusArray[] = $this->checkRegisterGlobals(); $statusArray[] = $this->checkLibXmlBug(); $statusArray[] = $this->isTrueTypeFontDpiStandard(); return $statusArray; } /** * Checks if current directory (.) is in PHP include path * * @return Status\StatusInterface */ protected function checkCurrentDirectoryIsInIncludePath() { $includePath = ini_get('include_path'); $delimiter = $this->isWindowsOs() ? ';' : ':'; $pathArray = $this->trimExplode($delimiter, $includePath); if (!in_array('.', $pathArray)) { $status = new Status\WarningStatus(); $status->setTitle('Current directory (./) is not within PHP include path'); $status->setMessage( 'include_path = ' . implode(' ', $pathArray) . LF . 'Normally the current path \'.\' is included in the' . ' include_path of PHP. Although TYPO3 does not rely on this,' . ' it is an unusual setting that may introduce problems for' . ' some extensions.' ); } else { $status = new Status\OkStatus(); $status->setTitle('Current directory (./) is within PHP include path.'); } return $status; } /** * Check if file uploads are enabled in PHP * * @return Status\StatusInterface */ protected function checkFileUploadEnabled() { if (!ini_get('file_uploads')) { $status = new Status\ErrorStatus(); $status->setTitle('File uploads not allowed in PHP'); $status->setMessage( 'file_uploads=' . ini_get('file_uploads') . LF . 'TYPO3 uses the ability to upload files from the browser in various cases.' . ' If this flag is disabled in PHP, you won\'t be able to upload files.' . ' But it doesn\'t end here, because not only are files not accepted by' . ' the server - ALL content in the forms are discarded and therefore' . ' nothing at all will be editable if you don\'t set this flag!' . ' However if you cannot enable fileupload for some reason in PHP, alternatively' . ' change the default form encoding value with \\$TYPO3_CONF_VARS[SYS][form_enctype].' ); } else { $status = new Status\OkStatus(); $status->setTitle('File uploads allowed in PHP'); } return $status; } /** * Check maximum file upload size against default value of 10MB * * @return Status\StatusInterface */ protected function checkMaximumFileUploadSize() { $maximumUploadFilesize = $this->getBytesFromSizeMeasurement(ini_get('upload_max_filesize')); $configuredMaximumUploadFilesize = 1024 * (int)$GLOBALS['TYPO3_CONF_VARS']['BE']['maxFileSize']; if ($maximumUploadFilesize < $configuredMaximumUploadFilesize) { $status = new Status\ErrorStatus(); $status->setTitle('PHP Maximum upload filesize too small'); $status->setMessage( 'PHP upload_max_filesize = ' . (int)($maximumUploadFilesize / 1024) . ' KB' . LF . 'TYPO3_CONF_VARS[BE][maxFileSize] = ' . (int)($configuredMaximumUploadFilesize / 1024) . ' KB' . LF . LF . 'Currently PHP determines the limits for uploaded file\'s sizes and not TYPO3.' . ' It is recommended that the value of upload_max_filesize is at least equal to the value' . ' of TYPO3_CONF_VARS[BE][maxFileSize].' ); } else { $status = new Status\OkStatus(); $status->setTitle('PHP Maximum file upload size is higher than or equal to [BE][maxFileSize]'); } return $status; } /** * Check maximum post upload size correlates with maximum file upload * * @return Status\StatusInterface */ protected function checkPostUploadSizeIsHigherOrEqualMaximumFileUploadSize() { $maximumUploadFilesize = $this->getBytesFromSizeMeasurement(ini_get('upload_max_filesize')); $maximumPostSize = $this->getBytesFromSizeMeasurement(ini_get('post_max_size')); if ($maximumPostSize < $maximumUploadFilesize) { $status = new Status\ErrorStatus(); $status->setTitle('Maximum size for POST requests is smaller than maximum upload filesize in PHP'); $status->setMessage( 'upload_max_filesize=' . ini_get('upload_max_filesize') . LF . 'post_max_size=' . ini_get('post_max_size') . LF . 'You have defined a maximum size for file uploads in PHP which' . ' exceeds the allowed size for POST requests. Therefore the' . ' file uploads can also not be larger than ' . ini_get('post_max_size') . '.' ); } else { $status = new Status\OkStatus(); $status->setTitle('Maximum post upload size correlates with maximum upload file size in PHP'); } return $status; } /** * Check memory settings * * @return Status\StatusInterface */ protected function checkMemorySettings() { $minimumMemoryLimit = 32; $recommendedMemoryLimit = 64; $memoryLimit = $this->getBytesFromSizeMeasurement(ini_get('memory_limit')); if ($memoryLimit <= 0) { $status = new Status\WarningStatus(); $status->setTitle('Unlimited memory limit for PHP'); $status->setMessage( 'PHP is configured not to limit memory usage at all. This is a risk' . ' and should be avoided in production setup. In general it\'s best practice to limit this.' . ' To be safe, set a limit in PHP, but with a minimum of ' . $recommendedMemoryLimit . 'MB:' . LF . 'memory_limit=' . $recommendedMemoryLimit . 'M' ); } elseif ($memoryLimit < 1024 * 1024 * $minimumMemoryLimit) { $status = new Status\ErrorStatus(); $status->setTitle('PHP Memory limit below ' . $minimumMemoryLimit . 'MB'); $status->setMessage( 'memory_limit=' . ini_get('memory_limit') . LF . 'Your system is configured to enforce a memory limit for PHP scripts lower than ' . $minimumMemoryLimit . 'MB. It is required to raise the limit.' . ' We recommend a minimum PHP memory limit of ' . $recommendedMemoryLimit . 'MB:' . LF . 'memory_limit=' . $recommendedMemoryLimit . 'M' ); } elseif ($memoryLimit < 1024 * 1024 * $recommendedMemoryLimit) { $status = new Status\WarningStatus(); $status->setTitle('PHP Memory limit below ' . $recommendedMemoryLimit . 'MB'); $status->setMessage( 'memory_limit=' . ini_get('memory_limit') . LF . 'Your system is configured to enforce a memory limit for PHP scripts lower than ' . $recommendedMemoryLimit . 'MB.' . ' A slim TYPO3 instance without many extensions will probably work, but you should monitor your' . ' system for "allowed memory size of X bytes exhausted" messages, especially if using the backend.' . ' To be on the safe side,' . ' we recommend a minimum PHP memory limit of ' . $recommendedMemoryLimit . 'MB:' . LF . 'memory_limit=' . $recommendedMemoryLimit . 'M' ); } else { $status = new Status\OkStatus(); $status->setTitle('PHP Memory limit is equal to or more than ' . $recommendedMemoryLimit . 'MB'); } return $status; } /** * Check minimum PHP version * * @return Status\StatusInterface */ protected function checkPhpVersion() { $minimumPhpVersion = '5.3.7'; $currentPhpVersion = phpversion(); if (version_compare($currentPhpVersion, $minimumPhpVersion) < 0) { $status = new Status\ErrorStatus(); $status->setTitle('PHP version too low'); $status->setMessage( 'Your PHP version ' . $currentPhpVersion . ' is too old. TYPO3 CMS does not run' . ' with this version. Update to at least PHP ' . $minimumPhpVersion ); } else { $status = new Status\OkStatus(); $status->setTitle('PHP version is fine'); } return $status; } /** * Check maximum execution time * * @return Status\StatusInterface */ protected function checkMaxExecutionTime() { $minimumMaximumExecutionTime = 30; $recommendedMaximumExecutionTime = 240; $currentMaximumExecutionTime = ini_get('max_execution_time'); if ($currentMaximumExecutionTime == 0) { if (PHP_SAPI === 'cli') { $status = new Status\OkStatus(); $status->setTitle('Infinite PHP script execution time'); $status->setMessage( 'Maximum PHP script execution time is always set to infinite (0) in cli mode.' . ' The setting used for web requests cannot be checked from command line.' ); } else { $status = new Status\WarningStatus(); $status->setTitle('Infinite PHP script execution time'); $status->setMessage( 'max_execution_time=' . $currentMaximumExecutionTime . LF . 'While TYPO3 is fine with this, you risk a denial-of-service for your system if for whatever' . ' reason some script hangs in an infinite loop. You are usually on the safe side ' . ' if it is reduced to ' . $recommendedMaximumExecutionTime . ' seconds:' . LF . 'max_execution_time=' . $recommendedMaximumExecutionTime ); } } elseif ($currentMaximumExecutionTime < $minimumMaximumExecutionTime) { $status = new Status\ErrorStatus(); $status->setTitle('Low PHP script execution time'); $status->setMessage( 'max_execution_time=' . $currentMaximumExecutionTime . LF . 'Your max_execution_time is too low. Some expensive operations in TYPO3 can take longer than that.' . ' It is recommended to raise the limit to ' . $recommendedMaximumExecutionTime . ' seconds:' . LF . 'max_execution_time=' . $recommendedMaximumExecutionTime ); } elseif ($currentMaximumExecutionTime < $recommendedMaximumExecutionTime) { $status = new Status\WarningStatus(); $status->setTitle('Low PHP script execution time'); $status->setMessage( 'max_execution_time=' . $currentMaximumExecutionTime . LF . 'Your max_execution_time is low. While TYPO3 often runs without problems' . ' with ' . $minimumMaximumExecutionTime . ' seconds,' . ' it may still happen that script execution is stopped before finishing' . ' calculations. You should monitor the system for messages in this area' . ' and maybe raise the limit to ' . $recommendedMaximumExecutionTime . ' seconds:' . LF . 'max_execution_time=' . $recommendedMaximumExecutionTime ); } else { $status = new Status\OkStatus(); $status->setTitle('Maximum PHP script execution time is equal to or more than ' . $recommendedMaximumExecutionTime); } return $status; } /** * Check for disabled functions * * @return Status\StatusInterface */ protected function checkDisableFunctions() { $disabledFunctions = trim(ini_get('disable_functions')); // Filter "disable_functions" $disabledFunctionsArray = $this->trimExplode(',', $disabledFunctions); // Array with strings to find $findStrings = array( // Disabled by default on Ubuntu OS but this is okay since the Core does not use them 'pcntl_', ); foreach ($disabledFunctionsArray as $key => $disabledFunction) { foreach ($findStrings as $findString) { if (strpos($disabledFunction, $findString) !== FALSE) { unset($disabledFunctionsArray[$key]); } } } if (strlen($disabledFunctions) > 0 && count($disabledFunctionsArray) > 0) { $status = new Status\ErrorStatus(); $status->setTitle('Some PHP functions disabled'); $status->setMessage( 'disable_functions=' . implode(' ', explode(',', $disabledFunctions)) . LF . 'These function(s) are disabled. TYPO3 uses some of those, so there might be trouble.' . ' TYPO3 is designed to use the default set of PHP functions plus some common extensions.' . ' Possibly these functions are disabled' . ' due to security considerations and most likely the list would include a function like' . ' exec() which is used by TYPO3 at various places. Depending on which exact functions' . ' are disabled, some parts of the system may just break without further notice.' ); } elseif (strlen($disabledFunctions) > 0 && count($disabledFunctionsArray) === 0) { $status = new Status\NoticeStatus(); $status->setTitle('Some PHP functions currently disabled but OK'); $status->setMessage( 'disable_functions=' . implode(' ', explode(',', $disabledFunctions)) . LF . 'These function(s) are disabled. TYPO3 uses currently none of those, so you are good to go.' ); } else { $status = new Status\OkStatus(); $status->setTitle('No disabled PHP functions'); } return $status; } /** * Check if it is possible to download external data (e.g. TER) * Either allow_url_fopen must be enabled or curl must be used * * @return Status\OkStatus|Status\WarningStatus */ protected function checkDownloadsPossible() { $allowUrlFopen = (bool)ini_get('allow_url_fopen'); $curlEnabled = !empty($GLOBALS['TYPO3_CONF_VARS']['SYS']['curlUse']); if ($allowUrlFopen || $curlEnabled) { $status = new Status\OkStatus(); $status->setTitle('Fetching external URLs is allowed'); } else { $status = new Status\WarningStatus(); $status->setTitle('Fetching external URLs is not allowed'); $status->setMessage( 'Either enable PHP runtime setting "allow_url_fopen"' . LF . 'or enable curl by setting [SYS][curlUse] accordingly.' ); } return $status; } /** * Check if safe mode is enabled * * @return Status\StatusInterface */ protected function checkSafeMode() { $safeModeEnabled = FALSE; if (version_compare(phpversion(), '5.4', '<')) { $safeModeEnabled = filter_var( ini_get('safe_mode'), FILTER_VALIDATE_BOOLEAN, array(FILTER_REQUIRE_SCALAR, FILTER_NULL_ON_FAILURE) ); } if ($safeModeEnabled) { $status = new Status\ErrorStatus(); $status->setTitle('PHP safe mode on'); $status->setMessage( 'PHP safe_mode enabled. This is unsupported by TYPO3 CMS, it must be turned off:' . LF . 'safe_mode=Off' ); } else { $status = new Status\OkStatus(); $status->setTitle('PHP safe mode off'); } return $status; } /** * Check for doc_root ini setting * * @return Status\StatusInterface */ protected function checkDocRoot() { $docRootSetting = trim(ini_get('doc_root')); if (strlen($docRootSetting) > 0) { $status = new Status\NoticeStatus(); $status->setTitle('doc_root is set'); $status->setMessage( 'doc_root=' . $docRootSetting . LF . 'PHP cannot execute scripts' . ' outside this directory. This setting is seldom used and must correlate' . ' with your actual document root. You might be in trouble if your' . ' TYPO3 CMS core code is linked to some different location.' . ' If that is a problem, the setting must be changed.' ); } else { $status = new Status\OkStatus(); $status->setTitle('PHP doc_root is not set'); } return $status; } /** * Check open_basedir * * @return Status\StatusInterface */ protected function checkOpenBaseDir() { $openBaseDirSetting = trim(ini_get('open_basedir')); if (strlen($openBaseDirSetting) > 0) { $status = new Status\NoticeStatus(); $status->setTitle('PHP open_basedir is set'); $status->setMessage( 'open_basedir = ' . ini_get('open_basedir') . LF . 'This restricts TYPO3 to open and include files only in this' . ' path. Please make sure that this does not prevent TYPO3 from running,' . ' if for example your TYPO3 CMS core is linked to a different directory' . ' not included in this path.' ); } else { $status = new Status\OkStatus(); $status->setTitle('PHP open_basedir is off'); } return $status; } /** * If xdebug is loaded, the default max_nesting_level of 100 must be raised * * @return Status\StatusInterface */ protected function checkXdebugMaxNestingLevel() { if (extension_loaded('xdebug')) { $recommendedMaxNestingLevel = 400; $currentMaxNestingLevel = ini_get('xdebug.max_nesting_level'); if ($currentMaxNestingLevel < $recommendedMaxNestingLevel) { $status = new Status\ErrorStatus(); $status->setTitle('PHP xdebug.max_nesting_level too low'); $status->setMessage( 'xdebug.max_nesting_level=' . $currentMaxNestingLevel . LF . 'This setting controls the maximum number of nested function calls to protect against' . ' infinite recursion. The current value is too low for TYPO3 CMS and must' . ' be either raised or xdebug unloaded. A value of ' . $recommendedMaxNestingLevel . ' is recommended. Warning: Expect fatal PHP errors in central parts of the CMS' . ' if the value is not raised significantly to:' . LF . 'xdebug.max_nesting_level=' . $recommendedMaxNestingLevel ); } else { $status = new Status\OkStatus(); $status->setTitle('PHP xdebug.max_nesting_level ok'); } } else { $status = new Status\OkStatus(); $status->setTitle('PHP xdebug extension not loaded'); } return $status; } /** * Check accessibility and functionality of OpenSSL * * @return Status\StatusInterface */ protected function checkOpenSslInstalled() { if (extension_loaded('openssl')) { $testKey = @openssl_pkey_new(); if (is_resource($testKey)) { openssl_free_key($testKey); $status = new Status\OkStatus(); $status->setTitle('PHP OpenSSL extension installed properly'); } else { $status = new Status\ErrorStatus(); $status->setTitle('PHP OpenSSL extension not working'); $status->setMessage( 'Something went wrong while trying to create a new private key for testing.' . ' Please check the integration of the PHP OpenSSL extension and if it is installed correctly.' ); } } else { $status = new Status\ErrorStatus(); $status->setTitle('PHP OpenSSL extension not loaded'); $status->setMessage( 'OpenSSL is a PHP extension to encrypt/decrypt data between requests.' . ' TYPO3 CMS requires it to be able to encrypt stored passwords to improve the security in the' . ' database layer.' ); } return $status; } /** * Check enabled suhosin * * @return Status\StatusInterface */ protected function checkSuhosinLoaded() { if ($this->isSuhosinLoaded()) { $status = new Status\OkStatus(); $status->setTitle('PHP suhosin extension loaded'); } else { $status = new Status\NoticeStatus(); $status->setTitle('PHP suhosin extension not loaded'); $status->setMessage( 'suhosin is an extension to harden the PHP environment. In general, it is' . ' good to have it from a security point of view. While TYPO3 CMS works' . ' fine with suhosin, it has some requirements different from the default settings' . ' to be set if enabled.' ); } return $status; } /** * Check suhosin.request.max_vars * * @return Status\StatusInterface */ protected function checkSuhosinRequestMaxVars() { $recommendedRequestMaxVars = 400; if ($this->isSuhosinLoaded()) { $currentRequestMaxVars = ini_get('suhosin.request.max_vars'); if ($currentRequestMaxVars < $recommendedRequestMaxVars) { $status = new Status\ErrorStatus(); $status->setTitle('PHP suhosin.request.max_vars too low'); $status->setMessage( 'suhosin.request.max_vars=' . $currentRequestMaxVars . LF . 'This setting can lead to lost information if submitting forms with lots of data in TYPO3 CMS' . ' (as the install tool does). It is highly recommended to raise this' . ' to at least ' . $recommendedRequestMaxVars . ':' . LF . 'suhosin.request.max_vars=' . $recommendedRequestMaxVars ); } else { $status = new Status\OkStatus(); $status->setTitle('PHP suhosin.request.max_vars ok'); } } else { $status = new Status\InfoStatus(); $status->setTitle('Suhosin not loaded'); $status->setMessage( 'If enabling suhosin, suhosin.request.max_vars' . ' should be set to at least ' . $recommendedRequestMaxVars . ':' . LF . 'suhosin.request.max_vars=' . $recommendedRequestMaxVars ); } return $status; } /** * Check suhosin.request.max_varname_length * * @return Status\StatusInterface */ protected function checkSuhosinRequestMaxVarnameLength() { $recommendedRequestMaxVarnameLength = 200; if ($this->isSuhosinLoaded()) { $currentRequestMaxVarnameLength = ini_get('suhosin.request.max_varname_length'); if ($currentRequestMaxVarnameLength < $recommendedRequestMaxVarnameLength) { $status = new Status\ErrorStatus(); $status->setTitle('PHP suhosin.request.max_varname_length too low'); $status->setMessage( 'suhosin.request.max_varname_length=' . $currentRequestMaxVarnameLength . LF . 'This setting can lead to lost information if submitting forms with lots of data in TYPO3 CMS' . ' (as the install tool does). It is highly recommended to raise this' . ' to at least ' . $recommendedRequestMaxVarnameLength . ':' . LF . 'suhosin.request.max_varname_length=' . $recommendedRequestMaxVarnameLength ); } else { $status = new Status\OkStatus(); $status->setTitle('PHP suhosin.request.max_varname_length ok'); } } else { $status = new Status\InfoStatus(); $status->setTitle('Suhosin not loaded'); $status->setMessage( 'If enabling suhosin, suhosin.request.max_varname_length' . ' should be set to at least ' . $recommendedRequestMaxVarnameLength . ':' . LF . 'suhosin.request.max_varname_length=' . $recommendedRequestMaxVarnameLength ); } return $status; } /** * Check suhosin.post.max_name_length * * @return Status\StatusInterface */ protected function checkSuhosinPostMaxNameLength() { $recommendedPostMaxNameLength = 200; if ($this->isSuhosinLoaded()) { $currentPostMaxNameLength = ini_get('suhosin.post.max_name_length'); if ($currentPostMaxNameLength < $recommendedPostMaxNameLength) { $status = new Status\ErrorStatus(); $status->setTitle('PHP suhosin.post.max_name_length too low'); $status->setMessage( 'suhosin.post.max_name_length=' . $currentPostMaxNameLength . LF . 'This setting can lead to lost information if submitting forms with lots of data in TYPO3 CMS' . ' (as the install tool does). It is highly recommended to raise this' . ' to at least ' . $recommendedPostMaxNameLength . ':' . LF . 'suhosin.post.max_name_length=' . $recommendedPostMaxNameLength ); } else { $status = new Status\OkStatus(); $status->setTitle('PHP suhosin.post.max_name_length ok'); } } else { $status = new Status\InfoStatus(); $status->setTitle('Suhosin not loaded'); $status->setMessage( 'If enabling suhosin, suhosin.post.max_name_length' . ' should be set to at least ' . $recommendedPostMaxNameLength . ':' . LF . 'suhosin.post.max_name_length=' . $recommendedPostMaxNameLength ); } return $status; } /** * Check suhosin.post.max_vars * * @return Status\StatusInterface */ protected function checkSuhosinPostMaxVars() { $recommendedPostMaxVars = 400; if ($this->isSuhosinLoaded()) { $currentPostMaxVars = ini_get('suhosin.post.max_vars'); if ($currentPostMaxVars < $recommendedPostMaxVars) { $status = new Status\ErrorStatus(); $status->setTitle('PHP suhosin.post.max_vars too low'); $status->setMessage( 'suhosin.post.max_vars=' . $currentPostMaxVars . LF . 'This setting can lead to lost information if submitting forms with lots of data in TYPO3 CMS' . ' (as the install tool does). It is highly recommended to raise this' . ' to at least ' . $recommendedPostMaxVars . ':' . LF . 'suhosin.post.max_vars=' . $recommendedPostMaxVars ); } else { $status = new Status\OkStatus(); $status->setTitle('PHP suhosin.post.max_vars ok'); } } else { $status = new Status\InfoStatus(); $status->setTitle('Suhosin not loaded'); $status->setMessage( 'If enabling suhosin, suhosin.post.max_vars' . ' should be set to at least ' . $recommendedPostMaxVars . ':' . LF . 'suhosin.post.max_vars=' . $recommendedPostMaxVars ); } return $status; } /** * Check suhosin.get.max_value_length * * @return Status\StatusInterface */ protected function checkSuhosinGetMaxValueLength() { $recommendedGetMaxValueLength = 2000; if ($this->isSuhosinLoaded()) { $currentGetMaxValueLength = ini_get('suhosin.get.max_value_length'); if ($currentGetMaxValueLength < $recommendedGetMaxValueLength) { $status = new Status\ErrorStatus(); $status->setTitle('PHP suhosin.get.max_value_length too low'); $status->setMessage( 'suhosin.get.max_value_length=' . $currentGetMaxValueLength . LF . 'This setting can lead to lost information if submitting forms with lots of data in TYPO3 CMS' . ' (as the install tool does). It is highly recommended to raise this' . ' to at least ' . $recommendedGetMaxValueLength . ':' . LF . 'suhosin.get.max_value_length=' . $recommendedGetMaxValueLength ); } else { $status = new Status\OkStatus(); $status->setTitle('PHP suhosin.get.max_value_length ok'); } } else { $status = new Status\InfoStatus(); $status->setTitle('Suhosin not loaded'); $status->setMessage( 'If enabling suhosin, suhosin.get.max_value_length' . ' should be set to at least ' . $recommendedGetMaxValueLength . ':' . LF . 'suhosin.get.max_value_length=' . $recommendedGetMaxValueLength ); } return $status; } /** * Check suhosin.get.max_name_length * * @return Status\StatusInterface */ protected function checkSuhosinGetMaxNameLength() { $recommendedGetMaxNameLength = 200; if ($this->isSuhosinLoaded()) { $currentGetMaxNameLength = ini_get('suhosin.get.max_name_length'); if ($currentGetMaxNameLength < $recommendedGetMaxNameLength) { $status = new Status\ErrorStatus(); $status->setTitle('PHP suhosin.get.max_name_length too low'); $status->setMessage( 'suhosin.get.max_name_length=' . $currentGetMaxNameLength . LF . 'This setting can lead to lost information if submitting forms with lots of data in TYPO3 CMS' . ' (as the install tool does). It is highly recommended to raise this' . ' to at least ' . $recommendedGetMaxNameLength . ':' . LF . 'suhosin.get.max_name_length=' . $recommendedGetMaxNameLength ); } else { $status = new Status\OkStatus(); $status->setTitle('PHP suhosin.get.max_name_length ok'); } } else { $status = new Status\InfoStatus(); $status->setTitle('Suhosin not loaded'); $status->setMessage( 'If enabling suhosin, suhosin.get.max_name_length' . ' should be set to at least ' . $recommendedGetMaxNameLength . ':' . LF . 'suhosin.get.max_name_length=' . $recommendedGetMaxNameLength ); } return $status; } /** * Check suhosin.executor.include.whitelist contains phar * * @return Status\StatusInterface */ protected function checkSuhosinExecutorIncludeWhiteListContainsPhar() { if ($this->isSuhosinLoaded()) { $whitelist = (string)ini_get('suhosin.executor.include.whitelist'); if (strpos($whitelist, 'phar') === FALSE) { $status = new Status\NoticeStatus(); $status->setTitle('PHP suhosin.executor.include.whitelist does not contain phar'); $status->setMessage( 'suhosin.executor.include.whitelist= ' . $whitelist . LF . '"phar" is currently not a hard requirement of TYPO3 CMS but is nice to have and a possible' . ' requirement in future versions. A useful setting is:' . LF . 'suhosin.executor.include.whitelist=phar,vfs' ); } else { $status = new Status\OkStatus(); $status->setTitle('PHP suhosin.executor.include.whitelist contains phar'); } } else { $status = new Status\InfoStatus(); $status->setTitle('Suhosin not loaded'); $status->setMessage( 'If enabling suhosin, a useful setting is:' . LF . 'suhosin.executor.include.whitelist=phar,vfs' ); } return $status; } /** * Check suhosin.executor.include.whitelist contains vfs * * @return Status\StatusInterface */ protected function checkSuhosinExecutorIncludeWhiteListContainsVfs() { if ($this->isSuhosinLoaded()) { $whitelist = (string)ini_get('suhosin.executor.include.whitelist'); if (strpos($whitelist, 'vfs') === FALSE) { $status = new Status\WarningStatus(); $status->setTitle('PHP suhosin.executor.include.whitelist does not contain vfs'); $status->setMessage( 'suhosin.executor.include.whitelist= ' . $whitelist . LF . '"vfs" is currently not a hard requirement of TYPO3 CMS but tons of unit tests rely on it.' . ' Furthermore, vfs will likely be a base for an additional compatibility layer in the future.' . ' A useful setting is:' . LF . 'suhosin.executor.include.whitelist=phar,vfs' ); } else { $status = new Status\OkStatus(); $status->setTitle('PHP suhosin.executor.include.whitelist contains vfs'); } } else { $status = new Status\InfoStatus(); $status->setTitle('Suhosin not loaded'); $status->setMessage( 'If enabling suhosin, a useful setting is:' . LF . 'suhosin.executor.include.whitelist=phar,vfs' ); } return $status; } /** * Check if some opcode cache is loaded * * @return Status\StatusInterface */ protected function checkSomePhpOpcodeCacheIsLoaded() { // Link to our wiki page, so we can update opcode cache issue information independent of TYPO3 CMS releases. $wikiLink = 'For more information take a look in our wiki ' . TYPO3_URL_WIKI_OPCODECACHE . '.'; $opcodeCaches = \TYPO3\CMS\Core\Utility\OpcodeCacheUtility::getAllActive(); if (count($opcodeCaches) === 0) { // Set status to notice. It needs to be notice so email won't be triggered. $status = new Status\NoticeStatus(); $status->setTitle('No PHP opcode cache loaded'); $status->setMessage( 'PHP opcode caches hold a compiled version of executed PHP scripts in' . ' memory and do not require to recompile a script each time it is accessed.' . ' This can be a massive performance improvement and can reduce the load on a' . ' server in general. A parse time reduction by factor three for fully cached' . ' pages can be achieved easily if using an opcode cache.' . LF . $wikiLink ); } else { $status = new Status\OkStatus(); $message = ''; foreach ($opcodeCaches as $opcodeCache => $properties) { $message .= 'Name: ' . $opcodeCache . ' Version: ' . $properties['version']; $message .= LF; if ($properties['error']) { // Set status to error if not already set if ($status->getSeverity() !== 'error') { $status = new Status\ErrorStatus(); } $message .= ' This opcode cache is marked as malfunctioning by the TYPO3 CMS Team.'; } elseif ($properties['canInvalidate']) { $message .= ' This opcode cache should work correctly and has good performance.'; } else { // Set status to notice if not already error set. It needs to be notice so email won't be triggered. if ($status->getSeverity() !== 'error' || $status->getSeverity() !== 'warning') { $status = new Status\NoticeStatus(); } $message .= ' This opcode cache may work correctly but has medium performance.'; } $message .= LF; } $message .= $wikiLink; // Set title of status depending on serverity switch ($status->getSeverity()) { case 'error': $status->setTitle('A possibly malfunctioning PHP opcode cache is loaded'); break; case 'warning': $status->setTitle('A PHP opcode cache is loaded which may cause problems'); break; case 'ok': default: $status->setTitle('A PHP opcode cache is loaded'); break; } $status->setMessage($message); } return $status; } /** * Check doc comments can be fetched by reflection * * @return Status\StatusInterface */ protected function checkReflectionDocComment() { $testReflection = new \ReflectionMethod(get_class($this), __FUNCTION__); if ($testReflection->getDocComment() === FALSE) { $status = new Status\AlertStatus(); $status->setTitle('PHP Doc comment reflection broken'); $status->setMessage( 'TYPO3 CMS core extensions like extbase and fluid heavily rely on method' . ' comment parsing to fetch annotations and add magic belonging to them.' . ' This does not work in the current environment and so we cannot install' . ' TYPO3 CMS.' . LF . ' Here are some possibilities: ' . LF . '* In Zend OPcache you can disable saving/loading comments. If you are using' . ' Zend OPcache (included since PHP 5.5) then check your php.ini settings for' . ' opcache.save_comments and opcache.load_comments and enable them.' . LF . '* In Zend Optimizer+ you can disable saving comments. If you are using' . ' Zend Optimizer+ then check your php.ini settings for' . ' zend_optimizerplus.save_comments and enable it.' . LF . '* The PHP extension eaccelerator is known to break this if' . ' it is compiled without --with-eaccelerator-doc-comment-inclusion flag.' . ' This compile flag must be specified, otherwise TYPO3 CMS will not work.' . LF . 'For more information take a look in our wiki ' . TYPO3_URL_WIKI_OPCODECACHE . '.' ); } else { $status = new Status\OkStatus(); $status->setTitle('PHP Doc comment reflection works'); } return $status; } /** * Check if systemLocale setting is correct (locale exists in the OS) * * @return Status\StatusInterface */ protected function checkSystemLocale() { $currentLocale = setlocale(LC_CTYPE, 0); // On Windows an empty locale value uses the regional settings from the Control Panel if ($GLOBALS['TYPO3_CONF_VARS']['SYS']['systemLocale'] === '' && TYPO3_OS !== 'WIN') { $status = new Status\InfoStatus(); $status->setTitle('Empty systemLocale setting'); $status->setMessage( '$GLOBALS[TYPO3_CONF_VARS][SYS][systemLocale] is not set. This is fine as long as no UTF-8' . ' file system is used.' ); } elseif (setlocale(LC_CTYPE, $GLOBALS['TYPO3_CONF_VARS']['SYS']['systemLocale']) === FALSE) { $status = new Status\ErrorStatus(); $status->setTitle('Incorrect systemLocale setting'); $status->setMessage( 'Current value of the $GLOBALS[TYPO3_CONF_VARS][SYS][systemLocale] is incorrect. A locale with' . ' this name doesn\'t exist in the operating system.' ); setlocale(LC_CTYPE, $currentLocale); } else { $status = new Status\OkStatus(); $status->setTitle('System locale is correct'); } return $status; } /** * Checks whether we can use file names with UTF-8 characters. * Configured system locale must support UTF-8 when UTF8filesystem is set * * @return Status\StatusInterface */ protected function checkLocaleWithUTF8filesystem() { if ($GLOBALS['TYPO3_CONF_VARS']['SYS']['UTF8filesystem']) { // On Windows an empty local value uses the regional settings from the Control Panel if ($GLOBALS['TYPO3_CONF_VARS']['SYS']['systemLocale'] === '' && TYPO3_OS !== 'WIN') { $status = new Status\ErrorStatus(); $status->setTitle('System locale not set on UTF-8 file system'); $status->setMessage( '$GLOBALS[TYPO3_CONF_VARS][SYS][UTF8filesystem] is set, but $GLOBALS[TYPO3_CONF_VARS][SYS][systemLocale]' . ' is empty. Make sure a valid locale which supports UTF-8 is set.' ); } else { $testString = 'ÖöĄĆŻĘĆćążąęó.jpg'; $currentLocale = setlocale(LC_CTYPE, 0); $quote = TYPO3_OS === 'WIN' ? '"' : '\''; setlocale(LC_CTYPE, $GLOBALS['TYPO3_CONF_VARS']['SYS']['systemLocale']); if (escapeshellarg($testString) === $quote . $testString . $quote) { $status = new Status\OkStatus(); $status->setTitle('File names with UTF-8 characters can be used.'); } else { $status = new Status\ErrorStatus(); $status->setTitle('System locale setting doesn\'t support UTF-8 file names.'); $status->setMessage( 'Please check your $GLOBALS[TYPO3_CONF_VARS][SYS][systemLocale] setting.' ); } setlocale(LC_CTYPE, $currentLocale); } } else { $status = new Status\OkStatus(); $status->setTitle('Skipping test, as UTF8filesystem is not enabled.'); } return $status; } /** * Checks thread stack size if on windows with apache * * @return Status\StatusInterface */ protected function checkWindowsApacheThreadStackSize() { if ( $this->isWindowsOs() && substr($_SERVER['SERVER_SOFTWARE'], 0, 6) === 'Apache' ) { $status = new Status\WarningStatus(); $status->setTitle('Windows apache thread stack size'); $status->setMessage( 'This current value cannot be checked by the system, so please ignore this warning if it' . ' is already taken care of: Fluid uses complex regular expressions which require a lot' . ' of stack space during the first processing.' . ' On Windows the default stack size for Apache is a lot smaller than on UNIX.' . ' You can increase the size to 8MB (default on UNIX) by adding the following configuration' . ' to httpd.conf and restarting Apache afterwards:' . LF . '' . LF . 'ThreadStackSize 8388608' . LF . '' ); } else { $status = new Status\OkStatus(); $status->setTitle('Apache ThreadStackSize is not an issue on UNIX systems'); } return $status; } /** * Check if a specific required PHP extension is loaded * * @param string $extension * @return Status\StatusInterface */ protected function checkRequiredPhpExtension($extension) { if (!extension_loaded($extension)) { $status = new Status\ErrorStatus(); $status->setTitle('PHP extension ' . $extension . ' not loaded'); $status->setMessage( 'TYPO3 CMS uses PHP extension ' . $extension . ' but it is not loaded' . ' in your environment. Change your environment to provide this extension.' ); } else { $status = new Status\OkStatus(); $status->setTitle('PHP extension ' . $extension . ' loaded'); } return $status; } /** * Check imagecreatetruecolor to verify gdlib works as expected * * @return Status\StatusInterface */ protected function checkGdLibTrueColorSupport() { if (function_exists('imagecreatetruecolor')) { $imageResource = @imagecreatetruecolor(50, 100); if (is_resource($imageResource)) { imagedestroy($imageResource); $status = new Status\OkStatus(); $status->setTitle('PHP GD library true color works'); } else { $status = new Status\ErrorStatus(); $status->setTitle('PHP GD library true color support broken'); $status->setMessage( 'GD is loaded, but calling imagecreatetruecolor() fails.' . ' This must be fixed, TYPO3 CMS won\'t work well otherwise.' ); } } else { $status = new Status\ErrorStatus(); $status->setTitle('PHP GD library true color support missing'); $status->setMessage( 'Gdlib is essential for TYPO3 CMS to work properly.' ); } return $status; } /** * Check gif support of GD library * * @return Status\StatusInterface */ protected function checkGdLibGifSupport() { if ( function_exists('imagecreatefromgif') && function_exists('imagegif') && (imagetypes() & IMG_GIF) ) { $imageResource = @imagecreatefromgif(__DIR__ . '/../../Resources/Public/Images/TestInput/Test.gif'); if (is_resource($imageResource)) { imagedestroy($imageResource); $status = new Status\OkStatus(); $status->setTitle('PHP GD library has gif support'); } else { $status = new Status\ErrorStatus(); $status->setTitle('PHP GD library gif support broken'); $status->setMessage( 'GD is loaded, but calling imagecreatefromgif() fails.' . ' This must be fixed, TYPO3 CMS won\'t work well otherwise.' ); } } else { $status = new Status\ErrorStatus(); $status->setTitle('PHP GD library gif support missing'); $status->setMessage( 'GD must be compiled with gif support. This is essential for' . ' TYPO3 CMS to work properly.' ); } return $status; } /** * Check jgp support of GD library * * @return Status\StatusInterface */ protected function checkGdLibJpgSupport() { if ( function_exists('imagecreatefromjpeg') && function_exists('imagejpeg') && (imagetypes() & IMG_JPG) ) { $status = new Status\OkStatus(); $status->setTitle('PHP GD library has jpg support'); } else { $status = new Status\ErrorStatus(); $status->setTitle('PHP GD library jpg support missing'); $status->setMessage( 'GD must be compiled with jpg support. This is essential for' . ' TYPO3 CMS to work properly.' ); } return $status; } /** * Check png support of GD library * * @return Status\StatusInterface */ protected function checkGdLibPngSupport() { if ( function_exists('imagecreatefrompng') && function_exists('imagepng') && (imagetypes() & IMG_PNG) ) { $imageResource = @imagecreatefrompng(__DIR__ . '/../../Resources/Public/Images/TestInput/Test.png'); if (is_resource($imageResource)) { imagedestroy($imageResource); $status = new Status\OkStatus(); $status->setTitle('PHP GD library has png support'); } else { $status = new Status\ErrorStatus(); $status->setTitle('PHP GD library png support broken'); $status->setMessage( 'GD is compiled with png support, but calling imagecreatefrompng() fails.' . ' Check your environment and fix it, png in GD lib is important' . ' for TYPO3 CMS to work properly.' ); } } else { $status = new Status\ErrorStatus(); $status->setTitle('PHP GD library png support missing'); $status->setMessage( 'GD must be compiled with png support. This is essential for' . ' TYPO3 CMS to work properly' ); } return $status; } /** * Check gdlib supports freetype * * @return Status\StatusInterface */ protected function checkGdLibFreeTypeSupport() { if (function_exists('imagettftext')) { $status = new Status\OkStatus(); $status->setTitle('PHP GD library has freetype font support'); $status->setMessage( 'There is a difference between the font size setting which the GD' . ' library should be supplied with. If installation is completed' . ' a test in the install tool helps to find out the value you need.' ); } else { $status = new Status\ErrorStatus(); $status->setTitle('PHP GD library freetype support missing'); $status->setMessage( 'Some core functionality and extension rely on the GD' . ' to render fonts on images. This support is missing' . ' in your environment. Install it.' ); } return $status; } /** * Create true type font test image * * @return Status\StatusInterface */ protected function isTrueTypeFontDpiStandard() { if (function_exists('imageftbbox')) { // 20 Pixels at 96 DPI - the DefaultConfiguration $fontSize = (20 / 96 * 72); $textDimensions = @imageftbbox( $fontSize, 0, __DIR__ . '/../../Resources/Private/Font/vera.ttf', 'Testing true type support' ); $fontBoxWidth = $textDimensions[2] - $textDimensions[0]; if ($fontBoxWidth < 300 && $fontBoxWidth > 200) { $status = new Status\OkStatus(); $status->setTitle('FreeType True Type Font DPI'); $status->setMessage('Fonts are rendered by FreeType library. ' . 'We need to ensure that the final dimensions are as expected. ' . 'This server renderes fonts based on 96 DPI correctly' ); } else { $status = new Status\NoticeStatus(); $status->setTitle('FreeType True Type Font DPI'); $status->setMessage('Fonts are rendered by FreeType library. ' . 'This server does not render fonts as expected. ' . 'Please configure FreeType or TYPO3_CONF_VARS[GFX][TTFdpi]' ); } } else { $status = new Status\ErrorStatus(); $status->setTitle('PHP GD library freetype2 support missing'); $status->setMessage( 'The core relies on GD library compiled into PHP with freetype2' . ' support. This is missing on your system. Please install it.' ); } return $status; } /** * Check php magic quotes * * @return Status\StatusInterface */ protected function checkPhpMagicQuotes() { $magicQuotesGpc = get_magic_quotes_gpc(); if ($magicQuotesGpc) { $status = new Status\WarningStatus(); $status->setTitle('PHP magic quotes on'); $status->setMessage( 'magic_quotes_gpc=' . $magicQuotesGpc . LF . 'Setting magic_quotes_gpc is deprecated since PHP 5.3.' . ' You are advised to disable it until it is completely removed:' . LF . 'magic_quotes_gpc=Off' ); } else { $status = new Status\OkStatus(); $status->setTitle('PHP magic quotes off'); } return $status; } /** * Check register globals * * @return Status\StatusInterface */ protected function checkRegisterGlobals() { $registerGlobalsEnabled = filter_var( ini_get('register_globals'), FILTER_VALIDATE_BOOLEAN, array(FILTER_REQUIRE_SCALAR, FILTER_NULL_ON_FAILURE) ); if ($registerGlobalsEnabled === TRUE) { $status = new Status\ErrorStatus(); $status->setTitle('PHP register globals on'); $status->setMessage( 'register_globals=' . ini_get('register_globals') . LF . 'TYPO3 requires PHP setting "register_globals" set to off.' . ' This ancient PHP setting is a big security problem and should' . ' never be enabled:' . LF . 'register_globals=Off' ); } else { $status = new Status\OkStatus(); $status->setTitle('PHP register globals off'); } return $status; } /** * Check for bug in libxml * * @return Status\StatusInterface */ protected function checkLibXmlBug() { $sampleArray = array('Test>><setTitle('PHP libxml bug present'); $status->setMessage( 'Some hosts have problems saving ">><<" in a flexform.' . ' To fix this, enable [BE][flexformForceCDATA] in' . ' All Configuration.' ); } else { $status = new Status\OkStatus(); $status->setTitle('PHP libxml bug not present'); } return $status; } /** * Helper methods */ /** * Validate a given IP address. * * @param string $ip IP address to be tested * @return boolean */ protected function isValidIp($ip) { return filter_var($ip, FILTER_VALIDATE_IP) !== FALSE; } /** * Test if this instance runs on windows OS * * @return boolean TRUE if operating system is windows */ protected function isWindowsOs() { $windowsOs = FALSE; if (!stristr(PHP_OS, 'darwin') && stristr(PHP_OS, 'win')) { $windowsOs = TRUE; } return $windowsOs; } /** * Helper method to find out if suhosin extension is loaded * * @return boolean TRUE if suhosin PHP extension is loaded */ protected function isSuhosinLoaded() { $suhosinLoaded = FALSE; if (extension_loaded('suhosin')) { $suhosinLoaded = TRUE; } return $suhosinLoaded; } /** * Helper method to explode a string by delimeter and throw away empty values. * Removes empty values from result array. * * @param string $delimiter Delimiter string to explode with * @param string $string The string to explode * @return array Exploded values */ protected function trimExplode($delimiter, $string) { $explodedValues = explode($delimiter, $string); $resultWithPossibleEmptyValues = array_map('trim', $explodedValues); $result = array(); foreach ($resultWithPossibleEmptyValues as $value) { if ($value !== '') { $result[] = $value; } } return $result; } /** * Helper method to get the bytes value from a measurement string like "100k". * * @param string $measurement The measurement (e.g. "100k") * @return integer The bytes value (e.g. 102400) */ protected function getBytesFromSizeMeasurement($measurement) { $bytes = doubleval($measurement); if (stripos($measurement, 'G')) { $bytes *= 1024 * 1024 * 1024; } elseif (stripos($measurement, 'M')) { $bytes *= 1024 * 1024; } elseif (stripos($measurement, 'K')) { $bytes *= 1024; } return (int)$bytes; } }