From cd2c9762d8c16f9c79a13e78d7e74d409aa63cfa Mon Sep 17 00:00:00 2001
From: Alexander Schnitzler <git@alexanderschnitzler.de>
Date: Mon, 11 May 2020 17:33:41 +0200
Subject: [PATCH] [TASK] Fix phpstan checkFunctionArgumentTypes errors in
 ext:core Utility

This patch fixes incompatible type usage in function arguments
and is preparatory work for introducing native type hints and
strict mode in all core files.

Releases: master, 10.4
Resolves: #92279
Change-Id: I469892a56334a13ab19df17aeaa39a68226b7510
Reviewed-on: https://review.typo3.org/c/Packages/TYPO3.CMS/+/65679
Tested-by: Alexander Schnitzler <git@alexanderschnitzler.de>
Tested-by: TYPO3com <noreply@typo3.com>
Tested-by: Daniel Goerz <daniel.goerz@posteo.de>
Reviewed-by: Alexander Schnitzler <git@alexanderschnitzler.de>
Reviewed-by: Daniel Goerz <daniel.goerz@posteo.de>
---
 .../core/Classes/Utility/ArrayUtility.php     |  4 +-
 .../core/Classes/Utility/CommandUtility.php   |  2 +-
 .../core/Classes/Utility/CsvUtility.php       |  1 +
 .../Utility/ExtensionManagementUtility.php    |  4 +-
 .../Classes/Utility/File/BasicFileUtility.php |  4 +-
 .../core/Classes/Utility/GeneralUtility.php   | 75 +++++++++++--------
 .../core/Classes/Utility/PathUtility.php      | 10 +--
 .../Classes/Utility/VersionNumberUtility.php  |  4 +-
 8 files changed, 59 insertions(+), 45 deletions(-)

diff --git a/typo3/sysext/core/Classes/Utility/ArrayUtility.php b/typo3/sysext/core/Classes/Utility/ArrayUtility.php
index f15692ea0a84..6b638725fa93 100644
--- a/typo3/sysext/core/Classes/Utility/ArrayUtility.php
+++ b/typo3/sysext/core/Classes/Utility/ArrayUtility.php
@@ -679,7 +679,7 @@ class ArrayUtility
      *
      * @param array $array The initial array to be filtered/reduced
      * @param mixed $keepItems The items which are allowed/kept in the array - accepts array or csv string
-     * @param string $getValueFunc (optional) Callback function used to get the value to keep
+     * @param callable|null $getValueFunc (optional) Callback function used to get the value to keep
      * @return array The filtered/reduced array with the kept items
      */
     public static function keepItemsInArray(array $array, $keepItems, $getValueFunc = null)
@@ -837,7 +837,7 @@ class ArrayUtility
             if (is_array($value)) {
                 $result[$key] = self::stripTagsFromValuesRecursive($value);
             } elseif (is_string($value) || (is_object($value) && method_exists($value, '__toString'))) {
-                $result[$key] = strip_tags($value);
+                $result[$key] = strip_tags((string)$value);
             }
         }
         return $result;
diff --git a/typo3/sysext/core/Classes/Utility/CommandUtility.php b/typo3/sysext/core/Classes/Utility/CommandUtility.php
index 69a2b8698a3f..bf1c47874bcd 100644
--- a/typo3/sysext/core/Classes/Utility/CommandUtility.php
+++ b/typo3/sysext/core/Classes/Utility/CommandUtility.php
@@ -453,7 +453,7 @@ class CommandUtility
         $isUTF8Filesystem = !empty($GLOBALS['TYPO3_CONF_VARS']['SYS']['UTF8filesystem']);
         $currentLocale = false;
         if ($isUTF8Filesystem) {
-            $currentLocale = setlocale(LC_CTYPE, 0);
+            $currentLocale = setlocale(LC_CTYPE, '0');
             setlocale(LC_CTYPE, $GLOBALS['TYPO3_CONF_VARS']['SYS']['systemLocale']);
         }
 
