Mercurial > hg > isophonics-drupal-site
comparison vendor/psy/psysh/src/Command/ShowCommand.php @ 13:5fb285c0d0e3
Update Drupal core to 8.4.7 via Composer. Security update; I *think* we've
been lucky to get away with this so far, as we don't support self-registration
which seems to be used by the so-called "drupalgeddon 2" attack that 8.4.5
was vulnerable to.
author | Chris Cannam |
---|---|
date | Mon, 23 Apr 2018 09:33:26 +0100 |
parents | |
children | 129ea1e6d783 |
comparison
equal
deleted
inserted
replaced
12:7a779792577d | 13:5fb285c0d0e3 |
---|---|
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 JakubOnderka\PhpConsoleHighlighter\Highlighter; | |
15 use Psy\Configuration; | |
16 use Psy\ConsoleColorFactory; | |
17 use Psy\Exception\RuntimeException; | |
18 use Psy\Formatter\CodeFormatter; | |
19 use Psy\Formatter\SignatureFormatter; | |
20 use Psy\Input\CodeArgument; | |
21 use Psy\Output\ShellOutput; | |
22 use Symfony\Component\Console\Formatter\OutputFormatter; | |
23 use Symfony\Component\Console\Input\InputInterface; | |
24 use Symfony\Component\Console\Input\InputOption; | |
25 use Symfony\Component\Console\Output\OutputInterface; | |
26 | |
27 /** | |
28 * Show the code for an object, class, constant, method or property. | |
29 */ | |
30 class ShowCommand extends ReflectingCommand | |
31 { | |
32 private $colorMode; | |
33 private $highlighter; | |
34 private $lastException; | |
35 private $lastExceptionIndex; | |
36 | |
37 /** | |
38 * @param null|string $colorMode (default: null) | |
39 */ | |
40 public function __construct($colorMode = null) | |
41 { | |
42 $this->colorMode = $colorMode ?: Configuration::COLOR_MODE_AUTO; | |
43 | |
44 parent::__construct(); | |
45 } | |
46 | |
47 /** | |
48 * {@inheritdoc} | |
49 */ | |
50 protected function configure() | |
51 { | |
52 $this | |
53 ->setName('show') | |
54 ->setDefinition([ | |
55 new CodeArgument('target', CodeArgument::OPTIONAL, 'Function, class, instance, constant, method or property to show.'), | |
56 new InputOption('ex', null, InputOption::VALUE_OPTIONAL, 'Show last exception context. Optionally specify a stack index.', 1), | |
57 ]) | |
58 ->setDescription('Show the code for an object, class, constant, method or property.') | |
59 ->setHelp( | |
60 <<<HELP | |
61 Show the code for an object, class, constant, method or property, or the context | |
62 of the last exception. | |
63 | |
64 <return>cat --ex</return> defaults to showing the lines surrounding the location of the last | |
65 exception. Invoking it more than once travels up the exception's stack trace, | |
66 and providing a number shows the context of the given index of the trace. | |
67 | |
68 e.g. | |
69 <return>>>> show \$myObject</return> | |
70 <return>>>> show Psy\Shell::debug</return> | |
71 <return>>>> show --ex</return> | |
72 <return>>>> show --ex 3</return> | |
73 HELP | |
74 ); | |
75 } | |
76 | |
77 /** | |
78 * {@inheritdoc} | |
79 */ | |
80 protected function execute(InputInterface $input, OutputInterface $output) | |
81 { | |
82 // n.b. As far as I can tell, InputInterface doesn't want to tell me | |
83 // whether an option with an optional value was actually passed. If you | |
84 // call `$input->getOption('ex')`, it will return the default, both when | |
85 // `--ex` is specified with no value, and when `--ex` isn't specified at | |
86 // all. | |
87 // | |
88 // So we're doing something sneaky here. If we call `getOptions`, it'll | |
89 // return the default value when `--ex` is not present, and `null` if | |
90 // `--ex` is passed with no value. /shrug | |
91 $opts = $input->getOptions(); | |
92 | |
93 // Strict comparison to `1` (the default value) here, because `--ex 1` | |
94 // will come in as `"1"`. Now we can tell the difference between | |
95 // "no --ex present", because it's the integer 1, "--ex with no value", | |
96 // because it's `null`, and "--ex 1", because it's the string "1". | |
97 if ($opts['ex'] !== 1) { | |
98 if ($input->getArgument('target')) { | |
99 throw new \InvalidArgumentException('Too many arguments (supply either "target" or "--ex")'); | |
100 } | |
101 | |
102 return $this->writeExceptionContext($input, $output); | |
103 } | |
104 | |
105 if ($input->getArgument('target')) { | |
106 return $this->writeCodeContext($input, $output); | |
107 } | |
108 | |
109 throw new RuntimeException('Not enough arguments (missing: "target")'); | |
110 } | |
111 | |
112 private function writeCodeContext(InputInterface $input, OutputInterface $output) | |
113 { | |
114 list($target, $reflector) = $this->getTargetAndReflector($input->getArgument('target')); | |
115 | |
116 // Set some magic local variables | |
117 $this->setCommandScopeVariables($reflector); | |
118 | |
119 try { | |
120 $output->page(CodeFormatter::format($reflector, $this->colorMode), ShellOutput::OUTPUT_RAW); | |
121 } catch (RuntimeException $e) { | |
122 $output->writeln(SignatureFormatter::format($reflector)); | |
123 throw $e; | |
124 } | |
125 } | |
126 | |
127 private function writeExceptionContext(InputInterface $input, OutputInterface $output) | |
128 { | |
129 $exception = $this->context->getLastException(); | |
130 if ($exception !== $this->lastException) { | |
131 $this->lastException = null; | |
132 $this->lastExceptionIndex = null; | |
133 } | |
134 | |
135 $opts = $input->getOptions(); | |
136 if ($opts['ex'] === null) { | |
137 if ($this->lastException && $this->lastExceptionIndex !== null) { | |
138 $index = $this->lastExceptionIndex + 1; | |
139 } else { | |
140 $index = 0; | |
141 } | |
142 } else { | |
143 $index = max(0, intval($input->getOption('ex')) - 1); | |
144 } | |
145 | |
146 $trace = $exception->getTrace(); | |
147 array_unshift($trace, [ | |
148 'file' => $exception->getFile(), | |
149 'line' => $exception->getLine(), | |
150 ]); | |
151 | |
152 if ($index >= count($trace)) { | |
153 $index = 0; | |
154 } | |
155 | |
156 $this->lastException = $exception; | |
157 $this->lastExceptionIndex = $index; | |
158 | |
159 $output->writeln($this->getApplication()->formatException($exception)); | |
160 $output->writeln('--'); | |
161 $this->writeTraceLine($output, $trace, $index); | |
162 $this->writeTraceCodeSnippet($output, $trace, $index); | |
163 | |
164 $this->setCommandScopeVariablesFromContext($trace[$index]); | |
165 } | |
166 | |
167 private function writeTraceLine(OutputInterface $output, array $trace, $index) | |
168 { | |
169 $file = isset($trace[$index]['file']) ? $this->replaceCwd($trace[$index]['file']) : 'n/a'; | |
170 $line = isset($trace[$index]['line']) ? $trace[$index]['line'] : 'n/a'; | |
171 | |
172 $output->writeln(sprintf( | |
173 'From <info>%s:%d</info> at <strong>level %d</strong> of backtrace (of %d).', | |
174 OutputFormatter::escape($file), | |
175 OutputFormatter::escape($line), | |
176 $index + 1, | |
177 count($trace) | |
178 )); | |
179 } | |
180 | |
181 private function replaceCwd($file) | |
182 { | |
183 if ($cwd = getcwd()) { | |
184 $cwd = rtrim($cwd, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR; | |
185 } | |
186 | |
187 if ($cwd === false) { | |
188 return $file; | |
189 } else { | |
190 return preg_replace('/^' . preg_quote($cwd, '/') . '/', '', $file); | |
191 } | |
192 } | |
193 | |
194 private function writeTraceCodeSnippet(OutputInterface $output, array $trace, $index) | |
195 { | |
196 if (!isset($trace[$index]['file'])) { | |
197 return; | |
198 } | |
199 | |
200 $file = $trace[$index]['file']; | |
201 if ($fileAndLine = $this->extractEvalFileAndLine($file)) { | |
202 list($file, $line) = $fileAndLine; | |
203 } else { | |
204 if (!isset($trace[$index]['line'])) { | |
205 return; | |
206 } | |
207 | |
208 $line = $trace[$index]['line']; | |
209 } | |
210 | |
211 if (is_file($file)) { | |
212 $code = @file_get_contents($file); | |
213 } | |
214 | |
215 if (empty($code)) { | |
216 return; | |
217 } | |
218 | |
219 $output->write($this->getHighlighter()->getCodeSnippet($code, $line, 5, 5), ShellOutput::OUTPUT_RAW); | |
220 } | |
221 | |
222 private function getHighlighter() | |
223 { | |
224 if (!$this->highlighter) { | |
225 $factory = new ConsoleColorFactory($this->colorMode); | |
226 $this->highlighter = new Highlighter($factory->getConsoleColor()); | |
227 } | |
228 | |
229 return $this->highlighter; | |
230 } | |
231 | |
232 private function setCommandScopeVariablesFromContext(array $context) | |
233 { | |
234 $vars = []; | |
235 | |
236 if (isset($context['class'])) { | |
237 $vars['__class'] = $context['class']; | |
238 if (isset($context['function'])) { | |
239 $vars['__method'] = $context['function']; | |
240 } | |
241 | |
242 try { | |
243 $refl = new \ReflectionClass($context['class']); | |
244 if ($namespace = $refl->getNamespaceName()) { | |
245 $vars['__namespace'] = $namespace; | |
246 } | |
247 } catch (\Exception $e) { | |
248 // oh well | |
249 } | |
250 } elseif (isset($context['function'])) { | |
251 $vars['__function'] = $context['function']; | |
252 | |
253 try { | |
254 $refl = new \ReflectionFunction($context['function']); | |
255 if ($namespace = $refl->getNamespaceName()) { | |
256 $vars['__namespace'] = $namespace; | |
257 } | |
258 } catch (\Exception $e) { | |
259 // oh well | |
260 } | |
261 } | |
262 | |
263 if (isset($context['file'])) { | |
264 $file = $context['file']; | |
265 if ($fileAndLine = $this->extractEvalFileAndLine($file)) { | |
266 list($file, $line) = $fileAndLine; | |
267 } elseif (isset($context['line'])) { | |
268 $line = $context['line']; | |
269 } | |
270 | |
271 if (is_file($file)) { | |
272 $vars['__file'] = $file; | |
273 if (isset($line)) { | |
274 $vars['__line'] = $line; | |
275 } | |
276 $vars['__dir'] = dirname($file); | |
277 } | |
278 } | |
279 | |
280 $this->context->setCommandScopeVariables($vars); | |
281 } | |
282 | |
283 private function extractEvalFileAndLine($file) | |
284 { | |
285 if (preg_match('/(.*)\\((\\d+)\\) : eval\\(\\)\'d code$/', $file, $matches)) { | |
286 return [$matches[1], $matches[2]]; | |
287 } | |
288 } | |
289 } |