From 051cf2072e178466e354fc8b8250a810d82386b8 Mon Sep 17 00:00:00 2001
From: jakotadesigngroup <suchowski@jakota.de>
Date: Thu, 16 Mar 2023 20:33:36 +0100
Subject: [PATCH] [BUGFIX] Backport null checks to Core QueryGenerator

This file was removed in v12 and only the version in
TYPO3\CMS\Lowlevel\Database\QueryGenerator
was updated with fixes.

This patch backports all the checks to the v11
TYPO3\CMS\Core\Database\QueryGenerator

Resolves: #98536
Releases: 11.5
Change-Id: I7aabc6e5b31529c15fd50b390c346b1ec7caf71d
Reviewed-on: https://review.typo3.org/c/Packages/TYPO3.CMS/+/78139
Reviewed-by: Andreas Fernandez <a.fernandez@scripting-base.de>
Tested-by: Christian Kuhn <lolli@schwarzbu.ch>
Tested-by: core-ci <typo3@b13.com>
Reviewed-by: Ralph Brugger <typo3bugs@public.linkpool.de>
Reviewed-by: Oliver Klee <typo3-coding@oliverklee.de>
Tested-by: Andreas Fernandez <a.fernandez@scripting-base.de>
Reviewed-by: Christian Kuhn <lolli@schwarzbu.ch>
---
 Build/phpstan/phpstan-baseline.neon           |   4 +-
 .../core/Classes/Database/QueryGenerator.php  | 249 +++++++++---------
 2 files changed, 126 insertions(+), 127 deletions(-)

diff --git a/Build/phpstan/phpstan-baseline.neon b/Build/phpstan/phpstan-baseline.neon
index 370993b29b32..b94a5c997a6e 100644
--- a/Build/phpstan/phpstan-baseline.neon
+++ b/Build/phpstan/phpstan-baseline.neon
@@ -951,7 +951,7 @@ parameters:
 			path: ../../typo3/sysext/core/Classes/Database/QueryGenerator.php
 
 		-
-			message: "#^Else branch is unreachable because previous condition is always true\\.$#"
+			message: "#^Offset 'queryConfig' on array on left side of \\?\\? always exists and is not nullable\\.$#"
 			count: 1
 			path: ../../typo3/sysext/core/Classes/Database/QueryGenerator.php
 
@@ -966,7 +966,7 @@ parameters:
 			path: ../../typo3/sysext/core/Classes/Database/QueryGenerator.php
 
 		-
-			message: "#^Offset 0 on \\*NEVER\\* in isset\\(\\) always exists and is always null\\.$#"
+			message: "#^Offset 0 on array\\{\\} in isset\\(\\) does not exist\\.$#"
 			count: 1
 			path: ../../typo3/sysext/core/Classes/Database/QueryGenerator.php
 
