1111
1212namespace Symfony \Bridge \Twig \Command ;
1313
14+ if (!defined ('JSON_PRETTY_PRINT ' )) {
15+ define ('JSON_PRETTY_PRINT ' , 128 );
16+ }
17+
1418use Symfony \Component \Console \Command \Command ;
1519use Symfony \Component \Console \Input \InputInterface ;
20+ use Symfony \Component \Console \Input \InputOption ;
1621use Symfony \Component \Console \Output \OutputInterface ;
1722use Symfony \Component \Finder \Finder ;
1823
@@ -56,23 +61,24 @@ protected function configure()
5661 {
5762 $ this
5863 ->setDescription ('Lints a template and outputs encountered errors ' )
64+ ->addOption ('format ' , null , InputOption::VALUE_REQUIRED , 'The output format ' , 'txt ' )
5965 ->addArgument ('filename ' )
6066 ->setHelp (<<<EOF
61- The <info>%command.name%</info> command lints a template and outputs to stdout
67+ The <info>%command.name%</info> command lints a template and outputs to STDOUT
6268the first encountered syntax error.
6369
70+ You can validate the syntax of a file:
71+
6472<info>php %command.full_name% filename</info>
6573
66- The command gets the contents of <comment>filename</comment> and validates its syntax.
74+ Or of a whole directory:
6775
6876<info>php %command.full_name% dirname</info>
77+ <info>php %command.full_name% dirname --format=json</info>
6978
70- The command finds all twig templates in <comment>dirname</comment> and validates the syntax
71- of each Twig template.
79+ You can also pass the template contents from STDIN:
7280
7381<info>cat filename | php %command.full_name%</info>
74-
75- The command gets the template contents from stdin and validates its syntax.
7682EOF
7783 )
7884 ;
@@ -81,29 +87,27 @@ protected function configure()
8187 protected function execute (InputInterface $ input , OutputInterface $ output )
8288 {
8389 $ twig = $ this ->getTwigEnvironment ();
84- $ template = null ;
8590 $ filename = $ input ->getArgument ('filename ' );
8691
8792 if (!$ filename ) {
8893 if (0 !== ftell (STDIN )) {
89- throw new \RuntimeException ("Please provide a filename or pipe template content to stdin . " );
94+ throw new \RuntimeException ("Please provide a filename or pipe template content to STDIN . " );
9095 }
9196
97+ $ template = '' ;
9298 while (!feof (STDIN )) {
9399 $ template .= fread (STDIN , 1024 );
94100 }
95101
96- return $ this ->validateTemplate ( $ twig , $ output , $ template );
102+ return $ this ->display ( $ input , $ output , array ( $ this -> validate ( $ twig , $ template)) );
97103 }
98104
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 );
105+ $ filesInfo = array ();
106+ foreach ($ this ->findFiles ($ filename ) as $ file ) {
107+ $ filesInfo [] = $ this ->validate ($ twig , file_get_contents ($ file ), $ file );
104108 }
105109
106- return $ errors > 0 ? 1 : 0 ;
110+ return $ this -> display ( $ input , $ output , $ filesInfo ) ;
107111 }
108112
109113 protected function findFiles ($ filename )
@@ -117,32 +121,77 @@ protected function findFiles($filename)
117121 throw new \RuntimeException (sprintf ('File or directory "%s" is not readable ' , $ filename ));
118122 }
119123
120- protected function validateTemplate (\Twig_Environment $ twig, OutputInterface $ output , $ template , $ file = null )
124+ private function validate (\Twig_Environment $ twig , $ template , $ file = null )
121125 {
122126 try {
123127 $ twig ->parse ($ twig ->tokenize ($ template , $ file ? (string ) $ file : null ));
124- $ output ->writeln ('<info>OK</info> ' .($ file ? sprintf (' in %s ' , $ file ) : '' ));
125128 } catch (\Twig_Error $ e ) {
126- $ this ->renderException ($ output , $ template , $ e , $ file );
129+ return array ('template ' => $ template , 'file ' => $ file , 'valid ' => false , 'exception ' => $ e );
130+ }
131+
132+ return array ('template ' => $ template , 'file ' => $ file , 'valid ' => true );
133+ }
134+
135+ private function display (InputInterface $ input , OutputInterface $ output , $ files )
136+ {
137+ switch ($ input ->getOption ('format ' )) {
138+ case 'txt ' :
139+ return $ this ->displayTxt ($ output , $ files );
140+ case 'json ' :
141+ return $ this ->displayJson ($ output , $ files );
142+ default :
143+ throw new \InvalidArgumentException (sprintf ('The format "%s" is not supported. ' , $ input ->getOption ('format ' )));
144+ }
145+ }
127146
128- return 1 ;
147+ private function displayTxt (OutputInterface $ output , $ filesInfo )
148+ {
149+ $ errors = 0 ;
150+
151+ foreach ($ filesInfo as $ info ) {
152+ if ($ info ['valid ' ] && $ output ->isVerbose ()) {
153+ $ output ->writeln ('<info>OK</info> ' .($ info ['file ' ] ? sprintf (' in %s ' , $ info ['file ' ]) : '' ));
154+ } elseif (!$ info ['valid ' ]) {
155+ $ errors ++;
156+ $ this ->renderException ($ output , $ info ['template ' ], $ info ['exception ' ], $ info ['file ' ]);
157+ }
129158 }
130159
131- return 0 ;
160+ $ output ->writeln (sprintf ('<comment>%d/%d valid files</comment> ' , count ($ filesInfo ) - $ errors , count ($ filesInfo )));
161+
162+ return min ($ errors , 1 );
163+ }
164+
165+ private function displayJson (OutputInterface $ output , $ filesInfo )
166+ {
167+ $ errors = 0 ;
168+
169+ array_walk ($ filesInfo , function (&$ v ) use (&$ errors ) {
170+ $ v ['file ' ] = (string ) $ v ['file ' ];
171+ unset($ v ['template ' ]);
172+ if (!$ v ['valid ' ]) {
173+ $ v ['message ' ] = $ v ['exception ' ]->getMessage ();
174+ unset($ v ['exception ' ]);
175+ $ errors ++;
176+ }
177+ });
178+
179+ $ output ->writeln (json_encode ($ filesInfo , JSON_PRETTY_PRINT ));
180+
181+ return min ($ errors , 1 );
132182 }
133183
134- protected function renderException (OutputInterface $ output , $ template , \Twig_Error $ exception , $ file = null )
184+ private function renderException (OutputInterface $ output , $ template , \Twig_Error $ exception , $ file = null )
135185 {
136186 $ line = $ exception ->getTemplateLine ();
137- $ lines = $ this ->getContext ($ template , $ line );
138187
139188 if ($ file ) {
140189 $ output ->writeln (sprintf ("<error>KO</error> in %s (line %s) " , $ file , $ line ));
141190 } else {
142191 $ output ->writeln (sprintf ("<error>KO</error> (line %s) " , $ line ));
143192 }
144193
145- foreach ($ lines as $ no => $ code ) {
194+ foreach ($ this -> getContext ( $ template , $ line ) as $ no => $ code ) {
146195 $ output ->writeln (sprintf (
147196 "%s %-6s %s " ,
148197 $ no == $ line ? '<error>>></error> ' : ' ' ,
@@ -155,7 +204,7 @@ protected function renderException(OutputInterface $output, $template, \Twig_Err
155204 }
156205 }
157206
158- protected function getContext ($ template , $ line , $ context = 3 )
207+ private function getContext ($ template , $ line , $ context = 3 )
159208 {
160209 $ lines = explode ("\n" , $ template );
161210
0 commit comments