diff --git a/src/DependencyInjection/Configuration.php b/src/DependencyInjection/Configuration.php index 049bc6d..b90b7cb 100644 --- a/src/DependencyInjection/Configuration.php +++ b/src/DependencyInjection/Configuration.php @@ -12,6 +12,7 @@ namespace GpsLab\Bundle\GeoIP2Bundle\DependencyInjection; use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition; +use Symfony\Component\Config\Definition\Builder\NodeDefinition; use Symfony\Component\Config\Definition\Builder\TreeBuilder; use Symfony\Component\Config\Definition\ConfigurationInterface; @@ -49,7 +50,84 @@ public function getConfigTreeBuilder(): TreeBuilder $root_node = $tree_builder->root('gpslab_geoip'); } - // normalize default_database from databases + $this->normalizeDefaultDatabase($root_node); + $this->normalizeRootConfigurationToDefaultDatabase($root_node); + $this->validateAvailableDefaultDatabase($root_node); + $this->allowGlobalLicense($root_node); + $this->allowGlobalLocales($root_node); + $this->validateDatabaseLocales($root_node); + + $root_node->fixXmlConfig('locale'); + $locales = $root_node->children()->arrayNode('locales'); + $locales->prototype('scalar'); + $locales + ->treatNullLike([]) + ->defaultValue(['en']); + + $root_node->children()->scalarNode('license'); + + $default_database = $root_node->children()->scalarNode('default_database'); + $default_database->defaultValue('default'); + + $root_node->fixXmlConfig('database'); + $root_node->append($this->getDatabaseNode()); + + return $tree_builder; + } + + /** + * @return ArrayNodeDefinition + */ + private function getDatabaseNode(): ArrayNodeDefinition + { + $tree_builder = new TreeBuilder('databases'); + + if (method_exists($tree_builder, 'getRootNode')) { + // Symfony 4.2 + + $root_node = $tree_builder->getRootNode(); + } else { + // Symfony 4.1 and below + $root_node = $tree_builder->root('databases'); + } + + $root_node->useAttributeAsKey('name'); + + /** @var ArrayNodeDefinition $database_node */ + $database_node = $root_node->prototype('array'); + + $this->normalizeUrl($database_node); + $this->normalizePath($database_node); + + $url = $database_node->children()->scalarNode('url'); + $url->isRequired(); + + $this->validateURL($url); + + $path = $database_node->children()->scalarNode('path'); + $path->isRequired(); + + $database_node->fixXmlConfig('locale'); + $locales = $database_node->children()->arrayNode('locales'); + $locales->prototype('scalar'); + $locales + ->treatNullLike([]) + ->requiresAtLeastOneElement() + ->defaultValue(['en']); + + $database_node->children()->scalarNode('license'); + + $database_node->children()->scalarNode('edition'); + + return $root_node; + } + + /** + * Normalize default_database from databases. + * + * @param NodeDefinition $root_node + */ + private function normalizeDefaultDatabase(NodeDefinition $root_node): void + { $root_node ->beforeNormalization() ->ifTrue(static function ($v): bool { @@ -57,7 +135,8 @@ public function getConfigTreeBuilder(): TreeBuilder is_array($v) && !array_key_exists('default_database', $v) && array_key_exists('databases', $v) && - is_array($v['databases']); + is_array($v['databases']) && + $v['databases']; }) ->then(static function (array $v): array { $keys = array_keys($v['databases']); @@ -65,8 +144,15 @@ public function getConfigTreeBuilder(): TreeBuilder return $v; }); + } - // normalize databases root configuration to default_database + /** + * Normalize databases root configuration to default_database. + * + * @param NodeDefinition $root_node + */ + private function normalizeRootConfigurationToDefaultDatabase(NodeDefinition $root_node): void + { $root_node ->beforeNormalization() ->ifTrue(static function ($v): bool { @@ -86,8 +172,15 @@ public function getConfigTreeBuilder(): TreeBuilder return $v; }); + } - // default_database should be exists in databases + /** + * Validate that the default_database exists in the list of databases. + * + * @param NodeDefinition $root_node + */ + private function validateAvailableDefaultDatabase(NodeDefinition $root_node): void + { $root_node ->validate() ->ifTrue(static function ($v): bool { @@ -103,8 +196,16 @@ public function getConfigTreeBuilder(): TreeBuilder throw new \InvalidArgumentException(sprintf('Undefined default database "%s". Available "%s" databases.', $v['default_database'], $databases)); }); + } - // add license to databases config if not exists (allow use a global license for all databases) + /** + * Add a license option to the databases configuration if it does not exist. + * Allow use a global license for all databases. + * + * @param NodeDefinition $root_node + */ + private function allowGlobalLicense(NodeDefinition $root_node): void + { $root_node ->beforeNormalization() ->ifTrue(static function ($v): bool { @@ -123,8 +224,16 @@ public function getConfigTreeBuilder(): TreeBuilder return $v; }); + } - // add locales to databases config if not exists (allow use a global locales for all databases) + /** + * Add a locales option to the databases configuration if it does not exist. + * Allow use a global locales for all databases. + * + * @param NodeDefinition $root_node + */ + private function allowGlobalLocales(NodeDefinition $root_node): void + { $root_node ->beforeNormalization() ->ifTrue(static function ($v): bool { @@ -143,66 +252,38 @@ public function getConfigTreeBuilder(): TreeBuilder return $v; }); + } - // validate database locales + /** + * Validate database locales. + * + * @param NodeDefinition $root_node + */ + private function validateDatabaseLocales(NodeDefinition $root_node): void + { $root_node ->validate() - ->ifTrue(static function ($v): bool { - return - is_array($v) && - array_key_exists('databases', $v) && - is_array($v['databases']); - }) - ->then(static function (array $v): array { - foreach ($v['databases'] as $name => $database) { - if (!array_key_exists('locales', $database) || empty($database['locales'])) { - throw new \InvalidArgumentException(sprintf('The list of locales should not be empty in databases "%s".', $name)); - } + ->ifTrue(static function ($v): bool { + return is_array($v) && array_key_exists('databases', $v) && is_array($v['databases']); + }) + ->then(static function (array $v): array { + foreach ($v['databases'] as $name => $database) { + if (empty($database['locales'])) { + throw new \InvalidArgumentException(sprintf('The list of locales should not be empty in databases "%s".', $name)); } + } - return $v; - }); - - $root_node->fixXmlConfig('locale'); - $locales = $root_node->children()->arrayNode('locales'); - $locales->prototype('scalar'); - $locales - ->treatNullLike([]) - ->defaultValue(['en']); - - $root_node->children()->scalarNode('license'); - - $default_database = $root_node->children()->scalarNode('default_database'); - $default_database->defaultValue('default'); - - $root_node->fixXmlConfig('database'); - $root_node->append($this->getDatabaseNode()); - - return $tree_builder; + return $v; + }); } /** - * @return ArrayNodeDefinition + * Normalize url option from license key and edition id. + * + * @param NodeDefinition $database_node */ - private function getDatabaseNode(): ArrayNodeDefinition + private function normalizeUrl(NodeDefinition $database_node): void { - $tree_builder = new TreeBuilder('databases'); - - if (method_exists($tree_builder, 'getRootNode')) { - // Symfony 4.2 + - $root_node = $tree_builder->getRootNode(); - } else { - // Symfony 4.1 and below - $root_node = $tree_builder->root('databases'); - } - - /** @var ArrayNodeDefinition $database_node */ - $database_node = $root_node - ->requiresAtLeastOneElement() - ->useAttributeAsKey('name') - ->prototype('array'); - - // normalize url from license and edition $database_node ->beforeNormalization() ->ifTrue(static function ($v): bool { @@ -217,8 +298,15 @@ private function getDatabaseNode(): ArrayNodeDefinition return $v; }); + } - // normalize path from edition + /** + * Normalize path option from edition id. + * + * @param NodeDefinition $database_node + */ + private function normalizePath(NodeDefinition $database_node): void + { $database_node ->beforeNormalization() ->ifTrue(static function ($v): bool { @@ -229,10 +317,15 @@ private function getDatabaseNode(): ArrayNodeDefinition return $v; }); + } - $url = $database_node->children()->scalarNode('url'); - $url->isRequired(); - // url must be a valid URL + /** + * The url option must be a valid URL. + * + * @param NodeDefinition $url + */ + private function validateURL(NodeDefinition $url): void + { $url ->validate() ->ifTrue(static function ($v): bool { @@ -241,22 +334,5 @@ private function getDatabaseNode(): ArrayNodeDefinition ->then(static function (string $v): array { throw new \InvalidArgumentException(sprintf('URL "%s" must be valid.', $v)); }); - - $path = $database_node->children()->scalarNode('path'); - $path->isRequired(); - - $database_node->fixXmlConfig('locale'); - $locales = $database_node->children()->arrayNode('locales'); - $locales->prototype('scalar'); - $locales - ->treatNullLike([]) - ->requiresAtLeastOneElement() - ->defaultValue(['en']); - - $database_node->children()->scalarNode('license'); - - $database_node->children()->scalarNode('edition'); - - return $root_node; } } diff --git a/tests/DependencyInjection/ConfigurationTest.php b/tests/DependencyInjection/ConfigurationTest.php index f8e84e3..763f551 100644 --- a/tests/DependencyInjection/ConfigurationTest.php +++ b/tests/DependencyInjection/ConfigurationTest.php @@ -29,16 +29,6 @@ public function getBadConfigs(): array { $return = []; foreach (['/tmp/var/cache', null] as $cache_dir) { - $return[] = [$cache_dir, [ - 'gpslab_geoip' => [ - 'databases' => null, - ], - ]]; - $return[] = [$cache_dir, [ - 'gpslab_geoip' => [ - 'databases' => [], - ], - ]]; $return[] = [$cache_dir, [ 'gpslab_geoip' => [ 'license' => 'LICENSE', @@ -151,6 +141,24 @@ public function getConfigs(): array 'default_database' => 'default', 'databases' => [], ]]; + $return[] = [$cache_dir, [ + 'gpslab_geoip' => [ + 'databases' => null, + ], + ], [ + 'databases' => [], + 'locales' => ['en'], + 'default_database' => 'default', + ]]; + $return[] = [$cache_dir, [ + 'gpslab_geoip' => [ + 'databases' => [], + ], + ], [ + 'databases' => [], + 'locales' => ['en'], + 'default_database' => 'default', + ]]; $return[] = [$cache_dir, [ 'gpslab_geoip' => [ 'license' => 'LICENSE',