‪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 
19 use TYPO3\CMS\Backend\Utility\BackendUtility;
30 use ‪TYPO3\CMS\Reports\Status as ReportStatus;
32 
37 {
43  public function ‪getStatus(): array
44  {
45  $statuses = [
46  'emptyReferenceIndex' => $this->‪getReferenceIndexStatus(),
47  ];
48  if ($this->‪isMemcachedUsed()) {
49  $statuses['memcachedConnection'] = $this->‪getMemcachedConnectionStatus();
50  }
52  $statuses['createdFilesWorldWritable'] = $this->‪getCreatedFilesWorldWritableStatus();
53  $statuses['createdDirectoriesWorldWritable'] = $this->‪getCreatedDirectoriesWorldWritableStatus();
54  }
55  if ($this->‪isMysqlUsed()) {
56  $statuses['mysqlDatabaseUsesUtf8'] = $this->‪getMysqlDatabaseUtf8Status();
57  }
58  return $statuses;
59  }
60 
61  public function ‪getLabel(): string
62  {
63  return 'configuration';
64  }
65 
71  protected function ‪getReferenceIndexStatus()
72  {
73  $value = $this->‪getLanguageService()->getLL('status_ok');
74  $message = '';
75  $severity = ContextualFeedbackSeverity::OK;
76 
77  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('sys_refindex');
78  $count = $queryBuilder
79  ->count('*')
80  ->from('sys_refindex')
81  ->executeQuery()
82  ->fetchOne();
83 
84  $registry = GeneralUtility::makeInstance(Registry::class);
85  $lastRefIndexUpdate = $registry->get('core', 'sys_refindex_lastUpdate');
86 
87  $uriBuilder = GeneralUtility::makeInstance(UriBuilder::class);
88  if (!$count && $lastRefIndexUpdate) {
89  $value = $this->‪getLanguageService()->getLL('status_empty');
90  $severity = ContextualFeedbackSeverity::WARNING;
91  $url = (string)$uriBuilder->buildUriFromRoute('system_dbint', ['id' => 0, 'SET' => ['function' => 'refindex']]);
92  $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));
93  }
94  return GeneralUtility::makeInstance(ReportStatus::class, $this->‪getLanguageService()->getLL('status_referenceIndex'), $value, $message, $severity);
95  }
96 
102  protected function ‪isMemcachedUsed()
103  {
104  $memcachedUsed = false;
105  $memcachedServers = $this->‪getConfiguredMemcachedServers();
106  if (!empty($memcachedServers)) {
107  $memcachedUsed = true;
108  }
109  return $memcachedUsed;
110  }
111 
117  protected function ‪getConfiguredMemcachedServers()
118  {
119  $configurations = ‪$GLOBALS['TYPO3_CONF_VARS']['SYS']['caching']['cacheConfigurations'] ?? [];
120  $memcachedServers = [];
121  foreach ($configurations as $table => $conf) {
122  if (is_array($conf)) {
123  foreach ($conf as $key => $value) {
124  if ($value === MemcachedBackend::class) {
125  $memcachedServers = $configurations[$table]['options']['servers'];
126  break;
127  }
128  }
129  }
130  }
131  return $memcachedServers;
132  }
133 
139  protected function ‪getMemcachedConnectionStatus()
140  {
141  $value = $this->‪getLanguageService()->getLL('status_ok');
142  $message = '';
143  $severity = ContextualFeedbackSeverity::OK;
144  $failedConnections = [];
145  $defaultMemcachedPort = ini_get('memcache.default_port');
146  $defaultMemcachedPort = ‪MathUtility::canBeInterpretedAsInteger($defaultMemcachedPort) ? (int)$defaultMemcachedPort : 11211;
147  $memcachedServers = $this->‪getConfiguredMemcachedServers();
148  if (function_exists('memcache_connect') && is_array($memcachedServers)) {
149  foreach ($memcachedServers as $testServer) {
150  $configuredServer = $testServer;
151  if (str_starts_with($testServer, 'unix://')) {
152  $host = $testServer;
153  $port = 0;
154  } else {
155  if (str_starts_with($testServer, 'tcp://')) {
156  $testServer = substr($testServer, 6);
157  }
158  if (str_contains($testServer, ':')) {
159  [$host, $port] = explode(':', $testServer, 2);
160  $port = (int)$port;
161  } else {
162  $host = $testServer;
163  $port = $defaultMemcachedPort;
164  }
165  }
166  $memcachedConnection = @memcache_connect($host, $port);
167  if ($memcachedConnection != null) {
168  memcache_close();
169  } else {
170  $failedConnections[] = $configuredServer;
171  }
172  }
173  }
174  if (!empty($failedConnections)) {
175  $value = $this->‪getLanguageService()->getLL('status_connectionFailed');
176  $severity = ContextualFeedbackSeverity::WARNING;
177  $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>';
178  }
179  return GeneralUtility::makeInstance(ReportStatus::class, $this->‪getLanguageService()->getLL('status_memcachedConfiguration'), $value, $message, $severity);
180  }
181 
188  {
189  $value = $this->‪getLanguageService()->getLL('status_ok');
190  $message = '';
191  $severity = ContextualFeedbackSeverity::OK;
192  if ((int)‪$GLOBALS['TYPO3_CONF_VARS']['SYS']['fileCreateMask'] % 10 & 2) {
193  $value = ‪$GLOBALS['TYPO3_CONF_VARS']['SYS']['fileCreateMask'];
194  $severity = ContextualFeedbackSeverity::WARNING;
195  $message = $this->‪getLanguageService()->getLL('status_CreatedFilePermissions.writable');
196  }
197  return GeneralUtility::makeInstance(ReportStatus::class, $this->‪getLanguageService()->getLL('status_CreatedFilePermissions'), $value, $message, $severity);
198  }
199 
206  {
207  $value = $this->‪getLanguageService()->getLL('status_ok');
208  $message = '';
209  $severity = ContextualFeedbackSeverity::OK;
210  if ((int)‪$GLOBALS['TYPO3_CONF_VARS']['SYS']['folderCreateMask'] % 10 & 2) {
211  $value = ‪$GLOBALS['TYPO3_CONF_VARS']['SYS']['folderCreateMask'];
212  $severity = ContextualFeedbackSeverity::WARNING;
213  $message = $this->‪getLanguageService()->getLL('status_CreatedDirectoryPermissions.writable');
214  }
215  return GeneralUtility::makeInstance(ReportStatus::class, $this->‪getLanguageService()->getLL('status_CreatedDirectoryPermissions'), $value, $message, $severity);
216  }
217 
223  protected function ‪isMysqlUsed()
224  {
225  $connection = GeneralUtility::makeInstance(ConnectionPool::class)
226  ->getConnectionByName(‪ConnectionPool::DEFAULT_CONNECTION_NAME);
227 
228  return str_starts_with($connection->getServerVersion(), 'MySQL');
229  }
230 
236  protected function ‪getMysqlDatabaseUtf8Status()
237  {
238  $collationConstraint = null;
239  $charset = '';
240  $connection = GeneralUtility::makeInstance(ConnectionPool::class)
241  ->getConnectionByName(‪ConnectionPool::DEFAULT_CONNECTION_NAME);
242  $queryBuilder = $connection->createQueryBuilder();
243  $defaultDatabaseCharset = (string)$queryBuilder->select('DEFAULT_CHARACTER_SET_NAME')
244  ->from('information_schema.SCHEMATA')
245  ->where(
246  $queryBuilder->expr()->eq(
247  'SCHEMA_NAME',
248  $queryBuilder->createNamedParameter($connection->getDatabase())
249  )
250  )
251  ->setMaxResults(1)
252  ->executeQuery()
253  ->fetchOne();
254 
255  $severity = ContextualFeedbackSeverity::OK;
256  $statusValue = $this->‪getLanguageService()->getLL('status_ok');
257  // also allow utf8mb4
258  if (!str_starts_with($defaultDatabaseCharset, 'utf8')) {
259  // If the default character set is e.g. latin1, BUT all tables in the system are UTF-8,
260  // we assume that TYPO3 has the correct charset for adding tables, and everything is fine
261  $queryBuilder = $connection->createQueryBuilder();
262  $nonUtf8TableCollationsFound = $queryBuilder->select('table_collation')
263  ->from('information_schema.tables')
264  ->where(
265  $queryBuilder->expr()->and(
266  $queryBuilder->expr()->eq('table_schema', $queryBuilder->quote($connection->getDatabase())),
267  $queryBuilder->expr()->notLike('table_collation', $queryBuilder->quote('utf8%'))
268  )
269  )
270  ->setMaxResults(1)
271  ->executeQuery();
272 
273  if ($nonUtf8TableCollationsFound->rowCount() > 0) {
274  $message = sprintf($this->‪getLanguageService()
275  ->getLL('status_MysqlDatabaseCharacterSet_Unsupported'), $defaultDatabaseCharset);
276  $severity = ContextualFeedbackSeverity::ERROR;
277  $statusValue = $this->‪getLanguageService()->getLL('status_wrongValue');
278  } else {
279  $message = $this->‪getLanguageService()->getLL('status_MysqlDatabaseCharacterSet_Info');
280  $severity = ContextualFeedbackSeverity::INFO;
281  $statusValue = $this->‪getLanguageService()->getLL('status_info');
282  }
283  } elseif (isset(‪$GLOBALS['TYPO3_CONF_VARS']['DB']['Connections'][‪ConnectionPool::DEFAULT_CONNECTION_NAME]['tableoptions'])) {
284  $message = $this->‪getLanguageService()->getLL('status_MysqlDatabaseCharacterSet_Ok');
285 
286  $tableOptions = ‪$GLOBALS['TYPO3_CONF_VARS']['DB']['Connections'][‪ConnectionPool::DEFAULT_CONNECTION_NAME]['tableoptions'];
287  if (isset($tableOptions['collate'])) {
288  $collationConstraint = $queryBuilder->expr()->neq('table_collation', $queryBuilder->quote($tableOptions['collate']));
289  $charset = $tableOptions['collate'];
290  } elseif (isset($tableOptions['charset'])) {
291  $collationConstraint = $queryBuilder->expr()->notLike('table_collation', $queryBuilder->quote($tableOptions['charset'] . '%'));
292  $charset = $tableOptions['charset'];
293  }
294 
295  if (isset($collationConstraint)) {
296  $queryBuilder = $connection->createQueryBuilder();
297  $wrongCollationTablesFound = $queryBuilder->select('table_collation')
298  ->from('information_schema.tables')
299  ->where(
300  $queryBuilder->expr()->and(
301  $queryBuilder->expr()->eq('table_schema', $queryBuilder->quote($connection->getDatabase())),
302  $collationConstraint
303  )
304  )
305  ->setMaxResults(1)
306  ->executeQuery();
307 
308  if ($wrongCollationTablesFound->rowCount() > 0) {
309  $message = sprintf($this->‪getLanguageService()->getLL('status_MysqlDatabaseCharacterSet_MixedCollations'), $charset);
310  $severity = ContextualFeedbackSeverity::ERROR;
311  $statusValue = $this->‪getLanguageService()->getLL('status_checkFailed');
312  } else {
313  if (isset($tableOptions['collate'])) {
314  $collationConstraint = $queryBuilder->expr()->neq('collation_name', $queryBuilder->quote($tableOptions['collate']));
315  } elseif (isset($tableOptions['charset'])) {
316  $collationConstraint = $queryBuilder->expr()->notLike('collation_name', $queryBuilder->quote($tableOptions['charset'] . '%'));
317  }
318 
319  $queryBuilder = $connection->createQueryBuilder();
320  $wrongCollationColumnsFound = $queryBuilder->select('collation_name')
321  ->from('information_schema.columns')
322  ->where(
323  $queryBuilder->expr()->and(
324  $queryBuilder->expr()->eq('table_schema', $queryBuilder->quote($connection->getDatabase())),
325  $collationConstraint
326  )
327  )
328  ->setMaxResults(1)
329  ->executeQuery();
330 
331  if ($wrongCollationColumnsFound->rowCount() > 0) {
332  $message = sprintf($this->‪getLanguageService()->getLL('status_MysqlDatabaseCharacterSet_MixedCollations'), $charset);
333  $severity = ContextualFeedbackSeverity::ERROR;
334  $statusValue = $this->‪getLanguageService()->getLL('status_checkFailed');
335  }
336  }
337  }
338  } else {
339  $message = $this->‪getLanguageService()->getLL('status_MysqlDatabaseCharacterSet_Ok');
340  }
341 
342  return GeneralUtility::makeInstance(
343  ReportStatus::class,
344  $this->‪getLanguageService()->getLL('status_MysqlDatabaseCharacterSet'),
345  $statusValue,
346  $message,
347  $severity
348  );
349  }
350 
352  {
353  return ‪$GLOBALS['LANG'];
354  }
355 }
‪TYPO3\CMS\Reports\StatusProviderInterface
Definition: StatusProviderInterface.php:22
‪TYPO3\CMS\Reports\Report\Status\ConfigurationStatus\isMysqlUsed
‪bool isMysqlUsed()
Definition: ConfigurationStatus.php:223
‪TYPO3\CMS\Reports\Report\Status\ConfigurationStatus\getMemcachedConnectionStatus
‪TYPO3 CMS Reports Status getMemcachedConnectionStatus()
Definition: ConfigurationStatus.php:139
‪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:187
‪TYPO3\CMS\Reports\Report\Status\ConfigurationStatus\isMemcachedUsed
‪bool isMemcachedUsed()
Definition: ConfigurationStatus.php:102
‪TYPO3\CMS\Reports\Report\Status\ConfigurationStatus
Definition: ConfigurationStatus.php:37
‪TYPO3\CMS\Core\Database\ConnectionPool\DEFAULT_CONNECTION_NAME
‪const DEFAULT_CONNECTION_NAME
Definition: ConnectionPool.php:55
‪TYPO3\CMS\Core\Type\ContextualFeedbackSeverity
‪ContextualFeedbackSeverity
Definition: ContextualFeedbackSeverity.php:25
‪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:42
‪TYPO3\CMS\Reports\Report\Status\ConfigurationStatus\getMysqlDatabaseUtf8Status
‪ReportStatus getMysqlDatabaseUtf8Status()
Definition: ConfigurationStatus.php:236
‪TYPO3\CMS\Reports\Report\Status\ConfigurationStatus\getStatus
‪Status[] getStatus()
Definition: ConfigurationStatus.php:43
‪TYPO3\CMS\Reports\Report\Status\ConfigurationStatus\getConfiguredMemcachedServers
‪array getConfiguredMemcachedServers()
Definition: ConfigurationStatus.php:117
‪TYPO3\CMS\Reports\Report\Status\ConfigurationStatus\getLanguageService
‪getLanguageService()
Definition: ConfigurationStatus.php:351
‪TYPO3\CMS\Reports\Report\Status\ConfigurationStatus\getReferenceIndexStatus
‪TYPO3 CMS Reports Status getReferenceIndexStatus()
Definition: ConfigurationStatus.php:71
‪TYPO3\CMS\Core\Database\Connection
Definition: Connection.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:61
‪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:51
‪TYPO3\CMS\Core\Cache\Backend\MemcachedBackend
Definition: MemcachedBackend.php:46
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:51
‪TYPO3\CMS\Reports\Report\Status\ConfigurationStatus\getCreatedDirectoriesWorldWritableStatus
‪TYPO3 CMS Reports Status getCreatedDirectoriesWorldWritableStatus()
Definition: ConfigurationStatus.php:205
‪TYPO3\CMS\Core\Core\Environment\isWindows
‪static isWindows()
Definition: Environment.php:287