Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 702e2a4

Browse filesBrowse files
committed
feature #9855 [Twig] Decouple Twig commands from the Famework (GromNaN)
This PR was squashed before being merged into the 2.5-dev branch (closes #9855). Discussion ---------- [Twig] Decouple Twig commands from the Famework I want to use the command `twig:lint` in a Silex project. In this PR, I've moved the class `Symfony\Bundle\TwigBundle\Command\LintCommand` to `Symfony\Bridge\Twig\Command\LintCommand` and removed dependency to the `ContainerAwareCommand`. | Q | A | ------------- | --- | Bug fix? | no | New feature? | yes | BC breaks? | yes (renamed class) | Deprecations? | no | Tests pass? | yes | Fixed tickets | #9818 (comment) | License | MIT | Doc PR | n/a - [ ] Move command `twig:debug` once merged. - [x] Lazy load twig service Commits ------- 907748d [Twig] Decouple Twig commands from the Famework
2 parents 3203793 + 907748d commit 702e2a4
Copy full SHA for 702e2a4

File tree

Expand file treeCollapse file tree

6 files changed

+319
-110
lines changed
Filter options
Expand file treeCollapse file tree

6 files changed

+319
-110
lines changed

‎src/Symfony/Bridge/Twig/CHANGELOG.md

Copy file name to clipboardExpand all lines: src/Symfony/Bridge/Twig/CHANGELOG.md
+5Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
CHANGELOG
22
=========
33

4+
2.5.0
5+
-----
6+
7+
* moved command `twig:lint` from `TwigBundle`
8+
49
2.4.0
510
-----
611

+173Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
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\Bridge\Twig\Command;
13+
14+
use Symfony\Component\Console\Command\Command;
15+
use Symfony\Component\Console\Input\InputInterface;
16+
use Symfony\Component\Console\Output\OutputInterface;
17+
use Symfony\Component\Finder\Finder;
18+
19+
/**
20+
* Command that will validate your template syntax and output encountered errors.
21+
*
22+
* @author Marc Weistroff <marc.weistroff@sensiolabs.com>
23+
* @author Jérôme Tamarelle <jerome@tamarelle.net>
24+
*/
25+
class LintCommand extends Command
26+
{
27+
private $twig;
28+
29+
/**
30+
* {@inheritDoc}
31+
*/
32+
public function __construct($name = 'twig:lint')
33+
{
34+
parent::__construct($name);
35+
}
36+
37+
/**
38+
* Sets the twig environment
39+
*
40+
* @param \Twig_Environment $twig
41+
*/
42+
public function setTwigEnvironment(\Twig_Environment $twig)
43+
{
44+
$this->twig = $twig;
45+
}
46+
47+
/**
48+
* @return \Twig_Environment $twig
49+
*/
50+
protected function getTwigEnvironment()
51+
{
52+
return $this->twig;
53+
}
54+
55+
protected function configure()
56+
{
57+
$this
58+
->setDescription('Lints a template and outputs encountered errors')
59+
->addArgument('filename')
60+
->setHelp(<<<EOF
61+
The <info>%command.name%</info> command lints a template and outputs to stdout
62+
the first encountered syntax error.
63+
64+
<info>php %command.full_name% filename</info>
65+
66+
The command gets the contents of <comment>filename</comment> and validates its syntax.
67+
68+
<info>php %command.full_name% dirname</info>
69+
70+
The command finds all twig templates in <comment>dirname</comment> and validates the syntax
71+
of each Twig template.
72+
73+
<info>cat filename | php %command.full_name%</info>
74+
75+
The command gets the template contents from stdin and validates its syntax.
76+
EOF
77+
)
78+
;
79+
}
80+
81+
protected function execute(InputInterface $input, OutputInterface $output)
82+
{
83+
$twig = $this->getTwigEnvironment();
84+
$template = null;
85+
$filename = $input->getArgument('filename');
86+
87+
if (!$filename) {
88+
if (0 !== ftell(STDIN)) {
89+
throw new \RuntimeException("Please provide a filename or pipe template content to stdin.");
90+
}
91+
92+
while (!feof(STDIN)) {
93+
$template .= fread(STDIN, 1024);
94+
}
95+
96+
return $this->validateTemplate($twig, $output, $template);
97+
}
98+
99+
$files = $this->findFiles($filename);
100+
101+
$errors = 0;
102+
foreach ($files as $file) {
103+
$errors += $this->validateTemplate($twig, $output, file_get_contents($file), $file);
104+
}
105+
106+
return $errors > 0 ? 1 : 0;
107+
}
108+
109+
protected function findFiles($filename)
110+
{
111+
if (is_file($filename)) {
112+
return array($filename);
113+
} elseif (is_dir($filename)) {
114+
return Finder::create()->files()->in($filename)->name('*.twig');
115+
}
116+
117+
throw new \RuntimeException(sprintf('File or directory "%s" is not readable', $filename));
118+
}
119+
120+
protected function validateTemplate(\Twig_Environment $twig, OutputInterface $output, $template, $file = null)
121+
{
122+
try {
123+
$twig->parse($twig->tokenize($template, $file ? (string) $file : null));
124+
$output->writeln('<info>OK</info>'.($file ? sprintf(' in %s', $file) : ''));
125+
} catch (\Twig_Error $e) {
126+
$this->renderException($output, $template, $e, $file);
127+
128+
return 1;
129+
}
130+
131+
return 0;
132+
}
133+
134+
protected function renderException(OutputInterface $output, $template, \Twig_Error $exception, $file = null)
135+
{
136+
$line = $exception->getTemplateLine();
137+
$lines = $this->getContext($template, $line);
138+
139+
if ($file) {
140+
$output->writeln(sprintf("<error>KO</error> in %s (line %s)", $file, $line));
141+
} else {
142+
$output->writeln(sprintf("<error>KO</error> (line %s)", $line));
143+
}
144+
145+
foreach ($lines as $no => $code) {
146+
$output->writeln(sprintf(
147+
"%s %-6s %s",
148+
$no == $line ? '<error>>></error>' : ' ',
149+
$no,
150+
$code
151+
));
152+
if ($no == $line) {
153+
$output->writeln(sprintf('<error>>> %s</error> ', $exception->getRawMessage()));
154+
}
155+
}
156+
}
157+
158+
protected function getContext($template, $line, $context = 3)
159+
{
160+
$lines = explode("\n", $template);
161+
162+
$position = max(0, $line - $context);
163+
$max = min(count($lines), $line - 1 + $context);
164+
165+
$result = array();
166+
while ($position < $max) {
167+
$result[$position + 1] = $lines[$position];
168+
$position++;
169+
}
170+
171+
return $result;
172+
}
173+
}
+102Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
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\Bridge\Twig\Tests\Command;
13+
14+
use Symfony\Component\Console\Tester\CommandTester;
15+
use Symfony\Component\Console\Application;
16+
use Symfony\Bridge\Twig\Command\LintCommand;
17+
18+
/**
19+
* @covers \Symfony\Bridge\Twig\Command\LintCommand
20+
*/
21+
class LintCommandTest extends \PHPUnit_Framework_TestCase
22+
{
23+
private $files;
24+
25+
public function testLintCorrectFile()
26+
{
27+
$tester = $this->createCommandTester();
28+
$filename = $this->createFile('{{ foo }}');
29+
30+
$ret = $tester->execute(array('filename' => $filename));
31+
32+
$this->assertEquals(0, $ret, 'Returns 0 in case of success');
33+
$this->assertRegExp('/^OK in /', $tester->getDisplay());
34+
}
35+
36+
public function testLintIncorrectFile()
37+
{
38+
$tester = $this->createCommandTester();
39+
$filename = $this->createFile('{{ foo');
40+
41+
$ret = $tester->execute(array('filename' => $filename));
42+
43+
$this->assertEquals(1, $ret, 'Returns 1 in case of error');
44+
$this->assertRegExp('/^KO in /', $tester->getDisplay());
45+
}
46+
47+
/**
48+
* @expectedException \RuntimeException
49+
*/
50+
public function testLintFileNotReadable()
51+
{
52+
$tester = $this->createCommandTester();
53+
$filename = $this->createFile('');
54+
unlink($filename);
55+
56+
$ret = $tester->execute(array('filename' => $filename));
57+
}
58+
59+
/**
60+
* @return CommandTester
61+
*/
62+
private function createCommandTester()
63+
{
64+
$twig = new \Twig_Environment(new \Twig_Loader_Filesystem());
65+
66+
$command = new LintCommand();
67+
$command->setTwigEnvironment($twig);
68+
69+
$application = new Application();
70+
$application->add($command);
71+
$command = $application->find('twig:lint');
72+
73+
return new CommandTester($command);
74+
}
75+
76+
/**
77+
* @return string Path to the new file
78+
*/
79+
private function createFile($content)
80+
{
81+
$filename = tempnam(sys_get_temp_dir(), 'sf-');
82+
file_put_contents($filename, $content);
83+
84+
$this->files[] = $filename;
85+
86+
return $filename;
87+
}
88+
89+
public function setUp()
90+
{
91+
$this->files = array();
92+
}
93+
94+
public function tearDown()
95+
{
96+
foreach ($this->files as $file) {
97+
if (file_exists($file)) {
98+
unlink($file);
99+
}
100+
}
101+
}
102+
}

‎src/Symfony/Bridge/Twig/composer.json

Copy file name to clipboardExpand all lines: src/Symfony/Bridge/Twig/composer.json
+2-1Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,8 @@
2828
"symfony/translation": "~2.2",
2929
"symfony/yaml": "~2.0",
3030
"symfony/security": "~2.4",
31-
"symfony/stopwatch": "~2.2"
31+
"symfony/stopwatch": "~2.2",
32+
"symfony/console": "~2.2"
3233
},
3334
"suggest": {
3435
"symfony/form": "For using the FormExtension",

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.