Updated to Drupal 8.5. Core Media not yet in use.
[yaffs-website] / vendor / symfony / yaml / Command / LintCommand.php
1 <?php
2
3 /*
4  * This file is part of the Symfony package.
5  *
6  * (c) Fabien Potencier <fabien@symfony.com>
7  *
8  * For the full copyright and license information, please view the LICENSE
9  * file that was distributed with this source code.
10  */
11
12 namespace Symfony\Component\Yaml\Command;
13
14 use Symfony\Component\Console\Command\Command;
15 use Symfony\Component\Console\Input\InputInterface;
16 use Symfony\Component\Console\Input\InputOption;
17 use Symfony\Component\Console\Output\OutputInterface;
18 use Symfony\Component\Console\Style\SymfonyStyle;
19 use Symfony\Component\Yaml\Exception\ParseException;
20 use Symfony\Component\Yaml\Parser;
21 use Symfony\Component\Yaml\Yaml;
22
23 /**
24  * Validates YAML files syntax and outputs encountered errors.
25  *
26  * @author GrĂ©goire Pineau <lyrixx@lyrixx.info>
27  * @author Robin Chalas <robin.chalas@gmail.com>
28  */
29 class LintCommand extends Command
30 {
31     protected static $defaultName = 'lint:yaml';
32
33     private $parser;
34     private $format;
35     private $displayCorrectFiles;
36     private $directoryIteratorProvider;
37     private $isReadableProvider;
38
39     public function __construct($name = null, $directoryIteratorProvider = null, $isReadableProvider = null)
40     {
41         parent::__construct($name);
42
43         $this->directoryIteratorProvider = $directoryIteratorProvider;
44         $this->isReadableProvider = $isReadableProvider;
45     }
46
47     /**
48      * {@inheritdoc}
49      */
50     protected function configure()
51     {
52         $this
53             ->setDescription('Lints a file and outputs encountered errors')
54             ->addArgument('filename', null, 'A file or a directory or STDIN')
55             ->addOption('format', null, InputOption::VALUE_REQUIRED, 'The output format', 'txt')
56             ->addOption('parse-tags', null, InputOption::VALUE_NONE, 'Parse custom tags')
57             ->setHelp(<<<EOF
58 The <info>%command.name%</info> command lints a YAML file and outputs to STDOUT
59 the first encountered syntax error.
60
61 You can validates YAML contents passed from STDIN:
62
63   <info>cat filename | php %command.full_name%</info>
64
65 You can also validate the syntax of a file:
66
67   <info>php %command.full_name% filename</info>
68
69 Or of a whole directory:
70
71   <info>php %command.full_name% dirname</info>
72   <info>php %command.full_name% dirname --format=json</info>
73
74 EOF
75             )
76         ;
77     }
78
79     protected function execute(InputInterface $input, OutputInterface $output)
80     {
81         $io = new SymfonyStyle($input, $output);
82         $filename = $input->getArgument('filename');
83         $this->format = $input->getOption('format');
84         $this->displayCorrectFiles = $output->isVerbose();
85         $flags = $input->getOption('parse-tags') ? Yaml::PARSE_CUSTOM_TAGS : 0;
86
87         if (!$filename) {
88             if (!$stdin = $this->getStdin()) {
89                 throw new \RuntimeException('Please provide a filename or pipe file content to STDIN.');
90             }
91
92             return $this->display($io, array($this->validate($stdin, $flags)));
93         }
94
95         if (!$this->isReadable($filename)) {
96             throw new \RuntimeException(sprintf('File or directory "%s" is not readable.', $filename));
97         }
98
99         $filesInfo = array();
100         foreach ($this->getFiles($filename) as $file) {
101             $filesInfo[] = $this->validate(file_get_contents($file), $flags, $file);
102         }
103
104         return $this->display($io, $filesInfo);
105     }
106
107     private function validate($content, $flags, $file = null)
108     {
109         $prevErrorHandler = set_error_handler(function ($level, $message, $file, $line) use (&$prevErrorHandler) {
110             if (E_USER_DEPRECATED === $level) {
111                 throw new ParseException($message, $this->getParser()->getRealCurrentLineNb() + 1);
112             }
113
114             return $prevErrorHandler ? $prevErrorHandler($level, $message, $file, $line) : false;
115         });
116
117         try {
118             $this->getParser()->parse($content, Yaml::PARSE_CONSTANT | $flags);
119         } catch (ParseException $e) {
120             return array('file' => $file, 'line' => $e->getParsedLine(), 'valid' => false, 'message' => $e->getMessage());
121         } finally {
122             restore_error_handler();
123         }
124
125         return array('file' => $file, 'valid' => true);
126     }
127
128     private function display(SymfonyStyle $io, array $files)
129     {
130         switch ($this->format) {
131             case 'txt':
132                 return $this->displayTxt($io, $files);
133             case 'json':
134                 return $this->displayJson($io, $files);
135             default:
136                 throw new \InvalidArgumentException(sprintf('The format "%s" is not supported.', $this->format));
137         }
138     }
139
140     private function displayTxt(SymfonyStyle $io, array $filesInfo)
141     {
142         $countFiles = count($filesInfo);
143         $erroredFiles = 0;
144
145         foreach ($filesInfo as $info) {
146             if ($info['valid'] && $this->displayCorrectFiles) {
147                 $io->comment('<info>OK</info>'.($info['file'] ? sprintf(' in %s', $info['file']) : ''));
148             } elseif (!$info['valid']) {
149                 ++$erroredFiles;
150                 $io->text('<error> ERROR </error>'.($info['file'] ? sprintf(' in %s', $info['file']) : ''));
151                 $io->text(sprintf('<error> >> %s</error>', $info['message']));
152             }
153         }
154
155         if (0 === $erroredFiles) {
156             $io->success(sprintf('All %d YAML files contain valid syntax.', $countFiles));
157         } else {
158             $io->warning(sprintf('%d YAML files have valid syntax and %d contain errors.', $countFiles - $erroredFiles, $erroredFiles));
159         }
160
161         return min($erroredFiles, 1);
162     }
163
164     private function displayJson(SymfonyStyle $io, array $filesInfo)
165     {
166         $errors = 0;
167
168         array_walk($filesInfo, function (&$v) use (&$errors) {
169             $v['file'] = (string) $v['file'];
170             if (!$v['valid']) {
171                 ++$errors;
172             }
173         });
174
175         $io->writeln(json_encode($filesInfo, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES));
176
177         return min($errors, 1);
178     }
179
180     private function getFiles($fileOrDirectory)
181     {
182         if (is_file($fileOrDirectory)) {
183             yield new \SplFileInfo($fileOrDirectory);
184
185             return;
186         }
187
188         foreach ($this->getDirectoryIterator($fileOrDirectory) as $file) {
189             if (!in_array($file->getExtension(), array('yml', 'yaml'))) {
190                 continue;
191             }
192
193             yield $file;
194         }
195     }
196
197     private function getStdin()
198     {
199         if (0 !== ftell(STDIN)) {
200             return;
201         }
202
203         $inputs = '';
204         while (!feof(STDIN)) {
205             $inputs .= fread(STDIN, 1024);
206         }
207
208         return $inputs;
209     }
210
211     private function getParser()
212     {
213         if (!$this->parser) {
214             $this->parser = new Parser();
215         }
216
217         return $this->parser;
218     }
219
220     private function getDirectoryIterator($directory)
221     {
222         $default = function ($directory) {
223             return new \RecursiveIteratorIterator(
224                 new \RecursiveDirectoryIterator($directory, \FilesystemIterator::SKIP_DOTS | \FilesystemIterator::FOLLOW_SYMLINKS),
225                 \RecursiveIteratorIterator::LEAVES_ONLY
226             );
227         };
228
229         if (null !== $this->directoryIteratorProvider) {
230             return call_user_func($this->directoryIteratorProvider, $directory, $default);
231         }
232
233         return $default($directory);
234     }
235
236     private function isReadable($fileOrDirectory)
237     {
238         $default = function ($fileOrDirectory) {
239             return is_readable($fileOrDirectory);
240         };
241
242         if (null !== $this->isReadableProvider) {
243             return call_user_func($this->isReadableProvider, $fileOrDirectory, $default);
244         }
245
246         return $default($fileOrDirectory);
247     }
248 }