[TASK] Specific exception in ArrayUtility::getValueByPath() 12/55412/9
authorChristian Kuhn <lolli@schwarzbu.ch>
Fri, 19 Jan 2018 13:34:21 +0000 (14:34 +0100)
committerChristian Kuhn <lolli@schwarzbu.ch>
Fri, 19 Jan 2018 15:08:52 +0000 (16:08 +0100)
If ArrayUtility::getValueByPath() does not find a given
path in an array structure, it currently throws a generic
\RuntimeException. Checking the path is one of the key
components of the method and consuming code may not know
if the path exists at all, which is fine.

Throwing global \RuntimeException thus forces various
places to catch \RuntimeException which possibly
hides away programming errors and mis-uses.

The patch makes getValueByPath() throw a specific exception
if path does not exists. Catching that specific exception
reduces the number of global 'catch \RuntimeException'
significantly.

The patch also adds another catch in early install tool
to fix a not recoverable fatal if LocalConfiguration
'EXTENSIONS' array does not yet exist, for instance if
upgrading from v8 to v9.

Change-Id: If65b1b6ba2181b4cd7de2ce41776c0d78a5b513f
Resolves: #83615
Resolves: #83590
Releases: master
Reviewed-on: https://review.typo3.org/55412
Reviewed-by: Frank Naegler <frank.naegler@typo3.org>
Tested-by: Frank Naegler <frank.naegler@typo3.org>
Reviewed-by: Mathias Brodala <mbrodala@pagemachine.de>
Tested-by: TYPO3com <no-reply@typo3.com>
Tested-by: Mathias Brodala <mbrodala@pagemachine.de>
Reviewed-by: Christian Kuhn <lolli@schwarzbu.ch>
Tested-by: Christian Kuhn <lolli@schwarzbu.ch>
16 files changed:
typo3/sysext/core/Classes/Utility/ArrayUtility.php
typo3/sysext/core/Classes/Utility/Exception/MissingArrayPathException.php [new file with mode: 0644]
typo3/sysext/core/Tests/Unit/Utility/ArrayUtilityTest.php
typo3/sysext/extbase/Classes/Mvc/Web/RequestBuilder.php
typo3/sysext/form/Classes/Domain/Finishers/AbstractFinisher.php
typo3/sysext/form/Classes/Domain/Finishers/FinisherVariableProvider.php
typo3/sysext/form/Classes/Domain/Runtime/FormRuntime.php
typo3/sysext/form/Classes/Domain/Runtime/FormState.php
typo3/sysext/form/Classes/Hooks/DataStructureIdentifierHook.php
typo3/sysext/form/Classes/Mvc/Configuration/InheritancesResolverService.php
typo3/sysext/form/Classes/Service/TranslationService.php
typo3/sysext/install/Classes/Configuration/AbstractPreset.php
typo3/sysext/install/Classes/Controller/LayoutController.php
typo3/sysext/install/Classes/Service/SilentConfigurationUpgradeService.php
typo3/sysext/install/Classes/Updates/WizardDoneToRegistry.php
typo3/sysext/install/Tests/Unit/Service/SilentConfigurationUpgradeServiceTest.php

index 13aa3b7..f332e4f 100644 (file)
@@ -14,6 +14,8 @@ namespace TYPO3\CMS\Core\Utility;
  * The TYPO3 project - inspiring people to share!
  */
 
+use TYPO3\CMS\Core\Utility\Exception\MissingArrayPathException;
+
 /**
  * Class with helper functions for array handling
  */
@@ -142,9 +144,8 @@ class ArrayUtility
     {
         $isValid = true;
         try {
-            // Use late static binding to enable mocking of this call in unit tests
             static::getValueByPath($array, $path, $delimiter);
-        } catch (\RuntimeException $e) {
+        } catch (MissingArrayPathException $e) {
             $isValid = false;
         }
         return $isValid;
@@ -180,10 +181,12 @@ class ArrayUtility
         // Extract parts of the path
         if (is_string($path)) {
             if ($path === '') {
+                // Programming error has to be sanitized before calling the method -> global exception
                 throw new \RuntimeException('Path must not be empty', 1341397767);
             }
             $path = str_getcsv($path, $delimiter);
         } elseif (!is_array($path)) {
+            // Programming error has to be sanitized before calling the method -> global exception
             throw new \InvalidArgumentException('getValueByPath() expects $path to be string or array, "' . gettype($path) . '" given.', 1476557628);
         }
         // Loop through each part and extract its value
@@ -193,8 +196,8 @@ class ArrayUtility
                 // Replace current value with child
                 $value = $value[$segment];
             } else {
-                // Fail if key does not exist
-                throw new \RuntimeException('Path does not exist in array', 1341397869);
+                // Throw specific exception if there is no such path
+                throw new MissingArrayPathException('Segment ' . $segment . ' of path ' . $path . ' does not exist in array', 1341397869);
             }
         }
         return $value;
