[ Index ]

PHP Cross Reference of phpBB-3.3.3-deutsch

title

Body

[close]

/vendor/symfony/twig-bridge/Command/ -> LintCommand.php (source)

   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\Exception\InvalidArgumentException;
  16  use Symfony\Component\Console\Exception\RuntimeException;
  17  use Symfony\Component\Console\Input\InputArgument;
  18  use Symfony\Component\Console\Input\InputInterface;
  19  use Symfony\Component\Console\Input\InputOption;
  20  use Symfony\Component\Console\Output\OutputInterface;
  21  use Symfony\Component\Console\Style\SymfonyStyle;
  22  use Symfony\Component\Finder\Finder;
  23  use Twig\Environment;
  24  use Twig\Error\Error;
  25  use Twig\Loader\ArrayLoader;
  26  use Twig\Source;
  27  
  28  /**
  29   * Command that will validate your template syntax and output encountered errors.
  30   *
  31   * @author Marc Weistroff <marc.weistroff@sensiolabs.com>
  32   * @author Jérôme Tamarelle <jerome@tamarelle.net>
  33   */
  34  class LintCommand extends Command
  35  {
  36      protected static $defaultName = 'lint:twig';
  37  
  38      private $twig;
  39  
  40      /**
  41       * @param Environment $twig
  42       */
  43      public function __construct($twig = null)
  44      {
  45          if (!$twig instanceof Environment) {
  46              @trigger_error(sprintf('Passing a command name as the first argument of "%s()" is deprecated since Symfony 3.4 and support for it will be removed in 4.0. If the command was registered by convention, make it a service instead.', __METHOD__), \E_USER_DEPRECATED);
  47  
  48              parent::__construct($twig);
  49  
  50              return;
  51          }
  52  
  53          parent::__construct();
  54  
  55          $this->twig = $twig;
  56      }
  57  
  58      public function setTwigEnvironment(Environment $twig)
  59      {
  60          @trigger_error(sprintf('The "%s()" method is deprecated since Symfony 3.4 and will be removed in 4.0.', __METHOD__), \E_USER_DEPRECATED);
  61  
  62          $this->twig = $twig;
  63      }
  64  
  65      /**
  66       * @return Environment $twig
  67       */
  68      protected function getTwigEnvironment()
  69      {
  70          @trigger_error(sprintf('The "%s()" method is deprecated since Symfony 3.4 and will be removed in 4.0.', __METHOD__), \E_USER_DEPRECATED);
  71  
  72          return $this->twig;
  73      }
  74  
  75      protected function configure()
  76      {
  77          $this
  78              ->setDescription('Lints a template and outputs encountered errors')
  79              ->addOption('format', null, InputOption::VALUE_REQUIRED, 'The output format', 'txt')
  80              ->addArgument('filename', InputArgument::IS_ARRAY)
  81              ->setHelp(<<<'EOF'
  82  The <info>%command.name%</info> command lints a template and outputs to STDOUT
  83  the first encountered syntax error.
  84  
  85  You can validate the syntax of contents passed from STDIN:
  86  
  87    <info>cat filename | php %command.full_name%</info>
  88  
  89  Or the syntax of a file:
  90  
  91    <info>php %command.full_name% filename</info>
  92  
  93  Or of a whole directory:
  94  
  95    <info>php %command.full_name% dirname</info>
  96    <info>php %command.full_name% dirname --format=json</info>
  97  
  98  EOF
  99              )
 100          ;
 101      }
 102  
 103      protected function execute(InputInterface $input, OutputInterface $output)
 104      {
 105          $io = new SymfonyStyle($input, $output);
 106  
 107          // BC to be removed in 4.0
 108          if (__CLASS__ !== static::class) {
 109              $r = new \ReflectionMethod($this, 'getTwigEnvironment');
 110              if (__CLASS__ !== $r->getDeclaringClass()->getName()) {
 111                  @trigger_error(sprintf('Usage of method "%s" is deprecated since Symfony 3.4 and will no longer be supported in 4.0. Construct the command with its required arguments instead.', static::class.'::getTwigEnvironment'), \E_USER_DEPRECATED);
 112  
 113                  $this->twig = $this->getTwigEnvironment();
 114              }
 115          }
 116          if (null === $this->twig) {
 117              throw new \RuntimeException('The Twig environment needs to be set.');
 118          }
 119  
 120          $filenames = $input->getArgument('filename');
 121  
 122          if (0 === \count($filenames)) {
 123              if (0 !== ftell(\STDIN)) {
 124                  throw new RuntimeException('Please provide a filename or pipe template content to STDIN.');
 125              }
 126  
 127              $template = '';
 128              while (!feof(\STDIN)) {
 129                  $template .= fread(\STDIN, 1024);
 130              }
 131  
 132              return $this->display($input, $output, $io, [$this->validate($template, uniqid('sf_', true))]);
 133          }
 134  
 135          $filesInfo = $this->getFilesInfo($filenames);
 136  
 137          return $this->display($input, $output, $io, $filesInfo);
 138      }
 139  
 140      private function getFilesInfo(array $filenames)
 141      {
 142          $filesInfo = [];
 143          foreach ($filenames as $filename) {
 144              foreach ($this->findFiles($filename) as $file) {
 145                  $filesInfo[] = $this->validate(file_get_contents($file), $file);
 146              }
 147          }
 148  
 149          return $filesInfo;
 150      }
 151  
 152      protected function findFiles($filename)
 153      {
 154          if (is_file($filename)) {
 155              return [$filename];
 156          } elseif (is_dir($filename)) {
 157              return Finder::create()->files()->in($filename)->name('*.twig');
 158          }
 159  
 160          throw new RuntimeException(sprintf('File or directory "%s" is not readable.', $filename));
 161      }
 162  
 163      private function validate($template, $file)
 164      {
 165          $realLoader = $this->twig->getLoader();
 166          try {
 167              $temporaryLoader = new ArrayLoader([(string) $file => $template]);
 168              $this->twig->setLoader($temporaryLoader);
 169              $nodeTree = $this->twig->parse($this->twig->tokenize(new Source($template, (string) $file)));
 170              $this->twig->compile($nodeTree);
 171              $this->twig->setLoader($realLoader);
 172          } catch (Error $e) {
 173              $this->twig->setLoader($realLoader);
 174  
 175              return ['template' => $template, 'file' => $file, 'line' => $e->getTemplateLine(), 'valid' => false, 'exception' => $e];
 176          }
 177  
 178          return ['template' => $template, 'file' => $file, 'valid' => true];
 179      }
 180  
 181      private function display(InputInterface $input, OutputInterface $output, SymfonyStyle $io, $files)
 182      {
 183          switch ($input->getOption('format')) {
 184              case 'txt':
 185                  return $this->displayTxt($output, $io, $files);
 186              case 'json':
 187                  return $this->displayJson($output, $files);
 188              default:
 189                  throw new InvalidArgumentException(sprintf('The format "%s" is not supported.', $input->getOption('format')));
 190          }
 191      }
 192  
 193      private function displayTxt(OutputInterface $output, SymfonyStyle $io, $filesInfo)
 194      {
 195          $errors = 0;
 196  
 197          foreach ($filesInfo as $info) {
 198              if ($info['valid'] && $output->isVerbose()) {
 199                  $io->comment('<info>OK</info>'.($info['file'] ? sprintf(' in %s', $info['file']) : ''));
 200              } elseif (!$info['valid']) {
 201                  ++$errors;
 202                  $this->renderException($io, $info['template'], $info['exception'], $info['file']);
 203              }
 204          }
 205  
 206          if (0 === $errors) {
 207              $io->success(sprintf('All %d Twig files contain valid syntax.', \count($filesInfo)));
 208          } else {
 209              $io->warning(sprintf('%d Twig files have valid syntax and %d contain errors.', \count($filesInfo) - $errors, $errors));
 210          }
 211  
 212          return min($errors, 1);
 213      }
 214  
 215      private function displayJson(OutputInterface $output, $filesInfo)
 216      {
 217          $errors = 0;
 218  
 219          array_walk($filesInfo, function (&$v) use (&$errors) {
 220              $v['file'] = (string) $v['file'];
 221              unset($v['template']);
 222              if (!$v['valid']) {
 223                  $v['message'] = $v['exception']->getMessage();
 224                  unset($v['exception']);
 225                  ++$errors;
 226              }
 227          });
 228  
 229          $output->writeln(json_encode($filesInfo, \JSON_PRETTY_PRINT | \JSON_UNESCAPED_SLASHES));
 230  
 231          return min($errors, 1);
 232      }
 233  
 234      private function renderException(OutputInterface $output, $template, Error $exception, $file = null)
 235      {
 236          $line = $exception->getTemplateLine();
 237  
 238          if ($file) {
 239              $output->text(sprintf('<error> ERROR </error> in %s (line %s)', $file, $line));
 240          } else {
 241              $output->text(sprintf('<error> ERROR </error> (line %s)', $line));
 242          }
 243  
 244          foreach ($this->getContext($template, $line) as $lineNumber => $code) {
 245              $output->text(sprintf(
 246                  '%s %-6s %s',
 247                  $lineNumber === $line ? '<error> >> </error>' : '    ',
 248                  $lineNumber,
 249                  $code
 250              ));
 251              if ($lineNumber === $line) {
 252                  $output->text(sprintf('<error> >> %s</error> ', $exception->getRawMessage()));
 253              }
 254          }
 255      }
 256  
 257      private function getContext($template, $line, $context = 3)
 258      {
 259          $lines = explode("\n", $template);
 260  
 261          $position = max(0, $line - $context);
 262          $max = min(\count($lines), $line - 1 + $context);
 263  
 264          $result = [];
 265          while ($position < $max) {
 266              $result[$position + 1] = $lines[$position];
 267              ++$position;
 268          }
 269  
 270          return $result;
 271      }
 272  }


Generated: Sun Feb 14 20:08:31 2021 Cross-referenced by PHPXref 0.7.1