Просмотр файла vendor/symfony/yaml/Command/LintCommand.php

Размер файла: 6.85Kb
  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.  
  22. /**
  23. * Validates YAML files syntax and outputs encountered errors.
  24. *
  25. * @author Grégoire Pineau <lyrixx@lyrixx.info>
  26. * @author Robin Chalas <robin.chalas@gmail.com>
  27. */
  28. class LintCommand extends Command
  29. {
  30. private $parser;
  31. private $format;
  32. private $displayCorrectFiles;
  33. private $directoryIteratorProvider;
  34. private $isReadableProvider;
  35.  
  36. public function __construct($name = null, $directoryIteratorProvider = null, $isReadableProvider = null)
  37. {
  38. parent::__construct($name);
  39.  
  40. $this->directoryIteratorProvider = $directoryIteratorProvider;
  41. $this->isReadableProvider = $isReadableProvider;
  42. }
  43.  
  44. /**
  45. * {@inheritdoc}
  46. */
  47. protected function configure()
  48. {
  49. $this
  50. ->setName('lint:yaml')
  51. ->setDescription('Lints a file and outputs encountered errors')
  52. ->addArgument('filename', null, 'A file or a directory or STDIN')
  53. ->addOption('format', null, InputOption::VALUE_REQUIRED, 'The output format', 'txt')
  54. ->setHelp(<<<EOF
  55. The <info>%command.name%</info> command lints a YAML file and outputs to STDOUT
  56. the first encountered syntax error.
  57.  
  58. You can validates YAML contents passed from STDIN:
  59.  
  60. <info>cat filename | php %command.full_name%</info>
  61.  
  62. You can also validate the syntax of a file:
  63.  
  64. <info>php %command.full_name% filename</info>
  65.  
  66. Or of a whole directory:
  67.  
  68. <info>php %command.full_name% dirname</info>
  69. <info>php %command.full_name% dirname --format=json</info>
  70.  
  71. EOF
  72. )
  73. ;
  74. }
  75.  
  76. protected function execute(InputInterface $input, OutputInterface $output)
  77. {
  78. $io = new SymfonyStyle($input, $output);
  79. $filename = $input->getArgument('filename');
  80. $this->format = $input->getOption('format');
  81. $this->displayCorrectFiles = $output->isVerbose();
  82.  
  83. if (!$filename) {
  84. if (!$stdin = $this->getStdin()) {
  85. throw new \RuntimeException('Please provide a filename or pipe file content to STDIN.');
  86. }
  87.  
  88. return $this->display($io, array($this->validate($stdin)));
  89. }
  90.  
  91. if (!$this->isReadable($filename)) {
  92. throw new \RuntimeException(sprintf('File or directory "%s" is not readable.', $filename));
  93. }
  94.  
  95. $filesInfo = array();
  96. foreach ($this->getFiles($filename) as $file) {
  97. $filesInfo[] = $this->validate(file_get_contents($file), $file);
  98. }
  99.  
  100. return $this->display($io, $filesInfo);
  101. }
  102.  
  103. private function validate($content, $file = null)
  104. {
  105. try {
  106. $this->getParser()->parse($content);
  107. } catch (ParseException $e) {
  108. return array('file' => $file, 'valid' => false, 'message' => $e->getMessage());
  109. }
  110.  
  111. return array('file' => $file, 'valid' => true);
  112. }
  113.  
  114. private function display(SymfonyStyle $io, array $files)
  115. {
  116. switch ($this->format) {
  117. case 'txt':
  118. return $this->displayTxt($io, $files);
  119. case 'json':
  120. return $this->displayJson($io, $files);
  121. default:
  122. throw new \InvalidArgumentException(sprintf('The format "%s" is not supported.', $this->format));
  123. }
  124. }
  125.  
  126. private function displayTxt(SymfonyStyle $io, array $filesInfo)
  127. {
  128. $countFiles = count($filesInfo);
  129. $erroredFiles = 0;
  130.  
  131. foreach ($filesInfo as $info) {
  132. if ($info['valid'] && $this->displayCorrectFiles) {
  133. $io->comment('<info>OK</info>'.($info['file'] ? sprintf(' in %s', $info['file']) : ''));
  134. } elseif (!$info['valid']) {
  135. ++$erroredFiles;
  136. $io->text('<error> ERROR </error>'.($info['file'] ? sprintf(' in %s', $info['file']) : ''));
  137. $io->text(sprintf('<error> >> %s</error>', $info['message']));
  138. }
  139. }
  140.  
  141. if ($erroredFiles === 0) {
  142. $io->success(sprintf('All %d YAML files contain valid syntax.', $countFiles));
  143. } else {
  144. $io->warning(sprintf('%d YAML files have valid syntax and %d contain errors.', $countFiles - $erroredFiles, $erroredFiles));
  145. }
  146.  
  147. return min($erroredFiles, 1);
  148. }
  149.  
  150. private function displayJson(SymfonyStyle $io, array $filesInfo)
  151. {
  152. $errors = 0;
  153.  
  154. array_walk($filesInfo, function (&$v) use (&$errors) {
  155. $v['file'] = (string) $v['file'];
  156. if (!$v['valid']) {
  157. ++$errors;
  158. }
  159. });
  160.  
  161. $io->writeln(json_encode($filesInfo, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES));
  162.  
  163. return min($errors, 1);
  164. }
  165.  
  166. private function getFiles($fileOrDirectory)
  167. {
  168. if (is_file($fileOrDirectory)) {
  169. yield new \SplFileInfo($fileOrDirectory);
  170.  
  171. return;
  172. }
  173.  
  174. foreach ($this->getDirectoryIterator($fileOrDirectory) as $file) {
  175. if (!in_array($file->getExtension(), array('yml', 'yaml'))) {
  176. continue;
  177. }
  178.  
  179. yield $file;
  180. }
  181. }
  182.  
  183. private function getStdin()
  184. {
  185. if (0 !== ftell(STDIN)) {
  186. return;
  187. }
  188.  
  189. $inputs = '';
  190. while (!feof(STDIN)) {
  191. $inputs .= fread(STDIN, 1024);
  192. }
  193.  
  194. return $inputs;
  195. }
  196.  
  197. private function getParser()
  198. {
  199. if (!$this->parser) {
  200. $this->parser = new Parser();
  201. }
  202.  
  203. return $this->parser;
  204. }
  205.  
  206. private function getDirectoryIterator($directory)
  207. {
  208. $default = function ($directory) {
  209. return new \RecursiveIteratorIterator(
  210. new \RecursiveDirectoryIterator($directory, \FilesystemIterator::SKIP_DOTS | \FilesystemIterator::FOLLOW_SYMLINKS),
  211. \RecursiveIteratorIterator::LEAVES_ONLY
  212. );
  213. };
  214.  
  215. if (null !== $this->directoryIteratorProvider) {
  216. return call_user_func($this->directoryIteratorProvider, $directory, $default);
  217. }
  218.  
  219. return $default($directory);
  220. }
  221.  
  222. private function isReadable($fileOrDirectory)
  223. {
  224. $default = function ($fileOrDirectory) {
  225. return is_readable($fileOrDirectory);
  226. };
  227.  
  228. if (null !== $this->isReadableProvider) {
  229. return call_user_func($this->isReadableProvider, $fileOrDirectory, $default);
  230. }
  231.  
  232. return $default($fileOrDirectory);
  233. }
  234. }