@@ -326,7 +329,7 @@ class ArrayUtility
                 throw new \RuntimeException('Invalid path segment specified', 1371757720);
             }
             if (!array_key_exists($segment, $pointer)) {
-                throw new \RuntimeException('Path segment ' . $segment . ' does not exist in array', 1371758436);
+                throw new \RuntimeException('Segment ' . $segment . ' of path ' . $path . ' does not exist in array', 1371758436);
             }
             if ($currentDepth === $pathDepth) {
                 unset($pointer[$segment]);
diff --git a/typo3/sysext/core/Classes/Utility/Exception/MissingArrayPathException.php b/typo3/sysext/core/Classes/Utility/Exception/MissingArrayPathException.php
new file mode 100644 (file)
index 0000000..04e21d0
--- /dev/null
@@ -0,0 +1,26 @@
+<?php
+declare(strict_types = 1);
+namespace TYPO3\CMS\Core\Utility\Exception;
+
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+/**
+ * Exception thrown if ArrayUtility::getValueByPath() does not find target path in array
+ *
+ * Note this extends from \RuntimeException to be backwards compatible with the
+ * formerly thrown \RuntimeException in the method.
+ */
+class MissingArrayPathException extends \RuntimeException
+{
+}
index b722b3d..2f3594f 100644 (file)
@@ -15,11 +15,13 @@ namespace TYPO3\CMS\Core\Tests\Unit\Utility;
  */
 
 use TYPO3\CMS\Core\Utility\ArrayUtility;
+use TYPO3\CMS\Core\Utility\Exception\MissingArrayPathException;
+use TYPO3\TestingFramework\Core\Unit\UnitTestCase;
 
 /**
  * Test case
  */
-class ArrayUtilityTest extends \TYPO3\TestingFramework\Core\Unit\UnitTestCase
+class ArrayUtilityTest extends UnitTestCase
 {
     ///////////////////////
     // Tests concerning filterByValueRecursive
@@ -196,11 +198,6 @@ class ArrayUtilityTest extends \TYPO3\TestingFramework\Core\Unit\UnitTestCase
     // Tests concerning isValidPath
     ///////////////////////
     /**
-     * Mock the class under test, isValidPath() (method under test), calls
-     * static getValuePath() internally, which is mocked here to return a specific
-     * result. This works because of 'static' keyword'  instead of 'self'
-     * for getValueByPath() call, using late static binding in PHP 5.3
-     *
      * @test
      */
     public function isValidPathReturnsTrueIfPathExists()
@@ -327,7 +324,19 @@ class ArrayUtilityTest extends \TYPO3\TestingFramework\Core\Unit\UnitTestCase
     {
         $this->expectException(\RuntimeException::class);
         $this->expectExceptionCode(1341397869);
+        ArrayUtility::getValueByPath($array, $path);
+    }
 
+    /**
+     * @test
+     * @dataProvider getValueByPathInvalidPathDataProvider
+     * @param array $array
+     * @param string $path
+     */
+    public function getValueByPathThrowsSpecificExceptionIfPathNotExists(array $array, string $path)
+    {
+        $this->expectException(MissingArrayPathException::class);
+        $this->expectExceptionCode(1341397869);
         ArrayUtility::getValueByPath($array, $path);
     }
 
index 8ac2377..6d08574 100644 (file)
@@ -14,6 +14,8 @@ namespace TYPO3\CMS\Extbase\Mvc\Web;
  * The TYPO3 project - inspiring people to share!
  */
 
+use TYPO3\CMS\Core\Utility\ArrayUtility;
+use TYPO3\CMS\Core\Utility\Exception\MissingArrayPathException;
 use TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface;
 use TYPO3\CMS\Extbase\Mvc\Exception as MvcException;
 
@@ -261,7 +263,6 @@ class RequestBuilder implements \TYPO3\CMS\Core\SingletonInterface
      *
      * @param array $convolutedFiles The _FILES superglobal
      * @return array Untangled files
-     * @see TYPO3\Flow\Utility\Environment
      */
     protected function untangleFilesArray(array $convolutedFiles)
     {
@@ -285,13 +286,13 @@ class RequestBuilder implements \TYPO3\CMS\Core\SingletonInterface
                 $fileInformation = [];
                 foreach ($convolutedFiles[$fieldPath[0]] as $key => $subStructure) {
                     try {
-                        $fileInformation[$key] = \TYPO3\CMS\Core\Utility\ArrayUtility::getValueByPath($subStructure, array_slice($fieldPath, 1));
-                    } catch (\RuntimeException $e) {
+                        $fileInformation[$key] = ArrayUtility::getValueByPath($subStructure, array_slice($fieldPath, 1));
+                    } catch (MissingArrayPathException $e) {
                         // do nothing if the path is invalid
                     }
                 }
             }
-            $untangledFiles = \TYPO3\CMS\Core\Utility\ArrayUtility::setValueByPath($untangledFiles, $fieldPath, $fileInformation);
+            $untangledFiles = ArrayUtility::setValueByPath($untangledFiles, $fieldPath, $fileInformation);
         }
         return $untangledFiles;
     }
index 74326fe..84e55aa 100644 (file)
@@ -18,6 +18,7 @@ namespace TYPO3\CMS\Form\Domain\Finishers;
  */
 
 use TYPO3\CMS\Core\Utility\ArrayUtility;
+use TYPO3\CMS\Core\Utility\Exception\MissingArrayPathException;
 use TYPO3\CMS\Extbase\Reflection\ObjectAccess;
 use TYPO3\CMS\Form\Service\TranslationService;
 use TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController;
@@ -153,12 +154,12 @@ abstract class AbstractFinisher implements FinisherInterface
 
         try {
             $optionValue = ArrayUtility::getValueByPath($this->options, $optionName, '.');
-        } catch (\RuntimeException $exception) {
+        } catch (MissingArrayPathException $exception) {
             $optionValue = null;
         }
         try {
             $defaultValue = ArrayUtility::getValueByPath($this->defaultOptions, $optionName, '.');
-        } catch (\RuntimeException $exception) {
+        } catch (MissingArrayPathException $exception) {
             $defaultValue = null;
         }
 
index 2b7f33d..fdce692 100644 (file)
@@ -16,6 +16,7 @@ namespace TYPO3\CMS\Form\Domain\Finishers;
  */
 
 use TYPO3\CMS\Core\Utility\ArrayUtility;
+use TYPO3\CMS\Core\Utility\Exception\MissingArrayPathException;
 
 /**
  * Store data for usage between the finishers.
@@ -98,7 +99,7 @@ final class FinisherVariableProvider implements \ArrayAccess, \IteratorAggregate
     {
         try {
             ArrayUtility::getValueByPath($this->objects[$finisherIdentifier], $key, '.');
-        } catch (\RuntimeException $e) {
+        } catch (MissingArrayPathException $e) {
             return false;
         }
         return true;
index 8eba997..54a8d96 100644 (file)
@@ -18,6 +18,7 @@ namespace TYPO3\CMS\Form\Domain\Runtime;
  */
 
 use TYPO3\CMS\Core\Utility\ArrayUtility;
