diff --git a/phpstan.neon b/phpstan.neon index 255d7ff7b1e6513e4c078dfe963288827582a805..cf80b7da69359633637fe6276da2ecd00628b924 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -160,3 +160,94 @@ parameters: message: "#^Instanceof between TYPO3\\\\CMS\\\\Core\\\\Resource\\\\File and TYPO3\\\\CMS\\\\Core\\\\Resource\\\\FileReference will always evaluate to false\\.$#" count: 1 path: typo3/sysext/frontend/Classes/ContentObject/ContentObjectRenderer.php + + # Ignored errors for level 5 + - + message: "#^Parameter \\#1 \\$var of function count expects array\\|Countable, string given\\.$#" + count: 2 + path: typo3/sysext/core/Classes/Database/QueryGenerator.php + + - + message: "#^Parameter \\#1 \\$input of function array_slice expects array, string given\\.$#" + count: 2 + path: typo3/sysext/core/Classes/Database/QueryGenerator.php + + - + message: "#^Parameter \\#1 \\$stack of function array_shift expects array, string given\\.$#" + count: 1 + path: typo3/sysext/core/Classes/Database/QueryGenerator.php + + - + message: "#^Parameter \\#1 \\$arr1 of function array_merge expects array, string given\\.$#" + count: 1 + path: typo3/sysext/core/Classes/Database/QueryGenerator.php + + - + message: "#^Parameter \\#3 \\.\\.\\.\\$args of function array_merge expects array, string given\\.$#" + count: 1 + path: typo3/sysext/core/Classes/Database/QueryGenerator.php + + - + message: "#^Parameter \\#2 \\$suffix of method TYPO3\\\\CMS\\\\Core\\\\Database\\\\QueryGenerator\\:\\:cleanInputVal\\(\\) expects string, int given\\.$#" + count: 1 + path: typo3/sysext/core/Classes/Database/QueryGenerator.php + + - + message: "#^Parameter \\#3 \\$draw of method TYPO3\\\\CMS\\\\Core\\\\Database\\\\QueryGenerator\\:\\:mkOperatorSelect\\(\\) expects bool, int given\\.$#" + count: 1 + path: typo3/sysext/core/Classes/Database/QueryGenerator.php + + - + message: "#^Parameter \\#2 \\$first of method TYPO3\\\\CMS\\\\Core\\\\Database\\\\QueryGenerator\\:\\:getQuerySingle\\(\\) expects bool, int given\\.$#" + count: 2 + path: typo3/sysext/core/Classes/Database/QueryGenerator.php + + - + message: "#^Parameter \\#2 \\$item of static method TYPO3\\\\CMS\\\\Core\\\\Utility\\\\GeneralUtility\\:\\:inList\\(\\) expects string, \\(float\\|int\\) given\\.$#" + count: 1 + path: typo3/sysext/core/Classes/Database/QueryGenerator.php + + - + message: "#^Parameter \\#2 \\$first of method TYPO3\\\\CMS\\\\Core\\\\Database\\\\QueryGenerator\\:\\:getUserDefQuery\\(\\) expects bool, int given\\.$#" + count: 1 + path: typo3/sysext/core/Classes/Database/QueryGenerator.php + + - + message: "#^Parameter \\#2 \\$time of static method DateTime\\:\\:createFromFormat\\(\\) expects string, int\\|string given\\.$#" + count: 1 + path: typo3/sysext/core/Classes/Database/QueryGenerator.php + + - + message: "#^Parameter \\#2 \\$replace of function str_replace expects array\\|string, int given\\.$#" + count: 1 + path: typo3/sysext/core/Classes/Database/QueryGenerator.php + + - + message: "#^Parameter \\#1 \\$str of function trim expects string, array\\|string given\\.$#" + count: 1 + path: typo3/sysext/core/Classes/Database/QueryGenerator.php + + - + message: "#^Parameter \\#2 \\$timestamp of function date expects int, int\\|false given\\.$#" + count: 1 + path: typo3/sysext/core/Classes/Database/QueryGenerator.php + + - + message: "#^Parameter \\#2 \\$storeIndex of method TYPO3\\\\CMS\\\\Core\\\\Database\\\\QueryView\\:\\:loadStoreQueryConfigs\\(\\) expects int, string given\\.$#" + count: 1 + path: typo3/sysext/core/Classes/Database/QueryView.php + + - + message: "#^Parameter \\#2 \\$index of method TYPO3\\\\CMS\\\\Core\\\\Database\\\\QueryView\\:\\:addToStoreQueryConfigs\\(\\) expects int, int\\|string\\|null given\\.$#" + count: 1 + path: typo3/sysext/core/Classes/Database/QueryView.php + + - + message: "#^Parameter \\#2 \\$timestamp of function strftime expects int, string given\\.$#" + count: 3 + path: typo3/sysext/core/Classes/Database/QueryView.php + + - + message: "#^Parameter \\#1 \\$constraint of static method TYPO3\\\\CMS\\\\Core\\\\Database\\\\Query\\\\QueryHelper\\:\\:stripLogicalOperatorPrefix\\(\\) expects string, string\\|null given\\.$#" + count: 1 + path: typo3/sysext/core/Classes/Database/QueryView.php diff --git a/typo3/sysext/core/Classes/Database/Query/Expression/ExpressionBuilder.php b/typo3/sysext/core/Classes/Database/Query/Expression/ExpressionBuilder.php index 4401243f9b298ecbdfedaf6db6225b56badfb440..6d384c682146697a9cfc647a4a882c77aaca1bb9 100644 --- a/typo3/sysext/core/Classes/Database/Query/Expression/ExpressionBuilder.php +++ b/typo3/sysext/core/Classes/Database/Query/Expression/ExpressionBuilder.php @@ -64,7 +64,7 @@ class ExpressionBuilder /** * Creates a conjunction of the given boolean expressions * - * @param array<int,mixed> $expressions Optional clause. Requires at least one defined when converting to string. + * @param string ...$expressions Optional clause. Requires at least one defined when converting to string. * * @return CompositeExpression */ @@ -76,7 +76,7 @@ class ExpressionBuilder /** * Creates a disjunction of the given boolean expressions. * - * @param array<int,mixed> $expressions Optional clause. Requires at least one defined when converting to string. + * @param string ...$expressions Optional clause. Requires at least one defined when converting to string. * * @return CompositeExpression */ diff --git a/typo3/sysext/core/Classes/Database/Query/QueryHelper.php b/typo3/sysext/core/Classes/Database/Query/QueryHelper.php index a4fc30dca06fe9782396dd8d3ddb193e1efdd459..6d8bde9d7d5de4df62704cf84de194e2e0b149f0 100644 --- a/typo3/sysext/core/Classes/Database/Query/QueryHelper.php +++ b/typo3/sysext/core/Classes/Database/Query/QueryHelper.php @@ -125,9 +125,9 @@ class QueryHelper $tableName = strtok($input, $quoteCharacter); } - $tableAlias = strtok($quoteCharacter); + $tableAlias = (string)strtok($quoteCharacter); if (strtolower($tableAlias) === 'as') { - $tableAlias = strtok($quoteCharacter); + $tableAlias = (string)strtok($quoteCharacter); // Skip the next token which must be ON strtok(' '); $joinCondition = strtok(''); @@ -145,7 +145,7 @@ class QueryHelper // that the quoted table alias contains whitespace. $firstCharacterOfTableAlias = $tableAlias[0] ?? null; if ($firstCharacterOfTableAlias === '`' || $firstCharacterOfTableAlias === '"') { - $tableAlias = substr($tableAlias, 1, -1); + $tableAlias = substr((string)$tableAlias, 1, -1); } $tableAlias = $tableAlias ?: $tableName; diff --git a/typo3/sysext/core/Classes/Database/ReferenceIndex.php b/typo3/sysext/core/Classes/Database/ReferenceIndex.php index 291125dd4d3b765153c170d838b415e918ec3763..7c01f147c4552646ff683eaee7c5d543baaf9ad8 100644 --- a/typo3/sysext/core/Classes/Database/ReferenceIndex.php +++ b/typo3/sysext/core/Classes/Database/ReferenceIndex.php @@ -454,7 +454,7 @@ class ReferenceIndex implements LoggerAwareInterface } return $this->createEntryDataUsingRecord( (string)$table, - $this->getRecordRawCached($table, $uid), + $this->getRecordRawCached($table, $uid) ?: [], (string)$field, (string)$flexPointer, $deleted ? (int)$deleted : 0, @@ -527,7 +527,7 @@ class ReferenceIndex implements LoggerAwareInterface } $this->createEntryDataForDatabaseRelationsUsingRecord( (string)$table, - $this->getRecordRawCached($table, $uid), + $this->getRecordRawCached($table, $uid) ?: [], (string)$fieldName, (string)$flexPointer, $deleted ? (int)$deleted : 0, @@ -570,7 +570,7 @@ class ReferenceIndex implements LoggerAwareInterface } $this->createEntryDataForSoftReferencesUsingRecord( (string)$table, - $this->getRecordRawCached($table, $uid), + $this->getRecordRawCached($table, $uid) ?: [], (string)$fieldName, (string)$flexPointer, $deleted ? (int)$deleted : 0, diff --git a/typo3/sysext/core/Classes/Database/RelationHandler.php b/typo3/sysext/core/Classes/Database/RelationHandler.php index fdf32c14faa47c7743aedccd562b78d96adc6b51..979325a7288dbef6bc5e729010eee3d8ad68f2cd 100644 --- a/typo3/sysext/core/Classes/Database/RelationHandler.php +++ b/typo3/sysext/core/Classes/Database/RelationHandler.php @@ -58,7 +58,7 @@ class RelationHandler /** * Contains items in a numeric array (table/id for each). Tablenames here might be "_NO_TABLE" * - * @var array + * @var array<int, array<string, mixed>> */ public $itemArray = []; @@ -494,7 +494,7 @@ class RelationHandler ); } elseif (count($this->tableArray) === 1) { reset($this->tableArray); - $table = key($this->tableArray); + $table = (string)key($this->tableArray); $connection = $this->getConnectionForTableName($table); $maxBindParameters = PlatformInformation::getMaxBindParameters($connection->getDatabasePlatform()); @@ -578,7 +578,7 @@ class RelationHandler } if ($this->MM_table_where) { $queryBuilder->andWhere( - QueryHelper::stripLogicalOperatorPrefix(str_replace('###THIS_UID###', (int)$uid, $this->MM_table_where)) + QueryHelper::stripLogicalOperatorPrefix(str_replace('###THIS_UID###', (string)$uid, $this->MM_table_where)) ); } foreach ($this->MM_match_fields as $field => $value) { @@ -654,7 +654,7 @@ class RelationHandler if ($this->MM_table_where) { $additionalWhere->add( QueryHelper::stripLogicalOperatorPrefix( - str_replace('###THIS_UID###', (int)$uid, $this->MM_table_where) + str_replace('###THIS_UID###', (string)$uid, $this->MM_table_where) ) ); } @@ -896,7 +896,7 @@ class RelationHandler // Add WHERE clause if configured if ($this->MM_table_where) { $queryBuilder->andWhere( - QueryHelper::stripLogicalOperatorPrefix(str_replace('###THIS_UID###', (int)$uid, $this->MM_table_where)) + QueryHelper::stripLogicalOperatorPrefix(str_replace('###THIS_UID###', (string)$uid, $this->MM_table_where)) ); } // Select, update or delete only those relations that match the configured fields @@ -1030,7 +1030,7 @@ class RelationHandler $rows = []; $result = $queryBuilder->execute(); while ($row = $result->fetch()) { - $rows[$row['uid']] = $row; + $rows[(int)$row['uid']] = $row; } if (!empty($rows)) { // Retrieve the parsed and prepared ORDER BY configuration for the resolver @@ -1098,7 +1098,7 @@ class RelationHandler } $isOnSymmetricSide = false; if ($symmetric_field) { - $isOnSymmetricSide = self::isOnSymmetricSide($parentUid, $conf, $row); + $isOnSymmetricSide = self::isOnSymmetricSide((string)$parentUid, $conf, $row); } $updateValues = $foreign_match_fields; // No update to the uid is requested, so this is the normal behaviour @@ -1426,7 +1426,7 @@ class RelationHandler /** * Handles a purge callback on $this->itemArray * - * @param callable $purgeCallback + * @param string $purgeCallback * @return bool Whether items have been purged */ protected function purgeItemArrayHandler($purgeCallback) @@ -1438,7 +1438,12 @@ class RelationHandler continue; } - $purgedItemIds = call_user_func([$this, $purgeCallback], $itemTableName, $itemIds); + $purgedItemIds = []; + $callable =[$this, $purgeCallback]; + if (is_callable($callable)) { + $purgedItemIds = call_user_func($callable, $itemTableName, $itemIds); + } + $removedItemIds = array_diff($itemIds, $purgedItemIds); foreach ($removedItemIds as $removedItemId) { $this->removeFromItemArray($itemTableName, $removedItemId); @@ -1462,7 +1467,7 @@ class RelationHandler protected function purgeVersionedIds($tableName, array $ids) { $ids = $this->sanitizeIds($ids); - $ids = array_combine($ids, $ids); + $ids = (array)array_combine($ids, $ids); $connection = $this->getConnectionForTableName($tableName); $maxBindParameters = PlatformInformation::getMaxBindParameters($connection->getDatabasePlatform()); @@ -1505,7 +1510,7 @@ class RelationHandler protected function purgeLiveVersionedIds($tableName, array $ids) { $ids = $this->sanitizeIds($ids); - $ids = array_combine($ids, $ids); + $ids = (array)array_combine($ids, $ids); $connection = $this->getConnectionForTableName($tableName); $maxBindParameters = PlatformInformation::getMaxBindParameters($connection->getDatabasePlatform()); @@ -1549,7 +1554,7 @@ class RelationHandler protected function purgeDeletePlaceholder($tableName, array $ids) { $ids = $this->sanitizeIds($ids); - $ids = array_combine($ids, $ids); + $ids = array_combine($ids, $ids) ?: []; $connection = $this->getConnectionForTableName($tableName); $maxBindParameters = PlatformInformation::getMaxBindParameters($connection->getDatabasePlatform()); diff --git a/typo3/sysext/core/Classes/Database/Schema/ConnectionMigrator.php b/typo3/sysext/core/Classes/Database/Schema/ConnectionMigrator.php index 1a52f1984d27bd1c59bace092587f4d333ac91d3..67bc45567e7cfcf4493ea6857d4162ba065296f3 100644 --- a/typo3/sysext/core/Classes/Database/Schema/ConnectionMigrator.php +++ b/typo3/sysext/core/Classes/Database/Schema/ConnectionMigrator.php @@ -164,7 +164,7 @@ class ConnectionMigrator $indexColumns = array_map( function ($columnName) { // Strip MySQL prefix length information to get real column names - $columnName = preg_replace('/\(\d+\)$/', '', $columnName); + $columnName = preg_replace('/\(\d+\)$/', '', $columnName) ?? ''; // Strip mssql '[' and ']' from column names $columnName = ltrim($columnName, '['); $columnName = rtrim($columnName, ']'); @@ -252,6 +252,7 @@ class ConnectionMigrator // Collect the table names that have been mapped to this connection. $connectionName = $this->connectionName; + /** @var string[] $tablesForConnection */ $tablesForConnection = array_keys( array_filter( $GLOBALS['TYPO3_CONF_VARS']['DB']['TableMapping'], @@ -575,7 +576,9 @@ class ConnectionMigrator continue; } - $changedColumn->fromColumn = $this->buildQuotedColumn($changedColumn->fromColumn); + if ($changedColumn->fromColumn !== null) { + $changedColumn->fromColumn = $this->buildQuotedColumn($changedColumn->fromColumn); + } // Get the current SQL declaration for the column $currentColumn = $fromTable->getColumn($changedColumn->getOldColumnName()->getName()); @@ -823,6 +826,9 @@ class ConnectionMigrator // Treat each removed foreign key with a new diff to get a dedicated suggestions // just for this foreign key. foreach ($changedTable->removedForeignKeys as $removedForeignKey) { + if (is_string($removedForeignKey)) { + continue; + } $fkIndex = $index . ':fk_' . $removedForeignKey->getName(); $changedTables[$fkIndex] = GeneralUtility::makeInstance( TableDiff::class, @@ -1081,9 +1087,9 @@ class ConnectionMigrator /** * Helper for buildSchemaDiff to filter an array of TableDiffs against a list of valid table names. * - * @param TableDiff[]|Table[] $tableDiffs + * @param \Doctrine\DBAL\Schema\TableDiff[]|Table[] $tableDiffs * @param string[] $validTableNames - * @return TableDiff[] + * @return \Doctrine\DBAL\Schema\TableDiff[] * @throws \InvalidArgumentException */ protected function removeUnrelatedTables(array $tableDiffs, array $validTableNames): array diff --git a/typo3/sysext/core/Classes/Database/Schema/EventListener/SchemaColumnDefinitionListener.php b/typo3/sysext/core/Classes/Database/Schema/EventListener/SchemaColumnDefinitionListener.php index 2625393d5cb453e5d6f3383e3bc137137b5fe6dc..3c77a763b5b5d429e45f71592301bed53398ab75 100644 --- a/typo3/sysext/core/Classes/Database/Schema/EventListener/SchemaColumnDefinitionListener.php +++ b/typo3/sysext/core/Classes/Database/Schema/EventListener/SchemaColumnDefinitionListener.php @@ -106,7 +106,7 @@ class SchemaColumnDefinitionListener */ protected function getUnquotedEnumerationValues(string $typeDefinition): array { - $valuesDefinition = preg_replace('#^(enum|set)\((.*)\)\s*$#i', '$2', $typeDefinition); + $valuesDefinition = preg_replace('#^(enum|set)\((.*)\)\s*$#i', '$2', $typeDefinition) ?? ''; $quoteChar = $valuesDefinition[0]; $separator = $quoteChar . ',' . $quoteChar; @@ -114,9 +114,9 @@ class SchemaColumnDefinitionListener '#' . $quoteChar . ',\s*' . $quoteChar . '#', $separator, $valuesDefinition - ); + ) ?? ''; - $values = explode($quoteChar . ',' . $quoteChar, substr($valuesDefinition, 1, -1)); + $values = explode($quoteChar . ',' . $quoteChar, substr($valuesDefinition, 1, -1)) ?: []; return array_map( function (string $value) use ($quoteChar) { diff --git a/typo3/sysext/core/Classes/Database/Schema/EventListener/SchemaIndexDefinitionListener.php b/typo3/sysext/core/Classes/Database/Schema/EventListener/SchemaIndexDefinitionListener.php index 5c3f8cd5b5184f21b595e63d4d5406ae098c110a..a52cbdafab19ded78bc28cc8febe8df1bc08c87d 100644 --- a/typo3/sysext/core/Classes/Database/Schema/EventListener/SchemaIndexDefinitionListener.php +++ b/typo3/sysext/core/Classes/Database/Schema/EventListener/SchemaIndexDefinitionListener.php @@ -38,7 +38,7 @@ class SchemaIndexDefinitionListener */ public function onSchemaIndexDefinition(SchemaIndexDefinitionEventArgs $event) { - if (strpos($event->getConnection()->getServerVersion(), 'MySQL') !== 0) { + if (strpos((string)$event->getConnection()->getServerVersion(), 'MySQL') !== 0) { return; } diff --git a/typo3/sysext/core/Classes/Database/Schema/Parser/Parser.php b/typo3/sysext/core/Classes/Database/Schema/Parser/Parser.php index 6853d266cce2f86cd7952383e7b75fccafa86f81..ab4819a98f8a886d03370d9d392f8a6d7e156672 100644 --- a/typo3/sysext/core/Classes/Database/Schema/Parser/Parser.php +++ b/typo3/sysext/core/Classes/Database/Schema/Parser/Parser.php @@ -135,12 +135,12 @@ class Parser if ($lookaheadType !== $token) { // If parameter is not identifier (1-99) must be exact match if ($token < Lexer::T_IDENTIFIER) { - $this->syntaxError($this->lexer->getLiteral($token)); + $this->syntaxError((string)$this->lexer->getLiteral($token)); } // If parameter is keyword (200+) must be exact match if ($token > Lexer::T_IDENTIFIER) { - $this->syntaxError($this->lexer->getLiteral($token)); + $this->syntaxError((string)$this->lexer->getLiteral($token)); } // If parameter is MATCH then FULL, PARTIAL or SIMPLE must follow @@ -149,11 +149,11 @@ class Parser && $lookaheadType !== Lexer::T_PARTIAL && $lookaheadType !== Lexer::T_SIMPLE ) { - $this->syntaxError($this->lexer->getLiteral($token)); + $this->syntaxError((string)$this->lexer->getLiteral($token)); } if ($token === Lexer::T_ON && $lookaheadType !== Lexer::T_DELETE && $lookaheadType !== Lexer::T_UPDATE) { - $this->syntaxError($this->lexer->getLiteral($token)); + $this->syntaxError((string)$this->lexer->getLiteral($token)); } } @@ -239,7 +239,7 @@ class Parser public function semanticalError($message = '', $token = null) { if ($token === null) { - $token = $this->lexer->lookahead; + $token = $this->lexer->lookahead ?? []; } // Minimum exposed chars ahead of token diff --git a/typo3/sysext/core/Classes/Database/Schema/SqlReader.php b/typo3/sysext/core/Classes/Database/Schema/SqlReader.php index a42fdb3b475835827af2041274cdc1a18b4cd1c4..9a1d9d12063287e482da936d497eff225d06baf6 100644 --- a/typo3/sysext/core/Classes/Database/Schema/SqlReader.php +++ b/typo3/sysext/core/Classes/Database/Schema/SqlReader.php @@ -64,10 +64,10 @@ class SqlReader foreach ($this->packageManager->getActivePackages() as $package) { $packagePath = $package->getPackagePath(); if (@file_exists($packagePath . 'ext_tables.sql')) { - $sqlString[] = file_get_contents($packagePath . 'ext_tables.sql'); + $sqlString[] = (string)file_get_contents($packagePath . 'ext_tables.sql'); } if ($withStatic && @file_exists($packagePath . 'ext_tables_static+adt.sql')) { - $sqlString[] = file_get_contents($packagePath . 'ext_tables_static+adt.sql'); + $sqlString[] = (string)file_get_contents($packagePath . 'ext_tables_static+adt.sql'); } } diff --git a/typo3/sysext/core/Classes/Database/SoftReferenceIndex.php b/typo3/sysext/core/Classes/Database/SoftReferenceIndex.php index fccfc54e2fc74d354f409f0ce8426d84675b23d6..6fa54017d6462f2fe8761455777d0fb70a06973c 100644 --- a/typo3/sysext/core/Classes/Database/SoftReferenceIndex.php +++ b/typo3/sysext/core/Classes/Database/SoftReferenceIndex.php @@ -395,10 +395,10 @@ class SoftReferenceIndex implements SingletonInterface { $elements = []; // Files starting with EXT: - $parts = preg_split('/([^[:alnum:]"\']+)(EXT:[[:alnum:]_]+\\/[^[:space:]"\',]*)/', ' ' . $content . ' ', 10000, PREG_SPLIT_DELIM_CAPTURE); + $parts = preg_split('/([^[:alnum:]"\']+)(EXT:[[:alnum:]_]+\\/[^[:space:]"\',]*)/', ' ' . $content . ' ', 10000, PREG_SPLIT_DELIM_CAPTURE) ?: []; foreach ($parts as $idx => $value) { if ($idx % 3 == 2) { - $this->makeTokenID($idx); + $this->makeTokenID((string)$idx); $elements[$idx] = []; $elements[$idx]['matchString'] = $value; }