diff --git a/typo3/sysext/core/Classes/Utility/CsvUtility.php b/typo3/sysext/core/Classes/Utility/CsvUtility.php
index 458b0749bc05..c64c21996fbd 100644
--- a/typo3/sysext/core/Classes/Utility/CsvUtility.php
+++ b/typo3/sysext/core/Classes/Utility/CsvUtility.php
@@ -40,6 +40,7 @@ class CsvUtility
             fwrite($handle, $input);
             rewind($handle);
             while (($cells = fgetcsv($handle, 0, $fieldDelimiter, $fieldEnclosure)) !== false) {
+                $cells = is_array($cells) ? $cells : [];
                 $maximumCellCount = max(count($cells), $maximumCellCount);
                 $multiArray[] = preg_replace('|<br */?>|i', LF, $cells);
             }
diff --git a/typo3/sysext/core/Classes/Utility/ExtensionManagementUtility.php b/typo3/sysext/core/Classes/Utility/ExtensionManagementUtility.php
index 2c3ca40d4e00..22724cfa09a8 100644
--- a/typo3/sysext/core/Classes/Utility/ExtensionManagementUtility.php
+++ b/typo3/sysext/core/Classes/Utility/ExtensionManagementUtility.php
@@ -1596,7 +1596,7 @@ tt_content.' . $key . $suffix . ' {
                 $phpCodeToCache[] = ' */';
                 $phpCodeToCache[] = '';
                 // Add ext_localconf.php content of extension
-                $phpCodeToCache[] = trim(file_get_contents($extLocalconfPath));
+                $phpCodeToCache[] = trim((string)file_get_contents($extLocalconfPath));
                 $phpCodeToCache[] = '';
                 $phpCodeToCache[] = '';
             }
@@ -1820,7 +1820,7 @@ tt_content.' . $key . $suffix . ' {
                 $phpCodeToCache[] = ' */';
                 $phpCodeToCache[] = '';
                 // Add ext_tables.php content of extension
-                $phpCodeToCache[] = trim(file_get_contents($extTablesPath));
+                $phpCodeToCache[] = trim((string)file_get_contents($extTablesPath));
                 $phpCodeToCache[] = '';
             }
         }
diff --git a/typo3/sysext/core/Classes/Utility/File/BasicFileUtility.php b/typo3/sysext/core/Classes/Utility/File/BasicFileUtility.php
index 60271210488d..f1a557717699 100644
--- a/typo3/sysext/core/Classes/Utility/File/BasicFileUtility.php
+++ b/typo3/sysext/core/Classes/Utility/File/BasicFileUtility.php
@@ -136,11 +136,11 @@ class BasicFileUtility
         // Handle UTF-8 characters
         if ($GLOBALS['TYPO3_CONF_VARS']['SYS']['UTF8filesystem']) {
             // allow ".", "-", 0-9, a-z, A-Z and everything beyond U+C0 (latin capital letter a with grave)
-            $cleanFileName = preg_replace('/[' . self::UNSAFE_FILENAME_CHARACTER_EXPRESSION . ']/u', '_', trim($fileName));
+            $cleanFileName = preg_replace('/[' . self::UNSAFE_FILENAME_CHARACTER_EXPRESSION . ']/u', '_', trim($fileName)) ?? '';
         } else {
             $fileName = GeneralUtility::makeInstance(CharsetConverter::class)->utf8_char_mapping($fileName);
             // Replace unwanted characters by underscores
-            $cleanFileName = preg_replace('/[' . self::UNSAFE_FILENAME_CHARACTER_EXPRESSION . '\\xC0-\\xFF]/', '_', trim($fileName));
+            $cleanFileName = preg_replace('/[' . self::UNSAFE_FILENAME_CHARACTER_EXPRESSION . '\\xC0-\\xFF]/', '_', trim($fileName)) ?? '';
         }
         // Strip trailing dots and return
         return rtrim($cleanFileName, '.');