+use TYPO3\CMS\Core\Utility\Exception\MissingArrayPathException;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
 use TYPO3\CMS\Extbase\Error\Result;
 use TYPO3\CMS\Extbase\Mvc\Controller\Arguments;
@@ -441,7 +442,7 @@ class FormRuntime implements RootRenderableInterface, \ArrayAccess
         foreach ($page->getElementsRecursively() as $element) {
             try {
                 $value = ArrayUtility::getValueByPath($requestArguments, $element->getIdentifier(), '.');
-            } catch (\RuntimeException $exception) {
+            } catch (MissingArrayPathException $exception) {
                 $value = null;
             }
 
index 0542724..85800a3 100644 (file)
@@ -18,6 +18,7 @@ namespace TYPO3\CMS\Form\Domain\Runtime;
  */
 
 use TYPO3\CMS\Core\Utility\ArrayUtility;
+use TYPO3\CMS\Core\Utility\Exception\MissingArrayPathException;
 
 /**
  * The current state of the form which is attached to the {@link FormRuntime}
@@ -102,7 +103,7 @@ class FormState
     {
         try {
             return ArrayUtility::getValueByPath($this->formValues, $propertyPath, '.');
-        } catch (\RuntimeException $exception) {
+        } catch (MissingArrayPathException $exception) {
             return null;
         }
     }
index b92c9f9..8b80202 100644 (file)
@@ -19,6 +19,7 @@ use TYPO3\CMS\Core\Messaging\AbstractMessage;
 use TYPO3\CMS\Core\Messaging\FlashMessage;
 use TYPO3\CMS\Core\Messaging\FlashMessageService;
 use TYPO3\CMS\Core\Utility\ArrayUtility;
+use TYPO3\CMS\Core\Utility\Exception\MissingArrayPathException;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
 use TYPO3\CMS\Extbase\Object\ObjectManager;
 use TYPO3\CMS\Form\Domain\Configuration\ConfigurationService;
@@ -223,12 +224,12 @@ class DataStructureIdentifierHook
                             $optionKey,
                             '.'
                         );
-                    } catch (\RuntimeException $exception) {
+                    } catch (MissingArrayPathException $exception) {
                         $elementConfiguration = null;
                     }
                     try {
                         $optionValue = ArrayUtility::getValueByPath($finisherValue['options'], $optionKey, '.');
-                    } catch (\RuntimeException $exception) {
+                    } catch (MissingArrayPathException $exception) {
                         $optionValue = null;
                     }
                 } else {
index 92eed00..0ab7993 100644 (file)
@@ -16,6 +16,7 @@ namespace TYPO3\CMS\Form\Mvc\Configuration;
  */
 
 use TYPO3\CMS\Core\Utility\ArrayUtility;
+use TYPO3\CMS\Core\Utility\Exception\MissingArrayPathException;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
 use TYPO3\CMS\Extbase\Object\ObjectManager;
 use TYPO3\CMS\Form\Mvc\Configuration\Exception\CycleInheritancesException;
@@ -149,7 +150,7 @@ class InheritancesResolverService
                             $path . '.' . self::INHERITANCE_OPERATOR,
                             '.'
                         );
-                    } catch (\RuntimeException $exception) {
+                    } catch (MissingArrayPathException $exception) {
                         $inheritances = null;
                     }
 
@@ -198,7 +199,7 @@ class InheritancesResolverService
                     $inheritancePath,
                     '.'
                 );
-            } catch (\RuntimeException $exception) {
+            } catch (MissingArrayPathException $exception) {
                 $inheritedConfiguration = null;
             }
 
@@ -262,7 +263,7 @@ class InheritancesResolverService
                 $path,
                 '.'
             );
-        } catch (\RuntimeException $exception) {
+        } catch (MissingArrayPathException $exception) {
             $configuration = null;
         }
 
@@ -273,7 +274,7 @@ class InheritancesResolverService
                     $path . '.' . self::INHERITANCE_OPERATOR,
                     '.'
                 );
-            } catch (\RuntimeException $exception) {
+            } catch (MissingArrayPathException $exception) {
                 $inheritances = null;
             }
 
@@ -285,7 +286,7 @@ class InheritancesResolverService
                             $inheritancePath,
                             '.'
                         );
-                    } catch (\RuntimeException $exception) {
+                    } catch (MissingArrayPathException $exception) {
                         $configuration = null;
                     }
 
@@ -296,7 +297,7 @@ class InheritancesResolverService
                                 $inheritancePath . '.' . self::INHERITANCE_OPERATOR,
                                 '.'
                             );
-                        } catch (\RuntimeException $exception) {
+                        } catch (MissingArrayPathException $exception) {
                             $_inheritances = null;
                         }
 
index 8aff6ab..9ad0d41 100644 (file)
@@ -20,6 +20,7 @@ use TYPO3\CMS\Core\Localization\Locales;
 use TYPO3\CMS\Core\Localization\LocalizationFactory;
 use TYPO3\CMS\Core\SingletonInterface;
 use TYPO3\CMS\Core\Utility\ArrayUtility;
+use TYPO3\CMS\Core\Utility\Exception\MissingArrayPathException;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
 use TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface;
 use TYPO3\CMS\Extbase\Object\ObjectManager;
@@ -260,7 +261,7 @@ class TranslationService implements SingletonInterface
 
         try {
             $arguments = ArrayUtility::getValueByPath($renderingOptions['arguments'] ?? [], $optionKey, '.');
-        } catch (\RuntimeException $e) {
+        } catch (MissingArrayPathException $e) {
             $arguments = [];
         }
 
