['utm_source', '^tx_my_plugin[aspects]', '~[temporary]'], * ... * ]); */ class CacheHashConfiguration { public const ASPECT_CACHED_PARAMETERS_WHITELIST = 'cachedParametersWhiteList'; public const ASPECT_EXCLUDED_PARAMETERS = 'excludedParameters'; public const ASPECT_EXCLUDED_PARAMETERS_IF_EMPTY = 'excludedParametersIfEmpty'; public const ASPECT_REQUIRED_CACHE_HASH_PRESENCE_PARAMETERS = 'requireCacheHashPresenceParameters'; protected const PROPERTY_EXCLUDE_ALL_EMPTY_PARAMETERS = 'excludeAllEmptyParameters'; protected const INDICATOR_STARTS_WITH = '^'; protected const INDICATOR_CONTAINS = '~'; protected const INDICATOR_EQUALS = '='; protected const ALLOWED_INDICATORS = [ self::INDICATOR_STARTS_WITH, self::INDICATOR_CONTAINS, self::INDICATOR_EQUALS, ]; protected const ALLOWED_PROPERTY_NAMES = [ self::PROPERTY_EXCLUDE_ALL_EMPTY_PARAMETERS, self::ASPECT_CACHED_PARAMETERS_WHITELIST, self::ASPECT_EXCLUDED_PARAMETERS, self::ASPECT_EXCLUDED_PARAMETERS_IF_EMPTY, self::ASPECT_REQUIRED_CACHE_HASH_PRESENCE_PARAMETERS, ]; /** * @var array $configuration The raw configuration of `$GLOBALS['TYPO3_CONF_VARS']['FE']['cacheHash']` */ protected array $configuration; /** * @var array $data The resolved aspects to be applied, based on the configuration */ protected array $data = []; public function __construct(?array $configuration = null) { $configuration = $configuration ?? $GLOBALS['TYPO3_CONF_VARS']['FE']['cacheHash'] ?? []; $this->configuration = array_filter($configuration, [$this, 'isAllowedProperty'], ARRAY_FILTER_USE_KEY); $this->processConfiguration(); } public function shallExcludeAllEmptyParameters(): bool { return !empty($this->configuration[self::PROPERTY_EXCLUDE_ALL_EMPTY_PARAMETERS]); } public function applies(string $aspect, string $value): bool { return $this->equals($aspect, $value) || $this->contains($aspect, $value) || $this->startsWith($aspect, $value); } public function equals(string $aspect, string $value): bool { $data = $this->getData($aspect, self::INDICATOR_EQUALS); return !empty($data) && in_array($value, $data, true); } public function startsWith(string $aspect, string $value): bool { $data = $this->getData($aspect, self::INDICATOR_STARTS_WITH); if (empty($data)) { return false; } foreach ($data as $item) { if (str_starts_with($value, $item)) { return true; } } return false; } public function contains(string $aspect, string $value): bool { $data = $this->getData($aspect, self::INDICATOR_CONTAINS); if (empty($data)) { return false; } foreach ($data as $item) { if (str_contains($value, $item)) { return true; } } return false; } public function hasData(string $aspect): bool { return !empty($this->data[$aspect]); } protected function getData(string $aspect, string $indicator): ?array { return $this->data[$aspect][$indicator] ?? null; } protected function defineData(string $aspect): void { if (empty($this->configuration[$aspect])) { return; } if (!is_array($this->configuration[$aspect])) { throw new \LogicException( sprintf('Expected array value, got %s', gettype($this->configuration[$aspect])), 1580225311 ); } $data = []; foreach ($this->configuration[$aspect] as $value) { if (!is_scalar($value)) { throw new \LogicException( sprintf('Expected scalar value, got %s', gettype($value)), 1580225312 ); } if ($value === '') { continue; } $indicator = $value[0] ?? null; // normalize value to be indicated if (!in_array($indicator, self::ALLOWED_INDICATORS, true)) { $indicator = self::INDICATOR_EQUALS; $value = self::INDICATOR_EQUALS . $value; } if (strlen((string)$value) === 1) { throw new \LogicException( sprintf('Empty value after %s indicator', $indicator), 1580225313 ); } $data[$indicator][] = substr((string)$value, 1); } if (!empty($data)) { $this->data[$aspect] = $data; } } protected function processConfiguration(): void { $this->data = []; $this->defineData(self::ASPECT_CACHED_PARAMETERS_WHITELIST); $this->defineData(self::ASPECT_EXCLUDED_PARAMETERS); $this->defineData(self::ASPECT_EXCLUDED_PARAMETERS_IF_EMPTY); $this->defineData(self::ASPECT_REQUIRED_CACHE_HASH_PRESENCE_PARAMETERS); } protected function isAllowedProperty(string $propertyName): bool { return in_array($propertyName, self::ALLOWED_PROPERTY_NAMES, true); } }