vendor/shopware/core/Framework/Feature.php line 195

Open in your IDE?
  1. <?php declare(strict_types=1);
  2. namespace Shopware\Core\Framework;
  3. use PHPUnit\Framework\TestCase;
  4. use Shopware\Core\DevOps\Environment\EnvironmentHelper;
  5. use Shopware\Core\Framework\Script\Debugging\ScriptTraces;
  6. class Feature
  7. {
  8.     public const ALL_MAJOR 'major';
  9.     /**
  10.      * @var array<bool>
  11.      */
  12.     private static array $silent = [];
  13.     /**
  14.      * @var array<string, array{name?: string, default?: boolean, major?: boolean, description?: string}>
  15.      */
  16.     private static array $registeredFeatures = [];
  17.     public static function normalizeName(string $name): string
  18.     {
  19.         /*
  20.          * Examples:
  21.          * - NEXT-1234
  22.          * - FEATURE_NEXT_1234
  23.          * - SAAS_321
  24.          * - v6.5.0.0 => v6_5_0_0
  25.          */
  26.         return \strtoupper(\str_replace(['.'':''-'], '_'$name));
  27.     }
  28.     /**
  29.      * @param array<string> $features
  30.      *
  31.      * @return mixed|null
  32.      */
  33.     public static function fake(array $features, \Closure $closure)
  34.     {
  35.         $before self::$registeredFeatures;
  36.         $serverVarsBackup $_SERVER;
  37.         $result null;
  38.         try {
  39.             self::$registeredFeatures = [];
  40.             foreach ($_SERVER as $key => $value) {
  41.                 if (str_starts_with($key'v6.') || $key === 'PERFORMANCE_TWEAKS' || str_starts_with($key'FEATURE_')) {
  42.                     // set to false so that $_ENV is not checked
  43.                     $_SERVER[$key] = false;
  44.                 }
  45.             }
  46.             if ($features) {
  47.                 foreach ($features as $feature) {
  48.                     $_SERVER[Feature::normalizeName($feature)] = true;
  49.                 }
  50.             }
  51.             $result $closure();
  52.         } finally {
  53.             self::$registeredFeatures $before;
  54.             $_SERVER $serverVarsBackup;
  55.         }
  56.         return $result;
  57.     }
  58.     public static function isActive(string $feature): bool
  59.     {
  60.         $env EnvironmentHelper::getVariable('APP_ENV''prod');
  61.         $feature self::normalizeName($feature);
  62.         if (self::$registeredFeatures !== []
  63.             && !isset(self::$registeredFeatures[$feature])
  64.             && $env !== 'prod'
  65.         ) {
  66.             trigger_error('Unknown feature "' $feature '"', \E_USER_WARNING);
  67.         }
  68.         $featureAll EnvironmentHelper::getVariable('FEATURE_ALL''');
  69.         if (self::isTrue((string) $featureAll) && (self::$registeredFeatures === [] || \array_key_exists($featureself::$registeredFeatures))) {
  70.             if ($featureAll === Feature::ALL_MAJOR) {
  71.                 return true;
  72.             }
  73.             // return true if it's registered and not a major feature
  74.             if (isset(self::$registeredFeatures[$feature]) && (self::$registeredFeatures[$feature]['major'] ?? false) === false) {
  75.                 return true;
  76.             }
  77.         }
  78.         if (!EnvironmentHelper::hasVariable($feature) && !EnvironmentHelper::hasVariable(\strtolower($feature))) {
  79.             $fallback self::$registeredFeatures[$feature]['default'] ?? false;
  80.             return (bool) $fallback;
  81.         }
  82.         return self::isTrue(trim((string) EnvironmentHelper::getVariable($feature)));
  83.     }
  84.     public static function ifActive(string $flagName, \Closure $closure): void
  85.     {
  86.         self::isActive($flagName) && $closure();
  87.     }
  88.     public static function callSilentIfInactive(string $flagName, \Closure $closure): void
  89.     {
  90.         $before = isset(self::$silent[$flagName]);
  91.         self::$silent[$flagName] = true;
  92.         try {
  93.             if (!self::isActive($flagName)) {
  94.                 $closure();
  95.             }
  96.         } finally {
  97.             if (!$before) {
  98.                 unset(self::$silent[$flagName]);
  99.             }
  100.         }
  101.     }
  102.     /**
  103.      * @param object $object
  104.      * @param mixed[] $arguments
  105.      */
  106.     public static function ifActiveCall(string $flagName$objectstring $methodName, ...$arguments): void
  107.     {
  108.         $closure = function () use ($object$methodName$arguments): void {
  109.             $object->{$methodName}(...$arguments);
  110.         };
  111.         self::ifActive($flagName, \Closure::bind($closure$object$object));
  112.     }
  113.     public static function skipTestIfInActive(string $flagNameTestCase $test): void
  114.     {
  115.         if (self::isActive($flagName)) {
  116.             return;
  117.         }
  118.         $test::markTestSkipped('Skipping feature test for flag  "' $flagName '"');
  119.     }
  120.     public static function skipTestIfActive(string $flagNameTestCase $test): void
  121.     {
  122.         if (!self::isActive($flagName)) {
  123.             return;
  124.         }
  125.         $test::markTestSkipped('Skipping feature test for flag  "' $flagName '"');
  126.     }
  127.     /**
  128.      * Triggers a silenced deprecation notice.
  129.      *
  130.      * @param string $sinceVersion  The version of the package that introduced the deprecation
  131.      * @param string $removeVersion The version of the package when the deprectated code will be removed
  132.      * @param string $message       The message of the deprecation
  133.      * @param mixed  ...$args       Values to insert in the message using printf() formatting
  134.      *
  135.      * @deprecated tag:v6.5.0 - will be removed, use `triggerDeprecationOrThrow` instead
  136.      */
  137.     public static function triggerDeprecated(string $flagstring $sinceVersionstring $removeVersionstring $message, ...$args): void
  138.     {
  139.         self::triggerDeprecationOrThrow(
  140.             'v6.5.0.0',
  141.             self::deprecatedMethodMessage(__CLASS____METHOD__'v6.5.0.0''Feature::triggerDeprecationOrThrow()')
  142.         );
  143.         $message 'Deprecated tag:' $removeVersion '(flag:' $flag '). ' $message;
  144.         if (self::isActive($flag) || !self::has($flag)) {
  145.             if (\PHP_SAPI !== 'cli') {
  146.                 ScriptTraces::addDeprecationNotice(sprintf($message, ...$args));
  147.             }
  148.             trigger_deprecation('shopware/core'$sinceVersion$message$args);
  149.         }
  150.     }
  151.     public static function throwException(string $flagstring $messagebool $state true): void
  152.     {
  153.         if (self::isActive($flag) === $state || (self::$registeredFeatures !== [] && !self::has($flag))) {
  154.             throw new \RuntimeException($message);
  155.         }
  156.         if (\PHP_SAPI !== 'cli') {
  157.             ScriptTraces::addDeprecationNotice($message);
  158.         }
  159.     }
  160.     public static function triggerDeprecationOrThrow(string $majorFlagstring $message): void
  161.     {
  162.         if (self::isActive($majorFlag) || (self::$registeredFeatures !== [] && !self::has($majorFlag))) {
  163.             throw new \RuntimeException('Tried to access deprecated functionality: ' $message);
  164.         }
  165.         if (!isset(self::$silent[$majorFlag]) || !self::$silent[$majorFlag]) {
  166.             if (\PHP_SAPI !== 'cli') {
  167.                 ScriptTraces::addDeprecationNotice($message);
  168.             }
  169.             trigger_deprecation('shopware/core'''$message);
  170.         }
  171.     }
  172.     public static function deprecatedMethodMessage(string $classstring $methodstring $majorVersion, ?string $replacement null): string
  173.     {
  174.         $message = \sprintf(
  175.             'Method "%s::%s()" is deprecated and will be removed in %s.',
  176.             $class,
  177.             $method,
  178.             $majorVersion
  179.         );
  180.         if ($replacement) {
  181.             $message = \sprintf('%s Use "%s" instead.'$message$replacement);
  182.         }
  183.         return $message;
  184.     }
  185.     public static function deprecatedClassMessage(string $classstring $majorVersion, ?string $replacement null): string
  186.     {
  187.         $message = \sprintf(
  188.             'Class "%s" is deprecated and will be removed in %s.',
  189.             $class,
  190.             $majorVersion
  191.         );
  192.         if ($replacement) {
  193.             $message = \sprintf('%s Use "%s" instead.'$message$replacement);
  194.         }
  195.         return $message;
  196.     }
  197.     public static function has(string $flag): bool
  198.     {
  199.         $flag self::normalizeName($flag);
  200.         return isset(self::$registeredFeatures[$flag]);
  201.     }
  202.     /**
  203.      * @return array<string, bool>
  204.      */
  205.     public static function getAll(bool $denormalized true): array
  206.     {
  207.         $resolvedFlags = [];
  208.         foreach (self::$registeredFeatures as $name => $_) {
  209.             $active self::isActive($name);
  210.             $resolvedFlags[$name] = $active;
  211.             if (!$denormalized) {
  212.                 continue;
  213.             }
  214.             $resolvedFlags[self::denormalize($name)] = $active;
  215.         }
  216.         return $resolvedFlags;
  217.     }
  218.     /**
  219.      * @param array{name?: string, default?: boolean, major?: boolean, description?: string} $metaData
  220.      *
  221.      * @internal
  222.      */
  223.     public static function registerFeature(string $name, array $metaData = []): void
  224.     {
  225.         $name self::normalizeName($name);
  226.         // merge with existing data
  227.         /** @var array{name?: string, default?: boolean, major?: boolean, description?: string} $metaData */
  228.         $metaData array_merge(
  229.             self::$registeredFeatures[$name] ?? [],
  230.             $metaData
  231.         );
  232.         // set defaults
  233.         $metaData['major'] = (bool) ($metaData['major'] ?? false);
  234.         $metaData['default'] = (bool) ($metaData['default'] ?? false);
  235.         $metaData['description'] = (string) ($metaData['description'] ?? '');
  236.         self::$registeredFeatures[$name] = $metaData;
  237.     }
  238.     /**
  239.      * @param array<string, array{name?: string, default?: boolean, major?: boolean, description?: string}>|string[] $registeredFeatures
  240.      *
  241.      * @internal
  242.      */
  243.     public static function registerFeatures(iterable $registeredFeatures): void
  244.     {
  245.         foreach ($registeredFeatures as $flag => $data) {
  246.             // old format
  247.             if (\is_string($data)) {
  248.                 $flag $data;
  249.                 $data = [];
  250.             }
  251.             self::registerFeature($flag$data);
  252.         }
  253.     }
  254.     /**
  255.      * @internal
  256.      */
  257.     public static function resetRegisteredFeatures(): void
  258.     {
  259.         self::$registeredFeatures = [];
  260.     }
  261.     /**
  262.      * @internal
  263.      *
  264.      * @return array<string, array{'name'?: string, 'default'?: boolean, 'major'?: boolean, 'description'?: string}>
  265.      */
  266.     public static function getRegisteredFeatures(): array
  267.     {
  268.         return self::$registeredFeatures;
  269.     }
  270.     private static function isTrue(string $value): bool
  271.     {
  272.         return $value
  273.             && $value !== 'false'
  274.             && $value !== '0'
  275.             && $value !== '';
  276.     }
  277.     private static function denormalize(string $name): string
  278.     {
  279.         return \strtolower(\str_replace(['_'], '.'$name));
  280.     }
  281. }