Mercurial > hg > isophonics-drupal-site
comparison vendor/psy/psysh/src/Command/TimeitCommand.php @ 16:c2387f117808
Routine composer update
author | Chris Cannam |
---|---|
date | Tue, 10 Jul 2018 15:07:59 +0100 |
parents | 5fb285c0d0e3 |
children | 129ea1e6d783 |
comparison
equal
deleted
inserted
replaced
15:e200cb7efeb3 | 16:c2387f117808 |
---|---|
9 * file that was distributed with this source code. | 9 * file that was distributed with this source code. |
10 */ | 10 */ |
11 | 11 |
12 namespace Psy\Command; | 12 namespace Psy\Command; |
13 | 13 |
14 use PhpParser\NodeTraverser; | |
15 use PhpParser\PrettyPrinter\Standard as Printer; | |
16 use Psy\Command\TimeitCommand\TimeitVisitor; | |
14 use Psy\Input\CodeArgument; | 17 use Psy\Input\CodeArgument; |
18 use Psy\ParserFactory; | |
15 use Psy\Shell; | 19 use Psy\Shell; |
16 use Symfony\Component\Console\Input\InputInterface; | 20 use Symfony\Component\Console\Input\InputInterface; |
17 use Symfony\Component\Console\Input\InputOption; | 21 use Symfony\Component\Console\Input\InputOption; |
18 use Symfony\Component\Console\Output\OutputInterface; | 22 use Symfony\Component\Console\Output\OutputInterface; |
19 | 23 |
22 */ | 26 */ |
23 class TimeitCommand extends Command | 27 class TimeitCommand extends Command |
24 { | 28 { |
25 const RESULT_MSG = '<info>Command took %.6f seconds to complete.</info>'; | 29 const RESULT_MSG = '<info>Command took %.6f seconds to complete.</info>'; |
26 const AVG_RESULT_MSG = '<info>Command took %.6f seconds on average (%.6f median; %.6f total) to complete.</info>'; | 30 const AVG_RESULT_MSG = '<info>Command took %.6f seconds on average (%.6f median; %.6f total) to complete.</info>'; |
31 | |
32 private static $start = null; | |
33 private static $times = []; | |
34 | |
35 private $parser; | |
36 private $traverser; | |
37 private $printer; | |
38 | |
39 /** | |
40 * {@inheritdoc} | |
41 */ | |
42 public function __construct($name = null) | |
43 { | |
44 $parserFactory = new ParserFactory(); | |
45 $this->parser = $parserFactory->createParser(); | |
46 | |
47 $this->traverser = new NodeTraverser(); | |
48 $this->traverser->addVisitor(new TimeitVisitor()); | |
49 | |
50 $this->printer = new Printer(); | |
51 | |
52 parent::__construct($name); | |
53 } | |
27 | 54 |
28 /** | 55 /** |
29 * {@inheritdoc} | 56 * {@inheritdoc} |
30 */ | 57 */ |
31 protected function configure() | 58 protected function configure() |
55 { | 82 { |
56 $code = $input->getArgument('code'); | 83 $code = $input->getArgument('code'); |
57 $num = $input->getOption('num') ?: 1; | 84 $num = $input->getOption('num') ?: 1; |
58 $shell = $this->getApplication(); | 85 $shell = $this->getApplication(); |
59 | 86 |
60 $times = []; | 87 $instrumentedCode = $this->instrumentCode($code); |
88 | |
89 self::$times = []; | |
90 | |
61 for ($i = 0; $i < $num; $i++) { | 91 for ($i = 0; $i < $num; $i++) { |
62 $start = microtime(true); | 92 $_ = $shell->execute($instrumentedCode); |
63 $_ = $shell->execute($code); | 93 $this->ensureEndMarked(); |
64 $times[] = microtime(true) - $start; | |
65 } | 94 } |
66 | 95 |
67 $shell->writeReturnValue($_); | 96 $shell->writeReturnValue($_); |
97 | |
98 $times = self::$times; | |
99 self::$times = []; | |
68 | 100 |
69 if ($num === 1) { | 101 if ($num === 1) { |
70 $output->writeln(sprintf(self::RESULT_MSG, $times[0])); | 102 $output->writeln(sprintf(self::RESULT_MSG, $times[0])); |
71 } else { | 103 } else { |
72 $total = array_sum($times); | 104 $total = array_sum($times); |
74 $median = $times[round($num / 2)]; | 106 $median = $times[round($num / 2)]; |
75 | 107 |
76 $output->writeln(sprintf(self::AVG_RESULT_MSG, $total / $num, $median, $total)); | 108 $output->writeln(sprintf(self::AVG_RESULT_MSG, $total / $num, $median, $total)); |
77 } | 109 } |
78 } | 110 } |
111 | |
112 /** | |
113 * Internal method for marking the start of timeit execution. | |
114 * | |
115 * A static call to this method will be injected at the start of the timeit | |
116 * input code to instrument the call. We will use the saved start time to | |
117 * more accurately calculate time elapsed during execution. | |
118 */ | |
119 public static function markStart() | |
120 { | |
121 self::$start = microtime(true); | |
122 } | |
123 | |
124 /** | |
125 * Internal method for marking the end of timeit execution. | |
126 * | |
127 * A static call to this method is injected by TimeitVisitor at the end | |
128 * of the timeit input code to instrument the call. | |
129 * | |
130 * Note that this accepts an optional $ret parameter, which is used to pass | |
131 * the return value of the last statement back out of timeit. This saves us | |
132 * a bunch of code rewriting shenanigans. | |
133 * | |
134 * @param mixed $ret | |
135 * | |
136 * @return mixed it just passes $ret right back | |
137 */ | |
138 public static function markEnd($ret = null) | |
139 { | |
140 self::$times[] = microtime(true) - self::$start; | |
141 self::$start = null; | |
142 | |
143 return $ret; | |
144 } | |
145 | |
146 /** | |
147 * Ensure that the end of code execution was marked. | |
148 * | |
149 * The end *should* be marked in the instrumented code, but just in case | |
150 * we'll add a fallback here. | |
151 */ | |
152 private function ensureEndMarked() | |
153 { | |
154 if (self::$start !== null) { | |
155 self::markEnd(); | |
156 } | |
157 } | |
158 | |
159 /** | |
160 * Instrument code for timeit execution. | |
161 * | |
162 * This inserts `markStart` and `markEnd` calls to ensure that (reasonably) | |
163 * accurate times are recorded for just the code being executed. | |
164 * | |
165 * @param string $code | |
166 * | |
167 * @return string | |
168 */ | |
169 private function instrumentCode($code) | |
170 { | |
171 return $this->printer->prettyPrint($this->traverser->traverse($this->parse($code))); | |
172 } | |
173 | |
174 /** | |
175 * Lex and parse a string of code into statements. | |
176 * | |
177 * @param string $code | |
178 * | |
179 * @return array Statements | |
180 */ | |
181 private function parse($code) | |
182 { | |
183 $code = '<?php ' . $code; | |
184 | |
185 try { | |
186 return $this->parser->parse($code); | |
187 } catch (\PhpParser\Error $e) { | |
188 if (strpos($e->getMessage(), 'unexpected EOF') === false) { | |
189 throw $e; | |
190 } | |
191 | |
192 // If we got an unexpected EOF, let's try it again with a semicolon. | |
193 return $this->parser->parse($code . ';'); | |
194 } | |
195 } | |
79 } | 196 } |