X-Git-Url: http://www.aleph1.co.uk/gitweb/?p=yaffs-website;a=blobdiff_plain;f=vendor%2Fpsy%2Fpsysh%2Fsrc%2FCommand%2FTimeitCommand.php;fp=vendor%2Fpsy%2Fpsysh%2Fsrc%2FCommand%2FTimeitCommand.php;h=c59663131132c3d041991e2f1038ef5fba2c3c2e;hp=1ac9281505c25ee7d3f96a450ad0877cd6519bc7;hb=0bf8d09d2542548982e81a441b1f16e75873a04f;hpb=74df008bdbb3a11eeea356744f39b802369bda3c diff --git a/vendor/psy/psysh/src/Command/TimeitCommand.php b/vendor/psy/psysh/src/Command/TimeitCommand.php index 1ac928150..c59663131 100644 --- a/vendor/psy/psysh/src/Command/TimeitCommand.php +++ b/vendor/psy/psysh/src/Command/TimeitCommand.php @@ -11,8 +11,11 @@ namespace Psy\Command; +use PhpParser\NodeTraverser; +use PhpParser\PrettyPrinter\Standard as Printer; +use Psy\Command\TimeitCommand\TimeitVisitor; use Psy\Input\CodeArgument; -use Psy\Shell; +use Psy\ParserFactory; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; @@ -25,6 +28,29 @@ class TimeitCommand extends Command const RESULT_MSG = 'Command took %.6f seconds to complete.'; const AVG_RESULT_MSG = 'Command took %.6f seconds on average (%.6f median; %.6f total) to complete.'; + private static $start = null; + private static $times = []; + + private $parser; + private $traverser; + private $printer; + + /** + * {@inheritdoc} + */ + public function __construct($name = null) + { + $parserFactory = new ParserFactory(); + $this->parser = $parserFactory->createParser(); + + $this->traverser = new NodeTraverser(); + $this->traverser->addVisitor(new TimeitVisitor()); + + $this->printer = new Printer(); + + parent::__construct($name); + } + /** * {@inheritdoc} */ @@ -57,23 +83,113 @@ HELP $num = $input->getOption('num') ?: 1; $shell = $this->getApplication(); - $times = []; + $instrumentedCode = $this->instrumentCode($code); + + self::$times = []; + for ($i = 0; $i < $num; $i++) { - $start = microtime(true); - $_ = $shell->execute($code); - $times[] = microtime(true) - $start; + $_ = $shell->execute($instrumentedCode); + $this->ensureEndMarked(); } $shell->writeReturnValue($_); + $times = self::$times; + self::$times = []; + if ($num === 1) { - $output->writeln(sprintf(self::RESULT_MSG, $times[0])); + $output->writeln(\sprintf(self::RESULT_MSG, $times[0])); } else { - $total = array_sum($times); - rsort($times); - $median = $times[round($num / 2)]; + $total = \array_sum($times); + \rsort($times); + $median = $times[\round($num / 2)]; + + $output->writeln(\sprintf(self::AVG_RESULT_MSG, $total / $num, $median, $total)); + } + } + + /** + * Internal method for marking the start of timeit execution. + * + * A static call to this method will be injected at the start of the timeit + * input code to instrument the call. We will use the saved start time to + * more accurately calculate time elapsed during execution. + */ + public static function markStart() + { + self::$start = \microtime(true); + } + + /** + * Internal method for marking the end of timeit execution. + * + * A static call to this method is injected by TimeitVisitor at the end + * of the timeit input code to instrument the call. + * + * Note that this accepts an optional $ret parameter, which is used to pass + * the return value of the last statement back out of timeit. This saves us + * a bunch of code rewriting shenanigans. + * + * @param mixed $ret + * + * @return mixed it just passes $ret right back + */ + public static function markEnd($ret = null) + { + self::$times[] = \microtime(true) - self::$start; + self::$start = null; + + return $ret; + } + + /** + * Ensure that the end of code execution was marked. + * + * The end *should* be marked in the instrumented code, but just in case + * we'll add a fallback here. + */ + private function ensureEndMarked() + { + if (self::$start !== null) { + self::markEnd(); + } + } + + /** + * Instrument code for timeit execution. + * + * This inserts `markStart` and `markEnd` calls to ensure that (reasonably) + * accurate times are recorded for just the code being executed. + * + * @param string $code + * + * @return string + */ + private function instrumentCode($code) + { + return $this->printer->prettyPrint($this->traverser->traverse($this->parse($code))); + } + + /** + * Lex and parse a string of code into statements. + * + * @param string $code + * + * @return array Statements + */ + private function parse($code) + { + $code = 'parser->parse($code); + } catch (\PhpParser\Error $e) { + if (\strpos($e->getMessage(), 'unexpected EOF') === false) { + throw $e; + } - $output->writeln(sprintf(self::AVG_RESULT_MSG, $total / $num, $median, $total)); + // If we got an unexpected EOF, let's try it again with a semicolon. + return $this->parser->parse($code . ';'); } } }