@@ -303,14 +304,14 @@ class TranslationService implements SingletonInterface
             if ($element instanceof FormElementInterface) {
                 try {
                     $defaultValue = ArrayUtility::getValueByPath($element->getProperties(), $propertyParts, '.');
-                } catch (\RuntimeException $exception) {
+                } catch (MissingArrayPathException $exception) {
                     $defaultValue = null;
                 }
             } else {
                 $propertyType = 'renderingOptions';
                 try {
                     $defaultValue = ArrayUtility::getValueByPath($renderingOptions, $propertyParts, '.');
-                } catch (\RuntimeException $exception) {
+                } catch (MissingArrayPathException $exception) {
                     $defaultValue = null;
                 }
             }
@@ -345,7 +346,7 @@ class TranslationService implements SingletonInterface
 
         try {
             $arguments = ArrayUtility::getValueByPath($renderingOptions['translation']['arguments'] ?? [], $propertyParts, '.');
-        } catch (\RuntimeException $e) {
+        } catch (MissingArrayPathException $e) {
             $arguments = [];
         }
 
index 0d11875..a66162b 100644 (file)
@@ -15,6 +15,7 @@ namespace TYPO3\CMS\Install\Configuration;
  */
 
 use TYPO3\CMS\Core\Configuration\ConfigurationManager;
