├── .gitignore ├── tests ├── fixture │ ├── invalid-yaml.yml │ ├── second-level.yml │ ├── first-and-second-level.yml │ ├── first-level.yml │ ├── third-level.yml │ ├── first-second-and-third-level.yml │ ├── ok-tagged.yml │ ├── ok.yml │ └── symfony-config.yml └── SortCheckerTest.php ├── bin ├── yaml-sort-checker └── yaml-sort-checker.php ├── docs ├── yaml-sort-checker-demo.png └── symfony-config │ └── yaml-sort-checker.yml ├── .travis.yml ├── src ├── SortCheckResult.php ├── CheckCommand.php └── SortChecker.php ├── phpunit.xml.dist ├── CHANGELOG.md ├── LICENSE ├── ruleset.xml ├── composer.json └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | /composer.lock 3 | /vendor 4 | .phpunit.result.cache 5 | -------------------------------------------------------------------------------- /tests/fixture/invalid-yaml.yml: -------------------------------------------------------------------------------- 1 | foo: 2 | loo: OK 3 | bar: 4 | data: "hello" 5 | -------------------------------------------------------------------------------- /bin/yaml-sort-checker: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | messages = $messages; 21 | } 22 | 23 | public function isOk(): bool 24 | { 25 | return count($this->messages) === 0; 26 | } 27 | 28 | /** 29 | * @return string[] 30 | */ 31 | public function getMessages(): array 32 | { 33 | return $this->messages; 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /phpunit.xml.dist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 12 | 13 | 14 | 15 | tests/ 16 | 17 | 18 | 19 | 20 | 21 | ./src 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## 1.5.0 (2020-12-01) 4 | - [#14](https://github.com/mhujer/yaml-sort-checker/pull/14) Allow PHP 8 5 | 6 | ## 1.4.0 (2019-11-22) 7 | - [#12](https://github.com/mhujer/yaml-sort-checker/pull/12) Add support for Symfony 5 8 | 9 | ## 1.3.0 (2019-11-22) 10 | - [#11](https://github.com/mhujer/yaml-sort-checker/pull/11) Add excluded sections support (thanks *@hojgr*) 11 | 12 | ## 1.2.0 (2018-03-26) 13 | - [#9](https://github.com/mhujer/yaml-sort-checker/pull/9) Add excluded sections support (thanks *@OndraM*) 14 | 15 | ## 1.1.0 (2018-03-20) 16 | - [#6](https://github.com/mhujer/yaml-sort-checker/pull/6) Symfony 4 compatibility (thanks *@OndraM*) 17 | 18 | ## 1.0.0 (2017-01-28) 19 | - initial release 20 | -------------------------------------------------------------------------------- /bin/yaml-sort-checker.php: -------------------------------------------------------------------------------- 1 | setCatchExceptions(false); 30 | $application->add(new CheckCommand()); 31 | $application->setDefaultCommand('yaml-check-sort', true); 32 | $application->run(); 33 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Martin Hujer 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /ruleset.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | vendor/ 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 16 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 40 | 41 | 42 | 43 | 5 44 | 45 | 46 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mhujer/yaml-sort-checker", 3 | "type": "library", 4 | "description": "YAML sort checker checks if your YML files are properly sorted to prevent merge conflicts", 5 | "keywords": [ 6 | "yaml", 7 | "sorter" 8 | ], 9 | "homepage": "https://github.com/mhujer/yaml-sort-checked", 10 | "license": "MIT", 11 | "authors": [ 12 | { 13 | "name": "Martin Hujer", 14 | "email": "mhujer@gmail.com", 15 | "homepage": "https://www.martinhujer.cz" 16 | } 17 | ], 18 | "bin": [ 19 | "bin/yaml-sort-checker" 20 | ], 21 | "require": { 22 | "php": "~7.3 | ^8.0", 23 | "symfony/console": "~3.4|~4.3|~5.0", 24 | "symfony/yaml": "~3.4|~4.3|~5.0" 25 | }, 26 | "require-dev": { 27 | "consistence/coding-standard": "3.10.1", 28 | "php-parallel-lint/php-parallel-lint": "1.2.0", 29 | "phpstan/phpstan": "0.12.57", 30 | "phpunit/phpunit": "9.4.3", 31 | "slevomat/coding-standard": "6.4.1" 32 | }, 33 | "autoload": { 34 | "psr-4": { 35 | "Mhujer\\YamlSortChecker\\": [ 36 | "src" 37 | ] 38 | }, 39 | "classmap": [ 40 | "src" 41 | ] 42 | }, 43 | "autoload-dev": { 44 | "psr-4": { 45 | "Mhujer\\YamlSortChecker\\": [ 46 | "tests" 47 | ] 48 | }, 49 | "classmap": [ 50 | "tests" 51 | ] 52 | }, 53 | "scripts": { 54 | "build": [ 55 | "@phplint", 56 | "@phpcs", 57 | "@phpstan", 58 | "@test" 59 | ], 60 | "phplint": "parallel-lint -j 10 --exclude vendor .", 61 | "phpcs": "phpcs --standard=ruleset.xml --extensions=php --encoding=utf-8 --tab-width=4 -sp bin src tests", 62 | "phpstan": "@php vendor/bin/phpstan analyse bin src tests --level 7 --no-progress", 63 | "test": "phpunit" 64 | }, 65 | "config": { 66 | "sort-packages": true 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /docs/symfony-config/yaml-sort-checker.yml: -------------------------------------------------------------------------------- 1 | files: 2 | app/config/config.yml: 3 | excludedKeys: 4 | doctrine: 5 | dbal: 6 | - dbname 7 | - port 8 | - user 9 | - password 10 | - charset 11 | - default_table_options 12 | swiftmailer: 13 | - transport 14 | - host 15 | - username 16 | - password 17 | excludedSections: 18 | 0: imports 19 | 1: parameters 20 | 21 | app/config/config_dev.yml: 22 | excludedKeys: 23 | 0: imports 24 | monolog: 25 | handlers: 26 | console: 27 | - type 28 | main: 29 | - type 30 | 31 | app/config/config_prod.yml: 32 | excludedKeys: 33 | monolog: 34 | handlers: 35 | main: 36 | - type 37 | nested: 38 | - type 39 | 40 | app/config/config_test.yml: 41 | excludedKeys: 42 | 0: imports 43 | 44 | app/config/parameters.yml.dist: 45 | excludedKeys: 46 | parameters: 47 | - database_host 48 | - database_port 49 | - database_name 50 | - database_user 51 | - database_password 52 | - mailer_transport 53 | - mailer_host 54 | - mailer_user 55 | - mailer_password 56 | 57 | app/config/routing.yml: 58 | depth: 1 59 | 60 | app/config/routing_dev.yml: 61 | depth: 1 62 | 63 | app/config/security.yml: 64 | 65 | app/config/services.yml: 66 | depth: 2 67 | 68 | yaml-sort-checker.yml: 69 | -------------------------------------------------------------------------------- /tests/fixture/symfony-config.yml: -------------------------------------------------------------------------------- 1 | imports: 2 | - { resource: parameters.yml } 3 | - { resource: security.yml } 4 | - { resource: services.yml } 5 | 6 | # Put parameters here that don't need to change on each machine where the app is deployed 7 | # http://symfony.com/doc/current/best_practices/configuration.html#application-related-configuration 8 | parameters: 9 | locale: en 10 | 11 | framework: 12 | #esi: ~ 13 | #translator: { fallbacks: ["%locale%"] } 14 | secret: "%secret%" 15 | router: 16 | resource: "%kernel.root_dir%/config/routing.yml" 17 | strict_requirements: ~ 18 | form: ~ 19 | csrf_protection: ~ 20 | validation: { enable_annotations: true } 21 | #serializer: { enable_annotations: true } 22 | templating: 23 | engines: ['twig'] 24 | default_locale: "%locale%" 25 | trusted_hosts: ~ 26 | trusted_proxies: ~ 27 | session: 28 | # http://symfony.com/doc/current/reference/configuration/framework.html#handler-id 29 | handler_id: session.handler.native_file 30 | save_path: "%kernel.root_dir%/../var/sessions/%kernel.environment%" 31 | fragments: ~ 32 | http_method_override: true 33 | assets: ~ 34 | php_errors: 35 | log: true 36 | 37 | # Twig Configuration 38 | twig: 39 | debug: "%kernel.debug%" 40 | strict_variables: "%kernel.debug%" 41 | 42 | # Doctrine Configuration 43 | doctrine: 44 | dbal: 45 | driver: pdo_mysql 46 | host: "%database_host%" 47 | port: "%database_port%" 48 | dbname: "%database_name%" 49 | user: "%database_user%" 50 | password: "%database_password%" 51 | charset: UTF8 52 | # if using pdo_sqlite as your database driver: 53 | # 1. add the path in parameters.yml 54 | # e.g. database_path: "%kernel.root_dir%/data/data.db3" 55 | # 2. Uncomment database_path in parameters.yml.dist 56 | # 3. Uncomment next line: 57 | # path: "%database_path%" 58 | 59 | orm: 60 | auto_generate_proxy_classes: "%kernel.debug%" 61 | naming_strategy: doctrine.orm.naming_strategy.underscore 62 | auto_mapping: true 63 | 64 | # Swiftmailer Configuration 65 | swiftmailer: 66 | transport: "%mailer_transport%" 67 | host: "%mailer_host%" 68 | username: "%mailer_user%" 69 | password: "%mailer_password%" 70 | spool: { type: memory } 71 | -------------------------------------------------------------------------------- /src/CheckCommand.php: -------------------------------------------------------------------------------- 1 | setName('yaml-check-sort'); 17 | } 18 | 19 | protected function execute(InputInterface $input, OutputInterface $output): ?int 20 | { 21 | $output->writeln('#### YAML Sort Checker ####'); 22 | 23 | $configFilePath = realpath('yaml-sort-checker.yml'); 24 | if ($configFilePath === false) { 25 | $output->writeln(sprintf('Config file "%s" not found!', 'yaml-sort-checker.yml')); 26 | exit(1); 27 | } 28 | $output->writeln(sprintf('Using config file "%s"', $configFilePath)); 29 | 30 | $configFileContents = file_get_contents($configFilePath); 31 | if ($configFileContents === false) { 32 | throw new \Exception(sprintf('File "%s" could not be loaded', $configFilePath)); 33 | } 34 | $config = Yaml::parse($configFileContents); 35 | 36 | if (!array_key_exists('files', $config)) { 37 | $output->writeln('There must be a key "files" in config'); 38 | exit(1); 39 | } 40 | 41 | if (count($config['files']) === 0) { 42 | $output->writeln('There must be some files in the config'); 43 | exit(1); 44 | } 45 | 46 | $output->writeln(''); 47 | 48 | $isOk = true; 49 | $sortChecker = new SortChecker(); 50 | foreach ($config['files'] as $filename => $options) { 51 | if (!is_array($options)) { 52 | $options = []; 53 | } 54 | 55 | $depth = array_key_exists('depth', $options) ? $options['depth'] : 999; 56 | $excludedKeys = array_key_exists('excludedKeys', $options) ? $options['excludedKeys'] : []; 57 | $excludedSections = array_key_exists('excludedSections', $options) ? $options['excludedSections'] : []; 58 | 59 | $output->write(sprintf('Checking %s: ', $filename)); 60 | if (realpath($filename) === false || !is_readable(realpath($filename))) { 61 | $output->writeln('NOT READABLE!'); 62 | exit(1); 63 | } 64 | 65 | $sortCheckResult = $sortChecker->isSorted($filename, $depth, $excludedKeys, $excludedSections); 66 | 67 | if ($sortCheckResult->isOk()) { 68 | $output->writeln('OK'); 69 | } else { 70 | $output->writeln('ERROR'); 71 | foreach ($sortCheckResult->getMessages() as $message) { 72 | $output->writeln(' ' . $message); 73 | } 74 | $isOk = false; 75 | } 76 | } 77 | 78 | $output->writeln(''); 79 | if (!$isOk) { 80 | $output->writeln('Fix the YAMLs or exclude the keys in the config.'); 81 | return 1; 82 | } else { 83 | $output->writeln('All YAMLs are properly sorted.'); 84 | return 0; 85 | } 86 | } 87 | 88 | } 89 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # YAML file sort checker [![Build Status](https://travis-ci.org/mhujer/yaml-sort-checker.svg?branch=master)](https://travis-ci.org/mhujer/yaml-sort-checker) 2 | 3 | [![Latest Stable Version](https://poser.pugx.org/mhujer/yaml-sort-checker/version.png)](https://packagist.org/packages/mhujer/yaml-sort-checker) [![Total Downloads](https://poser.pugx.org/mhujer/yaml-sort-checker/downloads.png)](https://packagist.org/packages/mhujer/yaml-sort-checker) [![License](https://poser.pugx.org/mhujer/yaml-sort-checker/license.svg)](https://packagist.org/packages/mhujer/yaml-sort-checker) 4 | 5 | This library helps you to keep YAML file sorted to prevent unnecessary merge conflicts. 6 | 7 | > Check out the article I've written about the tool: [Keep your YAML files sorted with YAML sort checker](https://blog.martinhujer.cz/yaml-sort-checker/) 8 | 9 | Typical example is when two developers register a new service in `services.yml`. If they both add it to the end, it unevitably will lead to a merge conflict. However, when the services are alphabetically sorted, the probability of merge conflict is much lower (because the added services probably won't clash). 10 | 11 | ![yaml-sort-checker DEMO](./docs/yaml-sort-checker-demo.png) 12 | 13 | Usage 14 | ---- 15 | 1. Install the latest version with `composer require --dev mhujer/yaml-sort-checker` 16 | 2. Create a configuration file `yaml-sort-checker.yml` in project root with list of the files for checking, see the [example configuration for Symfony app](/docs/symfony-config/yaml-sort-checker.yml). 17 | 3. Run `vendor/bin/yaml-sort-checker` (depends on where you have your Composer bin directory) 18 | 4. Exclude the yaml keys (or even whole sections) you don't want to sort - e.g. it makes more sense to have them unsorted (see the [example configuration](/docs/symfony-config/yaml-sort-checker.yml)) 19 | 20 | PHPStorm Integration 21 | --------------------- 22 | Until [WI-35271](https://youtrack.jetbrains.com/issue/WI-35271) is resolved, YAML sort checker can be integrated into PHPStorm by using File Watcher feature. 23 | 24 | 1. Open Settings -> Tools -> File Watchers 25 | 2. Add new 26 | 3. File type: `YAML` 27 | 4. Program: `PATH_TO_YOUR_PROJECT\vendor\bin\yaml-sort-checker.bat` 28 | 5. Open *Other Options* and enter: `$ProjectFileDir$` to *Working directory* 29 | 6. Now, when you are editing YAML files, it will run the checker on every file save and will open the console if there are errors 30 | 31 | Requirements 32 | ------------ 33 | Works with PHP 7.2 or higher and Symfony 3.4 or higher. 34 | 35 | Submitting bugs and feature requests 36 | ------------------------------------ 37 | Bugs and feature request are tracked on [GitHub](https://github.com/mhujer/yaml-sort-checker/issues) 38 | 39 | Author 40 | ------ 41 | [Martin Hujer](https://www.martinhujer.cz) 42 | 43 | Changelog 44 | ---------- 45 | 46 | See [CHANGELOG.md](./CHANGELOG.md) for latest changes. 47 | -------------------------------------------------------------------------------- /src/SortChecker.php: -------------------------------------------------------------------------------- 1 | areDataSorted($data, $excludedKeys, $excludedSections, null, $depth); 34 | 35 | return new SortCheckResult($errors); 36 | 37 | } catch (\Symfony\Component\Yaml\Exception\ParseException $e) { 38 | return new SortCheckResult([ 39 | sprintf('Unable to parse the YAML string: %s', $e->getMessage()), 40 | ]); 41 | } 42 | } 43 | 44 | /** 45 | * @param mixed[] $yamlData 46 | * @param mixed[]|string[]|string[][] $excludedKeys 47 | * @param mixed[]|string[]|string[][] $excludedSections 48 | * @param string|null $parent 49 | * @param int $depth 50 | * @return string[] array of error messages 51 | */ 52 | private function areDataSorted( 53 | array $yamlData, 54 | array $excludedKeys, 55 | array $excludedSections, 56 | ?string $parent = null, 57 | int $depth 58 | ): array 59 | { 60 | if ($depth === 0) { 61 | return []; 62 | } 63 | 64 | $errors = []; 65 | $lastKey = null; 66 | foreach ($yamlData as $key => $value) { 67 | $isSectionExcluded = false; 68 | if (in_array($key, $excludedSections, true)) { 69 | $isSectionExcluded = true; 70 | } 71 | 72 | if (!$isSectionExcluded && !in_array($key, $excludedKeys, true)) { // isn't excluded 73 | if ($lastKey !== null && is_string($lastKey) && is_string($key)) { 74 | if (strcasecmp($key, $lastKey) < 0) { 75 | if ($parent !== null) { 76 | $printKey = $parent . '.' . $key; 77 | $printLastKey = $parent . '.' . $lastKey; 78 | } else { 79 | $printKey = $key; 80 | $printLastKey = $lastKey; 81 | } 82 | $errors[] = sprintf('"%s" should be before "%s"', $printKey, $printLastKey); 83 | } 84 | } 85 | $lastKey = $key; 86 | } 87 | 88 | $nextExcludedKeys = []; 89 | if (array_key_exists($key, $excludedKeys)) { 90 | $nextExcludedKeys = $excludedKeys[$key]; 91 | } 92 | 93 | $nextExcludedSections = []; 94 | if (array_key_exists($key, $excludedSections)) { 95 | $nextExcludedSections = $excludedSections[$key]; 96 | } 97 | 98 | if (!$isSectionExcluded && is_array($value)) { 99 | $errors = array_merge( 100 | $errors, 101 | $this->areDataSorted( 102 | $value, 103 | $nextExcludedKeys, 104 | $nextExcludedSections, 105 | ($parent !== null ? $parent . '.' : '') . $key, 106 | $depth - 1 107 | ) 108 | ); 109 | } 110 | } 111 | 112 | return $errors; 113 | } 114 | 115 | } 116 | -------------------------------------------------------------------------------- /tests/SortCheckerTest.php: -------------------------------------------------------------------------------- 1 | isSorted(__DIR__ . '/fixture/ok.yml', 10); 14 | 15 | $this->assertTrue($result->isOk()); 16 | $this->assertCount(0, $result->getMessages()); 17 | } 18 | 19 | public function testSortedFileWithCustomTag(): void 20 | { 21 | $checker = new SortChecker(); 22 | $result = $checker->isSorted(__DIR__ . '/fixture/ok-tagged.yml', 10); 23 | 24 | $this->assertTrue($result->isOk()); 25 | $this->assertCount(0, $result->getMessages()); 26 | } 27 | 28 | public function testInvalidYamlIsInvalid(): void 29 | { 30 | $checker = new SortChecker(); 31 | $result = $checker->isSorted(__DIR__ . '/fixture/invalid-yaml.yml', 1); 32 | 33 | $this->assertFalse($result->isOk()); 34 | $this->assertCount(1, $result->getMessages()); 35 | $this->assertStringStartsWith('Unable to parse the YAML string', $result->getMessages()[0]); 36 | } 37 | 38 | public function testInvalidSortInFirstLevel(): void 39 | { 40 | $checker = new SortChecker(); 41 | $result = $checker->isSorted(__DIR__ . '/fixture/first-level.yml', 1); 42 | 43 | $this->assertFalse($result->isOk()); 44 | $this->assertCount(1, $result->getMessages()); 45 | $this->assertSame('"bar" should be before "foo"', $result->getMessages()[0]); 46 | } 47 | 48 | public function testInvalidSortInFirstLevelWithExcludeKeys(): void 49 | { 50 | $checker = new SortChecker(); 51 | $result = $checker->isSorted( 52 | __DIR__ . '/fixture/first-level.yml', 53 | 1, 54 | [ 55 | 'bar', 56 | ] 57 | ); 58 | $this->assertTrue($result->isOk()); 59 | } 60 | 61 | public function testInvalidSortInSecondLevel(): void 62 | { 63 | $checker = new SortChecker(); 64 | $result = $checker->isSorted(__DIR__ . '/fixture/second-level.yml', 2); 65 | 66 | $this->assertFalse($result->isOk()); 67 | $this->assertCount(1, $result->getMessages()); 68 | $this->assertSame('"foo.car" should be before "foo.dar"', $result->getMessages()[0]); 69 | } 70 | 71 | public function testInvalidSortInSecondLevelWithExcludeKeys(): void 72 | { 73 | $checker = new SortChecker(); 74 | $result = $checker->isSorted( 75 | __DIR__ . '/fixture/second-level.yml', 76 | 2, 77 | [ 78 | 'foo' => [ 79 | 'car', 80 | ], 81 | ] 82 | ); 83 | 84 | $this->assertTrue($result->isOk()); 85 | } 86 | 87 | public function testInvalidSortInFirstAndSecondLevel(): void 88 | { 89 | $checker = new SortChecker(); 90 | $result = $checker->isSorted(__DIR__ . '/fixture/first-and-second-level.yml', 2); 91 | 92 | $this->assertFalse($result->isOk()); 93 | $this->assertCount(2, $result->getMessages()); 94 | $this->assertSame('"foo" should be before "zoo"', $result->getMessages()[0]); 95 | $this->assertSame('"foo.car" should be before "foo.dar"', $result->getMessages()[1]); 96 | } 97 | 98 | public function testInvalidSortInFirstSecondAndThirdLevel(): void 99 | { 100 | $checker = new SortChecker(); 101 | $result = $checker->isSorted(__DIR__ . '/fixture/first-second-and-third-level.yml', 3); 102 | 103 | $this->assertFalse($result->isOk()); 104 | $this->assertCount(3, $result->getMessages()); 105 | $this->assertSame('"foo" should be before "zoo"', $result->getMessages()[0]); 106 | $this->assertSame('"foo.car" should be before "foo.dar"', $result->getMessages()[1]); 107 | $this->assertSame('"foo.car.c" should be before "foo.car.d"', $result->getMessages()[2]); 108 | } 109 | 110 | public function testExcludedFirstAndSecondLevelDoesNotPreventCheckingOfThirdLevel(): void 111 | { 112 | $checker = new SortChecker(); 113 | $result = $checker->isSorted( 114 | __DIR__ . '/fixture/first-second-and-third-level.yml', 115 | 3, 116 | [ 117 | 0 => 'foo', 118 | 'foo' => [ 119 | 'dar', 120 | ], 121 | ] 122 | ); 123 | 124 | $this->assertFalse($result->isOk()); 125 | $this->assertCount(1, $result->getMessages()); 126 | $this->assertSame('"foo.car.c" should be before "foo.car.d"', $result->getMessages()[0]); 127 | } 128 | 129 | public function testInvalidSortWithExcludeFirstLevelSection(): void 130 | { 131 | $checker = new SortChecker(); 132 | $result = $checker->isSorted( 133 | __DIR__ . '/fixture/third-level.yml', 134 | 999, 135 | [], 136 | ['foo'] 137 | ); 138 | $this->assertTrue($result->isOk()); 139 | } 140 | 141 | public function testInvalidSortWithExcludeSecondLevelSection(): void 142 | { 143 | $checker = new SortChecker(); 144 | $result = $checker->isSorted( 145 | __DIR__ . '/fixture/third-level.yml', 146 | 999, 147 | [], 148 | ['foo' => ['car']] 149 | ); 150 | $this->assertSame([], $result->getMessages()); 151 | $this->assertTrue($result->isOk()); 152 | } 153 | 154 | public function testSymfonyConfig(): void 155 | { 156 | $checker = new SortChecker(); 157 | $result = $checker->isSorted(__DIR__ . '/fixture/symfony-config.yml', 5); 158 | 159 | $this->assertFalse($result->isOk()); 160 | $this->assertCount(16, $result->getMessages()); 161 | $this->assertSame( 162 | [ 163 | '"framework" should be before "parameters"', 164 | '"framework.router" should be before "framework.secret"', 165 | '"framework.form" should be before "framework.router"', 166 | '"framework.csrf_protection" should be before "framework.form"', 167 | '"framework.templating" should be before "framework.validation"', 168 | '"framework.default_locale" should be before "framework.templating"', 169 | '"framework.session" should be before "framework.trusted_proxies"', 170 | '"framework.fragments" should be before "framework.session"', 171 | '"framework.assets" should be before "framework.http_method_override"', 172 | '"doctrine" should be before "twig"', 173 | '"doctrine.dbal.dbname" should be before "doctrine.dbal.port"', 174 | '"doctrine.dbal.password" should be before "doctrine.dbal.user"', 175 | '"doctrine.dbal.charset" should be before "doctrine.dbal.password"', 176 | '"doctrine.orm.auto_mapping" should be before "doctrine.orm.naming_strategy"', 177 | '"swiftmailer.host" should be before "swiftmailer.transport"', 178 | '"swiftmailer.password" should be before "swiftmailer.username"', 179 | ], 180 | $result->getMessages() 181 | ); 182 | } 183 | 184 | public function testSymfonyConfigWithExcludedKeys(): void 185 | { 186 | $checker = new SortChecker(); 187 | $result = $checker->isSorted( 188 | __DIR__ . '/fixture/symfony-config.yml', 189 | 5, 190 | [ 191 | 0 => 'imports', 192 | 1 => 'parameters', 193 | 'doctrine' => [ 194 | 'dbal' => [ 195 | 'dbname', 196 | 'charset', 197 | 'port', 198 | 'user', 199 | ], 200 | ], 201 | 'swiftmailer' => [ 202 | 'host', 203 | 'transport', 204 | 'username', 205 | 'password', 206 | ], 207 | ] 208 | ); 209 | 210 | $this->assertFalse($result->isOk()); 211 | $this->assertCount(10, $result->getMessages()); 212 | $this->assertSame( 213 | [ 214 | '"framework.router" should be before "framework.secret"', 215 | '"framework.form" should be before "framework.router"', 216 | '"framework.csrf_protection" should be before "framework.form"', 217 | '"framework.templating" should be before "framework.validation"', 218 | '"framework.default_locale" should be before "framework.templating"', 219 | '"framework.session" should be before "framework.trusted_proxies"', 220 | '"framework.fragments" should be before "framework.session"', 221 | '"framework.assets" should be before "framework.http_method_override"', 222 | '"doctrine" should be before "twig"', 223 | '"doctrine.orm.auto_mapping" should be before "doctrine.orm.naming_strategy"', 224 | ], 225 | $result->getMessages() 226 | ); 227 | } 228 | 229 | public function testSymfonyConfigWithExcludedSections(): void 230 | { 231 | $checker = new SortChecker(); 232 | $result = $checker->isSorted( 233 | __DIR__ . '/fixture/symfony-config.yml', 234 | 999, 235 | [], 236 | [ 237 | 'doctrine' => [ 238 | 'dbal', 239 | ], 240 | 0 => 'framework', 241 | ] 242 | ); 243 | 244 | $this->assertFalse($result->isOk()); 245 | $this->assertSame( 246 | [ 247 | '"doctrine" should be before "twig"', 248 | '"doctrine.orm.auto_mapping" should be before "doctrine.orm.naming_strategy"', 249 | '"swiftmailer.host" should be before "swiftmailer.transport"', 250 | '"swiftmailer.password" should be before "swiftmailer.username"', 251 | ], 252 | $result->getMessages() 253 | ); 254 | } 255 | 256 | } 257 | --------------------------------------------------------------------------------