SimplifiedConfigParser.php 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384
  1. <?php
  2. declare(strict_types=1);
  3. namespace Shlinkio\Shlink\Core\Config;
  4. use Laminas\Stdlib\ArrayUtils;
  5. use Shlinkio\Shlink\Installer\Util\PathCollection;
  6. use function array_flip;
  7. use function array_intersect_key;
  8. use function array_key_exists;
  9. use function array_keys;
  10. use function Functional\contains;
  11. use function Functional\reduce_left;
  12. use function uksort;
  13. class SimplifiedConfigParser
  14. {
  15. private const SIMPLIFIED_CONFIG_MAPPING = [
  16. 'disable_track_param' => ['app_options', 'disable_track_param'],
  17. 'short_domain_schema' => ['url_shortener', 'domain', 'schema'],
  18. 'short_domain_host' => ['url_shortener', 'domain', 'hostname'],
  19. 'validate_url' => ['url_shortener', 'validate_url'],
  20. 'invalid_short_url_redirect_to' => ['not_found_redirects', 'invalid_short_url'],
  21. 'regular_404_redirect_to' => ['not_found_redirects', 'regular_404'],
  22. 'base_url_redirect_to' => ['not_found_redirects', 'base_path'],
  23. 'db_config' => ['entity_manager', 'connection'],
  24. 'delete_short_url_threshold' => ['delete_short_urls', 'visits_threshold'],
  25. 'redis_servers' => ['redis', 'servers'],
  26. 'base_path' => ['router', 'base_path'],
  27. 'web_worker_num' => ['mezzio-swoole', 'swoole-http-server', 'options', 'worker_num'],
  28. 'task_worker_num' => ['mezzio-swoole', 'swoole-http-server', 'options', 'task_worker_num'],
  29. 'visits_webhooks' => ['url_shortener', 'visits_webhooks'],
  30. ];
  31. private const SIMPLIFIED_CONFIG_SIDE_EFFECTS = [
  32. 'delete_short_url_threshold' => [
  33. 'path' => ['delete_short_urls', 'check_visits_threshold'],
  34. 'value' => true,
  35. ],
  36. 'redis_servers' => [
  37. 'path' => ['dependencies', 'aliases', 'lock_store'],
  38. 'value' => 'redis_lock_store',
  39. ],
  40. ];
  41. private const SIMPLIFIED_MERGEABLE_CONFIG = ['db_config'];
  42. public function __invoke(array $config): array
  43. {
  44. $configForExistingKeys = $this->getConfigForKeysInMappingOrderedByMapping($config);
  45. return reduce_left($configForExistingKeys, function ($value, string $key, $c, PathCollection $collection) {
  46. $path = self::SIMPLIFIED_CONFIG_MAPPING[$key];
  47. if (contains(self::SIMPLIFIED_MERGEABLE_CONFIG, $key)) {
  48. $value = ArrayUtils::merge($collection->getValueInPath($path), $value);
  49. }
  50. $collection->setValueInPath($value, $path);
  51. if (array_key_exists($key, self::SIMPLIFIED_CONFIG_SIDE_EFFECTS)) {
  52. ['path' => $sideEffectPath, 'value' => $sideEffectValue] = self::SIMPLIFIED_CONFIG_SIDE_EFFECTS[$key];
  53. $collection->setValueInPath($sideEffectValue, $sideEffectPath);
  54. }
  55. return $collection;
  56. }, new PathCollection($config))->toArray();
  57. }
  58. private function getConfigForKeysInMappingOrderedByMapping(array $config): array
  59. {
  60. // Ignore any config which is not defined in the mapping
  61. $configForExistingKeys = array_intersect_key($config, self::SIMPLIFIED_CONFIG_MAPPING);
  62. // Order the config by their key, based on the order it was defined in the mapping.
  63. // This mainly allows deprecating keys and defining new ones that will replace the older and always take
  64. // preference, while the old one keeps working for backwards compatibility if the new one is not provided.
  65. $simplifiedConfigOrder = array_flip(array_keys(self::SIMPLIFIED_CONFIG_MAPPING));
  66. uksort(
  67. $configForExistingKeys,
  68. fn (string $a, string $b): int => $simplifiedConfigOrder[$a] - $simplifiedConfigOrder[$b],
  69. );
  70. return $configForExistingKeys;
  71. }
  72. }