Version 1
[yaffs-website] / vendor / psy / psysh / src / Psy / Command / TraceCommand.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 Psy\Output\ShellOutput;
15 use Symfony\Component\Console\Formatter\OutputFormatter;
16 use Symfony\Component\Console\Input\InputInterface;
17 use Symfony\Component\Console\Input\InputOption;
18 use Symfony\Component\Console\Output\OutputInterface;
19
20 /**
21  * Show the current stack trace.
22  */
23 class TraceCommand extends Command
24 {
25     /**
26      * {@inheritdoc}
27      */
28     protected function configure()
29     {
30         $this
31             ->setName('trace')
32             ->setDefinition(array(
33                 new InputOption('include-psy', 'p', InputOption::VALUE_NONE,     'Include Psy in the call stack.'),
34                 new InputOption('num',         'n', InputOption::VALUE_REQUIRED, 'Only include NUM lines.'),
35             ))
36             ->setDescription('Show the current call stack.')
37             ->setHelp(
38                 <<<'HELP'
39 Show the current call stack.
40
41 Optionally, include PsySH in the call stack by passing the <info>--include-psy</info> option.
42
43 e.g.
44 <return>> trace -n10</return>
45 <return>> trace --include-psy</return>
46 HELP
47             );
48     }
49
50     /**
51      * {@inheritdoc}
52      */
53     protected function execute(InputInterface $input, OutputInterface $output)
54     {
55         $trace = $this->getBacktrace(new \Exception(), $input->getOption('num'), $input->getOption('include-psy'));
56         $output->page($trace, ShellOutput::NUMBER_LINES);
57     }
58
59     /**
60      * Get a backtrace for an exception.
61      *
62      * Optionally limit the number of rows to include with $count, and exclude
63      * Psy from the trace.
64      *
65      * @param \Exception $e          The exception with a backtrace
66      * @param int        $count      (default: PHP_INT_MAX)
67      * @param bool       $includePsy (default: true)
68      *
69      * @return array Formatted stacktrace lines
70      */
71     protected function getBacktrace(\Exception $e, $count = null, $includePsy = true)
72     {
73         if ($cwd = getcwd()) {
74             $cwd = rtrim($cwd, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR;
75         }
76
77         if ($count === null) {
78             $count = PHP_INT_MAX;
79         }
80
81         $lines = array();
82
83         $trace = $e->getTrace();
84         array_unshift($trace, array(
85             'function' => '',
86             'file'     => $e->getFile() !== null ? $e->getFile() : 'n/a',
87             'line'     => $e->getLine() !== null ? $e->getLine() : 'n/a',
88             'args'     => array(),
89         ));
90
91         if (!$includePsy) {
92             for ($i = count($trace) - 1; $i >= 0; $i--) {
93                 $thing = isset($trace[$i]['class']) ? $trace[$i]['class'] : $trace[$i]['function'];
94                 if (preg_match('/\\\\?Psy\\\\/', $thing)) {
95                     $trace = array_slice($trace, $i + 1);
96                     break;
97                 }
98             }
99         }
100
101         for ($i = 0, $count = min($count, count($trace)); $i < $count; $i++) {
102             $class    = isset($trace[$i]['class']) ? $trace[$i]['class'] : '';
103             $type     = isset($trace[$i]['type']) ? $trace[$i]['type'] : '';
104             $function = $trace[$i]['function'];
105             $file     = isset($trace[$i]['file']) ? $this->replaceCwd($cwd, $trace[$i]['file']) : 'n/a';
106             $line     = isset($trace[$i]['line']) ? $trace[$i]['line'] : 'n/a';
107
108             $lines[] = sprintf(
109                 ' <class>%s</class>%s%s() at <info>%s:%s</info>',
110                 OutputFormatter::escape($class),
111                 OutputFormatter::escape($type),
112                 OutputFormatter::escape($function),
113                 OutputFormatter::escape($file),
114                 OutputFormatter::escape($line)
115             );
116         }
117
118         return $lines;
119     }
120
121     /**
122      * Replace the given directory from the start of a filepath.
123      *
124      * @param string $cwd
125      * @param string $file
126      *
127      * @return string
128      */
129     private function replaceCwd($cwd, $file)
130     {
131         if ($cwd === false) {
132             return $file;
133         } else {
134             return preg_replace('/^' . preg_quote($cwd, '/') . '/', '', $file);
135         }
136     }
137 }