diff --git a/typo3/sysext/core/Classes/Database/QueryGenerator.php b/typo3/sysext/core/Classes/Database/QueryGenerator.php
index 916dccec7133..a19e5a8b8d66 100644
--- a/typo3/sysext/core/Classes/Database/QueryGenerator.php
+++ b/typo3/sysext/core/Classes/Database/QueryGenerator.php
@@ -285,41 +285,41 @@ class QueryGenerator
      */
     public function init($name, $table, $fieldList = '', array $settings = [])
     {
-        // Analysing the fields in the table.
-        if (is_array($GLOBALS['TCA'][$table])) {
+        // Analyzing the fields in the table.
+        if (is_array($GLOBALS['TCA'][$table] ?? false)) {
             $this->name = $name;
             $this->table = $table;
             $this->fieldList = $fieldList ?: $this->makeFieldList();
             $this->settings = $settings;
             $fieldArr = GeneralUtility::trimExplode(',', $this->fieldList, true);
             foreach ($fieldArr as $fieldName) {
-                $fC = $GLOBALS['TCA'][$this->table]['columns'][$fieldName];
-                $this->fields[$fieldName] = $fC['config'];
-                $this->fields[$fieldName]['exclude'] = $fC['exclude'];
-                if ($this->fields[$fieldName]['type'] === 'user' && !isset($this->fields[$fieldName]['type']['userFunc'])
-                    || $this->fields[$fieldName]['type'] === 'none'
+                $fC = $GLOBALS['TCA'][$this->table]['columns'][$fieldName] ?? [];
+                $this->fields[$fieldName] = $fC['config'] ?? [];
+                $this->fields[$fieldName]['exclude'] = $fC['exclude'] ?? '';
+                if (($this->fields[$fieldName]['type'] ?? '') === 'user' && !isset($this->fields[$fieldName]['type']['userFunc'])
+                    || ($this->fields[$fieldName]['type'] ?? '') === 'none'
                 ) {
                     // Do not list type=none "virtual" fields or query them from db,
                     // and if type is user without defined userFunc
                     unset($this->fields[$fieldName]);
                     continue;
                 }
-                if (is_array($fC) && $fC['label']) {
+                if (is_array($fC) && ($fC['label'] ?? false)) {
                     $this->fields[$fieldName]['label'] = rtrim(trim($this->getLanguageService()->sL($fC['label'])), ':');
                     switch ($this->fields[$fieldName]['type']) {
                         case 'input':
-                            if (preg_match('/int|year/i', $this->fields[$fieldName]['eval'])) {
+                            if (preg_match('/int|year/i', ($this->fields[$fieldName]['eval'] ?? ''))) {
                                 $this->fields[$fieldName]['type'] = 'number';
-                            } elseif (preg_match('/time/i', $this->fields[$fieldName]['eval'])) {
+                            } elseif (preg_match('/time/i', ($this->fields[$fieldName]['eval'] ?? ''))) {
                                 $this->fields[$fieldName]['type'] = 'time';
-                            } elseif (preg_match('/date/i', $this->fields[$fieldName]['eval'])) {
+                            } elseif (preg_match('/date/i', ($this->fields[$fieldName]['eval'] ?? ''))) {
                                 $this->fields[$fieldName]['type'] = 'date';
                             } else {
                                 $this->fields[$fieldName]['type'] = 'text';
                             }
                             break;
                         case 'check':
-                            if (!$this->fields[$fieldName]['items'] || count($this->fields[$fieldName]['items']) <= 1) {
+                            if (count($this->fields[$fieldName]['items'] ?? []) <= 1) {
                                 $this->fields[$fieldName]['type'] = 'boolean';
                             } else {
                                 $this->fields[$fieldName]['type'] = 'binary';
@@ -331,10 +331,10 @@ class QueryGenerator
                         case 'select':
                         case 'category':
                             $this->fields[$fieldName]['type'] = 'multiple';
-                            if ($this->fields[$fieldName]['foreign_table']) {
+                            if ($this->fields[$fieldName]['foreign_table'] ?? false) {
                                 $this->fields[$fieldName]['type'] = 'relation';
                             }
-                            if ($this->fields[$fieldName]['special']) {
+                            if ($this->fields[$fieldName]['special'] ?? false) {
                                 $this->fields[$fieldName]['type'] = 'text';
                             }
                             break;
@@ -425,7 +425,7 @@ class QueryGenerator
         $fields = array_unique(GeneralUtility::trimExplode(',', $list . ',' . $force, true));
         $reList = [];
         foreach ($fields as $fieldName) {
-            if ($this->fields[$fieldName]) {
+            if (isset($this->fields[$fieldName])) {
                 $reList[] = $fieldName;
             }
         }
@@ -442,7 +442,7 @@ class QueryGenerator
         $this->queryConfig = $qC;
         $POST = GeneralUtility::_POST();
         // If delete...
-        if ($POST['qG_del']) {
+        if ($POST['qG_del'] ?? false) {
             // Initialize array to work on, save special parameters
             $ssArr = $this->getSubscript($POST['qG_del']);
             $workArr = &$this->queryConfig;
@@ -453,14 +453,14 @@ class QueryGenerator
             }
             // Delete the entry and move the other entries
             unset($workArr[$ssArr[$i]]);
-            $workArrSize = count($workArr);
+            $workArrSize = count((array)$workArr);
             for ($j = $ssArr[$i]; $j < $workArrSize; $j++) {
                 $workArr[$j] = $workArr[$j + 1];
                 unset($workArr[$j + 1]);
             }
         }
         // If insert...
-        if ($POST['qG_ins']) {
+        if ($POST['qG_ins'] ?? false) {
             // Initialize array to work on, save special parameters
             $ssArr = $this->getSubscript($POST['qG_ins']);
             $workArr = &$this->queryConfig;
@@ -470,7 +470,7 @@ class QueryGenerator
                 $workArr = &$workArr[$ssArr[$i]];
             }
             // Move all entries above position where new entry is to be inserted
-            $workArrSize = count($workArr);
+            $workArrSize = count((array)$workArr);
             for ($j = $workArrSize; $j > $ssArr[$i]; $j--) {
                 $workArr[$j] = $workArr[$j - 1];
             }
@@ -479,7 +479,7 @@ class QueryGenerator
             $workArr[$ssArr[$i] + 1]['type'] = 'FIELD_';
         }
         // If move up...
-        if ($POST['qG_up']) {
+        if ($POST['qG_up'] ?? false) {
             // Initialize array to work on
             $ssArr = $this->getSubscript($POST['qG_up']);
             $workArr = &$this->queryConfig;
@@ -494,7 +494,7 @@ class QueryGenerator
             $workArr[$ssArr[$i] - 1] = $qG_tmp;
         }
         // If new level...
-        if ($POST['qG_nl']) {
+        if ($POST['qG_nl'] ?? false) {
             // Initialize array to work on
             $ssArr = $this->getSubscript($POST['qG_nl']);
             $workArr = &$this->queryConfig;
@@ -516,7 +516,7 @@ class QueryGenerator
             }
         }
         // If collapse level...
-        if ($POST['qG_remnl']) {
+        if ($POST['qG_remnl'] ?? false) {
             // Initialize array to work on
             $ssArr = $this->getSubscript($POST['qG_remnl']);
             $workArr = &$this->queryConfig;
@@ -528,7 +528,7 @@ class QueryGenerator
             // Do stuff:
             $tempEl = $workArr[$ssArr[$i]];
             if (is_array($tempEl)) {
-                if ($tempEl['type'] === 'newlevel') {
+                if ($tempEl['type'] === 'newlevel' && is_array($workArr)) {
                     $a1 = array_slice($workArr, 0, $ssArr[$i]);
                     $a2 = array_slice($workArr, $ssArr[$i]);
                     array_shift($a2);
@@ -549,7 +549,7 @@ class QueryGenerator
     public function cleanUpQueryConfig($queryConfig)
     {
         // Since we don't traverse the array using numeric keys in the upcoming while-loop make sure it's fresh and clean before displaying
-        if (is_array($queryConfig)) {
+        if (!empty($queryConfig) && is_array($queryConfig)) {
             ksort($queryConfig);
         } else {
             // queryConfig should never be empty!
@@ -562,10 +562,10 @@ class QueryGenerator
         // Traverse:
         foreach ($queryConfig as $key => $conf) {
             $fieldName = '';
-            if (strpos($conf['type'], 'FIELD_') === 0) {
+            if (str_starts_with(($conf['type'] ?? ''), 'FIELD_')) {
                 $fieldName = substr($conf['type'], 6);
-                $fieldType = $this->fields[$fieldName]['type'];
-            } elseif ($conf['type'] === 'newlevel') {
+                $fieldType = $this->fields[$fieldName]['type'] ?? '';
+            } elseif (($conf['type'] ?? '') === 'newlevel') {
                 $fieldType = $conf['type'];
             } else {
                 $fieldType = 'ignore';
@@ -578,16 +578,15 @@ class QueryGenerator
                     $queryConfig[$key]['nl'] = $this->cleanUpQueryConfig($queryConfig[$key]['nl']);
                     break;
                 case 'userdef':
-                    $queryConfig[$key] = $this->userDefCleanUp($queryConfig[$key]);
                     break;
                 case 'ignore':
                 default:
                     $verifiedName = $this->verifyType($fieldName);
                     $queryConfig[$key]['type'] = 'FIELD_' . $this->verifyType($verifiedName);
-                    if ($this->comp_offsets[$fieldType] != $conf['comparison'] >> 5) {
-                        $conf['comparison'] = $this->comp_offsets[$fieldType] << 5;
+                    if ((int)($conf['comparison'] ?? 0) >> 5 !== (int)($this->comp_offsets[$fieldType] ?? 0)) {
+                        $conf['comparison'] = (int)($this->comp_offsets[$fieldType] ?? 0) << 5;
                     }
-                    $queryConfig[$key]['comparison'] = $this->verifyComparison($conf['comparison'], $conf['negate'] ? 1 : 0);
+                    $queryConfig[$key]['comparison'] = $this->verifyComparison($conf['comparison'] ?? '0', ($conf['negate'] ?? null) ? 1 : 0);
                     $queryConfig[$key]['inputValue'] = $this->cleanInputVal($queryConfig[$key]);
                     $queryConfig[$key]['inputValue1'] = $this->cleanInputVal($queryConfig[$key], '1');
             }
@@ -616,19 +615,19 @@ class QueryGenerator
             $fieldName = '';
             $subscript = $parent . '[' . $key . ']';
             $lineHTML = [];
-            $lineHTML[] = $this->mkOperatorSelect($this->name . $subscript, $conf['operator'], $c > 0, $conf['type'] !== 'FIELD_');
-            if (strpos($conf['type'], 'FIELD_') === 0) {
+            $lineHTML[] = $this->mkOperatorSelect($this->name . $subscript, ($conf['operator'] ?? ''), (bool)$c, ($conf['type'] ?? '') !== 'FIELD_');
+            if (str_starts_with(($conf['type'] ?? ''), 'FIELD_')) {
                 $fieldName = substr($conf['type'], 6);
                 $this->fieldName = $fieldName;
-                $fieldType = $this->fields[$fieldName]['type'];
-                if ($this->comp_offsets[$fieldType] != $conf['comparison'] >> 5) {
-                    $conf['comparison'] = $this->comp_offsets[$fieldType] << 5;
+                $fieldType = $this->fields[$fieldName]['type'] ?? '';
+                if ((int)($conf['comparison'] ?? 0) >> 5 !== (int)($this->comp_offsets[$fieldType] ?? 0)) {
+                    $conf['comparison'] = (int)($this->comp_offsets[$fieldType] ?? 0) << 5;
                 }
                 //nasty nasty...
                 //make sure queryConfig contains _actual_ comparevalue.
                 //mkCompSelect don't care, but getQuery does.
                 $queryConfig[$key]['comparison'] += isset($conf['negate']) - $conf['comparison'] % 2;
-            } elseif ($conf['type'] === 'newlevel') {
+            } elseif (($conf['type'] ?? '') === 'newlevel') {
                 $fieldType = $conf['type'];
             } else {
                 $fieldType = 'ignore';
@@ -645,7 +644,7 @@ class QueryGenerator
                     $codeArr[$arrCount]['sub'] = $this->getFormElements($subLevel + 1, $queryConfig[$key]['nl'], $subscript . '[nl]');
                     break;
                 case 'userdef':
-                    $lineHTML[] = $this->userDef($fieldPrefix, $conf, $fieldName, $fieldType);
+                    $lineHTML[] = '';
                     break;
                 case 'date':
                     $lineHTML[] = '<div class="form-inline">';
@@ -682,7 +681,7 @@ class QueryGenerator
                         if (is_array($conf['inputValue'])) {
                             $conf['inputValue'] = implode(',', $conf['inputValue']);
                         }
-                        $lineHTML[] = '<input class="form-control t3js-clearable" type="text" value="' . htmlspecialchars($conf['inputValue']) . '" name="' . $fieldPrefix . '[inputValue]">';
+                        $lineHTML[] = '<input class="form-control t3js-clearable" type="text" value="' . htmlspecialchars($conf['inputValue'] ?? '') . '" name="' . $fieldPrefix . '[inputValue]">';
                     } elseif ($conf['comparison'] === 64) {
                         if (is_array($conf['inputValue'])) {
                             $conf['inputValue'] = $conf['inputValue'][0];
@@ -708,10 +707,10 @@ class QueryGenerator
                     $lineHTML[] = $this->makeComparisonSelector($subscript, $fieldName, $conf);
                     if ($conf['comparison'] === 37 || $conf['comparison'] === 36) {
                         // between:
-                        $lineHTML[] = '<input class="form-control t3js-clearable" type="text" value="' . htmlspecialchars($conf['inputValue']) . '" name="' . $fieldPrefix . '[inputValue]">';
-                        $lineHTML[] = '<input class="form-control t3js-clearable" type="text" value="' . htmlspecialchars($conf['inputValue1']) . '" name="' . $fieldPrefix . '[inputValue1]">';
+                        $lineHTML[] = '<input class="form-control t3js-clearable" type="text" value="' . htmlspecialchars($conf['inputValue'] ?? '') . '" name="' . $fieldPrefix . '[inputValue]">';
+                        $lineHTML[] = '<input class="form-control t3js-clearable" type="text" value="' . htmlspecialchars($conf['inputValue1'] ?? '') . '" name="' . $fieldPrefix . '[inputValue1]">';
                     } else {
-                        $lineHTML[] = '<input class="form-control t3js-clearable" type="text" value="' . htmlspecialchars($conf['inputValue']) . '" name="' . $fieldPrefix . '[inputValue]">';
+                        $lineHTML[] = '<input class="form-control t3js-clearable" type="text" value="' . htmlspecialchars($conf['inputValue'] ?? '') . '" name="' . $fieldPrefix . '[inputValue]">';
                     }
                     $lineHTML[] = '</div>';
             }
@@ -756,9 +755,9 @@ class QueryGenerator
         $lineHTML = [];
         $lineHTML[] = $this->mkTypeSelect($fieldPrefix . '[type]', $fieldName);
         $lineHTML[] = '	<div class="input-group">';
-        $lineHTML[] = $this->mkCompSelect($fieldPrefix . '[comparison]', $conf['comparison'], $conf['negate'] ? 1 : 0);
+        $lineHTML[] = $this->mkCompSelect($fieldPrefix . '[comparison]', $conf['comparison'], ($conf['negate'] ?? null) ? 1 : 0);
         $lineHTML[] = '	<div class="input-group-addon">';
-        $lineHTML[] = '		<input type="checkbox" class="checkbox t3js-submit-click"' . ($conf['negate'] ? ' checked' : '') . ' name="' . htmlspecialchars($fieldPrefix) . '[negate]">';
+        $lineHTML[] = '		<input type="checkbox" class="checkbox t3js-submit-click"' . (($conf['negate'] ?? null) ? ' checked' : '') . ' name="' . htmlspecialchars($fieldPrefix) . '[negate]">';
         $lineHTML[] = '	</div>';
         $lineHTML[] = '	</div>';
         return implode(LF, $lineHTML);
@@ -780,7 +779,7 @@ class QueryGenerator
         $languageService = $this->getLanguageService();
         if ($fieldSetup['type'] === 'multiple') {
             $optGroupOpen = false;
-            foreach ($fieldSetup['items'] as $val) {
+            foreach (($fieldSetup['items'] ?? []) as $val) {
                 if (strpos($val[0], 'LLL:') === 0) {
                     $value = $languageService->sL($val[0]);
                 } else {
@@ -804,7 +803,7 @@ class QueryGenerator
             }
         }
         if ($fieldSetup['type'] === 'binary') {
-            foreach ($fieldSetup['items'] as $key => $val) {
+            foreach (($fieldSetup['items'] ?? []) as $key => $val) {
                 if (strpos($val[0], 'LLL:') === 0) {
                     $value = $languageService->sL($val[0]);
                 } else {
@@ -821,23 +820,22 @@ class QueryGenerator
         if ($fieldSetup['type'] === 'relation') {
             $useTablePrefix = 0;
             $dontPrefixFirstTable = 0;
-            if ($fieldSetup['items']) {
-                foreach ($fieldSetup['items'] as $val) {
-                    if (strpos($val[0], 'LLL:') === 0) {
-                        $value = $languageService->sL($val[0]);
-                    } else {
-                        $value = $val[0];
-                    }
-                    $outputValue = (string)($val[1] ?? '');
-                    if ($outputValue && GeneralUtility::inList($conf['inputValue'], $outputValue)) {
-                        $out[] = '<option value="' . htmlspecialchars($outputValue) . '" selected>' . htmlspecialchars($value) . '</option>';
-                    } else {
-                        $out[] = '<option value="' . htmlspecialchars($outputValue) . '">' . htmlspecialchars($value) . '</option>';
-                    }
+            foreach (($fieldSetup['items'] ?? []) as $val) {
+                if (strpos($val[0], 'LLL:') === 0) {
+                    $value = $languageService->sL($val[0]);
+                } else {
+                    $value = $val[0];
+                }
+                $outputValue = (string)($val[1] ?? '');
+                if (GeneralUtility::inList($conf['inputValue'], $outputValue)) {
+                    $out[] = '<option value="' . htmlspecialchars($outputValue) . '" selected>' . htmlspecialchars($value) . '</option>';
+                } else {
+                    $out[] = '<option value="' . htmlspecialchars($outputValue) . '">' . htmlspecialchars($value) . '</option>';
                 }
             }
-            if (str_contains($fieldSetup['allowed'], ',')) {
-                $from_table_Arr = explode(',', $fieldSetup['allowed']);
+            $allowedFields = $fieldSetup['allowed'] ?? '';
+            if (str_contains($allowedFields, ',')) {
+                $from_table_Arr = explode(',', $allowedFields);
                 $useTablePrefix = 1;
                 if (!$fieldSetup['prepend_tname']) {
                     $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($table);
@@ -862,12 +860,12 @@ class QueryGenerator
                     }
                 }
             } else {
-                $from_table_Arr[0] = $fieldSetup['allowed'];
+                $from_table_Arr[0] = $allowedFields;
             }
-            if ($fieldSetup['prepend_tname']) {
+            if (!empty($fieldSetup['prepend_tname'])) {
                 $useTablePrefix = 1;
             }
-            if ($fieldSetup['foreign_table']) {
+            if (!empty($fieldSetup['foreign_table'])) {
                 $from_table_Arr[0] = $fieldSetup['foreign_table'];
             }
             $counter = 0;
@@ -883,9 +881,9 @@ class QueryGenerator
                 }
                 $counter = 1;
                 if (is_array($GLOBALS['TCA'][$from_table])) {
-                    $labelField = $GLOBALS['TCA'][$from_table]['ctrl']['label'];
-                    $altLabelField = $GLOBALS['TCA'][$from_table]['ctrl']['label_alt'];
-                    if ($GLOBALS['TCA'][$from_table]['columns'][$labelField]['config']['items']) {
+                    $labelField = $GLOBALS['TCA'][$from_table]['ctrl']['label'] ?? '';
+                    $altLabelField = $GLOBALS['TCA'][$from_table]['ctrl']['label_alt'] ?? '';
+                    if ($GLOBALS['TCA'][$from_table]['columns'][$labelField]['config']['items'] ?? false) {
                         foreach ($GLOBALS['TCA'][$from_table]['columns'][$labelField]['config']['items'] as $labelArray) {
                             if (strpos($labelArray[0], 'LLL:') === 0) {
                                 $labelFieldSelect[$labelArray[1]] = $languageService->sL($labelArray[0]);
@@ -896,7 +894,7 @@ class QueryGenerator
                         $useSelectLabels = true;
                     }
                     $altLabelFieldSelect = [];
-                    if ($GLOBALS['TCA'][$from_table]['columns'][$altLabelField]['config']['items']) {
+                    if ($GLOBALS['TCA'][$from_table]['columns'][$altLabelField]['config']['items'] ?? false) {
                         foreach ($GLOBALS['TCA'][$from_table]['columns'][$altLabelField]['config']['items'] as $altLabelArray) {
                             if (strpos($altLabelArray[0], 'LLL:') === 0) {
                                 $altLabelFieldSelect[$altLabelArray[1]] = $languageService->sL($altLabelArray[0]);
@@ -907,12 +905,11 @@ class QueryGenerator
                         $useAltSelectLabels = true;
                     }
 
-                    if (!$this->tableArray[$from_table]) {
+                    if (!($this->tableArray[$from_table] ?? false)) {
                         $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($from_table);
-                        if (isset($this->settings['show_deleted']) && $this->settings['show_deleted']) {
-                            $queryBuilder->getRestrictions()->removeAll();
-                        } else {
-                            $queryBuilder->getRestrictions()->removeAll()->add(GeneralUtility::makeInstance(DeletedRestriction::class));
+                        $queryBuilder->getRestrictions()->removeAll();
+                        if (empty($this->settings['show_deleted'])) {
+                            $queryBuilder->getRestrictions()->add(GeneralUtility::makeInstance(DeletedRestriction::class));
                         }
                         $selectFields = ['uid', $labelField];
                         if ($altLabelField) {
@@ -963,7 +960,7 @@ class QueryGenerator
                         }
                     }
 
-                    foreach ($this->tableArray[$from_table] as $key => $val) {
+                    foreach (($this->tableArray[$from_table] ?? []) as $val) {
                         if ($useSelectLabels) {
                             $outArray[$tablePrefix . $val['uid']] = htmlspecialchars($labelFieldSelect[$val[$labelField]]);
                         } elseif ($val[$labelField]) {
@@ -1011,7 +1008,7 @@ class QueryGenerator
                 $out[] = htmlspecialchars($v['query']);
                 $out[] = '</pre>';
             }
-            if (is_array($v['sub'])) {
+            if (is_array($v['sub'] ?? false)) {
                 $out[] = '<div>';
                 $out[] = $this->printCodeArray($v['sub'], $recursionLevel + 1);
                 $out[] = '</div>';
@@ -1061,8 +1058,8 @@ class QueryGenerator
         $out[] = '<select class="form-select t3js-submit-change" name="' . htmlspecialchars($name) . '">';
         $out[] = '<option value=""></option>';
         foreach ($this->fields as $key => $value) {
-            if (!$value['exclude'] || $this->getBackendUserAuthentication()->check('non_exclude_fields', $this->table . ':' . $key)) {
-                $label = $this->fields[$key]['label'];
+            if (!($value['exclude'] ?? false) || $this->getBackendUserAuthentication()->check('non_exclude_fields', $this->table . ':' . $key)) {
+                $label = $this->fields[$key]['label'] ?? '[' . $key . ']';
                 $out[] = '<option value="' . htmlspecialchars($prepend . $key) . '"' . ($key === $fieldName ? ' selected' : '') . '>' . htmlspecialchars($label) . '</option>';
             }
         }
@@ -1175,7 +1172,7 @@ class QueryGenerator
         $out = [];
         $out[] = '<select class="form-select t3js-submit-change" name="' . $name . '">';
         for ($i = 32 * $compOffSet + $neg; $i < 32 * ($compOffSet + 1); $i += 2) {
-            if ($this->lang['comparison'][$i . '_']) {
+            if ($this->lang['comparison'][$i . '_'] ?? false) {
                 $out[] = '<option value="' . $i . '"' . ($i >> 1 === $comparison >> 1 ? ' selected' : '') . '>' . htmlspecialchars($this->lang['comparison'][$i . '_']) . '</option>';
             }
         }
@@ -1279,9 +1276,9 @@ class QueryGenerator
      */
     protected function convertIso8601DatetimeStringToUnixTimestamp(array $conf): array
     {
-        if ($this->isDateOfIso8601Format($conf['inputValue'])) {
+        if ($this->isDateOfIso8601Format($conf['inputValue'] ?? '')) {
             $conf['inputValue'] = strtotime($conf['inputValue']);
-            if ($this->isDateOfIso8601Format($conf['inputValue1'])) {
+            if ($this->isDateOfIso8601Format($conf['inputValue1'] ?? '')) {
                 $conf['inputValue1'] = strtotime($conf['inputValue1']);
             }
         }
@@ -1314,28 +1311,29 @@ class QueryGenerator
      */
     public function getQuerySingle($conf, $first)
     {
+        $comparison = (int)($conf['comparison'] ?? 0);
         $qs = '';
         $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable($this->table);
         $prefix = $this->enablePrefix ? $this->table . '.' : '';
         if (!$first) {
             // Is it OK to insert the AND operator if none is set?
-            $operator = strtoupper(trim($conf['operator']));
+            $operator = strtoupper(trim($conf['operator'] ?? ''));
             if (!in_array($operator, ['AND', 'OR'], true)) {
                 $operator = 'AND';
             }
             $qs .= $operator . ' ';
         }
-        $qsTmp = str_replace('#FIELD#', $prefix . trim(substr($conf['type'], 6)), $this->compSQL[$conf['comparison']]);
+        $qsTmp = str_replace('#FIELD#', $prefix . trim(substr($conf['type'], 6)), $this->compSQL[$comparison] ?? '');
         $inputVal = $this->cleanInputVal($conf);
-        if ($conf['comparison'] === 68 || $conf['comparison'] === 69) {
-            $inputVal = explode(',', $inputVal);
+        if ($comparison === 68 || $comparison === 69) {
+            $inputVal = explode(',', (string)$inputVal);
             foreach ($inputVal as $key => $fileName) {
                 $inputVal[$key] = $queryBuilder->quote($fileName);
             }
             $inputVal = implode(',', $inputVal);
             $qsTmp = str_replace('#VALUE#', $inputVal, $qsTmp);
-        } elseif ($conf['comparison'] === 162 || $conf['comparison'] === 163) {
-            $inputValArray = explode(',', $inputVal);
+        } elseif ($comparison === 162 || $comparison === 163) {
+            $inputValArray = explode(',', (string)$inputVal);
             $inputVal = 0;
             foreach ($inputValArray as $fileName) {
                 $inputVal += (int)$fileName;
@@ -1345,12 +1343,12 @@ class QueryGenerator
             if (is_array($inputVal)) {
                 $inputVal = $inputVal[0];
             }
-            $qsTmp = str_replace('#VALUE#', trim($queryBuilder->quote($inputVal), '\''), $qsTmp);
+            $qsTmp = str_replace('#VALUE#', trim($queryBuilder->quote((string)$inputVal), '\''), $qsTmp);
         }
-        if ($conf['comparison'] === 37 || $conf['comparison'] === 36 || $conf['comparison'] === 66 || $conf['comparison'] === 67 || $conf['comparison'] === 100 || $conf['comparison'] === 101) {
+        if ($comparison === 37 || $comparison === 36 || $comparison === 66 || $comparison === 67 || $comparison === 100 || $comparison === 101) {
             // between:
             $inputVal = $this->cleanInputVal($conf, '1');
-            $qsTmp = str_replace('#VALUE1#', trim($queryBuilder->quote($inputVal), '\''), $qsTmp);
+            $qsTmp = str_replace('#VALUE1#', trim($queryBuilder->quote((string)$inputVal), '\''), $qsTmp);
         }
         $qs .= trim((string)$qsTmp);
         return $qs;
@@ -1365,28 +1363,29 @@ class QueryGenerator
      */
     public function cleanInputVal($conf, $suffix = '')
     {
-        if ($conf['comparison'] >> 5 === 0 || ($conf['comparison'] === 32 || $conf['comparison'] === 33 || $conf['comparison'] === 64 || $conf['comparison'] === 65 || $conf['comparison'] === 66 || $conf['comparison'] === 67 || $conf['comparison'] === 96 || $conf['comparison'] === 97)) {
-            $inputVal = $conf['inputValue' . $suffix];
-        } elseif ($conf['comparison'] === 39 || $conf['comparison'] === 38) {
+        $comparison = (int)($conf['comparison'] ?? 0);
+        if ($comparison >> 5 === 0 || ($comparison === 32 || $comparison === 33 || $comparison === 64 || $comparison === 65 || $comparison === 66 || $comparison === 67 || $comparison === 96 || $comparison === 97)) {
+            $inputVal = $conf['inputValue' . $suffix] ?? null;
+        } elseif ($comparison === 39 || $comparison === 38) {
             // in list:
-            $inputVal = implode(',', GeneralUtility::intExplode(',', $conf['inputValue' . $suffix]));
-        } elseif ($conf['comparison'] === 68 || $conf['comparison'] === 69 || $conf['comparison'] === 162 || $conf['comparison'] === 163) {
+            $inputVal = implode(',', GeneralUtility::intExplode(',', ($conf['inputValue' . $suffix] ?? '')));
+        } elseif ($comparison === 68 || $comparison === 69 || $comparison === 162 || $comparison === 163) {
             // in list:
-            if (is_array($conf['inputValue' . $suffix])) {
+            if (is_array($conf['inputValue' . $suffix] ?? false)) {
                 $inputVal = implode(',', $conf['inputValue' . $suffix]);
-            } elseif ($conf['inputValue' . $suffix]) {
+            } elseif ($conf['inputValue' . $suffix] ?? false) {
                 $inputVal = $conf['inputValue' . $suffix];
             } else {
                 $inputVal = 0;
             }
-        } elseif (!is_array($conf['inputValue' . $suffix]) && strtotime($conf['inputValue' . $suffix])) {
+        } elseif (!is_array($conf['inputValue' . $suffix] ?? null) && strtotime($conf['inputValue' . $suffix] ?? '')) {
             $inputVal = $conf['inputValue' . $suffix];
-        } elseif (!is_array($conf['inputValue' . $suffix]) && MathUtility::canBeInterpretedAsInteger($conf['inputValue' . $suffix])) {
+        } elseif (!is_array($conf['inputValue' . $suffix] ?? null) && MathUtility::canBeInterpretedAsInteger($conf['inputValue' . $suffix] ?? null)) {
             $inputVal = (int)$conf['inputValue' . $suffix];
         } else {
             // TODO: Six eyes looked at this code and nobody understood completely what is going on here and why we
             // fallback to float casting, the whole class smells like it needs a refactoring.
-            $inputVal = (float)$conf['inputValue' . $suffix];
+            $inputVal = (float)($conf['inputValue' . $suffix] ?? 0.0);
         }
         return $inputVal;
     }
@@ -1435,7 +1434,7 @@ class QueryGenerator
         $userTsConfig = $this->getBackendUserAuthentication()->getTSConfig();
 
         // Make output
-        if (in_array('table', $enableArr) && !$userTsConfig['mod.']['dbint.']['disableSelectATable']) {
+        if (in_array('table', $enableArr) && !($userTsConfig['mod.']['dbint.']['disableSelectATable'] ?? false)) {
             $out[] = '<div class="form-group">';
             $out[] = '	<label for="SET[queryTable]">Select a table:</label>';
             $out[] =    $this->mkTableSelect('SET[queryTable]', $this->table);
@@ -1443,25 +1442,25 @@ class QueryGenerator
         }
         if ($this->table) {
             // Init fields:
-            $this->setAndCleanUpExternalLists('queryFields', $modSettings['queryFields'], 'uid,' . $this->getLabelCol());
-            $this->setAndCleanUpExternalLists('queryGroup', $modSettings['queryGroup']);
-            $this->setAndCleanUpExternalLists('queryOrder', $modSettings['queryOrder'] . ',' . $modSettings['queryOrder2']);
+            $this->setAndCleanUpExternalLists('queryFields', $modSettings['queryFields'] ?? '', 'uid,' . $this->getLabelCol());
+            $this->setAndCleanUpExternalLists('queryGroup', $modSettings['queryGroup'] ?? '');
+            $this->setAndCleanUpExternalLists('queryOrder', ($modSettings['queryOrder'] ?? '') . ',' . ($modSettings['queryOrder2'] ?? ''));
             // Limit:
-            $this->extFieldLists['queryLimit'] = $modSettings['queryLimit'];
+            $this->extFieldLists['queryLimit'] = $modSettings['queryLimit'] ?? '';
             if (!$this->extFieldLists['queryLimit']) {
                 $this->extFieldLists['queryLimit'] = 100;
             }
             $parts = GeneralUtility::intExplode(',', $this->extFieldLists['queryLimit']);
             $limitBegin = 0;
             $limitLength = (int)($this->extFieldLists['queryLimit'] ?? 0);
-            if ($parts[1]) {
+            if ($parts[1] ?? false) {
                 $limitBegin = (int)$parts[0];
                 $limitLength = (int)$parts[1];
             }
             $this->extFieldLists['queryLimit'] = implode(',', array_slice($parts, 0, 2));
             // Insert Descending parts
-            if ($this->extFieldLists['queryOrder']) {
-                $descParts = explode(',', $modSettings['queryOrderDesc'] . ',' . $modSettings['queryOrder2Desc']);
+            if ($this->extFieldLists['queryOrder'] ?? false) {
+                $descParts = explode(',', ($modSettings['queryOrderDesc'] ?? '') . ',' . ($modSettings['queryOrder2Desc'] ?? ''));
                 $orderParts = explode(',', $this->extFieldLists['queryOrder']);
                 $reList = [];
                 foreach ($orderParts as $kk => $vv) {
@@ -1470,30 +1469,30 @@ class QueryGenerator
                 $this->extFieldLists['queryOrder_SQL'] = implode(',', $reList);
             }
             // Query Generator:
-            $this->procesData($modSettings['queryConfig'] ? unserialize($modSettings['queryConfig'], ['allowed_classes' => false]) : []);
+            $this->procesData(($modSettings['queryConfig'] ?? false) ? unserialize($modSettings['queryConfig'] ?? '', ['allowed_classes' => false]) : []);
             $this->queryConfig = $this->cleanUpQueryConfig($this->queryConfig);
-            $this->enableQueryParts = (bool)$modSettings['search_query_smallparts'];
+            $this->enableQueryParts = (bool)($modSettings['search_query_smallparts'] ?? false);
             $codeArr = $this->getFormElements();
             $queryCode = $this->printCodeArray($codeArr);
-            if (in_array('fields', $enableArr) && !$userTsConfig['mod.']['dbint.']['disableSelectFields']) {
+            if (in_array('fields', $enableArr) && !($userTsConfig['mod.']['dbint.']['disableSelectFields'] ?? false)) {
                 $out[] = '<div class="form-group form-group-with-button-addon">';
                 $out[] = '	<label for="SET[queryFields]">Select fields:</label>';
                 $out[] =    $this->mkFieldToInputSelect('SET[queryFields]', $this->extFieldLists['queryFields']);
                 $out[] = '</div>';
             }
-            if (in_array('query', $enableArr) && !$userTsConfig['mod.']['dbint.']['disableMakeQuery']) {
+            if (in_array('query', $enableArr) && !($userTsConfig['mod.']['dbint.']['disableMakeQuery'] ?? false)) {
                 $out[] = '<div class="form-group">';
                 $out[] = '	<label>Make Query:</label>';
                 $out[] =    $queryCode;
                 $out[] = '</div>';
             }
-            if (in_array('group', $enableArr) && !$userTsConfig['mod.']['dbint.']['disableGroupBy']) {
+            if (in_array('group', $enableArr) && !($userTsConfig['mod.']['dbint.']['disableGroupBy'] ?? false)) {
                 $out[] = '<div class="form-group form-inline">';
                 $out[] = '	<label for="SET[queryGroup]">Group By:</label>';
                 $out[] =     $this->mkTypeSelect('SET[queryGroup]', $this->extFieldLists['queryGroup'], '');
                 $out[] = '</div>';
             }
-            if (in_array('order', $enableArr) && !$userTsConfig['mod.']['dbint.']['disableOrderBy']) {
+            if (in_array('order', $enableArr) && !($userTsConfig['mod.']['dbint.']['disableOrderBy'] ?? false)) {
                 $orderByArr = explode(',', $this->extFieldLists['queryOrder']);
                 $orderBy = [];
                 $orderBy[] = $this->mkTypeSelect('SET[queryOrder]', $orderByArr[0], '');
@@ -1507,7 +1506,7 @@ class QueryGenerator
                 if ($orderByArr[0]) {
                     $orderBy[] = $this->mkTypeSelect('SET[queryOrder2]', $orderByArr[1], '');
                     $orderBy[] = '<div class="form-check">';
-                    $orderBy[] = BackendUtility::getFuncCheck(0, 'SET[queryOrder2Desc]', $modSettings['queryOrder2Desc'], '', '', 'id="checkQueryOrder2Desc"');
+                    $orderBy[] = BackendUtility::getFuncCheck(0, 'SET[queryOrder2Desc]', $modSettings['queryOrder2Desc'] ?? false, '', '', 'id="checkQueryOrder2Desc"');
                     $orderBy[] = '	<label class="form-check-label" for="checkQueryOrder2Desc">';
                     $orderBy[] =        'Descending';
                     $orderBy[] = '	</label>';
@@ -1518,7 +1517,7 @@ class QueryGenerator
                 $out[] =     implode(LF, $orderBy);
                 $out[] = '</div>';
             }
-            if (in_array('limit', $enableArr) && !$userTsConfig['mod.']['dbint.']['disableLimit']) {
+            if (in_array('limit', $enableArr) && !($userTsConfig['mod.']['dbint.']['disableLimit'] ?? false)) {
                 $limit = [];
                 $limit[] = '<div class="input-group">';
                 $limit[] = '	<span class="input-group-btn">';
@@ -1584,8 +1583,8 @@ class QueryGenerator
         if ($id < 0) {
             $id = abs($id);
         }
-        if ($begin === 0) {
-            $theList = $id;
+        if ($begin == 0) {
+            $theList = (string)$id;
         } else {
             $theList = '';
         }
@@ -1629,30 +1628,30 @@ class QueryGenerator
     {
         $backendUserAuthentication = $this->getBackendUserAuthentication();
         $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($this->table);
-        if (isset($this->settings['show_deleted']) && $this->settings['show_deleted']) {
-            $queryBuilder->getRestrictions()->removeAll();
-        } else {
-            $queryBuilder->getRestrictions()->removeAll()->add(GeneralUtility::makeInstance(DeletedRestriction::class));
+        $queryBuilder->getRestrictions()->removeAll();
+        if (empty($this->settings['show_deleted'])) {
+            $queryBuilder->getRestrictions()->add(GeneralUtility::makeInstance(DeletedRestriction::class));
         }
+        $deleteField = $GLOBALS['TCA'][$this->table]['ctrl']['delete'] ?? '';
         $fieldList = GeneralUtility::trimExplode(
             ',',
             $this->extFieldLists['queryFields']
             . ',pid'
-            . ($GLOBALS['TCA'][$this->table]['ctrl']['delete'] ? ',' . $GLOBALS['TCA'][$this->table]['ctrl']['delete'] : '')
+            . ($deleteField ? ',' . $deleteField : '')
         );
         $queryBuilder->select(...$fieldList)
             ->from($this->table);
 
-        if ($this->extFieldLists['queryGroup']) {
+        if ($this->extFieldLists['queryGroup'] ?? false) {
             $queryBuilder->groupBy(...QueryHelper::parseGroupBy($this->extFieldLists['queryGroup']));
         }
-        if ($this->extFieldLists['queryOrder']) {
+        if ($this->extFieldLists['queryOrder'] ?? false) {
             foreach (QueryHelper::parseOrderBy($this->extFieldLists['queryOrder_SQL']) as $orderPair) {
                 [$fieldName, $order] = $orderPair;
                 $queryBuilder->addOrderBy($fieldName, $order);
             }
         }
-        if ($this->extFieldLists['queryLimit']) {
+        if ($this->extFieldLists['queryLimit'] ?? false) {
             // Explode queryLimit to fetch the limit and a possible offset
             $parts = GeneralUtility::intExplode(',', $this->extFieldLists['queryLimit']);
             if ($parts[1] ?? null) {
-- 
GitLab