Skip to content

[TwigBridge] Added support for json format in twig:lint command #10064

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jan 24, 2014
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
97 changes: 73 additions & 24 deletions src/Symfony/Bridge/Twig/Command/LintCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -56,23 +61,24 @@ 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(<<<EOF
The <info>%command.name%</info> command lints a template and outputs to stdout
The <info>%command.name%</info> command lints a template and outputs to STDOUT
the first encountered syntax error.

You can validate the syntax of a file:

<info>php %command.full_name% filename</info>

The command gets the contents of <comment>filename</comment> and validates its syntax.
Or of a whole directory:

<info>php %command.full_name% dirname</info>
<info>php %command.full_name% dirname --format=json</info>

The command finds all twig templates in <comment>dirname</comment> and validates the syntax
of each Twig template.
You can also pass the template contents from STDIN:

<info>cat filename | php %command.full_name%</info>

The command gets the template contents from stdin and validates its syntax.
EOF
)
;
Expand All @@ -81,29 +87,27 @@ protected function configure()
protected function execute(InputInterface $input, OutputInterface $output)
{
$twig = $this->getTwigEnvironment();
$template = null;
$filename = $input->getArgument('filename');

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.");
}

$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)
Expand All @@ -117,32 +121,77 @@ 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('<info>OK</info>'.($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')));
}
}

return 1;
private function displayTxt(OutputInterface $output, $filesInfo)
{
$errors = 0;

foreach ($filesInfo as $info) {
if ($info['valid'] && $output->isVerbose()) {
$output->writeln('<info>OK</info>'.($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('<comment>%d/%d valid files</comment>', count($filesInfo) - $errors, count($filesInfo)));

return min($errors, 1);
}

private function displayJson(OutputInterface $output, $filesInfo)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

some thought that came to me was, is it possible that we use descriptors here to support json and txt output? If so we should do that. It would be nice actually. 👶

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's not possible, as descriptor uses objects. Here, it's collection...

{
$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("<error>KO</error> in %s (line %s)", $file, $line));
} else {
$output->writeln(sprintf("<error>KO</error> (line %s)", $line));
}

foreach ($lines as $no => $code) {
foreach ($this->getContext($template, $line) as $no => $code) {
$output->writeln(sprintf(
"%s %-6s %s",
$no == $line ? '<error>>></error>' : ' ',
Expand All @@ -155,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);

Expand Down
7 changes: 4 additions & 3 deletions src/Symfony/Bridge/Twig/Tests/Command/LintCommandTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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());
Expand Down
5 changes: 2 additions & 3 deletions src/Symfony/Bundle/TwigBundle/Command/LintCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -57,10 +57,9 @@ protected function configure()
$this->getHelp().<<<EOF


<info>php %command.full_name% @AcmeMyBundle</info>
Or all template files in a bundle:

The command finds all twig templates in the <comment>AcmeMyBundle</comment> bundle and validates
the syntax of each Twig template.
<info>php %command.full_name% @AcmeDemoBundle</info>
EOF
)
;
Expand Down