Chris@13: colorMode = $colorMode ?: Configuration::COLOR_MODE_AUTO;
Chris@13: $this->backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
Chris@13:
Chris@13: parent::__construct();
Chris@13: }
Chris@13:
Chris@13: /**
Chris@13: * {@inheritdoc}
Chris@13: */
Chris@13: protected function configure()
Chris@13: {
Chris@13: $this
Chris@13: ->setName('whereami')
Chris@13: ->setDefinition([
Chris@13: new InputOption('num', 'n', InputOption::VALUE_OPTIONAL, 'Number of lines before and after.', '5'),
Chris@13: ])
Chris@13: ->setDescription('Show where you are in the code.')
Chris@13: ->setHelp(
Chris@13: <<<'HELP'
Chris@13: Show where you are in the code.
Chris@13:
Chris@13: Optionally, include how many lines before and after you want to display.
Chris@13:
Chris@13: e.g.
Chris@13: > whereami
Chris@13: > whereami -n10
Chris@13: HELP
Chris@13: );
Chris@13: }
Chris@13:
Chris@13: /**
Chris@13: * Obtains the correct stack frame in the full backtrace.
Chris@13: *
Chris@13: * @return array
Chris@13: */
Chris@13: protected function trace()
Chris@13: {
Chris@13: foreach (array_reverse($this->backtrace) as $stackFrame) {
Chris@13: if ($this->isDebugCall($stackFrame)) {
Chris@13: return $stackFrame;
Chris@13: }
Chris@13: }
Chris@13:
Chris@13: return end($this->backtrace);
Chris@13: }
Chris@13:
Chris@13: private static function isDebugCall(array $stackFrame)
Chris@13: {
Chris@13: $class = isset($stackFrame['class']) ? $stackFrame['class'] : null;
Chris@13: $function = isset($stackFrame['function']) ? $stackFrame['function'] : null;
Chris@13:
Chris@13: return ($class === null && $function === 'Psy\debug') ||
Chris@13: ($class === 'Psy\Shell' && in_array($function, ['__construct', 'debug']));
Chris@13: }
Chris@13:
Chris@13: /**
Chris@13: * Determine the file and line based on the specific backtrace.
Chris@13: *
Chris@13: * @return array
Chris@13: */
Chris@13: protected function fileInfo()
Chris@13: {
Chris@13: $stackFrame = $this->trace();
Chris@13: if (preg_match('/eval\(/', $stackFrame['file'])) {
Chris@13: preg_match_all('/([^\(]+)\((\d+)/', $stackFrame['file'], $matches);
Chris@13: $file = $matches[1][0];
Chris@13: $line = (int) $matches[2][0];
Chris@13: } else {
Chris@13: $file = $stackFrame['file'];
Chris@13: $line = $stackFrame['line'];
Chris@13: }
Chris@13:
Chris@13: return compact('file', 'line');
Chris@13: }
Chris@13:
Chris@13: /**
Chris@13: * {@inheritdoc}
Chris@13: */
Chris@13: protected function execute(InputInterface $input, OutputInterface $output)
Chris@13: {
Chris@13: $info = $this->fileInfo();
Chris@13: $num = $input->getOption('num');
Chris@13: $factory = new ConsoleColorFactory($this->colorMode);
Chris@13: $colors = $factory->getConsoleColor();
Chris@13: $highlighter = new Highlighter($colors);
Chris@13: $contents = file_get_contents($info['file']);
Chris@13:
Chris@13: $output->startPaging();
Chris@13: $output->writeln('');
Chris@13: $output->writeln(sprintf('From %s:%s:', $this->replaceCwd($info['file']), $info['line']));
Chris@13: $output->writeln('');
Chris@13: $output->write($highlighter->getCodeSnippet($contents, $info['line'], $num, $num), ShellOutput::OUTPUT_RAW);
Chris@13: $output->stopPaging();
Chris@13: }
Chris@13:
Chris@13: /**
Chris@13: * Replace the given directory from the start of a filepath.
Chris@13: *
Chris@13: * @param string $file
Chris@13: *
Chris@13: * @return string
Chris@13: */
Chris@13: private function replaceCwd($file)
Chris@13: {
Chris@13: $cwd = getcwd();
Chris@13: if ($cwd === false) {
Chris@13: return $file;
Chris@13: }
Chris@13:
Chris@13: $cwd = rtrim($cwd, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR;
Chris@13:
Chris@13: return preg_replace('/^' . preg_quote($cwd, '/') . '/', '', $file);
Chris@13: }
Chris@13: }