4b7003d1042d1f10a4900abbfc01666fed6d5a55
[yaffs-website] / vendor / psy / psysh / src / Command / ThrowUpCommand.php
1 <?php
2
3 /*
4  * This file is part of Psy Shell.
5  *
6  * (c) 2012-2018 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\Arg;
15 use PhpParser\Node\Expr\StaticCall;
16 use PhpParser\Node\Expr\Variable;
17 use PhpParser\Node\Name\FullyQualified as FullyQualifiedName;
18 use PhpParser\Node\Stmt\Throw_;
19 use PhpParser\PrettyPrinter\Standard as Printer;
20 use Psy\Context;
21 use Psy\ContextAware;
22 use Psy\Input\CodeArgument;
23 use Psy\ParserFactory;
24 use Symfony\Component\Console\Input\InputInterface;
25 use Symfony\Component\Console\Output\OutputInterface;
26
27 /**
28  * Throw an exception or error out of the Psy Shell.
29  */
30 class ThrowUpCommand extends Command implements ContextAware
31 {
32     const THROW_CLASS = 'Psy\Exception\ThrowUpException';
33
34     private $parser;
35     private $printer;
36
37     /**
38      * Context instance (for ContextAware interface).
39      *
40      * @var Context
41      */
42     protected $context;
43
44     /**
45      * {@inheritdoc}
46      */
47     public function __construct($name = null)
48     {
49         $parserFactory = new ParserFactory();
50
51         $this->parser  = $parserFactory->createParser();
52         $this->printer = new Printer();
53
54         parent::__construct($name);
55     }
56
57     /**
58      * ContextAware interface.
59      *
60      * @param Context $context
61      */
62     public function setContext(Context $context)
63     {
64         $this->context = $context;
65     }
66
67     /**
68      * {@inheritdoc}
69      */
70     protected function configure()
71     {
72         $this
73             ->setName('throw-up')
74             ->setDefinition([
75                 new CodeArgument('exception', CodeArgument::OPTIONAL, 'Exception or Error to throw.'),
76             ])
77             ->setDescription('Throw an exception or error out of the Psy Shell.')
78             ->setHelp(
79                 <<<'HELP'
80 Throws an exception or error out of the current the Psy Shell instance.
81
82 By default it throws the most recent exception.
83
84 e.g.
85 <return>>>> throw-up</return>
86 <return>>>> throw-up $e</return>
87 <return>>>> throw-up new Exception('WHEEEEEE!')</return>
88 HELP
89             );
90     }
91
92     /**
93      * {@inheritdoc}
94      *
95      * @throws InvalidArgumentException if there is no exception to throw
96      */
97     protected function execute(InputInterface $input, OutputInterface $output)
98     {
99         $args = $this->prepareArgs($input->getArgument('exception'));
100         $throwStmt = new Throw_(new StaticCall(new FullyQualifiedName(self::THROW_CLASS), 'fromThrowable', $args));
101         $throwCode = $this->printer->prettyPrint([$throwStmt]);
102
103         $shell = $this->getApplication();
104         $shell->addCode($throwCode, !$shell->hasCode());
105     }
106
107     /**
108      * Parse the supplied command argument.
109      *
110      * If no argument was given, this falls back to `$_e`
111      *
112      * @throws InvalidArgumentException if there is no exception to throw
113      *
114      * @param string $code
115      *
116      * @return Arg[]
117      */
118     private function prepareArgs($code = null)
119     {
120         if (!$code) {
121             // Default to last exception if nothing else was supplied
122             return [new Arg(new Variable('_e'))];
123         }
124
125         if (strpos('<?', $code) === false) {
126             $code = '<?php ' . $code;
127         }
128
129         $expr = $this->parse($code);
130
131         if (count($expr) !== 1) {
132             throw new \InvalidArgumentException('No idea how to throw this');
133         }
134
135         return [new Arg($expr[0])];
136     }
137
138     /**
139      * Lex and parse a string of code into statements.
140      *
141      * @param string $code
142      *
143      * @return array Statements
144      */
145     private function parse($code)
146     {
147         try {
148             return $this->parser->parse($code);
149         } catch (\PhpParser\Error $e) {
150             if (strpos($e->getMessage(), 'unexpected EOF') === false) {
151                 throw $e;
152             }
153
154             // If we got an unexpected EOF, let's try it again with a semicolon.
155             return $this->parser->parse($code . ';');
156         }
157     }
158 }