Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: symfony/yaml
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: v4.3.11
Choose a base ref
...
head repository: symfony/yaml
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: v4.4.0
Choose a head ref
Loading
Showing with 451 additions and 58 deletions.
  1. +3 −0 .gitattributes
  2. +7 −0 CHANGELOG.md
  3. +31 −32 Command/LintCommand.php
  4. +1 −1 Exception/ParseException.php
  5. +18 −13 Inline.php
  6. +179 −0 Parser.php
  7. +2 −8 Tests/Command/LintCommandTest.php
  8. +5 −0 Tests/DumperTest.php
  9. +1 −1 Tests/InlineTest.php
  10. +201 −1 Tests/ParserTest.php
  11. +1 −0 Yaml.php
  12. +2 −2 composer.json
3 changes: 3 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
/Tests export-ignore
/phpunit.xml.dist export-ignore
/.gitignore export-ignore
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
CHANGELOG
=========

4.4.0
-----

* Added support for parsing the inline notation spanning multiple lines.
* Added support to dump `null` as `~` by using the `Yaml::DUMP_NULL_AS_TILDE` flag.
* deprecated accepting STDIN implicitly when using the `lint:yaml` command, use `lint:yaml -` (append a dash) instead to make it explicit.

4.3.0
-----

63 changes: 31 additions & 32 deletions Command/LintCommand.php
Original file line number Diff line number Diff line change
@@ -54,7 +54,7 @@ protected function configure()
{
$this
->setDescription('Lints a file and outputs encountered errors')
->addArgument('filename', InputArgument::IS_ARRAY, 'A file or a directory or STDIN')
->addArgument('filename', InputArgument::IS_ARRAY, 'A file, a directory or "-" for reading from STDIN')
->addOption('format', null, InputOption::VALUE_REQUIRED, 'The output format', 'txt')
->addOption('parse-tags', null, InputOption::VALUE_NONE, 'Parse custom tags')
->setHelp(<<<EOF
@@ -63,7 +63,7 @@ protected function configure()
You can validates YAML contents passed from STDIN:
<info>cat filename | php %command.full_name%</info>
<info>cat filename | php %command.full_name% -</info>
You can also validate the syntax of a file:
@@ -87,12 +87,19 @@ protected function execute(InputInterface $input, OutputInterface $output)
$this->displayCorrectFiles = $output->isVerbose();
$flags = $input->getOption('parse-tags') ? Yaml::PARSE_CUSTOM_TAGS : 0;

if (0 === \count($filenames)) {
if (!$stdin = $this->getStdin()) {
throw new RuntimeException('Please provide a filename or pipe file content to STDIN.');
if (['-'] === $filenames) {
return $this->display($io, [$this->validate(file_get_contents('php://stdin'), $flags)]);
}

// @deprecated to be removed in 5.0
if (!$filenames) {
if (0 === ftell(STDIN)) {
@trigger_error('Piping content from STDIN to the "lint:yaml" command without passing the dash symbol "-" as argument is deprecated since Symfony 4.4.', E_USER_DEPRECATED);

return $this->display($io, [$this->validate(file_get_contents('php://stdin'), $flags)]);
}

return $this->display($io, [$this->validate($stdin, $flags)]);
throw new RuntimeException('Please provide a filename or pipe file content to STDIN.');
}

$filesInfo = [];
@@ -109,7 +116,7 @@ protected function execute(InputInterface $input, OutputInterface $output)
return $this->display($io, $filesInfo);
}

private function validate($content, $flags, $file = null)
private function validate(string $content, int $flags, string $file = null)
{
$prevErrorHandler = set_error_handler(function ($level, $message, $file, $line) use (&$prevErrorHandler) {
if (E_USER_DEPRECATED === $level) {
@@ -130,7 +137,7 @@ private function validate($content, $flags, $file = null)
return ['file' => $file, 'valid' => true];
}

private function display(SymfonyStyle $io, array $files)
private function display(SymfonyStyle $io, array $files): int
{
switch ($this->format) {
case 'txt':
@@ -142,10 +149,11 @@ private function display(SymfonyStyle $io, array $files)
}
}

private function displayTxt(SymfonyStyle $io, array $filesInfo)
private function displayTxt(SymfonyStyle $io, array $filesInfo): int
{
$countFiles = \count($filesInfo);
$erroredFiles = 0;
$suggestTagOption = false;

foreach ($filesInfo as $info) {
if ($info['valid'] && $this->displayCorrectFiles) {
@@ -154,19 +162,23 @@ private function displayTxt(SymfonyStyle $io, array $filesInfo)
++$erroredFiles;
$io->text('<error> ERROR </error>'.($info['file'] ? sprintf(' in %s', $info['file']) : ''));
$io->text(sprintf('<error> >> %s</error>', $info['message']));

if (false !== strpos($info['message'], 'PARSE_CUSTOM_TAGS')) {
$suggestTagOption = true;
}
}
}

if (0 === $erroredFiles) {
$io->success(sprintf('All %d YAML files contain valid syntax.', $countFiles));
} else {
$io->warning(sprintf('%d YAML files have valid syntax and %d contain errors.', $countFiles - $erroredFiles, $erroredFiles));
$io->warning(sprintf('%d YAML files have valid syntax and %d contain errors.%s', $countFiles - $erroredFiles, $erroredFiles, $suggestTagOption ? ' Use the --parse-tags option if you want parse custom tags.' : ''));
}

return min($erroredFiles, 1);
}

private function displayJson(SymfonyStyle $io, array $filesInfo)
private function displayJson(SymfonyStyle $io, array $filesInfo): int
{
$errors = 0;

@@ -175,14 +187,18 @@ private function displayJson(SymfonyStyle $io, array $filesInfo)
if (!$v['valid']) {
++$errors;
}

if (isset($v['message']) && false !== strpos($v['message'], 'PARSE_CUSTOM_TAGS')) {
$v['message'] .= ' Use the --parse-tags option if you want parse custom tags.';
}
});

$io->writeln(json_encode($filesInfo, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES));

return min($errors, 1);
}

private function getFiles($fileOrDirectory)
private function getFiles(string $fileOrDirectory): iterable
{
if (is_file($fileOrDirectory)) {
yield new \SplFileInfo($fileOrDirectory);
@@ -199,24 +215,7 @@ private function getFiles($fileOrDirectory)
}
}

/**
* @return string|null
*/
private function getStdin()
{
if (0 !== ftell(STDIN)) {
return null;
}

$inputs = '';
while (!feof(STDIN)) {
$inputs .= fread(STDIN, 1024);
}

return $inputs;
}

private function getParser()
private function getParser(): Parser
{
if (!$this->parser) {
$this->parser = new Parser();
@@ -225,7 +224,7 @@ private function getParser()
return $this->parser;
}

private function getDirectoryIterator($directory)
private function getDirectoryIterator(string $directory): iterable
{
$default = function ($directory) {
return new \RecursiveIteratorIterator(
@@ -241,7 +240,7 @@ private function getDirectoryIterator($directory)
return $default($directory);
}

private function isReadable($fileOrDirectory)
private function isReadable(string $fileOrDirectory): bool
{
$default = function ($fileOrDirectory) {
return is_readable($fileOrDirectory);
2 changes: 1 addition & 1 deletion Exception/ParseException.php
Original file line number Diff line number Diff line change
@@ -30,7 +30,7 @@ class ParseException extends RuntimeException
* @param string|null $parsedFile The file name where the error occurred
* @param \Exception|null $previous The previous exception
*/
public function __construct(string $message, int $parsedLine = -1, string $snippet = null, string $parsedFile = null, \Exception $previous = null)
public function __construct(string $message, int $parsedLine = -1, string $snippet = null, string $parsedFile = null, \Throwable $previous = null)
{
$this->parsedFile = $parsedFile;
$this->parsedLine = $parsedLine;
31 changes: 18 additions & 13 deletions Inline.php
Original file line number Diff line number Diff line change
@@ -34,12 +34,7 @@ class Inline
private static $objectForMap = false;
private static $constantSupport = false;

/**
* @param int $flags
* @param int|null $parsedLineNumber
* @param string|null $parsedFilename
*/
public static function initialize($flags, $parsedLineNumber = null, $parsedFilename = null)
public static function initialize(int $flags, int $parsedLineNumber = null, string $parsedFilename = null)
{
self::$exceptionOnInvalidType = (bool) (Yaml::PARSE_EXCEPTION_ON_INVALID_TYPE & $flags);
self::$objectSupport = (bool) (Yaml::PARSE_OBJECT & $flags);
@@ -129,7 +124,7 @@ public static function dump($value, int $flags = 0): string
throw new DumpException(sprintf('Unable to dump PHP resources in a YAML file ("%s").', get_resource_type($value)));
}

return 'null';
return self::dumpNull($flags);
case $value instanceof \DateTimeInterface:
return $value->format('c');
case \is_object($value):
@@ -155,11 +150,11 @@ public static function dump($value, int $flags = 0): string
throw new DumpException('Object support when dumping a YAML file has been disabled.');
}

return 'null';
return self::dumpNull($flags);
case \is_array($value):
return self::dumpArray($value, $flags);
case null === $value:
return 'null';
return self::dumpNull($flags);
case true === $value:
return 'true';
case false === $value:
@@ -256,6 +251,15 @@ private static function dumpArray(array $value, int $flags): string
return sprintf('{ %s }', implode(', ', $output));
}

private static function dumpNull(int $flags): string
{
if (Yaml::DUMP_NULL_AS_TILDE & $flags) {
return '~';
}

return 'null';
}

/**
* Parses a YAML scalar.
*
@@ -270,7 +274,7 @@ public static function parseScalar(string $scalar, int $flags = 0, array $delimi
$output = self::parseQuotedScalar($scalar, $i);

if (null !== $delimiters) {
$tmp = ltrim(substr($scalar, $i), ' ');
$tmp = ltrim(substr($scalar, $i), " \n");
if ('' === $tmp) {
throw new ParseException(sprintf('Unexpected end of line, expected one of "%s".', implode('', $delimiters)), self::$parsedLineNumber + 1, $scalar, self::$parsedFilename);
}
@@ -415,6 +419,7 @@ private static function parseMapping(string $mapping, int $flags, int &$i = 0, a
switch ($mapping[$i]) {
case ' ':
case ',':
case "\n":
++$i;
continue 2;
case '}':
@@ -446,7 +451,7 @@ private static function parseMapping(string $mapping, int $flags, int &$i = 0, a
}
}

if (!$isKeyQuoted && (!isset($mapping[$i + 1]) || !\in_array($mapping[$i + 1], [' ', ',', '[', ']', '{', '}'], true))) {
if (!$isKeyQuoted && (!isset($mapping[$i + 1]) || !\in_array($mapping[$i + 1], [' ', ',', '[', ']', '{', '}', "\n"], true))) {
throw new ParseException('Colons must be followed by a space or an indication character (i.e. " ", ",", "[", "]", "{", "}").', self::$parsedLineNumber + 1, $mapping);
}

@@ -455,7 +460,7 @@ private static function parseMapping(string $mapping, int $flags, int &$i = 0, a
}

while ($i < $len) {
if (':' === $mapping[$i] || ' ' === $mapping[$i]) {
if (':' === $mapping[$i] || ' ' === $mapping[$i] || "\n" === $mapping[$i]) {
++$i;

continue;
@@ -504,7 +509,7 @@ private static function parseMapping(string $mapping, int $flags, int &$i = 0, a
}
break;
default:
$value = self::parseScalar($mapping, $flags, [',', '}'], $i, null === $tag, $references);
$value = self::parseScalar($mapping, $flags, [',', '}', "\n"], $i, null === $tag, $references);
// Spec: Keys MUST be unique; first one wins.
// Parser cannot abort this mapping earlier, since lines
// are processed sequentially.
Loading