diff --git a/typo3/sysext/core/Classes/Utility/GeneralUtility.php b/typo3/sysext/core/Classes/Utility/GeneralUtility.php
index 7e338a00007c..bef0f6cda9c6 100644
--- a/typo3/sysext/core/Classes/Utility/GeneralUtility.php
+++ b/typo3/sysext/core/Classes/Utility/GeneralUtility.php
@@ -251,9 +251,10 @@ class GeneralUtility
                     $mask = false;
                 }
                 if ((int)$mask) {
+                    $mask = (int)$mask;
                     // "192.168.3.0/24"
-                    $lnet = ip2long($test);
-                    $lip = ip2long($baseIP);
+                    $lnet = (int)ip2long($test);
+                    $lip = (int)ip2long($baseIP);
                     $binnet = str_pad(decbin($lnet), 32, '0', STR_PAD_LEFT);
                     $firstpart = substr($binnet, 0, $mask);
                     $binip = str_pad(decbin($lip), 32, '0', STR_PAD_LEFT);
@@ -307,13 +308,15 @@ class GeneralUtility
                 } elseif ($maskInt == 128) {
                     $success = $test === $baseIP;
                 } else {
-                    $testBin = inet_pton($test);
-                    $baseIPBin = inet_pton($baseIP);
+                    $testBin = (string)inet_pton($test);
+                    $baseIPBin = (string)inet_pton($baseIP);
+
                     $success = true;
                     // Modulo is 0 if this is a 8-bit-boundary
                     $maskIntModulo = $maskInt % 8;
                     $numFullCharactersUntilBoundary = (int)($maskInt / 8);
-                    if (strpos($testBin, substr($baseIPBin, 0, $numFullCharactersUntilBoundary)) !== 0) {
+                    $substring = (string)substr($baseIPBin, 0, $numFullCharactersUntilBoundary);
+                    if (strpos($testBin, $substring) !== 0) {
                         $success = false;
                     } elseif ($maskIntModulo > 0) {
                         // If not an 8-bit-boundary, check bits of last character
@@ -450,7 +453,7 @@ class GeneralUtility
             // Resolve hostname
             // Note: this is reverse-lookup and can be randomly set as soon as somebody is able to set
             // the reverse-DNS for his IP (security when for example used with REMOTE_ADDR)
-            $baseHostName = gethostbyaddr($baseHost);
+            $baseHostName = (string)gethostbyaddr($baseHost);
             if ($baseHostName === $baseHost) {
                 // Unable to resolve hostname
                 return false;
@@ -835,7 +838,7 @@ class GeneralUtility
      */
     public static function camelCaseToLowerCaseUnderscored($string)
     {
-        $value = preg_replace('/(?<=\\w)([A-Z])/', '_\\1', $string);
+        $value = preg_replace('/(?<=\\w)([A-Z])/', '_\\1', $string) ?? '';
         return mb_strtolower($value, 'utf-8');
     }
 
@@ -909,7 +912,7 @@ class GeneralUtility
      */
     public static function intExplode($delimiter, $string, $removeEmptyValues = false, $limit = 0)
     {
-        $result = explode($delimiter, $string);
+        $result = explode($delimiter, $string) ?: [];
         foreach ($result as $key => &$value) {
             if ($removeEmptyValues && ($value === '' || trim($value) === '')) {
                 unset($result[$key]);
@@ -957,7 +960,7 @@ class GeneralUtility
         if ($count <= 1) {
             return [$string];
         }
-        $explodedValues = explode($delimiter, strrev($string), $count);
+        $explodedValues = explode($delimiter, strrev($string), $count) ?: [];
         $explodedValues = array_map('strrev', $explodedValues);
         return array_reverse($explodedValues);
     }
@@ -976,7 +979,7 @@ class GeneralUtility
      */
     public static function trimExplode($delim, $string, $removeEmptyValues = false, $limit = 0)
     {
-        $result = explode($delim, $string);
+        $result = explode($delim, $string) ?: [];
         if ($removeEmptyValues) {
             $temp = [];
             foreach ($result as $value) {
@@ -1106,7 +1109,7 @@ class GeneralUtility
      *
      * @param string $tag HTML-tag string (or attributes only)
      * @param bool $decodeEntities Whether to decode HTML entities
-     * @return string[] Array with the attribute values.
+     * @return array<string, string> Array with the attribute values.
      */
     public static function get_tag_attributes($tag, bool $decodeEntities = false)
     {
@@ -1124,7 +1127,7 @@ class GeneralUtility
                         $name = '';
                     }
                 } else {
-                    if ($key = strtolower(preg_replace('/[^[:alnum:]_\\:\\-]/', '', $val))) {
+                    if ($key = strtolower(preg_replace('/[^[:alnum:]_\\:\\-]/', '', $val) ?? '')) {
                         $attributes[$key] = '';
                         $name = $key;
                     }
@@ -1146,7 +1149,7 @@ class GeneralUtility
      */
     public static function split_tag_attributes($tag)
     {
-        $tag_tmp = trim(preg_replace('/^<[^[:space:]]*/', '', trim($tag)));
+        $tag_tmp = trim(preg_replace('/^<[^[:space:]]*/', '', trim($tag)) ?? '');
         // Removes any > in the end of the string
         $tag_tmp = trim(rtrim($tag_tmp, '>'));
         $value = [];
@@ -1709,13 +1712,13 @@ class GeneralUtility
         }
         if (static::isAllowedAbsPath($path)) {
             if (@is_file($path)) {
-                $targetPermissions = $GLOBALS['TYPO3_CONF_VARS']['SYS']['fileCreateMask'] ?? '0644';
+                $targetPermissions = (string)($GLOBALS['TYPO3_CONF_VARS']['SYS']['fileCreateMask'] ?? '0644');
             } elseif (@is_dir($path)) {
-                $targetPermissions = $GLOBALS['TYPO3_CONF_VARS']['SYS']['folderCreateMask'] ?? '0755';
+                $targetPermissions = (string)($GLOBALS['TYPO3_CONF_VARS']['SYS']['folderCreateMask'] ?? '0755');
             }
             if (!empty($targetPermissions)) {
                 // make sure it's always 4 digits
-                $targetPermissions = str_pad($targetPermissions, 4, 0, STR_PAD_LEFT);
+                $targetPermissions = str_pad($targetPermissions, 4, '0', STR_PAD_LEFT);
                 $targetPermissions = octdec($targetPermissions);
                 // "@" is there because file is not necessarily OWNED by the user
                 $result = @chmod($path, $targetPermissions);
@@ -1891,9 +1894,9 @@ class GeneralUtility
         if (!@is_dir($currentPath)) {
             do {
                 $firstCreatedPath = $currentPath;
-                $separatorPosition = strrpos($currentPath, DIRECTORY_SEPARATOR);
+                $separatorPosition = (int)strrpos($currentPath, DIRECTORY_SEPARATOR);
                 $currentPath = substr($currentPath, 0, $separatorPosition);
-            } while (!is_dir($currentPath) && $separatorPosition !== false);
+            } while (!is_dir($currentPath) && $separatorPosition > 0);
             $result = @mkdir($fullDirectoryPath, $permissionMask, true);
             // Check existence of directory again to avoid race condition. Directory could have get created by another process between previous is_dir() and mkdir()
             if (!$result && !@is_dir($fullDirectoryPath)) {
@@ -1914,7 +1917,7 @@ class GeneralUtility
     {
         $OK = false;
         // Remove trailing slash
-        $path = preg_replace('|/$|', '', $path);
+        $path = preg_replace('|/$|', '', $path) ?? '';
         $isWindows = DIRECTORY_SEPARATOR === '\\';
         if (file_exists($path)) {
             $OK = true;
@@ -2068,7 +2071,7 @@ class GeneralUtility
         if ($regDirs) {
             $fileArr[md5($path)] = $path;
         }
-        $fileArr = array_merge($fileArr, self::getFilesInDir($path, $extList, 1, 1, $excludePattern));
+        $fileArr = array_merge($fileArr, (array)self::getFilesInDir($path, $extList, true, '', $excludePattern));
         $dirs = self::get_dirs($path);
         if ($recursivityLevels > 0 && is_array($dirs)) {
             foreach ($dirs as $subdirs) {
@@ -2180,10 +2183,12 @@ class GeneralUtility
      */
     public static function getMaxUploadFileSize()
     {
+        $uploadMaxFilesize = (string)ini_get('upload_max_filesize');
+        $postMaxSize = (string)ini_get('post_max_size');
         // Check for PHP restrictions of the maximum size of one of the $_FILES
-        $phpUploadLimit = self::getBytesFromSizeMeasurement(ini_get('upload_max_filesize'));
+        $phpUploadLimit = self::getBytesFromSizeMeasurement($uploadMaxFilesize);
         // Check for PHP restrictions of the maximum $_POST size
-        $phpPostLimit = self::getBytesFromSizeMeasurement(ini_get('post_max_size'));
+        $phpPostLimit = self::getBytesFromSizeMeasurement($postMaxSize);
         // If the total amount of post data is smaller (!) than the upload_max_filesize directive,
         // then this is the real limit in PHP
         $phpUploadLimit = $phpPostLimit > 0 && $phpPostLimit < $phpUploadLimit ? $phpPostLimit : $phpUploadLimit;
@@ -2206,7 +2211,7 @@ class GeneralUtility
         } elseif (stripos($measurement, 'K')) {
             $bytes *= 1024;
         }
-        return $bytes;
+        return (int)$bytes;
     }
 
     /**
@@ -2478,7 +2483,7 @@ class GeneralUtility
                                 $ip = '';
                         }
                     }
-                    if (self::validIP($ip)) {
+                    if (self::validIP((string)$ip)) {
                         $retVal = $ip;
                     }
                 }
@@ -2991,13 +2996,14 @@ class GeneralUtility
             self::mkdir_deep($temporaryPath);
         }
         if ($fileSuffix === '') {
-            $tempFileName = $temporaryPath . PathUtility::basename(tempnam($temporaryPath, $filePrefix));
+            $path = (string)tempnam($temporaryPath, $filePrefix);
+            $tempFileName = $temporaryPath . PathUtility::basename($path);
         } else {
             do {
                 $tempFileName = $temporaryPath . $filePrefix . random_int(1, PHP_INT_MAX) . $fileSuffix;
             } while (file_exists($tempFileName));
             touch($tempFileName);
-            clearstatcache(null, $tempFileName);
+            clearstatcache(false, $tempFileName);
         }
         return $tempFileName;
     }
@@ -3079,16 +3085,18 @@ class GeneralUtility
             if (class_exists($parts[0])) {
                 // Create object
                 $classObj = self::makeInstance($parts[0]);
-                if (method_exists($classObj, $parts[1])) {
+                $methodName = (string)$parts[1];
+                $callable = [$classObj, $methodName];
+                if (is_callable($callable)) {
                     // Call method:
-                    $content = call_user_func_array([&$classObj, $parts[1]], [&$params, &$ref]);
+                    $content = call_user_func_array($callable, [&$params, &$ref]);
                 } else {
                     throw new \InvalidArgumentException('No method name \'' . $parts[1] . '\' in class ' . $parts[0], 1294585865);
                 }
             } else {
                 throw new \InvalidArgumentException('No class named ' . $parts[0], 1294585866);
             }
-        } elseif (function_exists($funcName)) {
+        } elseif (function_exists($funcName) && is_callable($funcName)) {
             // It's a function
             $content = call_user_func_array($funcName, [&$params, &$ref]);
         } else {
@@ -3478,8 +3486,13 @@ class GeneralUtility
      */
     public static function quoteJSvalue($value)
     {
+        $json = (string)json_encode(
+            (string)$value,
+            JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_TAG
+        );
+
         return strtr(
-            json_encode((string)$value, JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_TAG),
+            $json,
             [
                 '"' => '\'',
                 '\\\\' => '\\u005C',
@@ -3499,7 +3512,7 @@ class GeneralUtility
      */
     public static function jsonEncodeForHtmlAttribute($value, bool $useHtmlEntities = true): string
     {
-        $json = json_encode($value, JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_TAG);
+        $json = (string)json_encode($value, JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_TAG);
         return $useHtmlEntities ? htmlspecialchars($json) : $json;
     }
 
diff --git a/typo3/sysext/core/Classes/Utility/PathUtility.php b/typo3/sysext/core/Classes/Utility/PathUtility.php
index b20f8ed22363..8b43e7d23197 100644
--- a/typo3/sysext/core/Classes/Utility/PathUtility.php
+++ b/typo3/sysext/core/Classes/Utility/PathUtility.php
@@ -114,7 +114,7 @@ class PathUtility
         if (count($paths) === 1) {
             $commonPath = array_shift($paths);
         } elseif (count($paths) > 1) {
-            $parts = explode('/', array_shift($paths));
+            $parts = explode('/', (string)array_shift($paths));
             $comparePath = '';
             $break = false;
             foreach ($parts as $part) {
@@ -164,7 +164,7 @@ class PathUtility
      */
     public static function basename($path)
     {
-        $currentLocale = setlocale(LC_CTYPE, 0);
+        $currentLocale = (string)setlocale(LC_CTYPE, '0');
         setlocale(LC_CTYPE, $GLOBALS['TYPO3_CONF_VARS']['SYS']['systemLocale']);
         $basename = basename($path);
         setlocale(LC_CTYPE, $currentLocale);
@@ -185,7 +185,7 @@ class PathUtility
      */
     public static function dirname($path)
     {
-        $currentLocale = setlocale(LC_CTYPE, 0);
+        $currentLocale = (string)setlocale(LC_CTYPE, '0');
         setlocale(LC_CTYPE, $GLOBALS['TYPO3_CONF_VARS']['SYS']['systemLocale']);
         $dirname = dirname($path);
         setlocale(LC_CTYPE, $currentLocale);
@@ -203,11 +203,11 @@ class PathUtility
      * @param string $path
      * @param int $options
      *
-     * @return string|array
+     * @return string|string[]
      */
     public static function pathinfo($path, $options = null)
     {
-        $currentLocale = setlocale(LC_CTYPE, 0);
+        $currentLocale = (string)setlocale(LC_CTYPE, '0');
         setlocale(LC_CTYPE, $GLOBALS['TYPO3_CONF_VARS']['SYS']['systemLocale']);
         $pathinfo = $options == null ? pathinfo($path) : pathinfo($path, $options);
         setlocale(LC_CTYPE, $currentLocale);
diff --git a/typo3/sysext/core/Classes/Utility/VersionNumberUtility.php b/typo3/sysext/core/Classes/Utility/VersionNumberUtility.php
index e787858769c4..441ef2ac07a0 100644
--- a/typo3/sysext/core/Classes/Utility/VersionNumberUtility.php
+++ b/typo3/sysext/core/Classes/Utility/VersionNumberUtility.php
@@ -34,7 +34,7 @@ class VersionNumberUtility
         $version = $versionParts[0];
         for ($i = 1; $i < 3; $i++) {
             if (!empty($versionParts[$i])) {
-                $version .= str_pad((int)$versionParts[$i], 3, '0', STR_PAD_LEFT);
+                $version .= str_pad((string)(int)$versionParts[$i], 3, '0', STR_PAD_LEFT);
             } else {
                 $version .= '000';
             }
@@ -86,7 +86,7 @@ class VersionNumberUtility
             $cleanedVersion = GeneralUtility::trimExplode('.', $versions[$i]);
             $cleanedVersionCount = count($cleanedVersion);
             for ($j = 0; $j < $cleanedVersionCount; $j++) {
-                $cleanedVersion[$j] = MathUtility::forceIntegerInRange($cleanedVersion[$j], 0, 999);
+                $cleanedVersion[$j] = MathUtility::forceIntegerInRange((int)$cleanedVersion[$j], 0, 999);
             }
             $cleanedVersionString = implode('.', $cleanedVersion);
             if (static::convertVersionNumberToInteger($cleanedVersionString) === 0) {
-- 
GitLab