upgraded dependencies

This commit is contained in:
RafficMohammed
2023-01-08 01:59:16 +05:30
parent 51056e3aad
commit f9ae387337
6895 changed files with 133617 additions and 178680 deletions

View File

@@ -3,7 +3,7 @@
/*
* This file is part of Psy Shell.
*
* (c) 2012-2018 Justin Hileman
* (c) 2012-2022 Justin Hileman
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
@@ -11,6 +11,7 @@
namespace Psy\Command;
use Psy\Exception\RuntimeException;
use Psy\Output\ShellOutput;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
@@ -46,12 +47,19 @@ HELP
/**
* {@inheritdoc}
*
* @return int 0 if everything went fine, or an exit code
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
$buf = $this->getApplication()->getCodeBuffer();
$app = $this->getApplication();
if (!$app instanceof \Psy\Shell) {
throw new RuntimeException('Buffer command requires a \Psy\Shell application');
}
$buf = $app->getCodeBuffer();
if ($input->getOption('clear')) {
$this->getApplication()->resetCodeBuffer();
$app->resetCodeBuffer();
$output->writeln($this->formatLines($buf, 'urgent'), ShellOutput::NUMBER_LINES);
} else {
$output->writeln($this->formatLines($buf), ShellOutput::NUMBER_LINES);
@@ -68,7 +76,7 @@ HELP
*
* @return array Formatted strings
*/
protected function formatLines(array $lines, $type = 'return')
protected function formatLines(array $lines, string $type = 'return'): array
{
$template = \sprintf('<%s>%%s</%s>', $type, $type);

View File

@@ -3,7 +3,7 @@
/*
* This file is part of Psy Shell.
*
* (c) 2012-2018 Justin Hileman
* (c) 2012-2022 Justin Hileman
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
@@ -41,6 +41,8 @@ HELP
/**
* {@inheritdoc}
*
* @return int 0 if everything went fine, or an exit code
*/
protected function execute(InputInterface $input, OutputInterface $output)
{

View File

@@ -3,7 +3,7 @@
/*
* This file is part of Psy Shell.
*
* (c) 2012-2018 Justin Hileman
* (c) 2012-2022 Justin Hileman
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
@@ -27,7 +27,7 @@ abstract class Command extends BaseCommand
/**
* Sets the application instance for this command.
*
* @param Application $application An Application instance
* @param Application|null $application An Application instance
*
* @api
*/
@@ -43,11 +43,11 @@ abstract class Command extends BaseCommand
/**
* {@inheritdoc}
*/
public function asText()
public function asText(): string
{
$messages = [
'<comment>Usage:</comment>',
' ' . $this->getSynopsis(),
' '.$this->getSynopsis(),
'',
];
@@ -65,7 +65,7 @@ abstract class Command extends BaseCommand
if ($help = $this->getProcessedHelp()) {
$messages[] = '<comment>Help:</comment>';
$messages[] = ' ' . \str_replace("\n", "\n ", $help) . "\n";
$messages[] = ' '.\str_replace("\n", "\n ", $help)."\n";
}
return \implode("\n", $messages);
@@ -74,7 +74,7 @@ abstract class Command extends BaseCommand
/**
* {@inheritdoc}
*/
private function getArguments()
private function getArguments(): array
{
$hidden = $this->getHiddenArguments();
@@ -88,7 +88,7 @@ abstract class Command extends BaseCommand
*
* @return array
*/
protected function getHiddenArguments()
protected function getHiddenArguments(): array
{
return ['command'];
}
@@ -96,7 +96,7 @@ abstract class Command extends BaseCommand
/**
* {@inheritdoc}
*/
private function getOptions()
private function getOptions(): array
{
$hidden = $this->getHiddenOptions();
@@ -110,7 +110,7 @@ abstract class Command extends BaseCommand
*
* @return array
*/
protected function getHiddenOptions()
protected function getHiddenOptions(): array
{
return ['verbose'];
}
@@ -120,9 +120,9 @@ abstract class Command extends BaseCommand
*
* @return string
*/
private function aliasesAsText()
private function aliasesAsText(): string
{
return '<comment>Aliases:</comment> <info>' . \implode(', ', $this->getAliases()) . '</info>' . PHP_EOL;
return '<comment>Aliases:</comment> <info>'.\implode(', ', $this->getAliases()).'</info>'.\PHP_EOL;
}
/**
@@ -130,7 +130,7 @@ abstract class Command extends BaseCommand
*
* @return string
*/
private function argumentsAsText()
private function argumentsAsText(): string
{
$max = $this->getMaxWidth();
$messages = [];
@@ -145,15 +145,15 @@ abstract class Command extends BaseCommand
$default = '';
}
$description = \str_replace("\n", "\n" . \str_pad('', $max + 2, ' '), $argument->getDescription());
$description = \str_replace("\n", "\n".\str_pad('', $max + 2, ' '), $argument->getDescription());
$messages[] = \sprintf(" <info>%-${max}s</info> %s%s", $argument->getName(), $description, $default);
$messages[] = \sprintf(" <info>%-{$max}s</info> %s%s", $argument->getName(), $description, $default);
}
$messages[] = '';
}
return \implode(PHP_EOL, $messages);
return \implode(\PHP_EOL, $messages);
}
/**
@@ -161,7 +161,7 @@ abstract class Command extends BaseCommand
*
* @return string
*/
private function optionsAsText()
private function optionsAsText(): string
{
$max = $this->getMaxWidth();
$messages = [];
@@ -178,12 +178,12 @@ abstract class Command extends BaseCommand
}
$multiple = $option->isArray() ? '<comment> (multiple values allowed)</comment>' : '';
$description = \str_replace("\n", "\n" . \str_pad('', $max + 2, ' '), $option->getDescription());
$description = \str_replace("\n", "\n".\str_pad('', $max + 2, ' '), $option->getDescription());
$optionMax = $max - \strlen($option->getName()) - 2;
$messages[] = \sprintf(
" <info>%s</info> %-${optionMax}s%s%s%s",
'--' . $option->getName(),
" <info>%s</info> %-{$optionMax}s%s%s%s",
'--'.$option->getName(),
$option->getShortcut() ? \sprintf('(-%s) ', $option->getShortcut()) : '',
$description,
$default,
@@ -194,7 +194,7 @@ abstract class Command extends BaseCommand
$messages[] = '';
}
return \implode(PHP_EOL, $messages);
return \implode(\PHP_EOL, $messages);
}
/**
@@ -202,7 +202,7 @@ abstract class Command extends BaseCommand
*
* @return int
*/
private function getMaxWidth()
private function getMaxWidth(): int
{
$max = 0;
@@ -229,10 +229,10 @@ abstract class Command extends BaseCommand
*
* @return string
*/
private function formatDefaultValue($default)
private function formatDefaultValue($default): string
{
if (\is_array($default) && $default === \array_values($default)) {
return \sprintf("array('%s')", \implode("', '", $default));
return \sprintf("['%s']", \implode("', '", $default));
}
return \str_replace("\n", '', \var_export($default, true));
@@ -247,15 +247,22 @@ abstract class Command extends BaseCommand
*/
protected function getTable(OutputInterface $output)
{
if (!\class_exists('Symfony\Component\Console\Helper\Table')) {
if (!\class_exists(Table::class)) {
return $this->getTableHelper();
}
$style = new TableStyle();
$style
->setVerticalBorderChar(' ')
->setHorizontalBorderChar('')
->setCrossingChar('');
// Symfony 4.1 deprecated single-argument style setters.
if (\method_exists($style, 'setVerticalBorderChars')) {
$style->setVerticalBorderChars(' ');
$style->setHorizontalBorderChars('');
$style->setCrossingChars('', '', '', '', '', '', '', '', '');
} else {
$style->setVerticalBorderChar(' ');
$style->setHorizontalBorderChar('');
$style->setCrossingChar('');
}
$table = new Table($output);
@@ -269,7 +276,7 @@ abstract class Command extends BaseCommand
*
* @return TableHelper
*/
protected function getTableHelper()
protected function getTableHelper(): TableHelper
{
$table = $this->getApplication()->getHelperSet()->get('table');

View File

@@ -3,7 +3,7 @@
/*
* This file is part of Psy Shell.
*
* (c) 2012-2018 Justin Hileman
* (c) 2012-2022 Justin Hileman
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
@@ -14,8 +14,12 @@ namespace Psy\Command;
use Psy\Formatter\DocblockFormatter;
use Psy\Formatter\SignatureFormatter;
use Psy\Input\CodeArgument;
use Psy\Output\ShellOutput;
use Psy\Reflection\ReflectionClassConstant;
use Psy\Reflection\ReflectionConstant_;
use Psy\Reflection\ReflectionLanguageConstruct;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
/**
@@ -23,6 +27,8 @@ use Symfony\Component\Console\Output\OutputInterface;
*/
class DocCommand extends ReflectingCommand
{
const INHERIT_DOC_TAG = '{@inheritdoc}';
/**
* {@inheritdoc}
*/
@@ -32,6 +38,7 @@ class DocCommand extends ReflectingCommand
->setName('doc')
->setAliases(['rtfm', 'man'])
->setDefinition([
new InputOption('all', 'a', InputOption::VALUE_NONE, 'Show documentation for superclasses as well as the current class.'),
new CodeArgument('target', CodeArgument::REQUIRED, 'Function, class, instance, constant, method or property to document.'),
])
->setDescription('Read the documentation for an object, class, constant, method or property.')
@@ -53,6 +60,8 @@ HELP
/**
* {@inheritdoc}
*
* @return int 0 if everything went fine, or an exit code
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
@@ -67,18 +76,51 @@ HELP
$db = $this->getApplication()->getManualDb();
$output->page(function ($output) use ($reflector, $doc, $db) {
$output->writeln(SignatureFormatter::format($reflector));
$output->writeln('');
if ($output instanceof ShellOutput) {
$output->startPaging();
}
if (empty($doc) && !$db) {
$output->writeln('<warning>PHP manual not found</warning>');
$output->writeln(' To document core PHP functionality, download the PHP reference manual:');
$output->writeln(' https://github.com/bobthecow/psysh/wiki/PHP-manual');
} else {
$output->writeln($doc);
// Maybe include the declaring class
if ($reflector instanceof \ReflectionMethod || $reflector instanceof \ReflectionProperty) {
$output->writeln(SignatureFormatter::format($reflector->getDeclaringClass()));
}
$output->writeln(SignatureFormatter::format($reflector));
$output->writeln('');
if (empty($doc) && !$db) {
$output->writeln('<warning>PHP manual not found</warning>');
$output->writeln(' To document core PHP functionality, download the PHP reference manual:');
$output->writeln(' https://github.com/bobthecow/psysh/wiki/PHP-manual');
} else {
$output->writeln($doc);
}
// Implicit --all if the original docblock has an {@inheritdoc} tag.
if ($input->getOption('all') || \stripos($doc, self::INHERIT_DOC_TAG) !== false) {
$parent = $reflector;
foreach ($this->getParentReflectors($reflector) as $parent) {
$output->writeln('');
$output->writeln('---');
$output->writeln('');
// Maybe include the declaring class
if ($parent instanceof \ReflectionMethod || $parent instanceof \ReflectionProperty) {
$output->writeln(SignatureFormatter::format($parent->getDeclaringClass()));
}
$output->writeln(SignatureFormatter::format($parent));
$output->writeln('');
if ($doc = $this->getManualDoc($parent) ?: DocblockFormatter::format($parent)) {
$output->writeln($doc);
}
}
});
}
if ($output instanceof ShellOutput) {
$output->stopPaging();
}
// Set some magic local variables
$this->setCommandScopeVariables($reflector);
@@ -89,29 +131,29 @@ HELP
private function getManualDoc($reflector)
{
switch (\get_class($reflector)) {
case 'ReflectionClass':
case 'ReflectionObject':
case 'ReflectionFunction':
case \ReflectionClass::class:
case \ReflectionObject::class:
case \ReflectionFunction::class:
$id = $reflector->name;
break;
case 'ReflectionMethod':
$id = $reflector->class . '::' . $reflector->name;
case \ReflectionMethod::class:
$id = $reflector->class.'::'.$reflector->name;
break;
case 'ReflectionProperty':
$id = $reflector->class . '::$' . $reflector->name;
case \ReflectionProperty::class:
$id = $reflector->class.'::$'.$reflector->name;
break;
case 'ReflectionClassConstant':
case 'Psy\Reflection\ReflectionClassConstant':
case \ReflectionClassConstant::class:
case ReflectionClassConstant::class:
// @todo this is going to collide with ReflectionMethod ids
// someday... start running the query by id + type if the DB
// supports it.
$id = $reflector->class . '::' . $reflector->name;
$id = $reflector->class.'::'.$reflector->name;
break;
case 'Psy\Reflection\ReflectionConstant_':
case ReflectionConstant_::class:
$id = $reflector->name;
break;
@@ -122,12 +164,91 @@ HELP
return $this->getManualDocById($id);
}
/**
* Get all all parent Reflectors for a given Reflector.
*
* For example, passing a Class, Object or TraitReflector will yield all
* traits and parent classes. Passing a Method or PropertyReflector will
* yield Reflectors for the same-named method or property on all traits and
* parent classes.
*
* @return \Generator a whole bunch of \Reflector instances
*/
private function getParentReflectors($reflector): \Generator
{
$seenClasses = [];
switch (\get_class($reflector)) {
case \ReflectionClass::class:
case \ReflectionObject::class:
foreach ($reflector->getTraits() as $trait) {
if (!\in_array($trait->getName(), $seenClasses)) {
$seenClasses[] = $trait->getName();
yield $trait;
}
}
foreach ($reflector->getInterfaces() as $interface) {
if (!\in_array($interface->getName(), $seenClasses)) {
$seenClasses[] = $interface->getName();
yield $interface;
}
}
while ($reflector = $reflector->getParentClass()) {
yield $reflector;
foreach ($reflector->getTraits() as $trait) {
if (!\in_array($trait->getName(), $seenClasses)) {
$seenClasses[] = $trait->getName();
yield $trait;
}
}
foreach ($reflector->getInterfaces() as $interface) {
if (!\in_array($interface->getName(), $seenClasses)) {
$seenClasses[] = $interface->getName();
yield $interface;
}
}
}
return;
case \ReflectionMethod::class:
foreach ($this->getParentReflectors($reflector->getDeclaringClass()) as $parent) {
if ($parent->hasMethod($reflector->getName())) {
$parentMethod = $parent->getMethod($reflector->getName());
if (!\in_array($parentMethod->getDeclaringClass()->getName(), $seenClasses)) {
$seenClasses[] = $parentMethod->getDeclaringClass()->getName();
yield $parentMethod;
}
}
}
return;
case \ReflectionProperty::class:
foreach ($this->getParentReflectors($reflector->getDeclaringClass()) as $parent) {
if ($parent->hasProperty($reflector->getName())) {
$parentProperty = $parent->getProperty($reflector->getName());
if (!\in_array($parentProperty->getDeclaringClass()->getName(), $seenClasses)) {
$seenClasses[] = $parentProperty->getDeclaringClass()->getName();
yield $parentProperty;
}
}
}
break;
}
}
private function getManualDocById($id)
{
if ($db = $this->getApplication()->getManualDb()) {
return $db
->query(\sprintf('SELECT doc FROM php_manual WHERE id = %s', $db->quote($id)))
->fetchColumn(0);
$result = $db->query(\sprintf('SELECT doc FROM php_manual WHERE id = %s', $db->quote($id)));
if ($result !== false) {
return $result->fetchColumn(0);
}
}
}
}

View File

@@ -3,7 +3,7 @@
/*
* This file is part of Psy Shell.
*
* (c) 2012-2018 Justin Hileman
* (c) 2012-2022 Justin Hileman
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
@@ -66,10 +66,12 @@ HELP
/**
* {@inheritdoc}
*
* @return int 0 if everything went fine, or an exit code
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
$depth = $input->getOption('depth');
$depth = $input->getOption('depth');
$target = $this->resolveCode($input->getArgument('target'));
$output->page($this->presenter->present($target, $depth, $input->getOption('all') ? Presenter::VERBOSE : 0));
@@ -87,9 +89,9 @@ HELP
*
* @return mixed
*/
protected function resolveTarget($name)
protected function resolveTarget(string $name)
{
@\trigger_error('`resolveTarget` is deprecated; use `resolveCode` instead.', E_USER_DEPRECATED);
@\trigger_error('`resolveTarget` is deprecated; use `resolveCode` instead.', \E_USER_DEPRECATED);
return $this->resolveCode($name);
}

View File

@@ -3,7 +3,7 @@
/*
* This file is part of Psy Shell.
*
* (c) 2012-2018 Justin Hileman
* (c) 2012-2022 Justin Hileman
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
@@ -74,6 +74,8 @@ class EditCommand extends Command implements ContextAware
* @param InputInterface $input
* @param OutputInterface $output
*
* @return int 0 if everything went fine, or an exit code
*
* @throws \InvalidArgumentException when both exec and no-exec flags are given or if a given variable is not found in the current context
* @throws \UnexpectedValueException if file_get_contents on the edited file returns false instead of a string
*/
@@ -115,7 +117,7 @@ class EditCommand extends Command implements ContextAware
*
* @return bool
*/
private function shouldExecuteFile($execOption, $noExecOption, $filePath)
private function shouldExecuteFile(bool $execOption, bool $noExecOption, string $filePath = null): bool
{
if ($execOption) {
return true;
@@ -136,11 +138,11 @@ class EditCommand extends Command implements ContextAware
*
* @throws \InvalidArgumentException If the variable is not found in the current context
*/
private function extractFilePath($fileArgument)
private function extractFilePath(string $fileArgument = null)
{
// If the file argument was a variable, get it from the context
if ($fileArgument !== null &&
\strlen($fileArgument) > 0 &&
$fileArgument !== '' &&
$fileArgument[0] === '$') {
$fileArgument = $this->context->get(\preg_replace('/^\$/', '', $fileArgument));
}
@@ -150,18 +152,19 @@ class EditCommand extends Command implements ContextAware
/**
* @param string $filePath
* @param string $shouldRemoveFile
* @param bool $shouldRemoveFile
*
* @return string
*
* @throws \UnexpectedValueException if file_get_contents on $filePath returns false instead of a string
*/
private function editFile($filePath, $shouldRemoveFile)
private function editFile(string $filePath, bool $shouldRemoveFile): string
{
$escapedFilePath = \escapeshellarg($filePath);
$editor = (isset($_SERVER['EDITOR']) && $_SERVER['EDITOR']) ? $_SERVER['EDITOR'] : 'nano';
$pipes = [];
$proc = \proc_open((\getenv('EDITOR') ?: 'nano') . " {$escapedFilePath}", [STDIN, STDOUT, STDERR], $pipes);
$proc = \proc_open("{$editor} {$escapedFilePath}", [\STDIN, \STDOUT, \STDERR], $pipes);
\proc_close($proc);
$editedContent = @\file_get_contents($filePath);

View File

@@ -3,7 +3,7 @@
/*
* This file is part of Psy Shell.
*
* (c) 2012-2018 Justin Hileman
* (c) 2012-2022 Justin Hileman
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
@@ -44,6 +44,8 @@ HELP
/**
* {@inheritdoc}
*
* @return int 0 if everything went fine, or an exit code
*/
protected function execute(InputInterface $input, OutputInterface $output)
{

View File

@@ -3,7 +3,7 @@
/*
* This file is part of Psy Shell.
*
* (c) 2012-2018 Justin Hileman
* (c) 2012-2022 Justin Hileman
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
@@ -11,6 +11,7 @@
namespace Psy\Command;
use Psy\Output\ShellOutput;
use Symfony\Component\Console\Helper\TableHelper;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
@@ -45,13 +46,15 @@ class HelpCommand extends Command
*
* @param Command $command
*/
public function setCommand($command)
public function setCommand(Command $command)
{
$this->command = $command;
}
/**
* {@inheritdoc}
*
* @return int 0 if everything went fine, or an exit code
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
@@ -86,13 +89,19 @@ class HelpCommand extends Command
]);
}
$output->startPaging();
if ($output instanceof ShellOutput) {
$output->startPaging();
}
if ($table instanceof TableHelper) {
$table->render($output);
} else {
$table->render();
}
$output->stopPaging();
if ($output instanceof ShellOutput) {
$output->stopPaging();
}
}
return 0;

View File

@@ -3,7 +3,7 @@
/*
* This file is part of Psy Shell.
*
* (c) 2012-2018 Justin Hileman
* (c) 2012-2022 Justin Hileman
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
@@ -60,19 +60,19 @@ class HistoryCommand extends Command
->setName('history')
->setAliases(['hist'])
->setDefinition([
new InputOption('show', 's', InputOption::VALUE_REQUIRED, 'Show the given range of lines.'),
new InputOption('head', 'H', InputOption::VALUE_REQUIRED, 'Display the first N items.'),
new InputOption('tail', 'T', InputOption::VALUE_REQUIRED, 'Display the last N items.'),
new InputOption('show', 's', InputOption::VALUE_REQUIRED, 'Show the given range of lines.'),
new InputOption('head', 'H', InputOption::VALUE_REQUIRED, 'Display the first N items.'),
new InputOption('tail', 'T', InputOption::VALUE_REQUIRED, 'Display the last N items.'),
$grep,
$insensitive,
$invert,
new InputOption('no-numbers', 'N', InputOption::VALUE_NONE, 'Omit line numbers.'),
new InputOption('no-numbers', 'N', InputOption::VALUE_NONE, 'Omit line numbers.'),
new InputOption('save', '', InputOption::VALUE_REQUIRED, 'Save history to a file.'),
new InputOption('replay', '', InputOption::VALUE_NONE, 'Replay.'),
new InputOption('clear', '', InputOption::VALUE_NONE, 'Clear the history.'),
new InputOption('save', '', InputOption::VALUE_REQUIRED, 'Save history to a file.'),
new InputOption('replay', '', InputOption::VALUE_NONE, 'Replay.'),
new InputOption('clear', '', InputOption::VALUE_NONE, 'Clear the history.'),
])
->setDescription('Show the Psy Shell history.')
->setHelp(
@@ -90,6 +90,8 @@ HELP
/**
* {@inheritdoc}
*
* @return int 0 if everything went fine, or an exit code
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
@@ -105,14 +107,14 @@ HELP
$this->filter->bind($input);
if ($this->filter->hasFilter()) {
$matches = [];
$matches = [];
$highlighted = [];
foreach ($history as $i => $line) {
if ($this->filter->match($line, $matches)) {
if (isset($matches[0])) {
$chunks = \explode($matches[0], $history[$i]);
$chunks = \array_map([__CLASS__, 'escape'], $chunks);
$glue = \sprintf('<urgent>%s</urgent>', self::escape($matches[0]));
$glue = \sprintf('<urgent>%s</urgent>', self::escape($matches[0]));
$highlighted[$i] = \implode($glue, $chunks);
}
@@ -124,7 +126,7 @@ HELP
if ($save = $input->getOption('save')) {
$output->writeln(\sprintf('Saving history in %s...', $save));
\file_put_contents($save, \implode(PHP_EOL, $history) . PHP_EOL);
\file_put_contents($save, \implode(\PHP_EOL, $history).\PHP_EOL);
$output->writeln('<info>History saved.</info>');
} elseif ($input->getOption('replay')) {
if (!($input->getOption('show') || $input->getOption('head') || $input->getOption('tail'))) {
@@ -156,7 +158,7 @@ HELP
*
* @return array [ start, end ]
*/
private function extractRange($range)
private function extractRange(string $range): array
{
if (\preg_match('/^\d+$/', $range)) {
return [$range, $range + 1];
@@ -164,25 +166,25 @@ HELP
$matches = [];
if ($range !== '..' && \preg_match('/^(\d*)\.\.(\d*)$/', $range, $matches)) {
$start = $matches[1] ? \intval($matches[1]) : 0;
$end = $matches[2] ? \intval($matches[2]) + 1 : PHP_INT_MAX;
$start = $matches[1] ? (int) $matches[1] : 0;
$end = $matches[2] ? (int) $matches[2] + 1 : \PHP_INT_MAX;
return [$start, $end];
}
throw new \InvalidArgumentException('Unexpected range: ' . $range);
throw new \InvalidArgumentException('Unexpected range: '.$range);
}
/**
* Retrieve a slice of the readline history.
*
* @param string $show
* @param string $head
* @param string $tail
* @param string|null $show
* @param string|null $head
* @param string|null $tail
*
* @return array A slilce of history
* @return array A slice of history
*/
private function getHistorySlice($show, $head, $tail)
private function getHistorySlice($show, $head, $tail): array
{
$history = $this->readline->listHistory();
@@ -197,15 +199,15 @@ HELP
throw new \InvalidArgumentException('Please specify an integer argument for --head');
}
$start = 0;
$length = \intval($head);
$start = 0;
$length = (int) $head;
} elseif ($tail) {
if (!\preg_match('/^\d+$/', $tail)) {
throw new \InvalidArgumentException('Please specify an integer argument for --tail');
}
$start = \count($history) - $tail;
$length = \intval($tail) + 1;
$start = \count($history) - $tail;
$length = (int) $tail + 1;
} else {
return $history;
}
@@ -229,7 +231,7 @@ HELP
}
if ($count > 1) {
throw new \InvalidArgumentException('Please specify only one of --' . \implode(', --', $options));
throw new \InvalidArgumentException('Please specify only one of --'.\implode(', --', $options));
}
}
@@ -241,7 +243,7 @@ HELP
$this->readline->clearHistory();
}
public static function escape($string)
public static function escape(string $string): string
{
return OutputFormatter::escape($string);
}

View File

@@ -3,7 +3,7 @@
/*
* This file is part of Psy Shell.
*
* (c) 2012-2018 Justin Hileman
* (c) 2012-2022 Justin Hileman
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
@@ -22,6 +22,7 @@ use Psy\Command\ListCommand\VariableEnumerator;
use Psy\Exception\RuntimeException;
use Psy\Input\CodeArgument;
use Psy\Input\FilterOptions;
use Psy\Output\ShellOutput;
use Psy\VarDumper\Presenter;
use Psy\VarDumper\PresenterAware;
use Symfony\Component\Console\Formatter\OutputFormatter;
@@ -57,33 +58,33 @@ class ListCommand extends ReflectingCommand implements PresenterAware
$this
->setName('ls')
->setAliases(['list', 'dir'])
->setAliases(['dir'])
->setDefinition([
new CodeArgument('target', CodeArgument::OPTIONAL, 'A target class or object to list.'),
new InputOption('vars', '', InputOption::VALUE_NONE, 'Display variables.'),
new InputOption('constants', 'c', InputOption::VALUE_NONE, 'Display defined constants.'),
new InputOption('functions', 'f', InputOption::VALUE_NONE, 'Display defined functions.'),
new InputOption('classes', 'k', InputOption::VALUE_NONE, 'Display declared classes.'),
new InputOption('interfaces', 'I', InputOption::VALUE_NONE, 'Display declared interfaces.'),
new InputOption('traits', 't', InputOption::VALUE_NONE, 'Display declared traits.'),
new InputOption('vars', '', InputOption::VALUE_NONE, 'Display variables.'),
new InputOption('constants', 'c', InputOption::VALUE_NONE, 'Display defined constants.'),
new InputOption('functions', 'f', InputOption::VALUE_NONE, 'Display defined functions.'),
new InputOption('classes', 'k', InputOption::VALUE_NONE, 'Display declared classes.'),
new InputOption('interfaces', 'I', InputOption::VALUE_NONE, 'Display declared interfaces.'),
new InputOption('traits', 't', InputOption::VALUE_NONE, 'Display declared traits.'),
new InputOption('no-inherit', '', InputOption::VALUE_NONE, 'Exclude inherited methods, properties and constants.'),
new InputOption('no-inherit', '', InputOption::VALUE_NONE, 'Exclude inherited methods, properties and constants.'),
new InputOption('properties', 'p', InputOption::VALUE_NONE, 'Display class or object properties (public properties by default).'),
new InputOption('methods', 'm', InputOption::VALUE_NONE, 'Display class or object methods (public methods by default).'),
new InputOption('properties', 'p', InputOption::VALUE_NONE, 'Display class or object properties (public properties by default).'),
new InputOption('methods', 'm', InputOption::VALUE_NONE, 'Display class or object methods (public methods by default).'),
$grep,
$insensitive,
$invert,
new InputOption('globals', 'g', InputOption::VALUE_NONE, 'Include global variables.'),
new InputOption('internal', 'n', InputOption::VALUE_NONE, 'Limit to internal functions and classes.'),
new InputOption('user', 'u', InputOption::VALUE_NONE, 'Limit to user-defined constants, functions and classes.'),
new InputOption('category', 'C', InputOption::VALUE_REQUIRED, 'Limit to constants in a specific category (e.g. "date").'),
new InputOption('globals', 'g', InputOption::VALUE_NONE, 'Include global variables.'),
new InputOption('internal', 'n', InputOption::VALUE_NONE, 'Limit to internal functions and classes.'),
new InputOption('user', 'u', InputOption::VALUE_NONE, 'Limit to user-defined constants, functions and classes.'),
new InputOption('category', 'C', InputOption::VALUE_REQUIRED, 'Limit to constants in a specific category (e.g. "date").'),
new InputOption('all', 'a', InputOption::VALUE_NONE, 'Include private and protected methods and properties.'),
new InputOption('long', 'l', InputOption::VALUE_NONE, 'List in long format: includes class names and method signatures.'),
new InputOption('all', 'a', InputOption::VALUE_NONE, 'Include private and protected methods and properties.'),
new InputOption('long', 'l', InputOption::VALUE_NONE, 'List in long format: includes class names and method signatures.'),
])
->setDescription('List local, instance or class variables, methods and constants.')
->setHelp(
@@ -111,6 +112,8 @@ HELP
/**
* {@inheritdoc}
*
* @return int 0 if everything went fine, or an exit code
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
@@ -126,7 +129,7 @@ HELP
}
// @todo something cleaner than this :-/
if ($input->getOption('long')) {
if ($output instanceof ShellOutput && $input->getOption('long')) {
$output->startPaging();
}
@@ -134,7 +137,7 @@ HELP
$this->$method($output, $enumerator->enumerate($input, $reflector, $target));
}
if ($input->getOption('long')) {
if ($output instanceof ShellOutput && $input->getOption('long')) {
$output->stopPaging();
}
@@ -171,11 +174,11 @@ HELP
* Write the list items to $output.
*
* @param OutputInterface $output
* @param null|array $result List of enumerated items
* @param array $result List of enumerated items
*/
protected function write(OutputInterface $output, array $result = null)
protected function write(OutputInterface $output, array $result)
{
if ($result === null) {
if (\count($result) === 0) {
return;
}
@@ -191,11 +194,11 @@ HELP
* Items are listed one per line, and include the item signature.
*
* @param OutputInterface $output
* @param null|array $result List of enumerated items
* @param array $result List of enumerated items
*/
protected function writeLong(OutputInterface $output, array $result = null)
protected function writeLong(OutputInterface $output, array $result)
{
if ($result === null) {
if (\count($result) === 0) {
return;
}
@@ -225,7 +228,7 @@ HELP
*
* @return string
*/
private function formatItemName($item)
private function formatItemName(array $item): string
{
return \sprintf('<%s>%s</%s>', $item['style'], OutputFormatter::escape($item['name']), $item['style']);
}
@@ -243,7 +246,7 @@ HELP
// if no target is passed, there can be no properties or methods
foreach (['properties', 'methods', 'no-inherit'] as $option) {
if ($input->getOption($option)) {
throw new RuntimeException('--' . $option . ' does not make sense without a specified target');
throw new RuntimeException('--'.$option.' does not make sense without a specified target');
}
}
@@ -257,22 +260,23 @@ HELP
$input->setOption('vars', true);
} else {
// if a target is passed, classes, functions, etc don't make sense
foreach (['vars', 'globals', 'functions', 'classes', 'interfaces', 'traits'] as $option) {
foreach (['vars', 'globals'] as $option) {
if ($input->getOption($option)) {
throw new RuntimeException('--' . $option . ' does not make sense with a specified target');
throw new RuntimeException('--'.$option.' does not make sense with a specified target');
}
}
foreach (['constants', 'properties', 'methods'] as $option) {
// @todo ensure that 'functions', 'classes', 'interfaces', 'traits' only accept namespace target?
foreach (['constants', 'properties', 'methods', 'functions', 'classes', 'interfaces', 'traits'] as $option) {
if ($input->getOption($option)) {
return;
}
}
// default to --constants --properties --methods if no other options are passed
$input->setOption('constants', true);
$input->setOption('constants', true);
$input->setOption('properties', true);
$input->setOption('methods', true);
$input->setOption('methods', true);
}
}
}

View File

@@ -3,7 +3,7 @@
/*
* This file is part of Psy Shell.
*
* (c) 2012-2018 Justin Hileman
* (c) 2012-2022 Justin Hileman
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
@@ -22,30 +22,29 @@ class ClassConstantEnumerator extends Enumerator
/**
* {@inheritdoc}
*/
protected function listItems(InputInterface $input, \Reflector $reflector = null, $target = null)
protected function listItems(InputInterface $input, \Reflector $reflector = null, $target = null): array
{
// only list constants when a Reflector is present.
if ($reflector === null) {
return;
return [];
}
// We can only list constants on actual class (or object) reflectors.
if (!$reflector instanceof \ReflectionClass) {
// @todo handle ReflectionExtension as well
return;
return [];
}
// only list constants if we are specifically asked
if (!$input->getOption('constants')) {
return;
return [];
}
$noInherit = $input->getOption('no-inherit');
$constants = $this->prepareConstants($this->getConstants($reflector, $noInherit));
if (empty($constants)) {
return;
return [];
}
$ret = [];
@@ -62,7 +61,7 @@ class ClassConstantEnumerator extends Enumerator
*
* @return array
*/
protected function getConstants(\Reflector $reflector, $noInherit = false)
protected function getConstants(\Reflector $reflector, bool $noInherit = false): array
{
$className = $reflector->getName();
@@ -77,7 +76,7 @@ class ClassConstantEnumerator extends Enumerator
$constants[$name] = $constReflector;
}
\ksort($constants, SORT_NATURAL | SORT_FLAG_CASE);
\ksort($constants, \SORT_NATURAL | \SORT_FLAG_CASE);
return $constants;
}
@@ -89,7 +88,7 @@ class ClassConstantEnumerator extends Enumerator
*
* @return array
*/
protected function prepareConstants(array $constants)
protected function prepareConstants(array $constants): array
{
// My kingdom for a generator.
$ret = [];
@@ -114,12 +113,10 @@ class ClassConstantEnumerator extends Enumerator
*
* @return string
*/
protected function getKindLabel(\ReflectionClass $reflector)
protected function getKindLabel(\ReflectionClass $reflector): string
{
if ($reflector->isInterface()) {
return 'Interface Constants';
} elseif (\method_exists($reflector, 'isTrait') && $reflector->isTrait()) {
return 'Trait Constants';
} else {
return 'Class Constants';
}

View File

@@ -3,7 +3,7 @@
/*
* This file is part of Psy Shell.
*
* (c) 2012-2018 Justin Hileman
* (c) 2012-2022 Justin Hileman
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
@@ -11,6 +11,7 @@
namespace Psy\Command\ListCommand;
use Psy\Reflection\ReflectionNamespace;
use Symfony\Component\Console\Input\InputInterface;
/**
@@ -21,37 +22,31 @@ class ClassEnumerator extends Enumerator
/**
* {@inheritdoc}
*/
protected function listItems(InputInterface $input, \Reflector $reflector = null, $target = null)
protected function listItems(InputInterface $input, \Reflector $reflector = null, $target = null): array
{
// only list classes when no Reflector is present.
//
// @todo make a NamespaceReflector and pass that in for commands like:
//
// ls --classes Foo
//
// ... for listing classes in the Foo namespace
if ($reflector !== null || $target !== null) {
return;
// if we have a reflector, ensure that it's a namespace reflector
if (($target !== null || $reflector !== null) && !$reflector instanceof ReflectionNamespace) {
return [];
}
$user = $input->getOption('user');
$internal = $input->getOption('internal');
$user = $input->getOption('user');
$prefix = $reflector === null ? null : \strtolower($reflector->getName()).'\\';
$ret = [];
// only list classes, interfaces and traits if we are specifically asked
if ($input->getOption('classes')) {
$ret = \array_merge($ret, $this->filterClasses('Classes', \get_declared_classes(), $internal, $user));
$ret = \array_merge($ret, $this->filterClasses('Classes', \get_declared_classes(), $internal, $user, $prefix));
}
if ($input->getOption('interfaces')) {
$ret = \array_merge($ret, $this->filterClasses('Interfaces', \get_declared_interfaces(), $internal, $user));
$ret = \array_merge($ret, $this->filterClasses('Interfaces', \get_declared_interfaces(), $internal, $user, $prefix));
}
if ($input->getOption('traits')) {
$ret = \array_merge($ret, $this->filterClasses('Traits', \get_declared_traits(), $internal, $user));
$ret = \array_merge($ret, $this->filterClasses('Traits', \get_declared_traits(), $internal, $user, $prefix));
}
return \array_map([$this, 'prepareClasses'], \array_filter($ret));
@@ -67,15 +62,20 @@ class ClassEnumerator extends Enumerator
* @param array $classes
* @param bool $internal
* @param bool $user
* @param string $prefix
*
* @return array
*/
protected function filterClasses($key, $classes, $internal, $user)
protected function filterClasses(string $key, array $classes, bool $internal, bool $user, string $prefix = null): array
{
$ret = [];
if ($internal) {
$ret['Internal ' . $key] = \array_filter($classes, function ($class) {
$ret['Internal '.$key] = \array_filter($classes, function ($class) use ($prefix) {
if ($prefix !== null && \strpos(\strtolower($class), $prefix) !== 0) {
return false;
}
$refl = new \ReflectionClass($class);
return $refl->isInternal();
@@ -83,7 +83,11 @@ class ClassEnumerator extends Enumerator
}
if ($user) {
$ret['User ' . $key] = \array_filter($classes, function ($class) {
$ret['User '.$key] = \array_filter($classes, function ($class) use ($prefix) {
if ($prefix !== null && \strpos(\strtolower($class), $prefix) !== 0) {
return false;
}
$refl = new \ReflectionClass($class);
return !$refl->isInternal();
@@ -91,7 +95,9 @@ class ClassEnumerator extends Enumerator
}
if (!$user && !$internal) {
$ret[$key] = $classes;
$ret[$key] = \array_filter($classes, function ($class) use ($prefix) {
return $prefix === null || \strpos(\strtolower($class), $prefix) === 0;
});
}
return $ret;
@@ -104,7 +110,7 @@ class ClassEnumerator extends Enumerator
*
* @return array
*/
protected function prepareClasses(array $classes)
protected function prepareClasses(array $classes): array
{
\natcasesort($classes);

View File

@@ -3,7 +3,7 @@
/*
* This file is part of Psy Shell.
*
* (c) 2012-2018 Justin Hileman
* (c) 2012-2022 Justin Hileman
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
@@ -11,6 +11,7 @@
namespace Psy\Command\ListCommand;
use Psy\Reflection\ReflectionNamespace;
use Symfony\Component\Console\Input\InputInterface;
/**
@@ -18,31 +19,64 @@ use Symfony\Component\Console\Input\InputInterface;
*/
class ConstantEnumerator extends Enumerator
{
// Because `Json` is ugly.
private static $categoryLabels = [
'libxml' => 'libxml',
'openssl' => 'OpenSSL',
'pcre' => 'PCRE',
'sqlite3' => 'SQLite3',
'curl' => 'cURL',
'dom' => 'DOM',
'ftp' => 'FTP',
'gd' => 'GD',
'gmp' => 'GMP',
'iconv' => 'iconv',
'json' => 'JSON',
'ldap' => 'LDAP',
'mbstring' => 'mbstring',
'odbc' => 'ODBC',
'pcntl' => 'PCNTL',
'pgsql' => 'pgsql',
'posix' => 'POSIX',
'mysqli' => 'mysqli',
'soap' => 'SOAP',
'exif' => 'EXIF',
'sysvmsg' => 'sysvmsg',
'xml' => 'XML',
'xsl' => 'XSL',
];
/**
* {@inheritdoc}
*/
protected function listItems(InputInterface $input, \Reflector $reflector = null, $target = null)
protected function listItems(InputInterface $input, \Reflector $reflector = null, $target = null): array
{
// only list constants when no Reflector is present.
//
// @todo make a NamespaceReflector and pass that in for commands like:
//
// ls --constants Foo
//
// ... for listing constants in the Foo namespace
if ($reflector !== null || $target !== null) {
return;
// if we have a reflector, ensure that it's a namespace reflector
if (($target !== null || $reflector !== null) && !$reflector instanceof ReflectionNamespace) {
return [];
}
// only list constants if we are specifically asked
if (!$input->getOption('constants')) {
return;
return [];
}
$user = $input->getOption('user');
$user = $input->getOption('user');
$internal = $input->getOption('internal');
$category = $input->getOption('category');
if ($category) {
$category = \strtolower($category);
if ($category === 'internal') {
$internal = true;
$category = null;
} elseif ($category === 'user') {
$user = true;
$category = null;
}
}
$ret = [];
if ($user) {
@@ -50,11 +84,12 @@ class ConstantEnumerator extends Enumerator
}
if ($internal) {
$ret['Interal Constants'] = $this->getConstants('internal');
$ret['Internal Constants'] = $this->getConstants('internal');
}
if ($category) {
$label = \ucfirst($category) . ' Constants';
$caseCategory = \array_key_exists($category, self::$categoryLabels) ? self::$categoryLabels[$category] : \ucfirst($category);
$label = $caseCategory.' Constants';
$ret[$label] = $this->getConstants($category);
}
@@ -62,6 +97,18 @@ class ConstantEnumerator extends Enumerator
$ret['Constants'] = $this->getConstants();
}
if ($reflector !== null) {
$prefix = \strtolower($reflector->getName()).'\\';
foreach ($ret as $key => $names) {
foreach (\array_keys($names) as $name) {
if (\strpos(\strtolower($name), $prefix) !== 0) {
unset($ret[$key][$name]);
}
}
}
}
return \array_map([$this, 'prepareConstants'], \array_filter($ret));
}
@@ -75,7 +122,7 @@ class ConstantEnumerator extends Enumerator
*
* @return array
*/
protected function getConstants($category = null)
protected function getConstants(string $category = null): array
{
if (!$category) {
return \get_defined_constants();
@@ -86,10 +133,16 @@ class ConstantEnumerator extends Enumerator
if ($category === 'internal') {
unset($consts['user']);
return \call_user_func_array('array_merge', $consts);
return \array_merge(...\array_values($consts));
}
return isset($consts[$category]) ? $consts[$category] : [];
foreach ($consts as $key => $value) {
if (\strtolower($key) === $category) {
return $value;
}
}
return [];
}
/**
@@ -99,7 +152,7 @@ class ConstantEnumerator extends Enumerator
*
* @return array
*/
protected function prepareConstants(array $constants)
protected function prepareConstants(array $constants): array
{
// My kingdom for a generator.
$ret = [];

View File

@@ -3,7 +3,7 @@
/*
* This file is part of Psy Shell.
*
* (c) 2012-2018 Justin Hileman
* (c) 2012-2022 Justin Hileman
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
@@ -23,13 +23,13 @@ use Symfony\Component\Console\Input\InputInterface;
abstract class Enumerator
{
// Output styles
const IS_PUBLIC = 'public';
const IS_PUBLIC = 'public';
const IS_PROTECTED = 'protected';
const IS_PRIVATE = 'private';
const IS_GLOBAL = 'global';
const IS_CONSTANT = 'const';
const IS_CLASS = 'class';
const IS_FUNCTION = 'function';
const IS_PRIVATE = 'private';
const IS_GLOBAL = 'global';
const IS_CONSTANT = 'const';
const IS_CLASS = 'class';
const IS_FUNCTION = 'function';
private $filter;
private $presenter;
@@ -48,13 +48,13 @@ abstract class Enumerator
/**
* Return a list of categorized things with the given input options and target.
*
* @param InputInterface $input
* @param \Reflector $reflector
* @param mixed $target
* @param InputInterface $input
* @param \Reflector|null $reflector
* @param mixed $target
*
* @return array
*/
public function enumerate(InputInterface $input, \Reflector $reflector = null, $target = null)
public function enumerate(InputInterface $input, \Reflector $reflector = null, $target = null): array
{
$this->filter->bind($input);
@@ -76,13 +76,13 @@ abstract class Enumerator
* ],
* ]
*
* @param InputInterface $input
* @param \Reflector $reflector
* @param mixed $target
* @param InputInterface $input
* @param \Reflector|null $reflector
* @param mixed $target
*
* @return array
*/
abstract protected function listItems(InputInterface $input, \Reflector $reflector = null, $target = null);
abstract protected function listItems(InputInterface $input, \Reflector $reflector = null, $target = null): array;
protected function showItem($name)
{

View File

@@ -3,7 +3,7 @@
/*
* This file is part of Psy Shell.
*
* (c) 2012-2018 Justin Hileman
* (c) 2012-2022 Justin Hileman
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
@@ -11,6 +11,7 @@
namespace Psy\Command\ListCommand;
use Psy\Reflection\ReflectionNamespace;
use Symfony\Component\Console\Input\InputInterface;
/**
@@ -21,40 +22,34 @@ class FunctionEnumerator extends Enumerator
/**
* {@inheritdoc}
*/
protected function listItems(InputInterface $input, \Reflector $reflector = null, $target = null)
protected function listItems(InputInterface $input, \Reflector $reflector = null, $target = null): array
{
// only list functions when no Reflector is present.
//
// @todo make a NamespaceReflector and pass that in for commands like:
//
// ls --functions Foo
//
// ... for listing functions in the Foo namespace
if ($reflector !== null || $target !== null) {
return;
// if we have a reflector, ensure that it's a namespace reflector
if (($target !== null || $reflector !== null) && !$reflector instanceof ReflectionNamespace) {
return [];
}
// only list functions if we are specifically asked
if (!$input->getOption('functions')) {
return;
return [];
}
if ($input->getOption('user')) {
$label = 'User Functions';
$label = 'User Functions';
$functions = $this->getFunctions('user');
} elseif ($input->getOption('internal')) {
$label = 'Internal Functions';
$label = 'Internal Functions';
$functions = $this->getFunctions('internal');
} else {
$label = 'Functions';
$label = 'Functions';
$functions = $this->getFunctions();
}
$functions = $this->prepareFunctions($functions);
$prefix = $reflector === null ? null : \strtolower($reflector->getName()).'\\';
$functions = $this->prepareFunctions($functions, $prefix);
if (empty($functions)) {
return;
return [];
}
$ret = [];
@@ -68,11 +63,11 @@ class FunctionEnumerator extends Enumerator
*
* Optionally limit functions to "user" or "internal" functions.
*
* @param null|string $type "user" or "internal" (default: both)
* @param string|null $type "user" or "internal" (default: both)
*
* @return array
*/
protected function getFunctions($type = null)
protected function getFunctions(string $type = null): array
{
$funcs = \get_defined_functions();
@@ -86,11 +81,12 @@ class FunctionEnumerator extends Enumerator
/**
* Prepare formatted function array.
*
* @param array $functions
* @param array $functions
* @param string $prefix
*
* @return array
*/
protected function prepareFunctions(array $functions)
protected function prepareFunctions(array $functions, string $prefix = null): array
{
\natcasesort($functions);
@@ -98,12 +94,20 @@ class FunctionEnumerator extends Enumerator
$ret = [];
foreach ($functions as $name) {
if ($prefix !== null && \strpos(\strtolower($name), $prefix) !== 0) {
continue;
}
if ($this->showItem($name)) {
$ret[$name] = [
'name' => $name,
'style' => self::IS_FUNCTION,
'value' => $this->presentSignature($name),
];
try {
$ret[$name] = [
'name' => $name,
'style' => self::IS_FUNCTION,
'value' => $this->presentSignature($name),
];
} catch (\Throwable $e) {
// Ignore failures.
}
}
}

View File

@@ -3,7 +3,7 @@
/*
* This file is part of Psy Shell.
*
* (c) 2012-2018 Justin Hileman
* (c) 2012-2022 Justin Hileman
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
@@ -21,22 +21,22 @@ class GlobalVariableEnumerator extends Enumerator
/**
* {@inheritdoc}
*/
protected function listItems(InputInterface $input, \Reflector $reflector = null, $target = null)
protected function listItems(InputInterface $input, \Reflector $reflector = null, $target = null): array
{
// only list globals when no Reflector is present.
if ($reflector !== null || $target !== null) {
return;
return [];
}
// only list globals if we are specifically asked
if (!$input->getOption('globals')) {
return;
return [];
}
$globals = $this->prepareGlobals($this->getGlobals());
if (empty($globals)) {
return;
return [];
}
return [
@@ -49,7 +49,7 @@ class GlobalVariableEnumerator extends Enumerator
*
* @return array
*/
protected function getGlobals()
protected function getGlobals(): array
{
global $GLOBALS;
@@ -71,14 +71,14 @@ class GlobalVariableEnumerator extends Enumerator
*
* @return array
*/
protected function prepareGlobals($globals)
protected function prepareGlobals(array $globals): array
{
// My kingdom for a generator.
$ret = [];
foreach ($globals as $name => $value) {
if ($this->showItem($name)) {
$fname = '$' . $name;
$fname = '$'.$name;
$ret[$fname] = [
'name' => $fname,
'style' => self::IS_GLOBAL,

View File

@@ -1,89 +0,0 @@
<?php
/*
* This file is part of Psy Shell.
*
* (c) 2012-2018 Justin Hileman
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Psy\Command\ListCommand;
use Psy\VarDumper\Presenter;
use Symfony\Component\Console\Input\InputInterface;
/**
* Interface Enumerator class.
*
* @deprecated Nothing should use this anymore
*/
class InterfaceEnumerator extends Enumerator
{
public function __construct(Presenter $presenter)
{
@\trigger_error('InterfaceEnumerator is no longer used', E_USER_DEPRECATED);
parent::__construct($presenter);
}
/**
* {@inheritdoc}
*/
protected function listItems(InputInterface $input, \Reflector $reflector = null, $target = null)
{
// only list interfaces when no Reflector is present.
//
// @todo make a NamespaceReflector and pass that in for commands like:
//
// ls --interfaces Foo
//
// ... for listing interfaces in the Foo namespace
if ($reflector !== null || $target !== null) {
return;
}
// only list interfaces if we are specifically asked
if (!$input->getOption('interfaces')) {
return;
}
$interfaces = $this->prepareInterfaces(\get_declared_interfaces());
if (empty($interfaces)) {
return;
}
return [
'Interfaces' => $interfaces,
];
}
/**
* Prepare formatted interface array.
*
* @param array $interfaces
*
* @return array
*/
protected function prepareInterfaces(array $interfaces)
{
\natcasesort($interfaces);
// My kingdom for a generator.
$ret = [];
foreach ($interfaces as $name) {
if ($this->showItem($name)) {
$ret[$name] = [
'name' => $name,
'style' => self::IS_CLASS,
'value' => $this->presentSignature($name),
];
}
}
return $ret;
}
}

View File

@@ -3,7 +3,7 @@
/*
* This file is part of Psy Shell.
*
* (c) 2012-2018 Justin Hileman
* (c) 2012-2022 Justin Hileman
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
@@ -21,30 +21,29 @@ class MethodEnumerator extends Enumerator
/**
* {@inheritdoc}
*/
protected function listItems(InputInterface $input, \Reflector $reflector = null, $target = null)
protected function listItems(InputInterface $input, \Reflector $reflector = null, $target = null): array
{
// only list methods when a Reflector is present.
if ($reflector === null) {
return;
return [];
}
// We can only list methods on actual class (or object) reflectors.
if (!$reflector instanceof \ReflectionClass) {
return;
return [];
}
// only list methods if we are specifically asked
if (!$input->getOption('methods')) {
return;
return [];
}
$showAll = $input->getOption('all');
$showAll = $input->getOption('all');
$noInherit = $input->getOption('no-inherit');
$methods = $this->prepareMethods($this->getMethods($showAll, $reflector, $noInherit));
$methods = $this->prepareMethods($this->getMethods($showAll, $reflector, $noInherit));
if (empty($methods)) {
return;
return [];
}
$ret = [];
@@ -62,13 +61,15 @@ class MethodEnumerator extends Enumerator
*
* @return array
*/
protected function getMethods($showAll, \Reflector $reflector, $noInherit = false)
protected function getMethods(bool $showAll, \Reflector $reflector, bool $noInherit = false): array
{
$className = $reflector->getName();
$methods = [];
foreach ($reflector->getMethods() as $name => $method) {
if ($noInherit && $method->getDeclaringClass()->getName() !== $className) {
// For some reason PHP reflection shows private methods from the parent class, even
// though they're effectively worthless. Let's suppress them here, like --no-inherit
if (($noInherit || $method->isPrivate()) && $method->getDeclaringClass()->getName() !== $className) {
continue;
}
@@ -77,7 +78,7 @@ class MethodEnumerator extends Enumerator
}
}
\ksort($methods, SORT_NATURAL | SORT_FLAG_CASE);
\ksort($methods, \SORT_NATURAL | \SORT_FLAG_CASE);
return $methods;
}
@@ -89,7 +90,7 @@ class MethodEnumerator extends Enumerator
*
* @return array
*/
protected function prepareMethods(array $methods)
protected function prepareMethods(array $methods): array
{
// My kingdom for a generator.
$ret = [];
@@ -114,7 +115,7 @@ class MethodEnumerator extends Enumerator
*
* @return string
*/
protected function getKindLabel(\ReflectionClass $reflector)
protected function getKindLabel(\ReflectionClass $reflector): string
{
if ($reflector->isInterface()) {
return 'Interface Methods';
@@ -132,7 +133,7 @@ class MethodEnumerator extends Enumerator
*
* @return string
*/
private function getVisibilityStyle(\ReflectionMethod $method)
private function getVisibilityStyle(\ReflectionMethod $method): string
{
if ($method->isPublic()) {
return self::IS_PUBLIC;

View File

@@ -3,7 +3,7 @@
/*
* This file is part of Psy Shell.
*
* (c) 2012-2018 Justin Hileman
* (c) 2012-2022 Justin Hileman
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
@@ -21,30 +21,30 @@ class PropertyEnumerator extends Enumerator
/**
* {@inheritdoc}
*/
protected function listItems(InputInterface $input, \Reflector $reflector = null, $target = null)
protected function listItems(InputInterface $input, \Reflector $reflector = null, $target = null): array
{
// only list properties when a Reflector is present.
if ($reflector === null) {
return;
return [];
}
// We can only list properties on actual class (or object) reflectors.
if (!$reflector instanceof \ReflectionClass) {
return;
return [];
}
// only list properties if we are specifically asked
if (!$input->getOption('properties')) {
return;
return [];
}
$showAll = $input->getOption('all');
$noInherit = $input->getOption('no-inherit');
$showAll = $input->getOption('all');
$noInherit = $input->getOption('no-inherit');
$properties = $this->prepareProperties($this->getProperties($showAll, $reflector, $noInherit), $target);
if (empty($properties)) {
return;
return [];
}
$ret = [];
@@ -62,7 +62,7 @@ class PropertyEnumerator extends Enumerator
*
* @return array
*/
protected function getProperties($showAll, \Reflector $reflector, $noInherit = false)
protected function getProperties(bool $showAll, \Reflector $reflector, bool $noInherit = false): array
{
$className = $reflector->getName();
@@ -77,7 +77,7 @@ class PropertyEnumerator extends Enumerator
}
}
\ksort($properties, SORT_NATURAL | SORT_FLAG_CASE);
\ksort($properties, \SORT_NATURAL | \SORT_FLAG_CASE);
return $properties;
}
@@ -89,14 +89,14 @@ class PropertyEnumerator extends Enumerator
*
* @return array
*/
protected function prepareProperties(array $properties, $target = null)
protected function prepareProperties(array $properties, $target = null): array
{
// My kingdom for a generator.
$ret = [];
foreach ($properties as $name => $property) {
if ($this->showItem($name)) {
$fname = '$' . $name;
$fname = '$'.$name;
$ret[$fname] = [
'name' => $fname,
'style' => $this->getVisibilityStyle($property),
@@ -115,11 +115,9 @@ class PropertyEnumerator extends Enumerator
*
* @return string
*/
protected function getKindLabel(\ReflectionClass $reflector)
protected function getKindLabel(\ReflectionClass $reflector): string
{
if ($reflector->isInterface()) {
return 'Interface Properties';
} elseif (\method_exists($reflector, 'isTrait') && $reflector->isTrait()) {
if (\method_exists($reflector, 'isTrait') && $reflector->isTrait()) {
return 'Trait Properties';
} else {
return 'Class Properties';
@@ -133,7 +131,7 @@ class PropertyEnumerator extends Enumerator
*
* @return string
*/
private function getVisibilityStyle(\ReflectionProperty $property)
private function getVisibilityStyle(\ReflectionProperty $property): string
{
if ($property->isPublic()) {
return self::IS_PUBLIC;
@@ -152,9 +150,13 @@ class PropertyEnumerator extends Enumerator
*
* @return string
*/
protected function presentValue(\ReflectionProperty $property, $target)
protected function presentValue(\ReflectionProperty $property, $target): string
{
// If $target is a class, trait or interface (try to) get the default
if (!$target) {
return '';
}
// If $target is a class or trait (try to) get the default
// value for the property.
if (!\is_object($target)) {
try {
@@ -163,9 +165,9 @@ class PropertyEnumerator extends Enumerator
if (\array_key_exists($property->name, $props)) {
$suffix = $property->isStatic() ? '' : ' <aside>(default)</aside>';
return $this->presentRef($props[$property->name]) . $suffix;
return $this->presentRef($props[$property->name]).$suffix;
}
} catch (\Exception $e) {
} catch (\Throwable $e) {
// Well, we gave it a shot.
}

View File

@@ -1,89 +0,0 @@
<?php
/*
* This file is part of Psy Shell.
*
* (c) 2012-2018 Justin Hileman
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Psy\Command\ListCommand;
use Psy\VarDumper\Presenter;
use Symfony\Component\Console\Input\InputInterface;
/**
* Trait Enumerator class.
*
* @deprecated Nothing should use this anymore
*/
class TraitEnumerator extends Enumerator
{
public function __construct(Presenter $presenter)
{
@\trigger_error('TraitEnumerator is no longer used', E_USER_DEPRECATED);
parent::__construct($presenter);
}
/**
* {@inheritdoc}
*/
protected function listItems(InputInterface $input, \Reflector $reflector = null, $target = null)
{
// only list traits when no Reflector is present.
//
// @todo make a NamespaceReflector and pass that in for commands like:
//
// ls --traits Foo
//
// ... for listing traits in the Foo namespace
if ($reflector !== null || $target !== null) {
return;
}
// only list traits if we are specifically asked
if (!$input->getOption('traits')) {
return;
}
$traits = $this->prepareTraits(\get_declared_traits());
if (empty($traits)) {
return;
}
return [
'Traits' => $traits,
];
}
/**
* Prepare formatted trait array.
*
* @param array $traits
*
* @return array
*/
protected function prepareTraits(array $traits)
{
\natcasesort($traits);
// My kingdom for a generator.
$ret = [];
foreach ($traits as $name) {
if ($this->showItem($name)) {
$ret[$name] = [
'name' => $name,
'style' => self::IS_CLASS,
'value' => $this->presentSignature($name),
];
}
}
return $ret;
}
}

View File

@@ -3,7 +3,7 @@
/*
* This file is part of Psy Shell.
*
* (c) 2012-2018 Justin Hileman
* (c) 2012-2022 Justin Hileman
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
@@ -45,23 +45,23 @@ class VariableEnumerator extends Enumerator
/**
* {@inheritdoc}
*/
protected function listItems(InputInterface $input, \Reflector $reflector = null, $target = null)
protected function listItems(InputInterface $input, \Reflector $reflector = null, $target = null): array
{
// only list variables when no Reflector is present.
if ($reflector !== null || $target !== null) {
return;
return [];
}
// only list variables if we are specifically asked
if (!$input->getOption('vars')) {
return;
return [];
}
$showAll = $input->getOption('all');
$showAll = $input->getOption('all');
$variables = $this->prepareVariables($this->getVariables($showAll));
if (empty($variables)) {
return;
return [];
}
return [
@@ -76,7 +76,7 @@ class VariableEnumerator extends Enumerator
*
* @return array
*/
protected function getVariables($showAll)
protected function getVariables(bool $showAll): array
{
$scopeVars = $this->context->getAll();
\uksort($scopeVars, function ($a, $b) {
@@ -117,13 +117,13 @@ class VariableEnumerator extends Enumerator
*
* @return array
*/
protected function prepareVariables(array $variables)
protected function prepareVariables(array $variables): array
{
// My kingdom for a generator.
$ret = [];
foreach ($variables as $name => $val) {
if ($this->showItem($name)) {
$fname = '$' . $name;
$fname = '$'.$name;
$ret[$fname] = [
'name' => $fname,
'style' => \in_array($name, self::$specialNames) ? self::IS_PRIVATE : self::IS_PUBLIC,

View File

@@ -3,7 +3,7 @@
/*
* This file is part of Psy Shell.
*
* (c) 2012-2018 Justin Hileman
* (c) 2012-2022 Justin Hileman
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
@@ -46,7 +46,7 @@ class ParseCommand extends Command implements ContextAware, PresenterAware
public function __construct($name = null)
{
$this->parserFactory = new ParserFactory();
$this->parsers = [];
$this->parsers = [];
parent::__construct($name);
}
@@ -70,14 +70,14 @@ class ParseCommand extends Command implements ContextAware, PresenterAware
{
$this->presenter = clone $presenter;
$this->presenter->addCasters([
'PhpParser\Node' => function (Node $node, array $a) {
Node::class => function (Node $node, array $a) {
$a = [
Caster::PREFIX_VIRTUAL . 'type' => $node->getType(),
Caster::PREFIX_VIRTUAL . 'attributes' => $node->getAttributes(),
Caster::PREFIX_VIRTUAL.'type' => $node->getType(),
Caster::PREFIX_VIRTUAL.'attributes' => $node->getAttributes(),
];
foreach ($node->getSubNodeNames() as $name) {
$a[Caster::PREFIX_VIRTUAL . $name] = $node->$name;
$a[Caster::PREFIX_VIRTUAL.$name] = $node->$name;
}
return $a;
@@ -90,23 +90,17 @@ class ParseCommand extends Command implements ContextAware, PresenterAware
*/
protected function configure()
{
$definition = [
new CodeArgument('code', CodeArgument::REQUIRED, 'PHP code to parse.'),
new InputOption('depth', '', InputOption::VALUE_REQUIRED, 'Depth to parse.', 10),
];
if ($this->parserFactory->hasKindsSupport()) {
$msg = 'One of PhpParser\\ParserFactory constants: '
. \implode(', ', ParserFactory::getPossibleKinds())
. " (default is based on current interpreter's version).";
$defaultKind = $this->parserFactory->getDefaultKind();
$definition[] = new InputOption('kind', '', InputOption::VALUE_REQUIRED, $msg, $defaultKind);
}
$kindMsg = 'One of PhpParser\\ParserFactory constants: '
.\implode(', ', ParserFactory::getPossibleKinds())
." (default is based on current interpreter's version).";
$this
->setName('parse')
->setDefinition($definition)
->setDefinition([
new CodeArgument('code', CodeArgument::REQUIRED, 'PHP code to parse.'),
new InputOption('depth', '', InputOption::VALUE_REQUIRED, 'Depth to parse.', 10),
new InputOption('kind', '', InputOption::VALUE_REQUIRED, $kindMsg, $this->parserFactory->getDefaultKind()),
])
->setDescription('Parse PHP code and show the abstract syntax tree.')
->setHelp(
<<<'HELP'
@@ -128,13 +122,13 @@ HELP
protected function execute(InputInterface $input, OutputInterface $output)
{
$code = $input->getArgument('code');
if (\strpos('<?', $code) === false) {
$code = '<?php ' . $code;
if (\strpos($code, '<?') === false) {
$code = '<?php '.$code;
}
$parserKind = $this->parserFactory->hasKindsSupport() ? $input->getOption('kind') : null;
$depth = $input->getOption('depth');
$nodes = $this->parse($this->getParser($parserKind), $code);
$parserKind = $input->getOption('kind');
$depth = $input->getOption('depth');
$nodes = $this->parse($this->getParser($parserKind), $code);
$output->page($this->presenter->present($nodes, $depth));
$this->context->setReturnValue($nodes);
@@ -150,7 +144,7 @@ HELP
*
* @return array Statements
*/
private function parse(Parser $parser, $code)
private function parse(Parser $parser, string $code): array
{
try {
return $parser->parse($code);
@@ -160,7 +154,7 @@ HELP
}
// If we got an unexpected EOF, let's try it again with a semicolon.
return $parser->parse($code . ';');
return $parser->parse($code.';');
}
}
@@ -171,7 +165,7 @@ HELP
*
* @return Parser
*/
private function getParser($kind = null)
private function getParser(string $kind = null): Parser
{
if (!\array_key_exists($kind, $this->parsers)) {
$this->parsers[$kind] = $this->parserFactory->createParser($kind);

View File

@@ -3,7 +3,7 @@
/*
* This file is part of Psy Shell.
*
* (c) 2012-2018 Justin Hileman
* (c) 2012-2022 Justin Hileman
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.

View File

@@ -3,7 +3,7 @@
/*
* This file is part of Psy Shell.
*
* (c) 2012-2018 Justin Hileman
* (c) 2012-2022 Justin Hileman
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
@@ -16,6 +16,9 @@ use Psy\Context;
use Psy\ContextAware;
use Psy\Exception\ErrorException;
use Psy\Exception\RuntimeException;
use Psy\Exception\UnexpectedTargetException;
use Psy\Reflection\ReflectionClassConstant;
use Psy\Reflection\ReflectionConstant_;
use Psy\Util\Mirror;
/**
@@ -23,9 +26,9 @@ use Psy\Util\Mirror;
*/
abstract class ReflectingCommand extends Command implements ContextAware
{
const CLASS_OR_FUNC = '/^[\\\\\w]+$/';
const CLASS_MEMBER = '/^([\\\\\w]+)::(\w+)$/';
const CLASS_STATIC = '/^([\\\\\w]+)::\$(\w+)$/';
const CLASS_OR_FUNC = '/^[\\\\\w]+$/';
const CLASS_MEMBER = '/^([\\\\\w]+)::(\w+)$/';
const CLASS_STATIC = '/^([\\\\\w]+)::\$(\w+)$/';
const INSTANCE_MEMBER = '/^(\$\w+)(::|->)(\w+)$/';
/**
@@ -54,10 +57,10 @@ abstract class ReflectingCommand extends Command implements ContextAware
*
* @return array (class or instance name, member name, kind)
*/
protected function getTarget($valueName)
protected function getTarget(string $valueName): array
{
$valueName = \trim($valueName);
$matches = [];
$matches = [];
switch (true) {
case \preg_match(self::CLASS_OR_FUNC, $valueName, $matches):
return [$this->resolveName($matches[0], true), null, 0];
@@ -92,7 +95,7 @@ abstract class ReflectingCommand extends Command implements ContextAware
*
* @return string
*/
protected function resolveName($name, $includeFunctions = false)
protected function resolveName(string $name, bool $includeFunctions = false): string
{
$shell = $this->getApplication();
@@ -107,15 +110,24 @@ abstract class ReflectingCommand extends Command implements ContextAware
}
$msg = \sprintf('Cannot use "%s" when no class scope is active', \strtolower($name));
throw new ErrorException($msg, 0, E_USER_ERROR, "eval()'d code", 1);
throw new ErrorException($msg, 0, \E_USER_ERROR, "eval()'d code", 1);
}
if (\substr($name, 0, 1) === '\\') {
return $name;
}
// Check $name against the current namespace and use statements.
if (self::couldBeClassName($name)) {
try {
$name = $this->resolveCode($name.'::class');
} catch (RuntimeException $e) {
// /shrug
}
}
if ($namespace = $shell->getNamespace()) {
$fullName = $namespace . '\\' . $name;
$fullName = $namespace.'\\'.$name;
if (\class_exists($fullName) || \interface_exists($fullName) || ($includeFunctions && \function_exists($fullName))) {
return $fullName;
@@ -125,6 +137,15 @@ abstract class ReflectingCommand extends Command implements ContextAware
return $name;
}
/**
* Check whether a given name could be a class name.
*/
protected function couldBeClassName(string $name): bool
{
// Regex based on https://www.php.net/manual/en/language.oop5.basic.php#language.oop5.basic.class
return \preg_match('/^[a-zA-Z_\x80-\xff][a-zA-Z0-9_\x80-\xff]*(\\\\[a-zA-Z_\x80-\xff][a-zA-Z0-9_\x80-\xff]*)*$/', $name) === 1;
}
/**
* Get a Reflector and documentation for a function, class or instance, constant, method or property.
*
@@ -132,7 +153,7 @@ abstract class ReflectingCommand extends Command implements ContextAware
*
* @return array (value, Reflector)
*/
protected function getTargetAndReflector($valueName)
protected function getTargetAndReflector(string $valueName): array
{
list($value, $member, $kind) = $this->getTarget($valueName);
@@ -148,16 +169,16 @@ abstract class ReflectingCommand extends Command implements ContextAware
*
* @return mixed Variable value
*/
protected function resolveCode($code)
protected function resolveCode(string $code)
{
try {
$value = $this->getApplication()->execute($code, true);
} catch (\Exception $e) {
} catch (\Throwable $e) {
// Swallow all exceptions?
}
if (!isset($value) || $value instanceof NoReturnValue) {
throw new RuntimeException('Unknown target: ' . $code);
throw new RuntimeException('Unknown target: '.$code);
}
return $value;
@@ -166,18 +187,18 @@ abstract class ReflectingCommand extends Command implements ContextAware
/**
* Resolve code to an object in the current scope.
*
* @throws RuntimeException when the code resolves to a non-object value
* @throws UnexpectedTargetException when the code resolves to a non-object value
*
* @param string $code
*
* @return object Variable instance
*/
private function resolveObject($code)
private function resolveObject(string $code)
{
$value = $this->resolveCode($code);
if (!\is_object($value)) {
throw new RuntimeException('Unable to inspect a non-object');
throw new UnexpectedTargetException($value, 'Unable to inspect a non-object');
}
return $value;
@@ -190,9 +211,9 @@ abstract class ReflectingCommand extends Command implements ContextAware
*
* @return mixed Variable instance
*/
protected function resolveInstance($name)
protected function resolveInstance(string $name)
{
@\trigger_error('`resolveInstance` is deprecated; use `resolveCode` instead.', E_USER_DEPRECATED);
@\trigger_error('`resolveInstance` is deprecated; use `resolveCode` instead.', \E_USER_DEPRECATED);
return $this->resolveCode($name);
}
@@ -204,7 +225,7 @@ abstract class ReflectingCommand extends Command implements ContextAware
*
* @return mixed
*/
protected function getScopeVariable($name)
protected function getScopeVariable(string $name)
{
return $this->context->get($name);
}
@@ -214,7 +235,7 @@ abstract class ReflectingCommand extends Command implements ContextAware
*
* @return array
*/
protected function getScopeVariables()
protected function getScopeVariables(): array
{
return $this->context->getAll();
}
@@ -231,15 +252,15 @@ abstract class ReflectingCommand extends Command implements ContextAware
$vars = [];
switch (\get_class($reflector)) {
case 'ReflectionClass':
case 'ReflectionObject':
case \ReflectionClass::class:
case \ReflectionObject::class:
$vars['__class'] = $reflector->name;
if ($reflector->inNamespace()) {
$vars['__namespace'] = $reflector->getNamespaceName();
}
break;
case 'ReflectionMethod':
case \ReflectionMethod::class:
$vars['__method'] = \sprintf('%s::%s', $reflector->class, $reflector->name);
$vars['__class'] = $reflector->class;
$classReflector = $reflector->getDeclaringClass();
@@ -248,14 +269,14 @@ abstract class ReflectingCommand extends Command implements ContextAware
}
break;
case 'ReflectionFunction':
case \ReflectionFunction::class:
$vars['__function'] = $reflector->name;
if ($reflector->inNamespace()) {
$vars['__namespace'] = $reflector->getNamespaceName();
}
break;
case 'ReflectionGenerator':
case \ReflectionGenerator::class:
$funcReflector = $reflector->getFunction();
$vars['__function'] = $funcReflector->name;
if ($funcReflector->inNamespace()) {
@@ -264,13 +285,13 @@ abstract class ReflectingCommand extends Command implements ContextAware
if ($fileName = $reflector->getExecutingFile()) {
$vars['__file'] = $fileName;
$vars['__line'] = $reflector->getExecutingLine();
$vars['__dir'] = \dirname($fileName);
$vars['__dir'] = \dirname($fileName);
}
break;
case 'ReflectionProperty':
case 'ReflectionClassConstant':
case 'Psy\Reflection\ReflectionClassConstant':
case \ReflectionProperty::class:
case \ReflectionClassConstant::class:
case ReflectionClassConstant::class:
$classReflector = $reflector->getDeclaringClass();
$vars['__class'] = $classReflector->name;
if ($classReflector->inNamespace()) {
@@ -279,11 +300,11 @@ abstract class ReflectingCommand extends Command implements ContextAware
// no line for these, but this'll do
if ($fileName = $reflector->getDeclaringClass()->getFileName()) {
$vars['__file'] = $fileName;
$vars['__dir'] = \dirname($fileName);
$vars['__dir'] = \dirname($fileName);
}
break;
case 'Psy\Reflection\ReflectionConstant_':
case ReflectionConstant_::class:
if ($reflector->inNamespace()) {
$vars['__namespace'] = $reflector->getNamespaceName();
}
@@ -294,7 +315,7 @@ abstract class ReflectingCommand extends Command implements ContextAware
if ($fileName = $reflector->getFileName()) {
$vars['__file'] = $fileName;
$vars['__line'] = $reflector->getStartLine();
$vars['__dir'] = \dirname($fileName);
$vars['__dir'] = \dirname($fileName);
}
}

View File

@@ -3,7 +3,7 @@
/*
* This file is part of Psy Shell.
*
* (c) 2012-2018 Justin Hileman
* (c) 2012-2022 Justin Hileman
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
@@ -11,10 +11,8 @@
namespace Psy\Command;
use JakubOnderka\PhpConsoleHighlighter\Highlighter;
use Psy\Configuration;
use Psy\ConsoleColorFactory;
use Psy\Exception\RuntimeException;
use Psy\Exception\UnexpectedTargetException;
use Psy\Formatter\CodeFormatter;
use Psy\Formatter\SignatureFormatter;
use Psy\Input\CodeArgument;
@@ -28,18 +26,14 @@ use Symfony\Component\Console\Output\OutputInterface;
*/
class ShowCommand extends ReflectingCommand
{
private $colorMode;
private $highlighter;
private $lastException;
private $lastExceptionIndex;
/**
* @param null|string $colorMode (default: null)
* @param string|null $colorMode (deprecated and ignored)
*/
public function __construct($colorMode = null)
{
$this->colorMode = $colorMode ?: Configuration::COLOR_MODE_AUTO;
parent::__construct();
}
@@ -52,7 +46,7 @@ class ShowCommand extends ReflectingCommand
->setName('show')
->setDefinition([
new CodeArgument('target', CodeArgument::OPTIONAL, 'Function, class, instance, constant, method or property to show.'),
new InputOption('ex', null, InputOption::VALUE_OPTIONAL, 'Show last exception context. Optionally specify a stack index.', 1),
new InputOption('ex', null, InputOption::VALUE_OPTIONAL, 'Show last exception context. Optionally specify a stack index.', 1),
])
->setDescription('Show the code for an object, class, constant, method or property.')
->setHelp(
@@ -75,6 +69,8 @@ HELP
/**
* {@inheritdoc}
*
* @return int 0 if everything went fine, or an exit code
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
@@ -114,13 +110,33 @@ HELP
private function writeCodeContext(InputInterface $input, OutputInterface $output)
{
list($target, $reflector) = $this->getTargetAndReflector($input->getArgument('target'));
try {
list($target, $reflector) = $this->getTargetAndReflector($input->getArgument('target'));
} catch (UnexpectedTargetException $e) {
// If we didn't get a target and Reflector, maybe we got a filename?
$target = $e->getTarget();
if (\is_string($target) && \is_file($target) && $code = @\file_get_contents($target)) {
$file = \realpath($target);
if ($file !== $this->context->get('__file')) {
$this->context->setCommandScopeVariables([
'__file' => $file,
'__dir' => \dirname($file),
]);
}
$output->page(CodeFormatter::formatCode($code));
return;
} else {
throw $e;
}
}
// Set some magic local variables
$this->setCommandScopeVariables($reflector);
try {
$output->page(CodeFormatter::format($reflector, $this->colorMode), OutputInterface::OUTPUT_RAW);
$output->page(CodeFormatter::format($reflector));
} catch (RuntimeException $e) {
$output->writeln(SignatureFormatter::format($reflector));
throw $e;
@@ -143,7 +159,7 @@ HELP
$index = 0;
}
} else {
$index = \max(0, \intval($input->getOption('ex')) - 1);
$index = \max(0, (int) $input->getOption('ex') - 1);
}
$trace = $exception->getTrace();
@@ -173,7 +189,7 @@ HELP
$line = isset($trace[$index]['line']) ? $trace[$index]['line'] : 'n/a';
$output->writeln(\sprintf(
'From <info>%s:%d</info> at <strong>level %d</strong> of backtrace (of %d).',
'From <info>%s:%d</info> at <strong>level %d</strong> of backtrace (of %d):',
OutputFormatter::escape($file),
OutputFormatter::escape($line),
$index + 1,
@@ -181,16 +197,16 @@ HELP
));
}
private function replaceCwd($file)
private function replaceCwd(string $file): string
{
if ($cwd = \getcwd()) {
$cwd = \rtrim($cwd, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR;
$cwd = \rtrim($cwd, \DIRECTORY_SEPARATOR).\DIRECTORY_SEPARATOR;
}
if ($cwd === false) {
return $file;
} else {
return \preg_replace('/^' . \preg_quote($cwd, '/') . '/', '', $file);
return \preg_replace('/^'.\preg_quote($cwd, '/').'/', '', $file);
}
}
@@ -219,17 +235,10 @@ HELP
return;
}
$output->write($this->getHighlighter()->getCodeSnippet($code, $line, 5, 5), false, OutputInterface::OUTPUT_RAW);
}
$startLine = \max($line - 5, 0);
$endLine = $line + 5;
private function getHighlighter()
{
if (!$this->highlighter) {
$factory = new ConsoleColorFactory($this->colorMode);
$this->highlighter = new Highlighter($factory->getConsoleColor());
}
return $this->highlighter;
$output->write(CodeFormatter::formatCode($code, $startLine, $endLine, $line), false);
}
private function setCommandScopeVariablesFromContext(array $context)
@@ -247,7 +256,7 @@ HELP
if ($namespace = $refl->getNamespaceName()) {
$vars['__namespace'] = $namespace;
}
} catch (\Exception $e) {
} catch (\Throwable $e) {
// oh well
}
} elseif (isset($context['function'])) {
@@ -258,7 +267,7 @@ HELP
if ($namespace = $refl->getNamespaceName()) {
$vars['__namespace'] = $namespace;
}
} catch (\Exception $e) {
} catch (\Throwable $e) {
// oh well
}
}
@@ -283,7 +292,7 @@ HELP
$this->context->setCommandScopeVariables($vars);
}
private function extractEvalFileAndLine($file)
private function extractEvalFileAndLine(string $file)
{
if (\preg_match('/(.*)\\((\\d+)\\) : eval\\(\\)\'d code$/', $file, $matches)) {
return [$matches[1], $matches[2]];

View File

@@ -3,7 +3,7 @@
/*
* This file is part of Psy Shell.
*
* (c) 2012-2018 Justin Hileman
* (c) 2012-2022 Justin Hileman
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
@@ -95,6 +95,8 @@ HELP
/**
* {@inheritdoc}
*
* @return int 0 if everything went fine, or an exit code
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
@@ -109,8 +111,8 @@ HELP
$code = $history[\count($history) - 2];
}
if (\strpos('<?', $code) === false) {
$code = '<?php ' . $code;
if (\strpos($code, '<?') === false) {
$code = '<?php '.$code;
}
$nodes = $this->traverser->traverse($this->parse($code));
@@ -129,7 +131,7 @@ HELP
*
* @return array Statements
*/
private function parse($code)
private function parse(string $code): array
{
try {
return $this->parser->parse($code);
@@ -139,7 +141,7 @@ HELP
}
// If we got an unexpected EOF, let's try it again with a semicolon.
return $this->parser->parse($code . ';');
return $this->parser->parse($code.';');
}
}
}

View File

@@ -3,7 +3,7 @@
/*
* This file is part of Psy Shell.
*
* (c) 2012-2018 Justin Hileman
* (c) 2012-2022 Justin Hileman
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
@@ -13,7 +13,6 @@ namespace Psy\Command;
use PhpParser\Node\Arg;
use PhpParser\Node\Expr\New_;
use PhpParser\Node\Expr\StaticCall;
use PhpParser\Node\Expr\Variable;
use PhpParser\Node\Name\FullyQualified as FullyQualifiedName;
use PhpParser\Node\Scalar\String_;
@@ -21,6 +20,7 @@ use PhpParser\Node\Stmt\Throw_;
use PhpParser\PrettyPrinter\Standard as Printer;
use Psy\Context;
use Psy\ContextAware;
use Psy\Exception\ThrowUpException;
use Psy\Input\CodeArgument;
use Psy\ParserFactory;
use Symfony\Component\Console\Input\InputInterface;
@@ -31,18 +31,9 @@ use Symfony\Component\Console\Output\OutputInterface;
*/
class ThrowUpCommand extends Command implements ContextAware
{
const THROW_CLASS = 'Psy\Exception\ThrowUpException';
private $parser;
private $printer;
/**
* Context instance (for ContextAware interface).
*
* @var Context
*/
protected $context;
/**
* {@inheritdoc}
*/
@@ -50,20 +41,20 @@ class ThrowUpCommand extends Command implements ContextAware
{
$parserFactory = new ParserFactory();
$this->parser = $parserFactory->createParser();
$this->parser = $parserFactory->createParser();
$this->printer = new Printer();
parent::__construct($name);
}
/**
* ContextAware interface.
* @deprecated throwUp no longer needs to be ContextAware
*
* @param Context $context
*/
public function setContext(Context $context)
{
$this->context = $context;
// Do nothing
}
/**
@@ -95,12 +86,14 @@ HELP
/**
* {@inheritdoc}
*
* @throws InvalidArgumentException if there is no exception to throw
* @return int 0 if everything went fine, or an exit code
*
* @throws \InvalidArgumentException if there is no exception to throw
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
$args = $this->prepareArgs($input->getArgument('exception'));
$throwStmt = new Throw_(new StaticCall(new FullyQualifiedName(self::THROW_CLASS), 'fromThrowable', $args));
$throwStmt = new Throw_(new New_(new FullyQualifiedName(ThrowUpException::class), $args));
$throwCode = $this->printer->prettyPrint([$throwStmt]);
$shell = $this->getApplication();
@@ -114,21 +107,21 @@ HELP
*
* If no argument was given, this falls back to `$_e`
*
* @throws InvalidArgumentException if there is no exception to throw
* @throws \InvalidArgumentException if there is no exception to throw
*
* @param string $code
*
* @return Arg[]
*/
private function prepareArgs($code = null)
private function prepareArgs(string $code = null): array
{
if (!$code) {
// Default to last exception if nothing else was supplied
return [new Arg(new Variable('_e'))];
}
if (\strpos('<?', $code) === false) {
$code = '<?php ' . $code;
if (\strpos($code, '<?') === false) {
$code = '<?php '.$code;
}
$nodes = $this->parse($code);
@@ -145,7 +138,7 @@ HELP
// Allow throwing via a string, e.g. `throw-up "SUP"`
if ($expr instanceof String_) {
return [new New_(new FullyQualifiedName('Exception'), $args)];
return [new New_(new FullyQualifiedName(\Exception::class), $args)];
}
return $args;
@@ -158,7 +151,7 @@ HELP
*
* @return array Statements
*/
private function parse($code)
private function parse(string $code): array
{
try {
return $this->parser->parse($code);
@@ -168,7 +161,7 @@ HELP
}
// If we got an unexpected EOF, let's try it again with a semicolon.
return $this->parser->parse($code . ';');
return $this->parser->parse($code.';');
}
}
}

View File

@@ -3,7 +3,7 @@
/*
* This file is part of Psy Shell.
*
* (c) 2012-2018 Justin Hileman
* (c) 2012-2022 Justin Hileman
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
@@ -25,7 +25,7 @@ use Symfony\Component\Console\Output\OutputInterface;
*/
class TimeitCommand extends Command
{
const RESULT_MSG = '<info>Command took %.6f seconds to complete.</info>';
const RESULT_MSG = '<info>Command took %.6f seconds to complete.</info>';
const AVG_RESULT_MSG = '<info>Command took %.6f seconds on average (%.6f median; %.6f total) to complete.</info>';
private static $start = null;
@@ -76,6 +76,8 @@ HELP
/**
* {@inheritdoc}
*
* @return int 0 if everything went fine, or an exit code
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
@@ -167,7 +169,7 @@ HELP
*
* @return string
*/
private function instrumentCode($code)
private function instrumentCode(string $code): string
{
return $this->printer->prettyPrint($this->traverser->traverse($this->parse($code)));
}
@@ -179,9 +181,9 @@ HELP
*
* @return array Statements
*/
private function parse($code)
private function parse(string $code): array
{
$code = '<?php ' . $code;
$code = '<?php '.$code;
try {
return $this->parser->parse($code);
@@ -191,7 +193,7 @@ HELP
}
// If we got an unexpected EOF, let's try it again with a semicolon.
return $this->parser->parse($code . ';');
return $this->parser->parse($code.';');
}
}
}

View File

@@ -3,7 +3,7 @@
/*
* This file is part of Psy Shell.
*
* (c) 2012-2018 Justin Hileman
* (c) 2012-2022 Justin Hileman
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
@@ -21,6 +21,7 @@ use PhpParser\Node\Stmt\Expression;
use PhpParser\Node\Stmt\Return_;
use PhpParser\NodeVisitorAbstract;
use Psy\CodeCleaner\NoReturnValue;
use Psy\Command\TimeitCommand;
/**
* A node visitor for instrumenting code to be executed by the `timeit` command.
@@ -34,6 +35,8 @@ class TimeitVisitor extends NodeVisitorAbstract
/**
* {@inheritdoc}
*
* @return Node[]|null Array of nodes
*/
public function beforeTraverse(array $nodes)
{
@@ -42,6 +45,8 @@ class TimeitVisitor extends NodeVisitorAbstract
/**
* {@inheritdoc}
*
* @return int|Node|null Replacement node (or special return value)
*/
public function enterNode(Node $node)
{
@@ -61,6 +66,8 @@ class TimeitVisitor extends NodeVisitorAbstract
/**
* {@inheritdoc}
*
* @return int|Node|Node[]|null Replacement node (or special return value)
*/
public function leaveNode(Node $node)
{
@@ -71,6 +78,8 @@ class TimeitVisitor extends NodeVisitorAbstract
/**
* {@inheritdoc}
*
* @return Node[]|null Array of nodes
*/
public function afterTraverse(array $nodes)
{
@@ -97,11 +106,11 @@ class TimeitVisitor extends NodeVisitorAbstract
/**
* Get PhpParser AST nodes for a `markStart` call.
*
* @return PhpParser\Node\Expr\StaticCall
* @return \PhpParser\Node\Expr\StaticCall
*/
private function getStartCall()
private function getStartCall(): StaticCall
{
return new StaticCall(new FullyQualifiedName('Psy\Command\TimeitCommand'), 'markStart');
return new StaticCall(new FullyQualifiedName(TimeitCommand::class), 'markStart');
}
/**
@@ -111,15 +120,15 @@ class TimeitVisitor extends NodeVisitorAbstract
*
* @param Expr|null $arg
*
* @return PhpParser\Node\Expr\StaticCall
* @return \PhpParser\Node\Expr\StaticCall
*/
private function getEndCall(Expr $arg = null)
private function getEndCall(Expr $arg = null): StaticCall
{
if ($arg === null) {
$arg = NoReturnValue::create();
}
return new StaticCall(new FullyQualifiedName('Psy\Command\TimeitCommand'), 'markEnd', [new Arg($arg)]);
return new StaticCall(new FullyQualifiedName(TimeitCommand::class), 'markEnd', [new Arg($arg)]);
}
/**
@@ -127,13 +136,13 @@ class TimeitVisitor extends NodeVisitorAbstract
*
* Wrap $expr in a PhpParser\Node\Stmt\Expression if the class exists.
*
* @param PhpParser\Node $expr
* @param array $attrs
* @param \PhpParser\Node $expr
* @param array $attrs
*
* @return PhpParser\Node\Expr|PhpParser\Node\Stmt\Expression
* @return \PhpParser\Node\Expr|\PhpParser\Node\Stmt\Expression
*/
private function maybeExpression($expr, $attrs = [])
private function maybeExpression(Node $expr, array $attrs = [])
{
return \class_exists('PhpParser\Node\Stmt\Expression') ? new Expression($expr, $attrs) : $expr;
return \class_exists(Expression::class) ? new Expression($expr, $attrs) : $expr;
}
}

View File

@@ -3,7 +3,7 @@
/*
* This file is part of Psy Shell.
*
* (c) 2012-2018 Justin Hileman
* (c) 2012-2022 Justin Hileman
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
@@ -11,9 +11,9 @@
namespace Psy\Command;
use Psy\Formatter\TraceFormatter;
use Psy\Input\FilterOptions;
use Psy\Output\ShellOutput;
use Symfony\Component\Console\Formatter\OutputFormatter;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
@@ -45,8 +45,8 @@ class TraceCommand extends Command
$this
->setName('trace')
->setDefinition([
new InputOption('include-psy', 'p', InputOption::VALUE_NONE, 'Include Psy in the call stack.'),
new InputOption('num', 'n', InputOption::VALUE_REQUIRED, 'Only include NUM lines.'),
new InputOption('include-psy', 'p', InputOption::VALUE_NONE, 'Include Psy in the call stack.'),
new InputOption('num', 'n', InputOption::VALUE_REQUIRED, 'Only include NUM lines.'),
$grep,
$insensitive,
@@ -68,6 +68,8 @@ HELP
/**
* {@inheritdoc}
*
* @return int 0 if everything went fine, or an exit code
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
@@ -79,91 +81,19 @@ HELP
}
/**
* Get a backtrace for an exception.
* Get a backtrace for an exception or error.
*
* Optionally limit the number of rows to include with $count, and exclude
* Psy from the trace.
*
* @param \Exception $e The exception with a backtrace
* @param \Throwable $e The exception or error with a backtrace
* @param int $count (default: PHP_INT_MAX)
* @param bool $includePsy (default: true)
*
* @return array Formatted stacktrace lines
*/
protected function getBacktrace(\Exception $e, $count = null, $includePsy = true)
protected function getBacktrace(\Throwable $e, int $count = null, bool $includePsy = true): array
{
if ($cwd = \getcwd()) {
$cwd = \rtrim($cwd, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR;
}
if ($count === null) {
$count = PHP_INT_MAX;
}
$lines = [];
$trace = $e->getTrace();
\array_unshift($trace, [
'function' => '',
'file' => $e->getFile() !== null ? $e->getFile() : 'n/a',
'line' => $e->getLine() !== null ? $e->getLine() : 'n/a',
'args' => [],
]);
if (!$includePsy) {
for ($i = \count($trace) - 1; $i >= 0; $i--) {
$thing = isset($trace[$i]['class']) ? $trace[$i]['class'] : $trace[$i]['function'];
if (\preg_match('/\\\\?Psy\\\\/', $thing)) {
$trace = \array_slice($trace, $i + 1);
break;
}
}
}
for ($i = 0, $count = \min($count, \count($trace)); $i < $count; $i++) {
$class = isset($trace[$i]['class']) ? $trace[$i]['class'] : '';
$type = isset($trace[$i]['type']) ? $trace[$i]['type'] : '';
$function = $trace[$i]['function'];
$file = isset($trace[$i]['file']) ? $this->replaceCwd($cwd, $trace[$i]['file']) : 'n/a';
$line = isset($trace[$i]['line']) ? $trace[$i]['line'] : 'n/a';
// Leave execution loop out of the `eval()'d code` lines
if (\preg_match("#/src/Execution(?:Loop)?Closure.php\(\d+\) : eval\(\)'d code$#", \str_replace('\\', '/', $file))) {
$file = "eval()'d code";
}
// Skip any lines that don't match our filter options
if (!$this->filter->match(\sprintf('%s%s%s() at %s:%s', $class, $type, $function, $file, $line))) {
continue;
}
$lines[] = \sprintf(
' <class>%s</class>%s%s() at <info>%s:%s</info>',
OutputFormatter::escape($class),
OutputFormatter::escape($type),
OutputFormatter::escape($function),
OutputFormatter::escape($file),
OutputFormatter::escape($line)
);
}
return $lines;
}
/**
* Replace the given directory from the start of a filepath.
*
* @param string $cwd
* @param string $file
*
* @return string
*/
private function replaceCwd($cwd, $file)
{
if ($cwd === false) {
return $file;
} else {
return \preg_replace('/^' . \preg_quote($cwd, '/') . '/', '', $file);
}
return TraceFormatter::formatTrace($e, $this->filter, $count, $includePsy);
}
}

View File

@@ -3,7 +3,7 @@
/*
* This file is part of Psy Shell.
*
* (c) 2012-2018 Justin Hileman
* (c) 2012-2022 Justin Hileman
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
@@ -11,9 +11,9 @@
namespace Psy\Command;
use JakubOnderka\PhpConsoleHighlighter\Highlighter;
use Psy\Configuration;
use Psy\ConsoleColorFactory;
use Psy\Formatter\CodeFormatter;
use Psy\Output\ShellOutput;
use Psy\Shell;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
@@ -23,16 +23,14 @@ use Symfony\Component\Console\Output\OutputInterface;
*/
class WhereamiCommand extends Command
{
private $colorMode;
private $backtrace;
/**
* @param null|string $colorMode (default: null)
* @param string|null $colorMode (deprecated and ignored)
*/
public function __construct($colorMode = null)
{
$this->colorMode = $colorMode ?: Configuration::COLOR_MODE_AUTO;
$this->backtrace = \debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
$this->backtrace = \debug_backtrace(\DEBUG_BACKTRACE_IGNORE_ARGS);
parent::__construct();
}
@@ -46,17 +44,20 @@ class WhereamiCommand extends Command
->setName('whereami')
->setDefinition([
new InputOption('num', 'n', InputOption::VALUE_OPTIONAL, 'Number of lines before and after.', '5'),
new InputOption('file', 'f|a', InputOption::VALUE_NONE, 'Show the full source for the current file.'),
])
->setDescription('Show where you are in the code.')
->setHelp(
<<<'HELP'
Show where you are in the code.
Optionally, include how many lines before and after you want to display.
Optionally, include the number of lines before and after you want to display,
or --file for the whole file.
e.g.
<return>> whereami </return>
<return>> whereami -n10</return>
<return>> whereami --file</return>
HELP
);
}
@@ -66,7 +67,7 @@ HELP
*
* @return array
*/
protected function trace()
protected function trace(): array
{
foreach (\array_reverse($this->backtrace) as $stackFrame) {
if ($this->isDebugCall($stackFrame)) {
@@ -77,13 +78,13 @@ HELP
return \end($this->backtrace);
}
private static function isDebugCall(array $stackFrame)
private static function isDebugCall(array $stackFrame): bool
{
$class = isset($stackFrame['class']) ? $stackFrame['class'] : null;
$class = isset($stackFrame['class']) ? $stackFrame['class'] : null;
$function = isset($stackFrame['function']) ? $stackFrame['function'] : null;
return ($class === null && $function === 'Psy\debug') ||
($class === 'Psy\Shell' && \in_array($function, ['__construct', 'debug']));
return ($class === null && $function === 'Psy\\debug') ||
($class === Shell::class && \in_array($function, ['__construct', 'debug']));
}
/**
@@ -91,7 +92,7 @@ HELP
*
* @return array
*/
protected function fileInfo()
protected function fileInfo(): array
{
$stackFrame = $this->trace();
if (\preg_match('/eval\(/', $stackFrame['file'])) {
@@ -108,22 +109,33 @@ HELP
/**
* {@inheritdoc}
*
* @return int 0 if everything went fine, or an exit code
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
$info = $this->fileInfo();
$num = $input->getOption('num');
$factory = new ConsoleColorFactory($this->colorMode);
$colors = $factory->getConsoleColor();
$highlighter = new Highlighter($colors);
$contents = \file_get_contents($info['file']);
$info = $this->fileInfo();
$num = $input->getOption('num');
$lineNum = $info['line'];
$startLine = \max($lineNum - $num, 1);
$endLine = $lineNum + $num;
$code = \file_get_contents($info['file']);
$output->startPaging();
$output->writeln('');
$output->writeln(\sprintf('From <info>%s:%s</info>:', $this->replaceCwd($info['file']), $info['line']));
$output->writeln('');
$output->write($highlighter->getCodeSnippet($contents, $info['line'], $num, $num), false, OutputInterface::OUTPUT_RAW);
$output->stopPaging();
if ($input->getOption('file')) {
$startLine = 1;
$endLine = null;
}
if ($output instanceof ShellOutput) {
$output->startPaging();
}
$output->writeln(\sprintf('From <info>%s:%s</info>:', $this->replaceCwd($info['file']), $lineNum));
$output->write(CodeFormatter::formatCode($code, $startLine, $endLine, $lineNum), false);
if ($output instanceof ShellOutput) {
$output->stopPaging();
}
return 0;
}
@@ -135,15 +147,15 @@ HELP
*
* @return string
*/
private function replaceCwd($file)
private function replaceCwd(string $file): string
{
$cwd = \getcwd();
if ($cwd === false) {
return $file;
}
$cwd = \rtrim($cwd, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR;
$cwd = \rtrim($cwd, \DIRECTORY_SEPARATOR).\DIRECTORY_SEPARATOR;
return \preg_replace('/^' . \preg_quote($cwd, '/') . '/', '', $file);
return \preg_replace('/^'.\preg_quote($cwd, '/').'/', '', $file);
}
}

View File

@@ -3,7 +3,7 @@
/*
* This file is part of Psy Shell.
*
* (c) 2012-2018 Justin Hileman
* (c) 2012-2022 Justin Hileman
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
@@ -54,7 +54,7 @@ class WtfCommand extends TraceCommand implements ContextAware
->setAliases(['last-exception', 'wtf?'])
->setDefinition([
new InputArgument('incredulity', InputArgument::OPTIONAL | InputArgument::IS_ARRAY, 'Number of lines to show.'),
new InputOption('all', 'a', InputOption::VALUE_NONE, 'Show entire backtrace.'),
new InputOption('all', 'a', InputOption::VALUE_NONE, 'Show entire backtrace.'),
$grep,
$insensitive,
@@ -81,6 +81,8 @@ HELP
/**
* {@inheritdoc}
*
* @return int 0 if everything went fine, or an exit code
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
@@ -92,19 +94,23 @@ HELP
}
$exception = $this->context->getLastException();
$count = $input->getOption('all') ? PHP_INT_MAX : \max(3, \pow(2, \strlen($incredulity) + 1));
$count = $input->getOption('all') ? \PHP_INT_MAX : \max(3, \pow(2, \strlen($incredulity) + 1));
$shell = $this->getApplication();
$output->startPaging();
if ($output instanceof ShellOutput) {
$output->startPaging();
}
do {
$traceCount = \count($exception->getTrace());
$showLines = $count;
$showLines = $count;
// Show the whole trace if we'd only be hiding a few lines
if ($traceCount < \max($count * 1.2, $count + 2)) {
$showLines = PHP_INT_MAX;
$showLines = \PHP_INT_MAX;
}
$trace = $this->getBacktrace($exception, $showLines);
$trace = $this->getBacktrace($exception, $showLines);
$moreLines = $traceCount - \count($trace);
$output->writeln($shell->formatException($exception));
@@ -120,7 +126,10 @@ HELP
$output->writeln('');
}
} while ($exception = $exception->getPrevious());
$output->stopPaging();
if ($output instanceof ShellOutput) {
$output->stopPaging();
}
return 0;
}