+use TYPO3\CMS\Core\Utility\Exception\MissingArrayPathException;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
 
 /**
@@ -87,7 +88,7 @@ abstract class AbstractPreset implements PresetInterface
         foreach ($this->configurationValues as $configurationKey => $configurationValue) {
             try {
                 $currentValue = $this->configurationManager->getConfigurationValueByPath($configurationKey);
-            } catch (\RuntimeException $e) {
+            } catch (MissingArrayPathException $e) {
                 $currentValue = null;
             }
             if ($currentValue !== $configurationValue) {
index aca5aba..3ffe732 100644 (file)
@@ -20,6 +20,7 @@ use Psr\Http\Message\ServerRequestInterface;
 use TYPO3\CMS\Core\Configuration\ConfigurationManager;
 use TYPO3\CMS\Core\Http\HtmlResponse;
 use TYPO3\CMS\Core\Http\JsonResponse;
+use TYPO3\CMS\Core\Utility\Exception\MissingArrayPathException;
 use TYPO3\CMS\Install\Service\Exception\ConfigurationChangedException;
 use TYPO3\CMS\Install\Service\ExtensionConfigurationService;
 use TYPO3\CMS\Install\Service\SilentConfigurationUpgradeService;
@@ -106,11 +107,16 @@ class LayoutController extends AbstractController
         $configurationManager = new ConfigurationManager();
         try {
             $oldExtConfSettings = $configurationManager->getConfigurationValueByPath('EXT/extConf');
-        } catch (\RuntimeException $e) {
+        } catch (MissingArrayPathException $e) {
             // The old 'extConf' array may not exist anymore, set to empty array if so.
             $oldExtConfSettings = [];
         }
-        $newExtensionSettings = $configurationManager->getConfigurationValueByPath('EXTENSIONS');
+        try {
+            $newExtensionSettings = $configurationManager->getConfigurationValueByPath('EXTENSIONS');
+        } catch (MissingArrayPathException $e) {
+            // New 'EXTENSIONS' array may not exist yet, for instance if just upgrading to v9
+            $newExtensionSettings = [];
+        }
         foreach ($oldExtConfSettings as $extensionName => $extensionSettings) {
             if (!array_key_exists($extensionName, $newExtensionSettings)) {
                 $newExtensionSettings = $this->removeDotsFromArrayKeysRecursive(unserialize($extensionSettings, ['allowed_classes' => false]));
index eb06440..cea75a4 100644 (file)
@@ -16,6 +16,7 @@ namespace TYPO3\CMS\Install\Service;
 
 use TYPO3\CMS\Core\Configuration\ConfigurationManager;
 use TYPO3\CMS\Core\Crypto\Random;
+use TYPO3\CMS\Core\Utility\Exception\MissingArrayPathException;
 use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
 use TYPO3\CMS\Install\Service\Exception\ConfigurationChangedException;
@@ -187,7 +188,7 @@ class SilentConfigurationUpgradeService
                 $this->configurationManager->setLocalConfigurationValueByPath('BE/loginSecurityLevel', 'normal');
                 $this->throwConfigurationChangedException();
             }
-        } catch (\RuntimeException $e) {
+        } catch (MissingArrayPathException $e) {
             // If an exception is thrown, the value is not set in LocalConfiguration
             $this->configurationManager->setLocalConfigurationValueByPath(
                 'BE/loginSecurityLevel',
@@ -207,7 +208,7 @@ class SilentConfigurationUpgradeService
     {
         try {
             $currentValue = $this->configurationManager->getLocalConfigurationValueByPath('SYS/encryptionKey');
-        } catch (\RuntimeException $e) {
+        } catch (MissingArrayPathException $e) {
             // If an exception is thrown, the value is not set in LocalConfiguration
             $currentValue = '';
         }
@@ -233,42 +234,46 @@ class SilentConfigurationUpgradeService
             // Check if the adapter option is set, if so, set it to the parameters that are obsolete
             $this->configurationManager->getLocalConfigurationValueByPath('HTTP/adapter');
             $obsoleteParameters[] = 'HTTP/adapter';
-        } catch (\RuntimeException $e) {
+        } catch (MissingArrayPathException $e) {
+            // Migration done already
         }
         try {
             $newParameters['HTTP/version'] = $this->configurationManager->getLocalConfigurationValueByPath('HTTP/protocol_version');
             $obsoleteParameters[] = 'HTTP/protocol_version';
-        } catch (\RuntimeException $e) {
+        } catch (MissingArrayPathException $e) {
+            // Migration done already
         }
         try {
             $this->configurationManager->getLocalConfigurationValueByPath('HTTP/ssl_verify_host');
             $obsoleteParameters[] = 'HTTP/ssl_verify_host';
-        } catch (\RuntimeException $e) {
+        } catch (MissingArrayPathException $e) {
+            // Migration done already
         }
         try {
             $legacyUserAgent = $this->configurationManager->getLocalConfigurationValueByPath('HTTP/userAgent');
             $newParameters['HTTP/headers/User-Agent'] = $legacyUserAgent;
             $obsoleteParameters[] = 'HTTP/userAgent';
-        } catch (\RuntimeException $e) {
+        } catch (MissingArrayPathException $e) {
+            // Migration done already
         }
 
         // Redirects
         try {
             $legacyFollowRedirects = $this->configurationManager->getLocalConfigurationValueByPath('HTTP/follow_redirects');
             $obsoleteParameters[] = 'HTTP/follow_redirects';
-        } catch (\RuntimeException $e) {
+        } catch (MissingArrayPathException $e) {
             $legacyFollowRedirects = '';
         }
         try {
             $legacyMaximumRedirects = $this->configurationManager->getLocalConfigurationValueByPath('HTTP/max_redirects');
             $obsoleteParameters[] = 'HTTP/max_redirects';
-        } catch (\RuntimeException $e) {
+        } catch (MissingArrayPathException $e) {
             $legacyMaximumRedirects = '';
         }
         try {
             $legacyStrictRedirects = $this->configurationManager->getLocalConfigurationValueByPath('HTTP/strict_redirects');
             $obsoleteParameters[] = 'HTTP/strict_redirects';
-        } catch (\RuntimeException $e) {
+        } catch (MissingArrayPathException $e) {
             $legacyStrictRedirects = '';
         }
 
@@ -294,32 +299,32 @@ class SilentConfigurationUpgradeService
             // Currently without protocol or port
             $legacyProxyHost = $this->configurationManager->getLocalConfigurationValueByPath('HTTP/proxy_host');
             $obsoleteParameters[] = 'HTTP/proxy_host';
-        } catch (\RuntimeException $e) {
+        } catch (MissingArrayPathException $e) {
             $legacyProxyHost = '';
         }
         try {
             $legacyProxyPort = $this->configurationManager->getLocalConfigurationValueByPath('HTTP/proxy_port');
             $obsoleteParameters[] = 'HTTP/proxy_port';
-        } catch (\RuntimeException $e) {
+        } catch (MissingArrayPathException $e) {
             $legacyProxyPort = '';
         }
         try {
             $legacyProxyUser = $this->configurationManager->getLocalConfigurationValueByPath('HTTP/proxy_user');
             $obsoleteParameters[] = 'HTTP/proxy_user';
-        } catch (\RuntimeException $e) {
+        } catch (MissingArrayPathException $e) {
             $legacyProxyUser = '';
         }
         try {
             $legacyProxyPassword = $this->configurationManager->getLocalConfigurationValueByPath('HTTP/proxy_password');
             $obsoleteParameters[] = 'HTTP/proxy_password';
-        } catch (\RuntimeException $e) {
+        } catch (MissingArrayPathException $e) {
             $legacyProxyPassword = '';
         }
         // Auth Scheme: Basic, digest etc.
         try {
             $legacyProxyAuthScheme = $this->configurationManager->getLocalConfigurationValueByPath('HTTP/proxy_auth_scheme');
             $obsoleteParameters[] = 'HTTP/proxy_auth_scheme';
-        } catch (\RuntimeException $e) {
+        } catch (MissingArrayPathException $e) {
             $legacyProxyAuthScheme = '';
         }
 
@@ -340,7 +345,7 @@ class SilentConfigurationUpgradeService
         try {
             $legacySslVerifyPeer = $this->configurationManager->getLocalConfigurationValueByPath('HTTP/ssl_verify_peer');
             $obsoleteParameters[] = 'HTTP/ssl_verify_peer';
-        } catch (\RuntimeException $e) {
+        } catch (MissingArrayPathException $e) {
             $legacySslVerifyPeer = '';
         }
 
@@ -348,14 +353,14 @@ class SilentConfigurationUpgradeService
         try {
             $legacySslCaPath = $this->configurationManager->getLocalConfigurationValueByPath('HTTP/ssl_capath');
             $obsoleteParameters[] = 'HTTP/ssl_capath';
-        } catch (\RuntimeException $e) {
+        } catch (MissingArrayPathException $e) {
             $legacySslCaPath = '';
         }
         // Certificate Authority file to verify the peer with (use when ssl_verify_peer is TRUE)
         try {
             $legacySslCaFile = $this->configurationManager->getLocalConfigurationValueByPath('HTTP/ssl_cafile');
             $obsoleteParameters[] = 'HTTP/ssl_cafile';
-        } catch (\RuntimeException $e) {
+        } catch (MissingArrayPathException $e) {
             $legacySslCaFile = '';
         }
         if ($legacySslVerifyPeer !== '') {
@@ -371,7 +376,7 @@ class SilentConfigurationUpgradeService
         try {
             $legacySslLocalCert = $this->configurationManager->getLocalConfigurationValueByPath('HTTP/ssl_local_cert');
             $obsoleteParameters[] = 'HTTP/ssl_local_cert';
-        } catch (\RuntimeException $e) {
+        } catch (MissingArrayPathException $e) {
             $legacySslLocalCert = '';
         }
 
@@ -379,7 +384,7 @@ class SilentConfigurationUpgradeService
         try {
             $legacySslPassphrase = $this->configurationManager->getLocalConfigurationValueByPath('HTTP/ssl_passphrase');
             $obsoleteParameters[] = 'HTTP/ssl_passphrase';
-        } catch (\RuntimeException $e) {
+        } catch (MissingArrayPathException $e) {
             $legacySslPassphrase = '';
         }
 
@@ -421,31 +426,31 @@ class SilentConfigurationUpgradeService
         $changedValues = [];
         try {
             $currentImValue = $this->configurationManager->getLocalConfigurationValueByPath('GFX/processor_enabled');
-        } catch (\RuntimeException $e) {
+        } catch (MissingArrayPathException $e) {
             $currentImValue = $this->configurationManager->getDefaultConfigurationValueByPath('GFX/processor_enabled');
         }
 
         try {
             $currentImPathValue = $this->configurationManager->getLocalConfigurationValueByPath('GFX/processor_path');
-        } catch (\RuntimeException $e) {
+        } catch (MissingArrayPathException $e) {
             $currentImPathValue = $this->configurationManager->getDefaultConfigurationValueByPath('GFX/processor_path');
         }
 
         try {
             $currentImPathLzwValue = $this->configurationManager->getLocalConfigurationValueByPath('GFX/processor_path_lzw');
-        } catch (\RuntimeException $e) {
+        } catch (MissingArrayPathException $e) {
             $currentImPathLzwValue = $this->configurationManager->getDefaultConfigurationValueByPath('GFX/processor_path_lzw');
         }
 
         try {
             $currentImageFileExtValue = $this->configurationManager->getLocalConfigurationValueByPath('GFX/imagefile_ext');
-        } catch (\RuntimeException $e) {
+        } catch (MissingArrayPathException $e) {
             $currentImageFileExtValue = $this->configurationManager->getDefaultConfigurationValueByPath('GFX/imagefile_ext');
         }
 
         try {
             $currentThumbnailsValue = $this->configurationManager->getLocalConfigurationValueByPath('GFX/thumbnails');
-        } catch (\RuntimeException $e) {
+        } catch (MissingArrayPathException $e) {
             $currentThumbnailsValue = $this->configurationManager->getDefaultConfigurationValueByPath('GFX/thumbnails');
         }
 
@@ -482,19 +487,19 @@ class SilentConfigurationUpgradeService
         $changedValues = [];
         try {
             $currentProcessorValue = $this->configurationManager->getLocalConfigurationValueByPath('GFX/processor');
-        } catch (\RuntimeException $e) {
+        } catch (MissingArrayPathException $e) {
             $currentProcessorValue = $this->configurationManager->getDefaultConfigurationValueByPath('GFX/processor');
         }
 
         try {
             $currentProcessorMaskValue = $this->configurationManager->getLocalConfigurationValueByPath('GFX/processor_allowTemporaryMasksAsPng');
-        } catch (\RuntimeException $e) {
+        } catch (MissingArrayPathException $e) {
             $currentProcessorMaskValue = $this->configurationManager->getDefaultConfigurationValueByPath('GFX/processor_allowTemporaryMasksAsPng');
         }
 
         try {
             $currentProcessorEffectsValue = $this->configurationManager->getLocalConfigurationValueByPath('GFX/processor_effects');
-        } catch (\RuntimeException $e) {
+        } catch (MissingArrayPathException $e) {
             $currentProcessorEffectsValue = $this->configurationManager->getDefaultConfigurationValueByPath('GFX/processor_effects');
         }
 
@@ -540,7 +545,7 @@ class SilentConfigurationUpgradeService
                 $value = $this->configurationManager->getLocalConfigurationValueByPath($oldPath);
                 $this->configurationManager->setLocalConfigurationValueByPath($newPath, $value);
                 $changedSettings[$oldPath] = true;
-            } catch (\RuntimeException $e) {
+            } catch (MissingArrayPathException $e) {
                 // If an exception is thrown, the value is not set in LocalConfiguration
                 $changedSettings[$oldPath] = false;
             }
@@ -606,7 +611,7 @@ class SilentConfigurationUpgradeService
         $changedValues = [];
         try {
             $currentThumbnailsPngValue = $this->configurationManager->getLocalConfigurationValueByPath('GFX/thumbnails_png');
-        } catch (\RuntimeException $e) {
+        } catch (MissingArrayPathException $e) {
             $currentThumbnailsPngValue = $this->configurationManager->getDefaultConfigurationValueByPath('GFX/thumbnails_png');
         }
 
@@ -631,7 +636,7 @@ class SilentConfigurationUpgradeService
                 $this->configurationManager->setLocalConfigurationValueByPath('BE/lockSSL', true);
                 $this->throwConfigurationChangedException();
             }
-        } catch (\RuntimeException $e) {
+        } catch (MissingArrayPathException $e) {
             // no change inside the LocalConfiguration.php found, so nothing needs to be modified
         }
     }
@@ -650,7 +655,7 @@ class SilentConfigurationUpgradeService
             $value = $confManager->getLocalConfigurationValueByPath('DB/username');
             $removeSettings[] = 'DB/username';
             $newSettings['DB/Connections/Default/user'] = $value;
-        } catch (\RuntimeException $e) {
+        } catch (MissingArrayPathException $e) {
             // Old setting does not exist, do nothing
         }
 
@@ -658,7 +663,7 @@ class SilentConfigurationUpgradeService
             $value = $confManager->getLocalConfigurationValueByPath('DB/password');
             $removeSettings[] = 'DB/password';
             $newSettings['DB/Connections/Default/password'] = $value;
-        } catch (\RuntimeException $e) {
+        } catch (MissingArrayPathException $e) {
             // Old setting does not exist, do nothing
         }
 
@@ -666,7 +671,7 @@ class SilentConfigurationUpgradeService
             $value = $confManager->getLocalConfigurationValueByPath('DB/host');
             $removeSettings[] = 'DB/host';
             $newSettings['DB/Connections/Default/host'] = $value;
-        } catch (\RuntimeException $e) {
+        } catch (MissingArrayPathException $e) {
             // Old setting does not exist, do nothing
         }
 
@@ -674,7 +679,7 @@ class SilentConfigurationUpgradeService
             $value = $confManager->getLocalConfigurationValueByPath('DB/port');
             $removeSettings[] = 'DB/port';
             $newSettings['DB/Connections/Default/port'] = $value;
-        } catch (\RuntimeException $e) {
+        } catch (MissingArrayPathException $e) {
             // Old setting does not exist, do nothing
         }
 
@@ -685,7 +690,7 @@ class SilentConfigurationUpgradeService
             if (!empty($value)) {
                 $newSettings['DB/Connections/Default/unix_socket'] = $value;
             }
-        } catch (\RuntimeException $e) {
+        } catch (MissingArrayPathException $e) {
             // Old setting does not exist, do nothing
         }
 
@@ -693,7 +698,7 @@ class SilentConfigurationUpgradeService
             $value = $confManager->getLocalConfigurationValueByPath('DB/database');
             $removeSettings[] = 'DB/database';
             $newSettings['DB/Connections/Default/dbname'] = $value;
-        } catch (\RuntimeException $e) {
+        } catch (MissingArrayPathException $e) {
             // Old setting does not exist, do nothing
         }
 
@@ -705,7 +710,7 @@ class SilentConfigurationUpgradeService
                     'flags' => MYSQLI_CLIENT_COMPRESS,
                 ];
             }
-        } catch (\RuntimeException $e) {
+        } catch (MissingArrayPathException $e) {
             // Old setting does not exist, do nothing
         }
 
@@ -715,7 +720,7 @@ class SilentConfigurationUpgradeService
             if (!$value) {
                 $newSettings['DB/Connections/Default/persistentConnection'] = true;
             }
-        } catch (\RuntimeException $e) {
+        } catch (MissingArrayPathException $e) {
             // Old setting does not exist, do nothing
         }
 
@@ -723,20 +728,20 @@ class SilentConfigurationUpgradeService
             $value = $confManager->getLocalConfigurationValueByPath('SYS/setDBinit');
             $removeSettings[] = 'SYS/setDBinit';
             $newSettings['DB/Connections/Default/initCommands'] = $value;
-        } catch (\RuntimeException $e) {
+        } catch (MissingArrayPathException $e) {
             // Old setting does not exist, do nothing
         }
 
         try {
             $confManager->getLocalConfigurationValueByPath('DB/Connections/Default/charset');
-        } catch (\RuntimeException $e) {
+        } catch (MissingArrayPathException $e) {
             // If there is no charset option yet, add it.
             $newSettings['DB/Connections/Default/charset'] = 'utf8';
         }
 
         try {
             $confManager->getLocalConfigurationValueByPath('DB/Connections/Default/driver');
-        } catch (\RuntimeException $e) {
+        } catch (MissingArrayPathException $e) {
             // Use the mysqli driver by default if no value has been provided yet
             $newSettings['DB/Connections/Default/driver'] = 'mysqli';
         }
@@ -769,7 +774,7 @@ class SilentConfigurationUpgradeService
                 $confManager->setLocalConfigurationValueByPath('DB/Connections/Default/charset', 'utf8');
                 $this->throwConfigurationChangedException();
             }
-        } catch (\RuntimeException $e) {
+        } catch (MissingArrayPathException $e) {
             // no incompatible charset configuration found, so nothing needs to be modified
         }
     }
@@ -788,7 +793,7 @@ class SilentConfigurationUpgradeService
                     ['flags' => (int)$options]
                 );
             }
-        } catch (\RuntimeException $e) {
+        } catch (MissingArrayPathException $e) {
             // no driver options found, nothing needs to be modified
         }
     }
@@ -805,7 +810,7 @@ class SilentConfigurationUpgradeService
             if (isset($currentOption) && is_bool($currentOption)) {
                 $confManager->setLocalConfigurationValueByPath('BE/languageDebug', $currentOption);
             }
-        } catch (\RuntimeException $e) {
+        } catch (MissingArrayPathException $e) {
             // no change inside the LocalConfiguration.php found, so nothing needs to be modified
         }
     }
@@ -823,21 +828,24 @@ class SilentConfigurationUpgradeService
             $value = $confManager->getLocalConfigurationValueByPath('FE/cHashOnlyForParameters');
             $removeSettings[] = 'FE/cHashOnlyForParameters';
             $newSettings['FE/cacheHash/cachedParametersWhiteList'] = GeneralUtility::trimExplode(',', $value, true);
-        } catch (\RuntimeException $e) {
+        } catch (MissingArrayPathException $e) {
+            // Migration done already
         }
 
         try {
             $value = $confManager->getLocalConfigurationValueByPath('FE/cHashExcludedParameters');
             $removeSettings[] = 'FE/cHashExcludedParameters';
             $newSettings['FE/cacheHash/excludedParameters'] = GeneralUtility::trimExplode(',', $value, true);
-        } catch (\RuntimeException $e) {
+        } catch (MissingArrayPathException $e) {
+            // Migration done already
         }
 
         try {
             $value = $confManager->getLocalConfigurationValueByPath('FE/cHashRequiredParameters');
             $removeSettings[] = 'FE/cHashRequiredParameters';
             $newSettings['FE/cacheHash/requireCacheHashPresenceParameters'] = GeneralUtility::trimExplode(',', $value, true);
-        } catch (\RuntimeException $e) {
+        } catch (MissingArrayPathException $e) {
+            // Migration done already
         }
 
         try {
@@ -848,7 +856,8 @@ class SilentConfigurationUpgradeService
             } else {
                 $newSettings['FE/cacheHash/excludedParametersIfEmpty'] = GeneralUtility::trimExplode(',', $value, true);
             }
-        } catch (\RuntimeException $e) {
+        } catch (MissingArrayPathException $e) {
+            // Migration done already
         }
 
         // Add new settings and remove old ones
@@ -877,7 +886,7 @@ class SilentConfigurationUpgradeService
             if ($currentOption & E_USER_DEPRECATED) {
                 $confManager->setLocalConfigurationValueByPath('SYS/exceptionalErrors', $currentOption & ~E_USER_DEPRECATED);
             }
-        } catch (\RuntimeException $e) {
+        } catch (MissingArrayPathException $e) {
             // no change inside the LocalConfiguration.php found, so nothing needs to be modified
         }
     }
index e25a19e..8275033 100644 (file)
@@ -16,6 +16,7 @@ namespace TYPO3\CMS\Install\Updates;
 
 use TYPO3\CMS\Core\Configuration\ConfigurationManager;
 use TYPO3\CMS\Core\Registry;
+use TYPO3\CMS\Core\Utility\Exception\MissingArrayPathException;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
 
 /**
@@ -41,11 +42,11 @@ class WizardDoneToRegistry extends AbstractUpdate
 
         try {
             $wizardsDone = GeneralUtility::makeInstance(ConfigurationManager::class)->getLocalConfigurationValueByPath('INSTALL/wizardDone');
-
             if (!empty($wizardsDone)) {
                 $result = true;
             }
-        } catch (\RuntimeException $e) {
+        } catch (MissingArrayPathException $e) {
+            // Result stays false with broken path
         }
 
         return $result;
index 9e21bf9..6c2530e 100644 (file)
@@ -18,6 +18,7 @@ use Prophecy\Prophecy\ObjectProphecy;
 use TYPO3\CMS\Core\Configuration\ConfigurationManager;
 use TYPO3\CMS\Core\Package\PackageManager;
 use TYPO3\CMS\Core\Tests\Unit\Utility\AccessibleProxies\ExtensionManagementUtilityAccessibleProxy;
+use TYPO3\CMS\Core\Utility\Exception\MissingArrayPathException;
 use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
 use TYPO3\CMS\Install\Service\Exception\ConfigurationChangedException;
 use TYPO3\CMS\Install\Service\SilentConfigurationUpgradeService;
@@ -108,7 +109,7 @@ class SilentConfigurationUpgradeServiceTest extends \TYPO3\TestingFramework\Core
             ['BE/loginSecurityLevel', $current]
         ];
         $closure = function () {
-            throw new \RuntimeException('Path does not exist in array', 1476109311);
+            throw new MissingArrayPathException('Path does not exist in array', 1476109311);
         };
 
         $this->createConfigurationManagerWithMockedMethods(
@@ -261,7 +262,7 @@ class SilentConfigurationUpgradeServiceTest extends \TYPO3\TestingFramework\Core
         );
 
         $closure = function () {
-            throw new \RuntimeException('Path does not exist in array', 1476109266);
+            throw new MissingArrayPathException('Path does not exist in array', 1476109266);
         };
 
         $this->createConfigurationManagerWithMockedMethods(
@@ -673,26 +674,28 @@ class SilentConfigurationUpgradeServiceTest extends \TYPO3\TestingFramework\Core
 
     /**
      * @test
-     *
-     * @param array $oldValues
-     * @param array $newValues
-     *
-     * @dataProvider migrateCacheHashOptionsDataProvider
      */
