diff --git a/doc/tasks/yamllint.md b/doc/tasks/yamllint.md index 3f60f3f91..4b1671cbf 100644 --- a/doc/tasks/yamllint.md +++ b/doc/tasks/yamllint.md @@ -11,6 +11,8 @@ parameters: ignore_patterns: [] object_support: false exception_on_invalid_type: false + parse_constant: false + parse_custom_tags: false ``` **ignore_patterns** @@ -34,3 +36,19 @@ This option indicates if the Yaml parser supports serialized PHP objects. By enabling this option, the types of the yaml values are validated. When the value has an incorrect type, a lint error will be triggered. + + +**parse_constant** + +*Default: false* + +By enabling this option, constants defined by the special `!php/const:` syntax is parsed and validated. +When this option is not set, the constant syntax will trigger an error + + +**parse_custom_tags** + +*Default: false* + +By enabling this option, custom tags in the yaml file will be parsed and validated (E.G `!my_tag { foo: bar }`). +When this option is not set, using custom tags will trigger an error diff --git a/spec/Task/YamlLintSpec.php b/spec/Task/YamlLintSpec.php index d7df94a3e..5be3d6aff 100644 --- a/spec/Task/YamlLintSpec.php +++ b/spec/Task/YamlLintSpec.php @@ -42,6 +42,8 @@ function it_should_have_configurable_options() $options->shouldBeAnInstanceOf(OptionsResolver::class); $options->getDefinedOptions()->shouldContain('object_support'); $options->getDefinedOptions()->shouldContain('exception_on_invalid_type'); + $options->getDefinedOptions()->shouldContain('parse_custom_tags'); + $options->getDefinedOptions()->shouldContain('parse_constant'); } function it_should_run_in_git_pre_commit_context(GitPreCommitContext $context) @@ -70,6 +72,8 @@ function it_runs_the_suite(YamlLinter $linter, ContextInterface $context) $linter->isInstalled()->willReturn(true); $linter->setObjectSupport(false)->shouldBeCalled(); $linter->setExceptionOnInvalidType(false)->shouldBeCalled(); + $linter->setParseCustomTags(false)->shouldBeCalled(); + $linter->setParseConstants(false)->shouldBeCalled(); $linter->lint(Argument::type('SplFileInfo'))->willReturn(new LintErrorsCollection()); $context->getFiles()->willReturn(new FilesCollection([ @@ -86,6 +90,8 @@ function it_throws_exception_if_the_process_fails(YamlLinter $linter, ContextInt $linter->isInstalled()->willReturn(true); $linter->setObjectSupport(false)->shouldBeCalled(); $linter->setExceptionOnInvalidType(false)->shouldBeCalled(); + $linter->setParseCustomTags(false)->shouldBeCalled(); + $linter->setParseConstants(false)->shouldBeCalled(); $linter->lint(Argument::type('SplFileInfo'))->willReturn(new LintErrorsCollection([ new YamlLintError(LintError::TYPE_ERROR, 0, 'error', 'file.yaml', 1, 1) ])); diff --git a/src/Linter/Yaml/YamlLinter.php b/src/Linter/Yaml/YamlLinter.php index a641d6643..1d2858165 100644 --- a/src/Linter/Yaml/YamlLinter.php +++ b/src/Linter/Yaml/YamlLinter.php @@ -26,6 +26,20 @@ class YamlLinter implements LinterInterface */ private $exceptionOnInvalidType = false; + /** + * True if custom tags needs to be parsed + * + * @var bool + */ + private $parseCustomTags = false; + + /** + * True if PHP constants needs to be parsed + * + * @var bool + */ + private $parseConstants = false; + /** * @var Filesystem */ @@ -92,8 +106,14 @@ private function parseYaml($content) // Lint on Symfony Yaml >= 3.1 $flags = 0; - $flags += $this->objectSupport ? Yaml::PARSE_OBJECT : 0; - $flags += $this->exceptionOnInvalidType ? Yaml::PARSE_EXCEPTION_ON_INVALID_TYPE : 0; + $flags |= $this->objectSupport ? Yaml::PARSE_OBJECT : 0; + $flags |= $this->exceptionOnInvalidType ? Yaml::PARSE_EXCEPTION_ON_INVALID_TYPE : 0; + + // Yaml::PARSE_CONSTANT is only available in Symfony Yaml >= 3.2 + $flags |= $this->parseConstants && defined('Symfony\Component\Yaml\Yaml::PARSE_CONSTANT') ? Yaml::PARSE_CONSTANT : 0; + + // Yaml::PARSE_CUSTOM_TAGS is only available in Symfony Yaml >= 3.3 + $flags |= $this->parseCustomTags && defined('Symfony\Component\Yaml\Yaml::PARSE_CUSTOM_TAGS') ? Yaml::PARSE_CUSTOM_TAGS : 0; Yaml::parse($content, $flags); } @@ -120,4 +140,20 @@ public function setExceptionOnInvalidType($exceptionOnInvalidType) { $this->exceptionOnInvalidType = $exceptionOnInvalidType; } + + /** + * @param bool $parseCustomTags + */ + public function setParseCustomTags($parseCustomTags) + { + $this->parseCustomTags = $parseCustomTags; + } + + /** + * @param bool $parseConstants + */ + public function setParseConstants($parseConstants) + { + $this->parseConstants = $parseConstants; + } } diff --git a/src/Task/YamlLint.php b/src/Task/YamlLint.php index a20bc6f6f..856353704 100644 --- a/src/Task/YamlLint.php +++ b/src/Task/YamlLint.php @@ -31,10 +31,14 @@ public function getConfigurableOptions() $resolver->setDefaults([ 'object_support' => false, 'exception_on_invalid_type' => false, + 'parse_constant' => false, + 'parse_custom_tags' => false, ]); $resolver->addAllowedTypes('object_support', ['bool']); $resolver->addAllowedTypes('exception_on_invalid_type', ['bool']); + $resolver->addAllowedTypes('parse_constant', ['bool']); + $resolver->addAllowedTypes('parse_custom_tags', ['bool']); return $resolver; } @@ -60,6 +64,8 @@ public function run(ContextInterface $context) $config = $this->getConfiguration(); $this->linter->setObjectSupport($config['object_support']); $this->linter->setExceptionOnInvalidType($config['exception_on_invalid_type']); + $this->linter->setParseCustomTags($config['parse_custom_tags']); + $this->linter->setParseConstants($config['parse_constant']); try { $lintErrors = $this->lint($files); diff --git a/test/Linter/Yaml/YamlLinterTest.php b/test/Linter/Yaml/YamlLinterTest.php index fcf52c931..e21d24112 100644 --- a/test/Linter/Yaml/YamlLinterTest.php +++ b/test/Linter/Yaml/YamlLinterTest.php @@ -82,6 +82,64 @@ function it_should_handle_exceptions_on_invalid_type() $this->validateFixture($fixture, 1); } + /** + * @test + */ + function it_should_handle_exceptions_on_constants() + { + if (!YamlLinter::supportsFlags()) { + $this->markTestSkipped('Parsing constants is not supported by the current version of symfony/yaml'); + } + + $this->linter->setExceptionOnInvalidType(true); + $fixture = 'constant-support.yml'; + $this->validateFixture($fixture, 1); + } + + /** + * @test + */ + function it_should_validate_constants() + { + if (!YamlLinter::supportsFlags()) { + $this->markTestSkipped('Parsing constants is not supported by the current version of symfony/yaml'); + } + + $this->linter->setExceptionOnInvalidType(true); + $this->linter->setParseConstants(true); + $fixture = 'constant-support.yml'; + $this->validateFixture($fixture, 0); + } + + /** + * @test + */ + function it_should_handle_exceptions_on_custom_tags() + { + if (!YamlLinter::supportsFlags()) { + $this->markTestSkipped('Parsing custom tags is not supported by the current version of symfony/yaml'); + } + + $this->linter->setExceptionOnInvalidType(true); + $fixture = 'tags-support.yml'; + $this->validateFixture($fixture, 1); + } + + /** + * @test + */ + function it_should_validate_custom_tags() + { + if (!YamlLinter::supportsFlags()) { + $this->markTestSkipped('Parsing custom tags is not supported by the current version of symfony/yaml'); + } + + $this->linter->setExceptionOnInvalidType(true); + $this->linter->setParseCustomTags(true); + $fixture = 'tags-support.yml'; + $this->validateFixture($fixture, 0); + } + /** * @return array */ diff --git a/test/fixtures/linters/yaml/constant-support.yml b/test/fixtures/linters/yaml/constant-support.yml new file mode 100644 index 000000000..604c1496e --- /dev/null +++ b/test/fixtures/linters/yaml/constant-support.yml @@ -0,0 +1 @@ +foo: !php/const:PHP_EOL \ No newline at end of file diff --git a/test/fixtures/linters/yaml/tags-support.yml b/test/fixtures/linters/yaml/tags-support.yml new file mode 100644 index 000000000..6da551f78 --- /dev/null +++ b/test/fixtures/linters/yaml/tags-support.yml @@ -0,0 +1 @@ +foo: !my_tag {foo: bar} \ No newline at end of file