Yaffs site version 1.1
[yaffs-website] / vendor / psy / psysh / src / Psy / Command / ParseCommand.php
1 <?php
2
3 /*
4  * This file is part of Psy Shell.
5  *
6  * (c) 2012-2017 Justin Hileman
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 Psy\Command;
13
14 use PhpParser\Node;
15 use PhpParser\Parser;
16 use Psy\Input\CodeArgument;
17 use Psy\ParserFactory;
18 use Psy\VarDumper\Presenter;
19 use Psy\VarDumper\PresenterAware;
20 use Symfony\Component\Console\Input\InputArgument;
21 use Symfony\Component\Console\Input\InputInterface;
22 use Symfony\Component\Console\Input\InputOption;
23 use Symfony\Component\Console\Output\OutputInterface;
24 use Symfony\Component\VarDumper\Caster\Caster;
25
26 /**
27  * Parse PHP code and show the abstract syntax tree.
28  */
29 class ParseCommand extends Command implements PresenterAware
30 {
31     private $presenter;
32     private $parserFactory;
33     private $parsers;
34
35     /**
36      * {@inheritdoc}
37      */
38     public function __construct($name = null)
39     {
40         $this->parserFactory = new ParserFactory();
41         $this->parsers = array();
42
43         parent::__construct($name);
44     }
45
46     /**
47      * PresenterAware interface.
48      *
49      * @param Presenter $presenter
50      */
51     public function setPresenter(Presenter $presenter)
52     {
53         $this->presenter = clone $presenter;
54         $this->presenter->addCasters(array(
55             'PhpParser\Node' => function (Node $node, array $a) {
56                 $a = array(
57                     Caster::PREFIX_VIRTUAL . 'type'       => $node->getType(),
58                     Caster::PREFIX_VIRTUAL . 'attributes' => $node->getAttributes(),
59                 );
60
61                 foreach ($node->getSubNodeNames() as $name) {
62                     $a[Caster::PREFIX_VIRTUAL . $name] = $node->$name;
63                 }
64
65                 return $a;
66             },
67         ));
68     }
69
70     /**
71      * {@inheritdoc}
72      */
73     protected function configure()
74     {
75         $definition = array(
76             new CodeArgument('code', InputArgument::REQUIRED, 'PHP code to parse.'),
77             new InputOption('depth', '', InputOption::VALUE_REQUIRED, 'Depth to parse', 10),
78         );
79
80         if ($this->parserFactory->hasKindsSupport()) {
81             $msg = 'One of PhpParser\\ParserFactory constants: '
82                 . implode(', ', ParserFactory::getPossibleKinds())
83                 . " (default is based on current interpreter's version)";
84             $defaultKind = $this->parserFactory->getDefaultKind();
85
86             $definition[] = new InputOption('kind', '', InputOption::VALUE_REQUIRED, $msg, $defaultKind);
87         }
88
89         $this
90             ->setName('parse')
91             ->setDefinition($definition)
92             ->setDescription('Parse PHP code and show the abstract syntax tree.')
93             ->setHelp(
94                 <<<'HELP'
95 Parse PHP code and show the abstract syntax tree.
96
97 This command is used in the development of PsySH. Given a string of PHP code,
98 it pretty-prints the PHP Parser parse tree.
99
100 See https://github.com/nikic/PHP-Parser
101
102 It prolly won't be super useful for most of you, but it's here if you want to play.
103 HELP
104             );
105     }
106
107     /**
108      * {@inheritdoc}
109      */
110     protected function execute(InputInterface $input, OutputInterface $output)
111     {
112         $code = $input->getArgument('code');
113         if (strpos('<?', $code) === false) {
114             $code = '<?php ' . $code;
115         }
116
117         $parserKind = $this->parserFactory->hasKindsSupport() ? $input->getOption('kind') : null;
118         $depth      = $input->getOption('depth');
119         $nodes      = $this->parse($this->getParser($parserKind), $code);
120         $output->page($this->presenter->present($nodes, $depth));
121     }
122
123     /**
124      * Lex and parse a string of code into statements.
125      *
126      * @param Parser $parser
127      * @param string $code
128      *
129      * @return array Statements
130      */
131     private function parse(Parser $parser, $code)
132     {
133         try {
134             return $parser->parse($code);
135         } catch (\PhpParser\Error $e) {
136             if (strpos($e->getMessage(), 'unexpected EOF') === false) {
137                 throw $e;
138             }
139
140             // If we got an unexpected EOF, let's try it again with a semicolon.
141             return $parser->parse($code . ';');
142         }
143     }
144
145     /**
146      * Get (or create) the Parser instance.
147      *
148      * @param string|null $kind One of Psy\ParserFactory constants (only for PHP parser 2.0 and above)
149      *
150      * @return Parser
151      */
152     private function getParser($kind = null)
153     {
154         if (!array_key_exists($kind, $this->parsers)) {
155             $this->parsers[$kind] = $this->parserFactory->createParser($kind);
156         }
157
158         return $this->parsers[$kind];
159     }
160 }