From 531be24a96726849402bfe2e9edc04730e9c96d8 Mon Sep 17 00:00:00 2001 From: Benni Mack <benni@typo3.org> Date: Thu, 2 Nov 2017 11:46:40 +0100 Subject: [PATCH] [FEATURE] Introduce Feature Toggles A new API class "Features" allows to check if a feature is enabled/disabled for a certain installation. This API method can be used to have admins switch to new features explicitly or disable legacy functionality. Examples for using the feature toggles in the future: - Do not load TCA for pages_language_overlay after DB migration - Do not parse HTML for legacy <link> tag anymore - Explicitly enable new Logging API in DataHandler (skipping sys_log calls) Resolves: #83429 Releases: master Change-Id: I5da8f66e593e311c83fefe5fe9edb503a885943b Reviewed-on: https://review.typo3.org/54529 Reviewed-by: Anja Leichsenring <aleichsenring@ab-softlab.de> Tested-by: Anja Leichsenring <aleichsenring@ab-softlab.de> Tested-by: TYPO3com <no-reply@typo3.com> Reviewed-by: Andreas Fernandez <typo3@scripting-base.de> Tested-by: Andreas Fernandez <typo3@scripting-base.de> --- .../Configuration/ConfigurationManager.php | 24 +++++++ .../core/Classes/Configuration/Features.php | 65 +++++++++++++++++++ .../Configuration/DefaultConfiguration.php | 1 + .../master/Feature-83429-FeatureToggles.rst | 40 ++++++++++++ .../Tests/Unit/Configuration/FeaturesTest.php | 64 ++++++++++++++++++ 5 files changed, 194 insertions(+) create mode 100644 typo3/sysext/core/Classes/Configuration/Features.php create mode 100644 typo3/sysext/core/Documentation/Changelog/master/Feature-83429-FeatureToggles.rst create mode 100644 typo3/sysext/core/Tests/Unit/Configuration/FeaturesTest.php diff --git a/typo3/sysext/core/Classes/Configuration/ConfigurationManager.php b/typo3/sysext/core/Classes/Configuration/ConfigurationManager.php index 05e60cd2d74b..d3ce2786c824 100644 --- a/typo3/sysext/core/Classes/Configuration/ConfigurationManager.php +++ b/typo3/sysext/core/Classes/Configuration/ConfigurationManager.php @@ -286,6 +286,30 @@ class ConfigurationManager return $result; } + /** + * Enables a certain feature and writes the option to LocalConfiguration.php + * Short-hand method + * + * @param string $featureName something like "InlineSvgImages" + * @return bool true on successful writing the setting + */ + public function enableFeature(string $featureName): bool + { + return $this->setLocalConfigurationValueByPath('SYS/features/' . $featureName, true); + } + + /** + * Disables a feature and writes the option to LocalConfiguration.php + * Short-hand method + * + * @param string $featureName something like "InlineSvgImages" + * @return bool true on successful writing the setting + */ + public function disableFeature(string $featureName): bool + { + return $this->setLocalConfigurationValueByPath('SYS/features/' . $featureName, false); + } + /** * Checks if the configuration can be written. * diff --git a/typo3/sysext/core/Classes/Configuration/Features.php b/typo3/sysext/core/Classes/Configuration/Features.php new file mode 100644 index 000000000000..1cc400401836 --- /dev/null +++ b/typo3/sysext/core/Classes/Configuration/Features.php @@ -0,0 +1,65 @@ +<?php +declare(strict_types=1); +namespace TYPO3\CMS\Core\Configuration; + +/* + * 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! + */ + +/** + * A lightweight API class to check if a feature is enabled. + * + * Features are simple options (true/false), and are stored in the + * global configuration array $TYPO3_CONF_VARS[SYS][features]. + * + * For disabling or enabling a feature the "ConfigurationManager" + * should be used. + * + * -- Naming -- + * + * Feature names should NEVER named "enable" or having a negation, or containing versions or years + * "enableFeatureXyz" + * "disableOverlays" + * "schedulerRevamped" + * "useDoctrineQueries" + * "disablePreparedStatements" + * "disableHooksInFE" + * + * Proper namings for features + * "ExtendedRichtextFormat" + * "NativeYamlParser" + * "InlinePageTranslations" + * "TypoScriptParserIncludesAsXml" + * "NativeDoctrineQueries" + * + * Ideally, these feature switches are added via the Install Tool or via FactoryConfiguration + * and can be used for Extensions as well. + * + * --- Usage --- + * + * if (GeneralUtility::makeInstance(Features::class)->isFeatureEnabled('InlineSvg')) { + * ... do stuff here ... + * } + */ +class Features +{ + /** + * Checks if a feature is active + * + * @param string $featureName the name of the feature + * @return bool + */ + public function isFeatureEnabled(string $featureName): bool + { + return isset($GLOBALS['TYPO3_CONF_VARS']['SYS']['features'][$featureName]) && $GLOBALS['TYPO3_CONF_VARS']['SYS']['features'][$featureName] === true; + } +} diff --git a/typo3/sysext/core/Configuration/DefaultConfiguration.php b/typo3/sysext/core/Configuration/DefaultConfiguration.php index 8a5cc4279d03..6edf83a26d23 100644 --- a/typo3/sysext/core/Configuration/DefaultConfiguration.php +++ b/typo3/sysext/core/Configuration/DefaultConfiguration.php @@ -67,6 +67,7 @@ return [ ], 'fileCreateMask' => '0664', 'folderCreateMask' => '2775', + 'features' => [], 'createGroup' => '', 'sitename' => 'TYPO3', 'encryptionKey' => '', diff --git a/typo3/sysext/core/Documentation/Changelog/master/Feature-83429-FeatureToggles.rst b/typo3/sysext/core/Documentation/Changelog/master/Feature-83429-FeatureToggles.rst new file mode 100644 index 000000000000..952ed62ca726 --- /dev/null +++ b/typo3/sysext/core/Documentation/Changelog/master/Feature-83429-FeatureToggles.rst @@ -0,0 +1,40 @@ +.. include:: ../../Includes.txt + +================================= +Feature: #83429 - Feature Toggles +================================= + +See :issue:`83429` + +Description +=========== + +In order to allow better support for alternative functionality while keeping old functionality, a new API +for enabling installation-wide features - called "Feature Toggle" - has been added. + +The new API checks against a system-wide option array within :php:`$TYPO3_CONF_VARS['SYS']['features']` which can be +enabled system-wide. Both TYPO3 Core and Extensions can then provide alternative functionality for a certain +feature. + +Features are usually breaking changes for a minor version / sprint release, which site administrators can enable +at their own risk, or stay fully compatible with third-party extensions. + +Examples for having features are: +* Throw exceptions on certain occasions instead of just returning a string message as error message. +* Disable obsolete functionality which might still be used, but slows down the system. +* Enable alternative PageNotFound handling for an installation. + + +Impact +====== + +Features are documented for TYPO3 Core. For extension authors, the API can be used for any custom +feature provided by an extension: + +.. code-block:: php + + if (GeneralUtility::makeInstance(Features::class)->isFeatureEnabled('myFeatureName')) { + // do custom processing + } + +.. index:: LocalConfiguration, PHP-API diff --git a/typo3/sysext/core/Tests/Unit/Configuration/FeaturesTest.php b/typo3/sysext/core/Tests/Unit/Configuration/FeaturesTest.php new file mode 100644 index 000000000000..8f093aac07bd --- /dev/null +++ b/typo3/sysext/core/Tests/Unit/Configuration/FeaturesTest.php @@ -0,0 +1,64 @@ +<?php +declare(strict_types=1); +namespace TYPO3\CMS\Core\Tests\Unit\Configuration; + +/* + * 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! + */ + +use TYPO3\CMS\Core\Configuration\Features; +use TYPO3\TestingFramework\Core\Unit\UnitTestCase; + +/** + * Test case + */ +class FeaturesTest extends UnitTestCase +{ + /** + * @test + */ + public function nonExistingFeatureReturnsFalse() + { + $features = new Features(); + $this->assertFalse($features->isFeatureEnabled('nonExistingFeature')); + } + + /** + * @test + */ + public function checkIfExistingDisabledFeatureIsDisabled() + { + $GLOBALS['TYPO3_CONF_VARS']['SYS']['features']['nativeFunctionality'] = false; + $features = new Features(); + $this->assertFalse($features->isFeatureEnabled('nativeFunctionality')); + } + + /** + * @test + */ + public function checkIfExistingEnabledFeatureIsEnabled() + { + $GLOBALS['TYPO3_CONF_VARS']['SYS']['features']['nativeFunctionality'] = true; + $features = new Features(); + $this->assertTrue($features->isFeatureEnabled('nativeFunctionality')); + } + + /** + * @test + */ + public function checkIfExistingEnabledFeatureIsDisabled() + { + $GLOBALS['TYPO3_CONF_VARS']['SYS']['features']['nativeFunctionality'] = false; + $features = new Features(); + $this->assertFalse($features->isFeatureEnabled('nativeFunctionality')); + } +} -- GitLab