‪TYPO3CMS  ‪main
ConfigurationStatus.php
Go to the documentation of this file.
1 <?php
2 
3 /*
4  * This file is part of the TYPO3 CMS project.
5  *
6  * It is free software; you can redistribute it and/or modify it under
7  * the terms of the GNU General Public License, either version 2
8  * of the License, or any later version.
9  *
10  * For the full copyright and license information, please read the
11  * LICENSE.txt file that was distributed with this source code.
12  *
13  * The TYPO3 project - inspiring people to share!
14  */
15 
17 
18 use Doctrine\DBAL\Platforms\MariaDBPlatform as DoctrineMariaDBPlatform;
19 use Doctrine\DBAL\Platforms\MySQLPlatform as DoctrineMySQLPlatform;
21 use TYPO3\CMS\Backend\Utility\BackendUtility;
33 
38 {
44  public function ‪getStatus(): array
45  {
46  $statuses = [
47  'emptyReferenceIndex' => $this->‪getReferenceIndexStatus(),
48  ];
49  if ($this->‪isMemcachedUsed()) {
50  $statuses['memcachedConnection'] = $this->‪getMemcachedConnectionStatus();
51  }
53  $statuses['createdFilesWorldWritable'] = $this->‪getCreatedFilesWorldWritableStatus();
54  $statuses['createdDirectoriesWorldWritable'] = $this->‪getCreatedDirectoriesWorldWritableStatus();
55  }
56  if ($this->‪isMysqlUsed()) {
57  $statuses['mysqlDatabaseUsesUtf8'] = $this->‪getMysqlDatabaseUtf8Status();
58  }
59  return $statuses;
60  }
61 
62  public function ‪getLabel(): string
63  {
64  return 'configuration';
65  }
66 
72  protected function ‪getReferenceIndexStatus()
73  {
74  $value = $this->‪getLanguageService()->sL('LLL:EXT:reports/Resources/Private/Language/locallang_reports.xlf:status_ok');
75  $message = '';
76  $severity = ContextualFeedbackSeverity::OK;
77 
78  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('sys_refindex');
79  $count = $queryBuilder
80  ->count('*')
81  ->from('sys_refindex')
82  ->executeQuery()
83  ->fetchOne();
84 
85  $registry = GeneralUtility::makeInstance(Registry::class);
86  $lastRefIndexUpdate = $registry->get('core', 'sys_refindex_lastUpdate');
87 
88  $uriBuilder = GeneralUtility::makeInstance(UriBuilder::class);
89  if (!$count && $lastRefIndexUpdate) {
90  $value = $this->‪getLanguageService()->sL('LLL:EXT:reports/Resources/Private/Language/locallang_reports.xlf:status_empty');
91  $severity = ContextualFeedbackSeverity::WARNING;
92  ‪$url = (string)$uriBuilder->buildUriFromRoute('system_dbint', ['id' => 0, 'SET' => ['function' => 'refindex']]);
93  $message = sprintf($this->‪getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:warning.backend_reference_index'), '<a href="' . htmlspecialchars(‪$url) . '">', '</a>', BackendUtility::datetime($lastRefIndexUpdate));
94  }
95  return GeneralUtility::makeInstance(ReportStatus::class, $this->‪getLanguageService()->sL('LLL:EXT:reports/Resources/Private/Language/locallang_reports.xlf:status_referenceIndex'), $value, $message, $severity);
96  }
97 
103  protected function ‪isMemcachedUsed()
104  {
105  $memcachedUsed = false;
106  $memcachedServers = $this->‪getConfiguredMemcachedServers();
107  if (!empty($memcachedServers)) {
108  $memcachedUsed = true;
109  }
110  return $memcachedUsed;
111  }
112 
118  protected function ‪getConfiguredMemcachedServers()
119  {
120  $configurations = ‪$GLOBALS['TYPO3_CONF_VARS']['SYS']['caching']['cacheConfigurations'] ?? [];
121  $memcachedServers = [];
122  foreach ($configurations as $table => $conf) {
123  if (is_array($conf)) {
124  foreach ($conf as $key => $value) {
125  if ($value === MemcachedBackend::class) {
126  $memcachedServers = $configurations[$table]['options']['servers'];
127  break;
128  }
129  }
130  }
131  }
132  return $memcachedServers;
133  }
134 
140  protected function ‪getMemcachedConnectionStatus()
141  {
142  $value = $this->‪getLanguageService()->sL('LLL:EXT:reports/Resources/Private/Language/locallang_reports.xlf:status_ok');
143  $message = '';
144  $severity = ContextualFeedbackSeverity::OK;
145  $failedConnections = [];
146  $defaultMemcachedPort = ini_get('memcache.default_port');
147  $defaultMemcachedPort = ‪MathUtility::canBeInterpretedAsInteger($defaultMemcachedPort) ? (int)$defaultMemcachedPort : 11211;
148  $memcachedServers = $this->‪getConfiguredMemcachedServers();
149  if (function_exists('memcache_connect') && is_array($memcachedServers)) {
150  foreach ($memcachedServers as $testServer) {
151  $configuredServer = $testServer;
152  if (str_starts_with($testServer, 'unix://')) {
153  $host = $testServer;
154  $port = 0;
155  } else {
156  if (str_starts_with($testServer, 'tcp://')) {
157  $testServer = substr($testServer, 6);
158  }
159  if (str_contains($testServer, ':')) {
160  [$host, $port] = explode(':', $testServer, 2);
161  $port = (int)$port;
162  } else {
163  $host = $testServer;
164  $port = $defaultMemcachedPort;
165  }
166  }
167  $memcachedConnection = @memcache_connect($host, $port);
168  if ($memcachedConnection != null) {
169  memcache_close();
170  } else {
171  $failedConnections[] = $configuredServer;
172  }
173  }
174  }
175  if (!empty($failedConnections)) {
176  $value = $this->‪getLanguageService()->sL('LLL:EXT:reports/Resources/Private/Language/locallang_reports.xlf:status_connectionFailed');
177  $severity = ContextualFeedbackSeverity::WARNING;
178  $message = $this->‪getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:warning.memcache_not_usable') . '<br /><br /><ul><li>' . implode('</li><li>', $failedConnections) . '</li></ul>';
179  }
180  return GeneralUtility::makeInstance(ReportStatus::class, $this->‪getLanguageService()->sL('LLL:EXT:reports/Resources/Private/Language/locallang_reports.xlf:status_memcachedConfiguration'), $value, $message, $severity);
181  }
182 
189  {
190  $value = $this->‪getLanguageService()->sL('LLL:EXT:reports/Resources/Private/Language/locallang_reports.xlf:status_ok');
191  $message = '';
192  $severity = ContextualFeedbackSeverity::OK;
193  if ((int)‪$GLOBALS['TYPO3_CONF_VARS']['SYS']['fileCreateMask'] % 10 & 2) {
194  $value = ‪$GLOBALS['TYPO3_CONF_VARS']['SYS']['fileCreateMask'];
195  $severity = ContextualFeedbackSeverity::WARNING;
196  $message = $this->‪getLanguageService()->sL('LLL:EXT:reports/Resources/Private/Language/locallang_reports.xlf:status_CreatedFilePermissions.writable');
197  }
198  return GeneralUtility::makeInstance(ReportStatus::class, $this->‪getLanguageService()->sL('LLL:EXT:reports/Resources/Private/Language/locallang_reports.xlf:status_CreatedFilePermissions'), $value, $message, $severity);
199  }
200 
207  {
208  $value = $this->‪getLanguageService()->sL('LLL:EXT:reports/Resources/Private/Language/locallang_reports.xlf:status_ok');
209  $message = '';
210  $severity = ContextualFeedbackSeverity::OK;
211  if ((int)‪$GLOBALS['TYPO3_CONF_VARS']['SYS']['folderCreateMask'] % 10 & 2) {
212  $value = ‪$GLOBALS['TYPO3_CONF_VARS']['SYS']['folderCreateMask'];
213  $severity = ContextualFeedbackSeverity::WARNING;
214  $message = $this->‪getLanguageService()->sL('LLL:EXT:reports/Resources/Private/Language/locallang_reports.xlf:status_CreatedDirectoryPermissions.writable');
215  }
216  return GeneralUtility::makeInstance(ReportStatus::class, $this->‪getLanguageService()->sL('LLL:EXT:reports/Resources/Private/Language/locallang_reports.xlf:status_CreatedDirectoryPermissions'), $value, $message, $severity);
217  }
218 
224  protected function ‪isMysqlUsed()
225  {
226  $platform = GeneralUtility::makeInstance(ConnectionPool::class)
227  ->getConnectionByName(‪ConnectionPool::DEFAULT_CONNECTION_NAME)
228  ->getDatabasePlatform();
229 
230  return $platform instanceof DoctrineMariaDBPlatform || $platform instanceof DoctrineMySQLPlatform;
231  }
232 
238  protected function ‪getMysqlDatabaseUtf8Status()
239  {
240  $collationConstraint = null;
241  $charset = '';
242  $connection = GeneralUtility::makeInstance(ConnectionPool::class)
243  ->getConnectionByName(‪ConnectionPool::DEFAULT_CONNECTION_NAME);
244  $queryBuilder = $connection->createQueryBuilder();
245  $defaultDatabaseCharset = (string)$queryBuilder->select('DEFAULT_CHARACTER_SET_NAME')
246  ->from('information_schema.SCHEMATA')
247  ->where(
248  $queryBuilder->expr()->eq(
249  'SCHEMA_NAME',
250  $queryBuilder->createNamedParameter($connection->getDatabase())
251  )
252  )
253  ->setMaxResults(1)
254  ->executeQuery()
255  ->fetchOne();
256 
257  $severity = ContextualFeedbackSeverity::OK;
258  $statusValue = $this->‪getLanguageService()->sL('LLL:EXT:reports/Resources/Private/Language/locallang_reports.xlf:status_ok');
259  // also allow utf8mb3 and utf8mb4
260  if (!str_starts_with($defaultDatabaseCharset, 'utf8')) {
261  // If the default character set is e.g. latin1, BUT all tables in the system are UTF-8,
262  // we assume that TYPO3 has the correct charset for adding tables, and everything is fine
263  $queryBuilder = $connection->createQueryBuilder();
264  $nonUtf8TableCollationsFound = $queryBuilder->select('table_collation')
265  ->from('information_schema.tables')
266  ->where(
267  $queryBuilder->expr()->and(
268  $queryBuilder->expr()->eq('table_schema', $queryBuilder->quote((string)$connection->getDatabase())),
269  $queryBuilder->expr()->notLike('table_collation', $queryBuilder->quote('utf8%'))
270  )
271  )
272  ->setMaxResults(1)
273  ->executeQuery();
274 
275  if ($nonUtf8TableCollationsFound->rowCount() > 0) {
276  $message = sprintf($this->‪getLanguageService()
277  ->sL('LLL:EXT:reports/Resources/Private/Language/locallang_reports.xlf:status_MysqlDatabaseCharacterSet_Unsupported'), $defaultDatabaseCharset);
278  $severity = ContextualFeedbackSeverity::ERROR;
279  $statusValue = $this->‪getLanguageService()->sL('LLL:EXT:reports/Resources/Private/Language/locallang_reports.xlf:status_wrongValue');
280  } else {
281  $message = $this->‪getLanguageService()->sL('LLL:EXT:reports/Resources/Private/Language/locallang_reports.xlf:status_MysqlDatabaseCharacterSet_Info');
282  $severity = ContextualFeedbackSeverity::INFO;
283  $statusValue = $this->‪getLanguageService()->sL('LLL:EXT:reports/Resources/Private/Language/locallang_reports.xlf:status_info');
284  }
285  } elseif (isset(‪$GLOBALS['TYPO3_CONF_VARS']['DB']['Connections'][‪ConnectionPool::DEFAULT_CONNECTION_NAME]['tableoptions'])) {
286  $message = $this->‪getLanguageService()->sL('LLL:EXT:reports/Resources/Private/Language/locallang_reports.xlf:status_MysqlDatabaseCharacterSet_Ok');
287 
288  $tableOptions = ‪$GLOBALS['TYPO3_CONF_VARS']['DB']['Connections'][‪ConnectionPool::DEFAULT_CONNECTION_NAME]['tableoptions'];
289  if (isset($tableOptions['collate'])) {
290  $collationConstraint = $queryBuilder->expr()->neq('table_collation', $queryBuilder->quote($tableOptions['collate']));
291  $charset = $tableOptions['collate'];
292  } elseif (isset($tableOptions['charset'])) {
293  $collationConstraint = $queryBuilder->expr()->notLike('table_collation', $queryBuilder->quote($tableOptions['charset'] . '%'));
294  $charset = $tableOptions['charset'];
295  }
296 
297  if (isset($collationConstraint)) {
298  $queryBuilder = $connection->createQueryBuilder();
299  $wrongCollationTablesFound = $queryBuilder->select('table_collation')
300  ->from('information_schema.tables')
301  ->where(
302  $queryBuilder->expr()->and(
303  $queryBuilder->expr()->eq('table_schema', $queryBuilder->quote($connection->getDatabase())),
304  $collationConstraint
305  )
306  )
307  ->setMaxResults(1)
308  ->executeQuery();
309 
310  if ($wrongCollationTablesFound->rowCount() > 0) {
311  $message = sprintf($this->‪getLanguageService()->sL('LLL:EXT:reports/Resources/Private/Language/locallang_reports.xlf:status_MysqlDatabaseCharacterSet_MixedCollations'), $charset);
312  $severity = ContextualFeedbackSeverity::ERROR;
313  $statusValue = $this->‪getLanguageService()->sL('LLL:EXT:reports/Resources/Private/Language/locallang_reports.xlf:status_checkFailed');
314  } else {
315  if (isset($tableOptions['collate'])) {
316  $collationConstraint = $queryBuilder->expr()->neq('collation_name', $queryBuilder->quote($tableOptions['collate']));
317  } elseif (isset($tableOptions['charset'])) {
318  $collationConstraint = $queryBuilder->expr()->notLike('collation_name', $queryBuilder->quote($tableOptions['charset'] . '%'));
319  }
320 
321  $queryBuilder = $connection->createQueryBuilder();
322  $wrongCollationColumnsFound = $queryBuilder->select('collation_name')
323  ->from('information_schema.columns')
324  ->where(
325  $queryBuilder->expr()->and(
326  $queryBuilder->expr()->eq('table_schema', $queryBuilder->quote($connection->getDatabase())),
327  $collationConstraint
328  )
329  )
330  ->setMaxResults(1)
331  ->executeQuery();
332 
333  if ($wrongCollationColumnsFound->rowCount() > 0) {
334  $message = sprintf($this->‪getLanguageService()->sL('LLL:EXT:reports/Resources/Private/Language/locallang_reports.xlf:status_MysqlDatabaseCharacterSet_MixedCollations'), $charset);
335  $severity = ContextualFeedbackSeverity::ERROR;
336  $statusValue = $this->‪getLanguageService()->sL('LLL:EXT:reports/Resources/Private/Language/locallang_reports.xlf:status_checkFailed');
337  }
338  }
339  }
340  } else {
341  $message = $this->‪getLanguageService()->sL('LLL:EXT:reports/Resources/Private/Language/locallang_reports.xlf:status_MysqlDatabaseCharacterSet_Ok');
342  }
343 
344  return GeneralUtility::makeInstance(
345  ReportStatus::class,
346  $this->‪getLanguageService()->sL('LLL:EXT:reports/Resources/Private/Language/locallang_reports.xlf:status_MysqlDatabaseCharacterSet'),
347  $statusValue,
348  $message,
349  $severity
350  );
351  }
352 
354  {
355  return ‪$GLOBALS['LANG'];
356  }
357 }
‪TYPO3\CMS\Reports\StatusProviderInterface
Definition: StatusProviderInterface.php:22
‪TYPO3\CMS\Reports\Report\Status\ConfigurationStatus\isMysqlUsed
‪bool isMysqlUsed()
Definition: ConfigurationStatus.php:224
‪TYPO3\CMS\Reports\Report\Status\ConfigurationStatus\getMemcachedConnectionStatus
‪TYPO3 CMS Reports Status getMemcachedConnectionStatus()
Definition: ConfigurationStatus.php:140
‪TYPO3\CMS\Reports\Report\Status\Status
Definition: Status.php:34
‪TYPO3\CMS\Core\Registry
Definition: Registry.php:33
‪TYPO3\CMS\Reports\Report\Status\ConfigurationStatus\getCreatedFilesWorldWritableStatus
‪TYPO3 CMS Reports Status getCreatedFilesWorldWritableStatus()
Definition: ConfigurationStatus.php:188
‪TYPO3\CMS\Reports\Report\Status\ConfigurationStatus\isMemcachedUsed
‪bool isMemcachedUsed()
Definition: ConfigurationStatus.php:103
‪TYPO3\CMS\Reports\Report\Status\ConfigurationStatus
Definition: ConfigurationStatus.php:38
‪TYPO3\CMS\Core\Database\ConnectionPool\DEFAULT_CONNECTION_NAME
‪const DEFAULT_CONNECTION_NAME
Definition: ConnectionPool.php:50
‪TYPO3\CMS\Core\Type\ContextualFeedbackSeverity
‪ContextualFeedbackSeverity
Definition: ContextualFeedbackSeverity.php:25
‪TYPO3\CMS\Core\Security\ContentSecurityPolicy\Reporting\ReportStatus
‪ReportStatus
Definition: ReportStatus.php:24
‪TYPO3\CMS\Core\Utility\MathUtility\canBeInterpretedAsInteger
‪static bool canBeInterpretedAsInteger(mixed $var)
Definition: MathUtility.php:69
‪TYPO3\CMS\Reports\Status
Definition: Status.php:24
‪TYPO3\CMS\Backend\Routing\UriBuilder
Definition: UriBuilder.php:44
‪TYPO3\CMS\Reports\Report\Status\ConfigurationStatus\getMysqlDatabaseUtf8Status
‪ReportStatus getMysqlDatabaseUtf8Status()
Definition: ConfigurationStatus.php:238
‪TYPO3\CMS\Reports\Report\Status\ConfigurationStatus\getStatus
‪Status[] getStatus()
Definition: ConfigurationStatus.php:44
‪TYPO3\CMS\Reports\Report\Status\ConfigurationStatus\getConfiguredMemcachedServers
‪array getConfiguredMemcachedServers()
Definition: ConfigurationStatus.php:118
‪TYPO3\CMS\Reports\Report\Status\ConfigurationStatus\getLanguageService
‪getLanguageService()
Definition: ConfigurationStatus.php:353
‪TYPO3\CMS\Reports\Report\Status\ConfigurationStatus\getReferenceIndexStatus
‪TYPO3 CMS Reports Status getReferenceIndexStatus()
Definition: ConfigurationStatus.php:72
‪TYPO3\CMS\Webhooks\Message\$url
‪identifier readonly UriInterface $url
Definition: LoginErrorOccurredMessage.php:36
‪$GLOBALS
‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['adminpanel']['modules']
Definition: ext_localconf.php:25
‪TYPO3\CMS\Core\Core\Environment
Definition: Environment.php:41
‪TYPO3\CMS\Core\Utility\MathUtility
Definition: MathUtility.php:24
‪TYPO3\CMS\Reports\Report\Status\ConfigurationStatus\getLabel
‪getLabel()
Definition: ConfigurationStatus.php:62
‪TYPO3\CMS\Reports\Report\Status
Definition: ConfigurationStatus.php:16
‪TYPO3\CMS\Core\Localization\LanguageService
Definition: LanguageService.php:46
‪TYPO3\CMS\Core\Database\ConnectionPool
Definition: ConnectionPool.php:46
‪TYPO3\CMS\Core\Cache\Backend\MemcachedBackend
Definition: MemcachedBackend.php:46
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:52
‪TYPO3\CMS\Reports\Report\Status\ConfigurationStatus\getCreatedDirectoriesWorldWritableStatus
‪TYPO3 CMS Reports Status getCreatedDirectoriesWorldWritableStatus()
Definition: ConfigurationStatus.php:206
‪TYPO3\CMS\Core\Core\Environment\isWindows
‪static isWindows()
Definition: Environment.php:276