From 621f991e1514348b83700c733bdf3bc4dde124d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Pineau?= Date: Sat, 18 Jan 2014 13:57:30 +0100 Subject: [PATCH 1/2] [TwigBridge] Cleaned documentation of twig:lint command --- src/Symfony/Bridge/Twig/Command/LintCommand.php | 13 ++++++------- .../Bundle/TwigBundle/Command/LintCommand.php | 5 ++--- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/src/Symfony/Bridge/Twig/Command/LintCommand.php b/src/Symfony/Bridge/Twig/Command/LintCommand.php index 5ebd255450d4c..105628fe4c659 100644 --- a/src/Symfony/Bridge/Twig/Command/LintCommand.php +++ b/src/Symfony/Bridge/Twig/Command/LintCommand.php @@ -58,21 +58,20 @@ protected function configure() ->setDescription('Lints a template and outputs encountered errors') ->addArgument('filename') ->setHelp(<<%command.name% command lints a template and outputs to stdout +The %command.name% command lints a template and outputs to STDOUT the first encountered syntax error. +You can validate the syntax of a file: + php %command.full_name% filename -The command gets the contents of filename and validates its syntax. +Or of a whole directory: php %command.full_name% dirname -The command finds all twig templates in dirname and validates the syntax -of each Twig template. +You can also pass the template contents from STDIN: cat filename | php %command.full_name% - -The command gets the template contents from stdin and validates its syntax. EOF ) ; @@ -86,7 +85,7 @@ protected function execute(InputInterface $input, OutputInterface $output) if (!$filename) { if (0 !== ftell(STDIN)) { - throw new \RuntimeException("Please provide a filename or pipe template content to stdin."); + throw new \RuntimeException("Please provide a filename or pipe template content to STDIN."); } while (!feof(STDIN)) { diff --git a/src/Symfony/Bundle/TwigBundle/Command/LintCommand.php b/src/Symfony/Bundle/TwigBundle/Command/LintCommand.php index 79a765d768e5f..95c892cb0433b 100644 --- a/src/Symfony/Bundle/TwigBundle/Command/LintCommand.php +++ b/src/Symfony/Bundle/TwigBundle/Command/LintCommand.php @@ -57,10 +57,9 @@ protected function configure() $this->getHelp().<<php %command.full_name% @AcmeMyBundle +Or all template files in a bundle: -The command finds all twig templates in the AcmeMyBundle bundle and validates -the syntax of each Twig template. +php %command.full_name% @AcmeDemoBundle EOF ) ; From 4d2f94a7c304a4e78346d99668ea3e0743b32933 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Pineau?= Date: Sat, 18 Jan 2014 14:22:54 +0100 Subject: [PATCH 2/2] [TwigBridge] Added support for json format in twig:lint command --- .../Bridge/Twig/Command/LintCommand.php | 84 +++++++++++++++---- .../Twig/Tests/Command/LintCommandTest.php | 7 +- 2 files changed, 71 insertions(+), 20 deletions(-) diff --git a/src/Symfony/Bridge/Twig/Command/LintCommand.php b/src/Symfony/Bridge/Twig/Command/LintCommand.php index 105628fe4c659..6636f6cce0e82 100644 --- a/src/Symfony/Bridge/Twig/Command/LintCommand.php +++ b/src/Symfony/Bridge/Twig/Command/LintCommand.php @@ -11,8 +11,13 @@ namespace Symfony\Bridge\Twig\Command; +if (!defined('JSON_PRETTY_PRINT')) { + define('JSON_PRETTY_PRINT', 128); +} + use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Finder\Finder; @@ -56,6 +61,7 @@ protected function configure() { $this ->setDescription('Lints a template and outputs encountered errors') + ->addOption('format', null, InputOption::VALUE_REQUIRED, 'The output format', 'txt') ->addArgument('filename') ->setHelp(<<%command.name% command lints a template and outputs to STDOUT @@ -68,6 +74,7 @@ protected function configure() Or of a whole directory: php %command.full_name% dirname +php %command.full_name% dirname --format=json You can also pass the template contents from STDIN: @@ -80,7 +87,6 @@ protected function configure() protected function execute(InputInterface $input, OutputInterface $output) { $twig = $this->getTwigEnvironment(); - $template = null; $filename = $input->getArgument('filename'); if (!$filename) { @@ -88,21 +94,20 @@ protected function execute(InputInterface $input, OutputInterface $output) throw new \RuntimeException("Please provide a filename or pipe template content to STDIN."); } + $template = ''; while (!feof(STDIN)) { $template .= fread(STDIN, 1024); } - return $this->validateTemplate($twig, $output, $template); + return $this->display($input, $output, array($this->validate($twig, $template))); } - $files = $this->findFiles($filename); - - $errors = 0; - foreach ($files as $file) { - $errors += $this->validateTemplate($twig, $output, file_get_contents($file), $file); + $filesInfo = array(); + foreach ($this->findFiles($filename) as $file) { + $filesInfo[] = $this->validate($twig, file_get_contents($file), $file); } - return $errors > 0 ? 1 : 0; + return $this->display($input, $output, $filesInfo); } protected function findFiles($filename) @@ -116,24 +121,69 @@ protected function findFiles($filename) throw new \RuntimeException(sprintf('File or directory "%s" is not readable', $filename)); } - protected function validateTemplate(\Twig_Environment $twig, OutputInterface $output, $template, $file = null) + private function validate(\Twig_Environment $twig, $template, $file = null) { try { $twig->parse($twig->tokenize($template, $file ? (string) $file : null)); - $output->writeln('OK'.($file ? sprintf(' in %s', $file) : '')); } catch (\Twig_Error $e) { - $this->renderException($output, $template, $e, $file); + return array('template' => $template, 'file' => $file, 'valid' => false, 'exception' => $e); + } + + return array('template' => $template, 'file' => $file, 'valid' => true); + } + + private function display(InputInterface $input, OutputInterface $output, $files) + { + switch ($input->getOption('format')) { + case 'txt': + return $this->displayTxt($output, $files); + case 'json': + return $this->displayJson($output, $files); + default: + throw new \InvalidArgumentException(sprintf('The format "%s" is not supported.', $input->getOption('format'))); + } + } + + private function displayTxt(OutputInterface $output, $filesInfo) + { + $errors = 0; - return 1; + foreach ($filesInfo as $info) { + if ($info['valid'] && $output->isVerbose()) { + $output->writeln('OK'.($info['file'] ? sprintf(' in %s', $info['file']) : '')); + } elseif (!$info['valid']) { + $errors++; + $this->renderException($output, $info['template'], $info['exception'], $info['file']); + } } - return 0; + $output->writeln(sprintf('%d/%d valid files', count($filesInfo) - $errors, count($filesInfo))); + + return min($errors, 1); + } + + private function displayJson(OutputInterface $output, $filesInfo) + { + $errors = 0; + + array_walk($filesInfo, function (&$v) use (&$errors) { + $v['file'] = (string) $v['file']; + unset($v['template']); + if (!$v['valid']) { + $v['message'] = $v['exception']->getMessage(); + unset($v['exception']); + $errors++; + } + }); + + $output->writeln(json_encode($filesInfo, JSON_PRETTY_PRINT)); + + return min($errors, 1); } - protected function renderException(OutputInterface $output, $template, \Twig_Error $exception, $file = null) + private function renderException(OutputInterface $output, $template, \Twig_Error $exception, $file = null) { $line = $exception->getTemplateLine(); - $lines = $this->getContext($template, $line); if ($file) { $output->writeln(sprintf("KO in %s (line %s)", $file, $line)); @@ -141,7 +191,7 @@ protected function renderException(OutputInterface $output, $template, \Twig_Err $output->writeln(sprintf("KO (line %s)", $line)); } - foreach ($lines as $no => $code) { + foreach ($this->getContext($template, $line) as $no => $code) { $output->writeln(sprintf( "%s %-6s %s", $no == $line ? '>>' : ' ', @@ -154,7 +204,7 @@ protected function renderException(OutputInterface $output, $template, \Twig_Err } } - protected function getContext($template, $line, $context = 3) + private function getContext($template, $line, $context = 3) { $lines = explode("\n", $template); diff --git a/src/Symfony/Bridge/Twig/Tests/Command/LintCommandTest.php b/src/Symfony/Bridge/Twig/Tests/Command/LintCommandTest.php index da4460ccbe938..3fe54cbabf547 100644 --- a/src/Symfony/Bridge/Twig/Tests/Command/LintCommandTest.php +++ b/src/Symfony/Bridge/Twig/Tests/Command/LintCommandTest.php @@ -11,9 +11,10 @@ namespace Symfony\Bridge\Twig\Tests\Command; -use Symfony\Component\Console\Tester\CommandTester; -use Symfony\Component\Console\Application; use Symfony\Bridge\Twig\Command\LintCommand; +use Symfony\Component\Console\Application; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Tester\CommandTester; /** * @covers \Symfony\Bridge\Twig\Command\LintCommand @@ -27,7 +28,7 @@ public function testLintCorrectFile() $tester = $this->createCommandTester(); $filename = $this->createFile('{{ foo }}'); - $ret = $tester->execute(array('filename' => $filename)); + $ret = $tester->execute(array('filename' => $filename), array('verbosity' => OutputInterface::VERBOSITY_VERBOSE)); $this->assertEquals(0, $ret, 'Returns 0 in case of success'); $this->assertRegExp('/^OK in /', $tester->getDisplay());