-    public function migrateCacheHashOptions(array $oldValues, array $newValues)
+    public function migrateCacheHashOptions()
     {
+        $oldConfig = [
+            'FE/cHashOnlyForParameters' => 'foo,bar',
+            'FE/cHashExcludedParameters' => 'bar,foo',
+            'FE/cHashRequiredParameters' => 'bar,baz',
+            'FE/cHashExcludedParametersIfEmpty' => '*'
+        ];
+
         /** @var ConfigurationManager|ObjectProphecy $configurationManager */
         $configurationManager = $this->prophesize(ConfigurationManager::class);
 
-        foreach ($oldValues as $key => $value) {
+        foreach ($oldConfig as $key => $value) {
             $configurationManager->getLocalConfigurationValueByPath($key)
                 ->shouldBeCalled()
                 ->willReturn($value);
         }
 
-        $configurationManager->setLocalConfigurationValuesByPathValuePairs($newValues)
+        $configurationManager->setLocalConfigurationValuesByPathValuePairs(\Prophecy\Argument::cetera())
             ->shouldBeCalled();
-        $configurationManager->removeLocalConfigurationKeysByPath(array_keys($oldValues))
+        $configurationManager->removeLocalConfigurationKeysByPath(\Prophecy\Argument::cetera())
             ->shouldBeCalled();
 
         $this->expectException(ConfigurationChangedException::class);
@@ -710,35 +713,4 @@ class SilentConfigurationUpgradeServiceTest extends \TYPO3\TestingFramework\Core
 
         $silentConfigurationUpgradeServiceInstance->_call('migrateCacheHashOptions');
     }
-
-    /**
-     * @return array
-     */
-    public function migrateCacheHashOptionsDataProvider()
-    {
-        return [
-            [
-                'old' => [
-                    'FE/cHashOnlyForParameters' => 'foo,bar',
-                    'FE/cHashExcludedParameters' => 'bar,foo',
-                    'FE/cHashRequiredParameters' => 'bar,baz',
-                    'FE/cHashExcludedParametersIfEmpty' => '*'
-                ],
-                'new' => [
-                    'FE/cacheHash/cachedParametersWhiteList' => ['foo', 'bar'],
-                    'FE/cacheHash/excludedParameters' => ['bar', 'foo'],
-                    'FE/cacheHash/requireCacheHashPresenceParameters' => ['bar', 'baz'],
-                    'FE/cacheHash/excludeAllEmptyParameters' => true
-                ]
-            ],
-            [
-                'old' => [
-                    'FE/cHashExcludedParametersIfEmpty' => 'foo,bar'
-                ],
-                'new' => [
-                    'FE/cacheHash/excludedParametersIfEmpty' => ['foo', 'bar']
-                ]
-            ]
-        ];
-    }
 }