This commit is contained in:
2025-05-12 14:25:25 +02:00
parent ab2db755ef
commit 9e378ca2b7
2719 changed files with 46505 additions and 60181 deletions

0
vendor/psy/psysh/bin/psysh vendored Normal file → Executable file
View File

View File

@@ -48,13 +48,13 @@ use Psy\Exception\ParseErrorException;
*/
class CodeCleaner
{
private $yolo = false;
private $strictTypes = false;
private bool $yolo = false;
private bool $strictTypes = false;
private $parser;
private $printer;
private $traverser;
private $namespace;
private Parser $parser;
private Printer $printer;
private NodeTraverser $traverser;
private ?array $namespace = null;
/**
* CodeCleaner constructor.
@@ -65,7 +65,7 @@ class CodeCleaner
* @param bool $yolo run without input validation
* @param bool $strictTypes enforce strict types by default
*/
public function __construct(Parser $parser = null, Printer $printer = null, NodeTraverser $traverser = null, bool $yolo = false, bool $strictTypes = false)
public function __construct(?Parser $parser = null, ?Printer $printer = null, ?NodeTraverser $traverser = null, bool $yolo = false, bool $strictTypes = false)
{
$this->yolo = $yolo;
$this->strictTypes = $strictTypes;
@@ -94,10 +94,6 @@ class CodeCleaner
*/
private function getDefaultPasses(): array
{
if ($this->yolo) {
return $this->getYoloPasses();
}
$useStatementPass = new UseStatementPass();
$namespacePass = new NamespacePass($this);
@@ -105,6 +101,25 @@ class CodeCleaner
// based on the file in which the `debug` call was made.
$this->addImplicitDebugContext([$useStatementPass, $namespacePass]);
// A set of code cleaner passes that don't try to do any validation, and
// only do minimal rewriting to make things work inside the REPL.
//
// When in --yolo mode, these are the only code cleaner passes used.
$rewritePasses = [
new LeavePsyshAlonePass(),
$useStatementPass, // must run before the namespace pass
new ExitPass(),
new ImplicitReturnPass(),
new MagicConstantsPass(),
$namespacePass, // must run after the implicit return pass
new RequirePass(),
new StrictTypesPass($this->strictTypes),
];
if ($this->yolo) {
return $rewritePasses;
}
return [
// Validation passes
new AbstractClassPass(),
@@ -116,7 +131,6 @@ class CodeCleaner
new FunctionReturnInWriteContextPass(),
new IssetPass(),
new LabelContextPass(),
new LeavePsyshAlonePass(),
new ListPass(),
new LoopContextPass(),
new PassableByReferencePass(),
@@ -125,13 +139,7 @@ class CodeCleaner
new ValidConstructorPass(),
// Rewriting shenanigans
$useStatementPass, // must run before the namespace pass
new ExitPass(),
new ImplicitReturnPass(),
new MagicConstantsPass(),
$namespacePass, // must run after the implicit return pass
new RequirePass(),
new StrictTypesPass($this->strictTypes),
...$rewritePasses,
// Namespace-aware validation (which depends on aforementioned shenanigans)
new ValidClassNamePass(),
@@ -139,36 +147,6 @@ class CodeCleaner
];
}
/**
* A set of code cleaner passes that don't try to do any validation, and
* only do minimal rewriting to make things work inside the REPL.
*
* This list should stay in sync with the "rewriting shenanigans" in
* getDefaultPasses above.
*
* @return CodeCleanerPass[]
*/
private function getYoloPasses(): array
{
$useStatementPass = new UseStatementPass();
$namespacePass = new NamespacePass($this);
// Try to add implicit `use` statements and an implicit namespace,
// based on the file in which the `debug` call was made.
$this->addImplicitDebugContext([$useStatementPass, $namespacePass]);
return [
new LeavePsyshAlonePass(),
$useStatementPass, // must run before the namespace pass
new ExitPass(),
new ImplicitReturnPass(),
new MagicConstantsPass(),
$namespacePass, // must run after the implicit return pass
new RequirePass(),
new StrictTypesPass($this->strictTypes),
];
}
/**
* "Warm up" code cleaner passes when we're coming from a debug call.
*
@@ -197,6 +175,7 @@ class CodeCleaner
}
// Set up a clean traverser for just these code cleaner passes
// @todo Pass visitors directly to once we drop support for PHP-Parser 4.x
$traverser = new NodeTraverser();
foreach ($passes as $pass) {
$traverser->addVisitor($pass);
@@ -280,10 +259,8 @@ class CodeCleaner
/**
* Set the current local namespace.
*
* @param array|null $namespace (default: null)
*/
public function setNamespace(array $namespace = null)
public function setNamespace(?array $namespace = null)
{
$this->namespace = $namespace;
}
@@ -306,9 +283,6 @@ class CodeCleaner
* @throws ParseErrorException for parse errors that can't be resolved by
* waiting a line to see what comes next
*
* @param string $code
* @param bool $requireSemicolons
*
* @return array|false A set of statements, or false if incomplete
*/
protected function parse(string $code, bool $requireSemicolons = false)
@@ -358,9 +332,6 @@ class CodeCleaner
* Unlike (all?) other unclosed statements, single quoted strings have
* their own special beautiful snowflake syntax error just for
* themselves.
*
* @param \PhpParser\Error $e
* @param string $code
*/
private function parseErrorIsUnclosedString(\PhpParser\Error $e, string $code): bool
{
@@ -377,12 +348,12 @@ class CodeCleaner
return true;
}
private function parseErrorIsUnterminatedComment(\PhpParser\Error $e, $code): bool
private function parseErrorIsUnterminatedComment(\PhpParser\Error $e, string $code): bool
{
return $e->getRawMessage() === 'Unterminated comment';
}
private function parseErrorIsTrailingComma(\PhpParser\Error $e, $code): bool
private function parseErrorIsTrailingComma(\PhpParser\Error $e, string $code): bool
{
return ($e->getRawMessage() === 'A trailing comma is not allowed here') && (\substr(\rtrim($code), -1) === ',');
}

View File

@@ -21,8 +21,8 @@ use Psy\Exception\FatalErrorException;
*/
class AbstractClassPass extends CodeCleanerPass
{
private $class;
private $abstractMethods;
private Class_ $class;
private array $abstractMethods;
/**
* @throws FatalErrorException if the node is an abstract function with a body

View File

@@ -26,7 +26,7 @@ use Psy\Exception\ErrorException;
*/
class CalledClassPass extends CodeCleanerPass
{
private $inClass;
private bool $inClass = false;
/**
* @param array $nodes

View File

@@ -25,7 +25,7 @@ class EmptyArrayDimFetchPass extends CodeCleanerPass
{
const EXCEPTION_MESSAGE = 'Cannot use [] for reading';
private $theseOnesAreFine = [];
private array $theseOnesAreFine = [];
/**
* @return Node[]|null Array of nodes

View File

@@ -20,7 +20,7 @@ use Psy\Exception\FatalErrorException;
*/
class FinalClassPass extends CodeCleanerPass
{
private $finalClasses;
private array $finalClasses = [];
/**
* @param array $nodes

View File

@@ -18,8 +18,7 @@ use Psy\Exception\FatalErrorException;
class FunctionContextPass extends CodeCleanerPass
{
/** @var int */
private $functionDepth;
private int $functionDepth = 0;
/**
* @param array $nodes

View File

@@ -80,7 +80,7 @@ class ImplicitReturnPass extends CodeCleanerPass
'startLine' => $last->getStartLine(),
'endLine' => $last->getEndLine(),
]);
// @codeCoverageIgnoreEnd
// @codeCoverageIgnoreEnd
} elseif ($last instanceof Expression && !($last->expr instanceof Exit_)) {
$nodes[\count($nodes) - 1] = new Return_($last->expr, [
'startLine' => $last->getStartLine(),

View File

@@ -31,13 +31,9 @@ use Psy\Exception\FatalErrorException;
*/
class LabelContextPass extends CodeCleanerPass
{
/** @var int */
private $functionDepth;
/** @var array */
private $labelDeclarations;
/** @var array */
private $labelGotos;
private int $functionDepth = 0;
private array $labelDeclarations = [];
private array $labelGotos = [];
/**
* @param array $nodes

View File

@@ -12,9 +12,11 @@
namespace Psy\CodeCleaner;
use PhpParser\Node;
use PhpParser\Node\ArrayItem;
use PhpParser\Node\Expr\Array_;
use PhpParser\Node\Expr\ArrayDimFetch;
use PhpParser\Node\Expr\ArrayItem;
// @todo Drop PhpParser\Node\Expr\ArrayItem once we drop support for PHP-Parser 4.x
use PhpParser\Node\Expr\ArrayItem as LegacyArrayItem;
use PhpParser\Node\Expr\Assign;
use PhpParser\Node\Expr\FuncCall;
use PhpParser\Node\Expr\List_;
@@ -80,7 +82,7 @@ class ListPass extends CodeCleanerPass
*/
private static function isValidArrayItem(Node $item): bool
{
$value = ($item instanceof ArrayItem) ? $item->value : $item;
$value = ($item instanceof ArrayItem || $item instanceof LegacyArrayItem) ? $item->value : $item;
while ($value instanceof ArrayDimFetch || $value instanceof PropertyFetch) {
$value = $value->var;

View File

@@ -13,6 +13,8 @@ namespace Psy\CodeCleaner;
use PhpParser\Node;
use PhpParser\Node\Scalar\DNumber;
use PhpParser\Node\Scalar\Float_;
use PhpParser\Node\Scalar\Int_;
use PhpParser\Node\Scalar\LNumber;
use PhpParser\Node\Stmt\Break_;
use PhpParser\Node\Stmt\Continue_;
@@ -28,7 +30,7 @@ use Psy\Exception\FatalErrorException;
*/
class LoopContextPass extends CodeCleanerPass
{
private $loopDepth;
private int $loopDepth = 0;
/**
* {@inheritdoc}
@@ -70,7 +72,13 @@ class LoopContextPass extends CodeCleanerPass
throw new FatalErrorException($msg, 0, \E_ERROR, null, $node->getStartLine());
}
if ($node->num instanceof LNumber || $node->num instanceof DNumber) {
// @todo Remove LNumber and DNumber once we drop support for PHP-Parser 4.x
if (
$node->num instanceof LNumber ||
$node->num instanceof DNumber ||
$node->num instanceof Int_ ||
$node->num instanceof Float_
) {
$num = $node->num->value;
if ($node->num instanceof DNumber || $num < 1) {
$msg = \sprintf("'%s' operator accepts only positive numbers", $operator);

View File

@@ -21,8 +21,8 @@ use PhpParser\Node\Stmt\Namespace_;
*/
abstract class NamespaceAwarePass extends CodeCleanerPass
{
protected $namespace;
protected $currentScope;
protected array $namespace = [];
protected array $currentScope = [];
/**
* @todo should this be final? Extending classes should be sure to either

View File

@@ -29,8 +29,8 @@ use Psy\CodeCleaner;
*/
class NamespacePass extends CodeCleanerPass
{
private $namespace = null;
private $cleaner;
private ?Name $namespace = null;
private CodeCleaner $cleaner;
/**
* @param CodeCleaner $cleaner
@@ -83,7 +83,7 @@ class NamespacePass extends CodeCleanerPass
*
* @param Name|null $namespace
*/
private function setNamespace($namespace)
private function setNamespace(?Name $namespace)
{
$this->namespace = $namespace;
$this->cleaner->setNamespace($namespace === null ? null : $this->getParts($namespace));

View File

@@ -21,6 +21,7 @@ use PhpParser\Node\Expr\MethodCall;
use PhpParser\Node\Expr\PropertyFetch;
use PhpParser\Node\Expr\StaticCall;
use PhpParser\Node\Expr\Variable;
use PhpParser\Node\VariadicPlaceholder;
use Psy\Exception\FatalErrorException;
/**
@@ -59,9 +60,18 @@ class PassableByReferencePass extends CodeCleanerPass
return;
}
$args = [];
foreach ($node->args as $position => $arg) {
if ($arg instanceof VariadicPlaceholder) {
continue;
}
$args[$arg->name !== null ? $arg->name->name : $position] = $arg;
}
foreach ($refl->getParameters() as $key => $param) {
if (\array_key_exists($key, $node->args)) {
$arg = $node->args[$key];
if (\array_key_exists($key, $args) || \array_key_exists($param->name, $args)) {
$arg = $args[$param->name] ?? $args[$key];
if ($param->isPassedByReference() && !$this->isPassableByReference($arg)) {
throw new FatalErrorException(self::EXCEPTION_MESSAGE, 0, \E_ERROR, null, $node->getStartLine());
}

View File

@@ -16,6 +16,7 @@ use PhpParser\Node\Arg;
use PhpParser\Node\Expr\Include_;
use PhpParser\Node\Expr\StaticCall;
use PhpParser\Node\Name\FullyQualified as FullyQualifiedName;
use PhpParser\Node\Scalar\Int_;
use PhpParser\Node\Scalar\LNumber;
use Psy\Exception\ErrorException;
use Psy\Exception\FatalErrorException;
@@ -25,7 +26,7 @@ use Psy\Exception\FatalErrorException;
*/
class RequirePass extends CodeCleanerPass
{
private static $requireTypes = [Include_::TYPE_REQUIRE, Include_::TYPE_REQUIRE_ONCE];
private const REQUIRE_TYPES = [Include_::TYPE_REQUIRE, Include_::TYPE_REQUIRE_ONCE];
/**
* {@inheritdoc}
@@ -49,10 +50,15 @@ class RequirePass extends CodeCleanerPass
*
* $foo = require \Psy\CodeCleaner\RequirePass::resolve($bar)
*/
// @todo Remove LNumber once we drop support for PHP-Parser 4.x
$arg = \class_exists('PhpParser\Node\Scalar\Int_') ?
new Int_($origNode->getStartLine()) :
new LNumber($origNode->getStartLine());
$node->expr = new StaticCall(
new FullyQualifiedName(self::class),
'resolve',
[new Arg($origNode->expr), new Arg(new LNumber($origNode->getStartLine()))],
[new Arg($origNode->expr), new Arg($arg)],
$origNode->getAttributes()
);
@@ -119,7 +125,7 @@ class RequirePass extends CodeCleanerPass
private function isRequireNode(Node $node): bool
{
return $node instanceof Include_ && \in_array($node->type, self::$requireTypes);
return $node instanceof Include_ && \in_array($node->type, self::REQUIRE_TYPES);
}
private static function getIncludePath(): array

View File

@@ -15,6 +15,8 @@ use PhpParser\Node;
use PhpParser\Node\Expr\Closure;
use PhpParser\Node\Expr\ConstFetch;
use PhpParser\Node\Identifier;
use PhpParser\Node\IntersectionType;
use PhpParser\Node\Name;
use PhpParser\Node\NullableType;
use PhpParser\Node\Stmt\Function_;
use PhpParser\Node\Stmt\Return_;
@@ -32,7 +34,7 @@ class ReturnTypePass extends CodeCleanerPass
const VOID_NULL_MESSAGE = 'A void function must not return a value (did you mean "return;" instead of "return null;"?)';
const NULLABLE_VOID_MESSAGE = 'Void type cannot be nullable';
private $returnTypeStack = [];
private array $returnTypeStack = [];
/**
* {@inheritdoc}
@@ -100,12 +102,16 @@ class ReturnTypePass extends CodeCleanerPass
return \implode('|', \array_map([$this, 'typeName'], $node->types));
}
if ($node instanceof NullableType) {
return \strtolower($node->type->name);
if ($node instanceof IntersectionType) {
return \implode('&', \array_map([$this, 'typeName'], $node->types));
}
if ($node instanceof Identifier) {
return \strtolower($node->name);
if ($node instanceof NullableType) {
return $this->typeName($node->type);
}
if ($node instanceof Identifier || $node instanceof Name) {
return $node->toLowerString();
}
throw new \InvalidArgumentException('Unable to find type name');

View File

@@ -12,6 +12,8 @@
namespace Psy\CodeCleaner;
use PhpParser\Node;
use PhpParser\Node\DeclareItem;
use PhpParser\Node\Scalar\Int_;
use PhpParser\Node\Scalar\LNumber;
use PhpParser\Node\Stmt\Declare_;
use PhpParser\Node\Stmt\DeclareDeclare;
@@ -31,7 +33,7 @@ class StrictTypesPass extends CodeCleanerPass
{
const EXCEPTION_MESSAGE = 'strict_types declaration must have 0 or 1 as its value';
private $strictTypes = false;
private bool $strictTypes;
/**
* @param bool $strictTypes enforce strict types by default
@@ -62,7 +64,8 @@ class StrictTypesPass extends CodeCleanerPass
foreach ($node->declares as $declare) {
if ($declare->key->toString() === 'strict_types') {
$value = $declare->value;
if (!$value instanceof LNumber || ($value->value !== 0 && $value->value !== 1)) {
// @todo Remove LNumber once we drop support for PHP-Parser 4.x
if ((!$value instanceof LNumber && !$value instanceof Int_) || ($value->value !== 0 && $value->value !== 1)) {
throw new FatalErrorException(self::EXCEPTION_MESSAGE, 0, \E_ERROR, null, $node->getStartLine());
}
@@ -75,7 +78,13 @@ class StrictTypesPass extends CodeCleanerPass
if ($prependStrictTypes) {
$first = \reset($nodes);
if (!$first instanceof Declare_) {
$declare = new Declare_([new DeclareDeclare('strict_types', new LNumber(1))]);
// @todo Switch to PhpParser\Node\DeclareItem once we drop support for PHP-Parser 4.x
// @todo Remove LNumber once we drop support for PHP-Parser 4.x
$arg = \class_exists('PhpParser\Node\Scalar\Int_') ? new Int_(1) : new LNumber(1);
$declareItem = \class_exists('PhpParser\Node\DeclareItem') ?
new DeclareItem('strict_types', $arg) :
new DeclareDeclare('strict_types', $arg);
$declare = new Declare_([$declareItem]);
\array_unshift($nodes, $declare);
}
}

View File

@@ -33,9 +33,9 @@ use PhpParser\NodeTraverser;
*/
class UseStatementPass extends CodeCleanerPass
{
private $aliases = [];
private $lastAliases = [];
private $lastNamespace = null;
private array $aliases = [];
private array $lastAliases = [];
private ?Name $lastNamespace = null;
/**
* Re-load the last set of use statements on re-entering a namespace.
@@ -77,6 +77,7 @@ class UseStatementPass extends CodeCleanerPass
$this->aliases[\strtolower($useItem->getAlias())] = $useItem->name;
}
// @todo Rename to Node_Visitor::REMOVE_NODE once we drop support for PHP-Parser 4.x
return NodeTraverser::REMOVE_NODE;
}
@@ -89,6 +90,7 @@ class UseStatementPass extends CodeCleanerPass
]);
}
// @todo Rename to Node_Visitor::REMOVE_NODE once we drop support for PHP-Parser 4.x
return NodeTraverser::REMOVE_NODE;
}

View File

@@ -36,7 +36,7 @@ class ValidClassNamePass extends NamespaceAwarePass
const INTERFACE_TYPE = 'interface';
const TRAIT_TYPE = 'trait';
private $conditionalScopes = 0;
private int $conditionalScopes = 0;
/**
* Validate class, interface and trait definitions.

View File

@@ -33,7 +33,7 @@ use Psy\Exception\FatalErrorException;
*/
class ValidConstructorPass extends CodeCleanerPass
{
private $namespace;
private array $namespace = [];
/**
* @return Node[]|null Array of nodes

View File

@@ -27,7 +27,7 @@ use Psy\Exception\FatalErrorException;
*/
class ValidFunctionNamePass extends NamespaceAwarePass
{
private $conditionalScopes = 0;
private int $conditionalScopes = 0;
/**
* Store newly defined function names on the way in, to allow recursion.

View File

@@ -11,7 +11,6 @@
namespace Psy\Command;
use Psy\Exception\RuntimeException;
use Psy\Output\ShellOutput;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
@@ -52,14 +51,11 @@ HELP
*/
protected function execute(InputInterface $input, OutputInterface $output): int
{
$app = $this->getApplication();
if (!$app instanceof \Psy\Shell) {
throw new RuntimeException('Buffer command requires a \Psy\Shell application');
}
$shell = $this->getShell();
$buf = $app->getCodeBuffer();
$buf = $shell->getCodeBuffer();
if ($input->getOption('clear')) {
$app->resetCodeBuffer();
$shell->resetCodeBuffer();
$output->writeln($this->formatLines($buf, 'urgent'), ShellOutput::NUMBER_LINES);
} else {
$output->writeln($this->formatLines($buf), ShellOutput::NUMBER_LINES);

View File

@@ -20,9 +20,9 @@ use Psy\ParserFactory;
*/
class CodeArgumentParser
{
private $parser;
private Parser $parser;
public function __construct(Parser $parser = null)
public function __construct(?Parser $parser = null)
{
$this->parser = $parser ?? (new ParserFactory())->createParser();
}

View File

@@ -30,7 +30,7 @@ abstract class Command extends BaseCommand
*
* @api
*/
public function setApplication(Application $application = null): void
public function setApplication(?Application $application = null): void
{
if ($application !== null && !$application instanceof Shell) {
throw new \InvalidArgumentException('PsySH Commands require an instance of Psy\Shell');
@@ -39,6 +39,19 @@ abstract class Command extends BaseCommand
parent::setApplication($application);
}
/**
* getApplication, but is guaranteed to return a Shell instance.
*/
protected function getShell(): Shell
{
$shell = $this->getApplication();
if (!$shell instanceof Shell) {
throw new \RuntimeException('PsySH Commands require an instance of Psy\Shell');
}
return $shell;
}
/**
* {@inheritdoc}
*/
@@ -140,9 +153,11 @@ abstract class Command extends BaseCommand
$default = '';
}
$name = $argument->getName();
$pad = \str_pad('', $max - \strlen($name));
$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>%s</info>%s %s%s', $name, $pad, $description, $default);
}
$messages[] = '';

View File

@@ -73,7 +73,7 @@ HELP
$doc = $this->getManualDoc($reflector) ?: DocblockFormatter::format($reflector);
}
$db = $this->getApplication()->getManualDb();
$db = $this->getShell()->getManualDb();
if ($output instanceof ShellOutput) {
$output->startPaging();
@@ -242,7 +242,7 @@ HELP
private function getManualDocById($id)
{
if ($db = $this->getApplication()->getManualDb()) {
if ($db = $this->getShell()->getManualDb()) {
$result = $db->query(\sprintf('SELECT doc FROM php_manual WHERE id = %s', $db->quote($id)));
if ($result !== false) {
return $result->fetchColumn(0);

View File

@@ -11,7 +11,9 @@
namespace Psy\Command;
use Psy\Exception\RuntimeException;
use Psy\Input\CodeArgument;
use Psy\Output\ShellOutput;
use Psy\VarDumper\Presenter;
use Psy\VarDumper\PresenterAware;
use Symfony\Component\Console\Input\InputInterface;
@@ -25,7 +27,7 @@ use Symfony\Component\Console\Output\OutputInterface;
*/
class DumpCommand extends ReflectingCommand implements PresenterAware
{
private $presenter;
private Presenter $presenter;
/**
* PresenterAware interface.
@@ -71,6 +73,10 @@ HELP
*/
protected function execute(InputInterface $input, OutputInterface $output): int
{
if (!$output instanceof ShellOutput) {
throw new RuntimeException('DumpCommand requires a ShellOutput');
}
$depth = $input->getOption('depth');
$target = $this->resolveCode($input->getArgument('target'));
$output->page($this->presenter->present($target, $depth, $input->getOption('all') ? Presenter::VERBOSE : 0));

View File

@@ -11,6 +11,7 @@
namespace Psy\Command;
use Psy\ConfigPaths;
use Psy\Context;
use Psy\ContextAware;
use Symfony\Component\Console\Input\InputArgument;
@@ -20,15 +21,8 @@ use Symfony\Component\Console\Output\OutputInterface;
class EditCommand extends Command implements ContextAware
{
/**
* @var string
*/
private $runtimeDir = '';
/**
* @var Context
*/
private $context;
private string $runtimeDir = '';
private Context $context;
/**
* Constructor.
@@ -97,6 +91,7 @@ class EditCommand extends Command implements ContextAware
$shouldRemoveFile = false;
if ($filePath === null) {
ConfigPaths::ensureDir($this->runtimeDir);
$filePath = \tempnam($this->runtimeDir, 'psysh-edit-command');
$shouldRemoveFile = true;
}
@@ -104,7 +99,7 @@ class EditCommand extends Command implements ContextAware
$editedContent = $this->editFile($filePath, $shouldRemoveFile);
if ($execute) {
$this->getApplication()->addInput($editedContent);
$this->getShell()->addInput($editedContent);
}
return 0;
@@ -115,7 +110,7 @@ class EditCommand extends Command implements ContextAware
* @param bool $noExecOption
* @param string|null $filePath
*/
private function shouldExecuteFile(bool $execOption, bool $noExecOption, string $filePath = null): bool
private function shouldExecuteFile(bool $execOption, bool $noExecOption, ?string $filePath = null): bool
{
if ($execOption) {
return true;
@@ -136,7 +131,7 @@ class EditCommand extends Command implements ContextAware
*
* @throws \InvalidArgumentException If the variable is not found in the current context
*/
private function extractFilePath(string $fileArgument = null)
private function extractFilePath(?string $fileArgument = null)
{
// If the file argument was a variable, get it from the context
if ($fileArgument !== null &&

View File

@@ -23,7 +23,7 @@ use Symfony\Component\Console\Output\OutputInterface;
*/
class HelpCommand extends Command
{
private $command;
private ?Command $command = null;
/**
* {@inheritdoc}

View File

@@ -26,8 +26,8 @@ use Symfony\Component\Console\Output\OutputInterface;
*/
class HistoryCommand extends Command
{
private $filter;
private $readline;
private FilterOptions $filter;
private Readline $readline;
/**
* {@inheritdoc}
@@ -135,7 +135,8 @@ HELP
$count = \count($history);
$output->writeln(\sprintf('Replaying %d line%s of history', $count, ($count !== 1) ? 's' : ''));
$this->getApplication()->addInput($history);
$this->getShell()->addInput($history);
} elseif ($input->getOption('clear')) {
$this->clearHistory();
$output->writeln('<info>History cleared.</info>');
@@ -156,12 +157,12 @@ HELP
*
* @param string $range
*
* @return array [ start, end ]
* @return int[] [ start, end ]
*/
private function extractRange(string $range): array
{
if (\preg_match('/^\d+$/', $range)) {
return [$range, $range + 1];
return [(int) $range, (int) $range + 1];
}
$matches = [];
@@ -206,7 +207,7 @@ HELP
throw new \InvalidArgumentException('Please specify an integer argument for --tail');
}
$start = \count($history) - $tail;
$start = \count($history) - (int) $tail;
$length = (int) $tail + 1;
} else {
return $history;

View File

@@ -35,8 +35,8 @@ use Symfony\Component\Console\Output\OutputInterface;
*/
class ListCommand extends ReflectingCommand implements PresenterAware
{
protected $presenter;
protected $enumerators;
protected Presenter $presenter;
protected array $enumerators;
/**
* PresenterAware interface.

View File

@@ -21,7 +21,7 @@ class ClassConstantEnumerator extends Enumerator
/**
* {@inheritdoc}
*/
protected function listItems(InputInterface $input, \Reflector $reflector = null, $target = null): array
protected function listItems(InputInterface $input, ?\Reflector $reflector = null, $target = null): array
{
// only list constants when a Reflector is present.
if ($reflector === null) {

View File

@@ -22,7 +22,7 @@ class ClassEnumerator extends Enumerator
/**
* {@inheritdoc}
*/
protected function listItems(InputInterface $input, \Reflector $reflector = null, $target = null): array
protected function listItems(InputInterface $input, ?\Reflector $reflector = null, $target = null): array
{
// if we have a reflector, ensure that it's a namespace reflector
if (($target !== null || $reflector !== null) && !$reflector instanceof ReflectionNamespace) {
@@ -66,7 +66,7 @@ class ClassEnumerator extends Enumerator
*
* @return array
*/
protected function filterClasses(string $key, array $classes, bool $internal, bool $user, string $prefix = null): array
protected function filterClasses(string $key, array $classes, bool $internal, bool $user, ?string $prefix = null): array
{
$ret = [];

View File

@@ -20,7 +20,7 @@ use Symfony\Component\Console\Input\InputInterface;
class ConstantEnumerator extends Enumerator
{
// Because `Json` is ugly.
private static $categoryLabels = [
private const CATEGORY_LABELS = [
'libxml' => 'libxml',
'openssl' => 'OpenSSL',
'pcre' => 'PCRE',
@@ -49,7 +49,7 @@ class ConstantEnumerator extends Enumerator
/**
* {@inheritdoc}
*/
protected function listItems(InputInterface $input, \Reflector $reflector = null, $target = null): array
protected function listItems(InputInterface $input, ?\Reflector $reflector = null, $target = null): array
{
// if we have a reflector, ensure that it's a namespace reflector
if (($target !== null || $reflector !== null) && !$reflector instanceof ReflectionNamespace) {
@@ -88,7 +88,7 @@ class ConstantEnumerator extends Enumerator
}
if ($category) {
$caseCategory = \array_key_exists($category, self::$categoryLabels) ? self::$categoryLabels[$category] : \ucfirst($category);
$caseCategory = \array_key_exists($category, self::CATEGORY_LABELS) ? self::CATEGORY_LABELS[$category] : \ucfirst($category);
$label = $caseCategory.' Constants';
$ret[$label] = $this->getConstants($category);
}
@@ -122,7 +122,7 @@ class ConstantEnumerator extends Enumerator
*
* @return array
*/
protected function getConstants(string $category = null): array
protected function getConstants(?string $category = null): array
{
if (!$category) {
return \get_defined_constants();

View File

@@ -31,8 +31,8 @@ abstract class Enumerator
const IS_CLASS = 'class';
const IS_FUNCTION = 'function';
private $filter;
private $presenter;
private FilterOptions $filter;
private Presenter $presenter;
/**
* Enumerator constructor.
@@ -54,7 +54,7 @@ abstract class Enumerator
*
* @return array
*/
public function enumerate(InputInterface $input, \Reflector $reflector = null, $target = null): array
public function enumerate(InputInterface $input, ?\Reflector $reflector = null, $target = null): array
{
$this->filter->bind($input);
@@ -82,7 +82,7 @@ abstract class Enumerator
*
* @return array
*/
abstract protected function listItems(InputInterface $input, \Reflector $reflector = null, $target = null): array;
abstract protected function listItems(InputInterface $input, ?\Reflector $reflector = null, $target = null): array;
protected function showItem($name)
{

View File

@@ -22,7 +22,7 @@ class FunctionEnumerator extends Enumerator
/**
* {@inheritdoc}
*/
protected function listItems(InputInterface $input, \Reflector $reflector = null, $target = null): array
protected function listItems(InputInterface $input, ?\Reflector $reflector = null, $target = null): array
{
// if we have a reflector, ensure that it's a namespace reflector
if (($target !== null || $reflector !== null) && !$reflector instanceof ReflectionNamespace) {
@@ -67,7 +67,7 @@ class FunctionEnumerator extends Enumerator
*
* @return array
*/
protected function getFunctions(string $type = null): array
protected function getFunctions(?string $type = null): array
{
$funcs = \get_defined_functions();
@@ -86,7 +86,7 @@ class FunctionEnumerator extends Enumerator
*
* @return array
*/
protected function prepareFunctions(array $functions, string $prefix = null): array
protected function prepareFunctions(array $functions, ?string $prefix = null): array
{
\natcasesort($functions);

View File

@@ -21,7 +21,7 @@ class GlobalVariableEnumerator extends Enumerator
/**
* {@inheritdoc}
*/
protected function listItems(InputInterface $input, \Reflector $reflector = null, $target = null): array
protected function listItems(InputInterface $input, ?\Reflector $reflector = null, $target = null): array
{
// only list globals when no Reflector is present.
if ($reflector !== null || $target !== null) {

View File

@@ -21,7 +21,7 @@ class MethodEnumerator extends Enumerator
/**
* {@inheritdoc}
*/
protected function listItems(InputInterface $input, \Reflector $reflector = null, $target = null): array
protected function listItems(InputInterface $input, ?\Reflector $reflector = null, $target = null): array
{
// only list methods when a Reflector is present.
if ($reflector === null) {

View File

@@ -21,7 +21,7 @@ class PropertyEnumerator extends Enumerator
/**
* {@inheritdoc}
*/
protected function listItems(InputInterface $input, \Reflector $reflector = null, $target = null): array
protected function listItems(InputInterface $input, ?\Reflector $reflector = null, $target = null): array
{
// only list properties when a Reflector is present.

View File

@@ -21,7 +21,7 @@ use Symfony\Component\Console\Input\InputInterface;
class VariableEnumerator extends Enumerator
{
// n.b. this array is the order in which special variables will be listed
private static $specialNames = [
private const SPECIAL_NAMES = [
'_', '_e', '__out', '__function', '__method', '__class', '__namespace', '__file', '__line', '__dir',
];
@@ -45,7 +45,7 @@ class VariableEnumerator extends Enumerator
/**
* {@inheritdoc}
*/
protected function listItems(InputInterface $input, \Reflector $reflector = null, $target = null): array
protected function listItems(InputInterface $input, ?\Reflector $reflector = null, $target = null): array
{
// only list variables when no Reflector is present.
if ($reflector !== null || $target !== null) {
@@ -80,8 +80,8 @@ class VariableEnumerator extends Enumerator
{
$scopeVars = $this->context->getAll();
\uksort($scopeVars, function ($a, $b) {
$aIndex = \array_search($a, self::$specialNames);
$bIndex = \array_search($b, self::$specialNames);
$aIndex = \array_search($a, self::SPECIAL_NAMES);
$bIndex = \array_search($b, self::SPECIAL_NAMES);
if ($aIndex !== false) {
if ($bIndex !== false) {
@@ -100,7 +100,7 @@ class VariableEnumerator extends Enumerator
$ret = [];
foreach ($scopeVars as $name => $val) {
if (!$showAll && \in_array($name, self::$specialNames)) {
if (!$showAll && \in_array($name, self::SPECIAL_NAMES)) {
continue;
}
@@ -126,7 +126,7 @@ class VariableEnumerator extends Enumerator
$fname = '$'.$name;
$ret[$fname] = [
'name' => $fname,
'style' => \in_array($name, self::$specialNames) ? self::IS_PRIVATE : self::IS_PUBLIC,
'style' => \in_array($name, self::SPECIAL_NAMES) ? self::IS_PRIVATE : self::IS_PUBLIC,
'value' => $this->presentRef($val),
];
}

View File

@@ -12,6 +12,7 @@
namespace Psy\Command;
use PhpParser\Node;
use PhpParser\Parser;
use Psy\Context;
use Psy\ContextAware;
use Psy\Input\CodeArgument;
@@ -28,15 +29,9 @@ use Symfony\Component\VarDumper\Caster\Caster;
*/
class ParseCommand extends Command implements ContextAware, PresenterAware
{
/**
* Context instance (for ContextAware interface).
*
* @var Context
*/
protected $context;
private $presenter;
private $parser;
protected Context $context;
private Presenter $presenter;
private Parser $parser;
/**
* {@inheritdoc}
@@ -90,9 +85,9 @@ class ParseCommand extends Command implements ContextAware, PresenterAware
$this
->setName('parse')
->setDefinition([
new CodeArgument('code', CodeArgument::REQUIRED, 'PHP code to parse.'),
new InputOption('depth', '', InputOption::VALUE_REQUIRED, 'Depth to parse.', 10),
])
new CodeArgument('code', CodeArgument::REQUIRED, 'PHP code to parse.'),
new InputOption('depth', '', InputOption::VALUE_REQUIRED, 'Depth to parse.', 10),
])
->setDescription('Parse PHP code and show the abstract syntax tree.')
->setHelp(
<<<'HELP'
@@ -114,7 +109,6 @@ HELP
protected function execute(InputInterface $input, OutputInterface $output): int
{
$code = $input->getArgument('code');
$parserKind = $input->getOption('kind');
$depth = $input->getOption('depth');
$nodes = $this->parser->parse($code);

View File

@@ -33,16 +33,10 @@ abstract class ReflectingCommand extends Command implements ContextAware
const CLASS_STATIC = '/^([\\\\\w]+)::\$(\w+)$/';
const INSTANCE_MEMBER = '/^(\$\w+)(::|->)(\w+)$/';
/**
* Context instance (for ContextAware interface).
*
* @var Context
*/
protected $context;
private $parser;
private $traverser;
private $printer;
protected Context $context;
private CodeArgumentParser $parser;
private NodeTraverser $traverser;
private Printer $printer;
/**
* {@inheritdoc}
@@ -51,6 +45,7 @@ abstract class ReflectingCommand extends Command implements ContextAware
{
$this->parser = new CodeArgumentParser();
// @todo Pass visitor directly to once we drop support for PHP-Parser 4.x
$this->traverser = new NodeTraverser();
$this->traverser->addVisitor(new SudoVisitor());
@@ -116,7 +111,7 @@ abstract class ReflectingCommand extends Command implements ContextAware
*/
protected function resolveName(string $name, bool $includeFunctions = false): string
{
$shell = $this->getApplication();
$shell = $this->getShell();
// While not *technically* 100% accurate, let's treat `self` and `static` as equivalent.
if (\in_array(\strtolower($name), ['self', 'static'])) {
@@ -194,7 +189,7 @@ abstract class ReflectingCommand extends Command implements ContextAware
// Add an implicit `sudo` to target resolution.
$nodes = $this->traverser->traverse($this->parser->parse($code));
$sudoCode = $this->printer->prettyPrint($nodes);
$value = $this->getApplication()->execute($sudoCode, true);
$value = $this->getShell()->execute($sudoCode, true);
} catch (\Throwable $e) {
// Swallow all exceptions?
}

View File

@@ -26,8 +26,8 @@ use Symfony\Component\Console\Output\OutputInterface;
*/
class ShowCommand extends ReflectingCommand
{
private $lastException;
private $lastExceptionIndex;
private ?\Throwable $lastException = null;
private ?int $lastExceptionIndex = null;
/**
* {@inheritdoc}
@@ -46,7 +46,7 @@ class ShowCommand extends ReflectingCommand
Show the code for an object, class, constant, method or property, or the context
of the last exception.
<return>cat --ex</return> defaults to showing the lines surrounding the location of the last
<return>show --ex</return> defaults to showing the lines surrounding the location of the last
exception. Invoking it more than once travels up the exception's stack trace,
and providing a number shows the context of the given index of the trace.
@@ -167,7 +167,7 @@ HELP
$this->lastException = $exception;
$this->lastExceptionIndex = $index;
$output->writeln($this->getApplication()->formatException($exception));
$output->writeln($this->getShell()->formatException($exception));
$output->writeln('--');
$this->writeTraceLine($output, $trace, $index);
$this->writeTraceCodeSnippet($output, $trace, $index);

View File

@@ -24,10 +24,10 @@ use Symfony\Component\Console\Output\OutputInterface;
*/
class SudoCommand extends Command
{
private $readline;
private $parser;
private $traverser;
private $printer;
private Readline $readline;
private CodeArgumentParser $parser;
private NodeTraverser $traverser;
private Printer $printer;
/**
* {@inheritdoc}
@@ -36,6 +36,7 @@ class SudoCommand extends Command
{
$this->parser = new CodeArgumentParser();
// @todo Pass visitor directly to once we drop support for PHP-Parser 4.x
$this->traverser = new NodeTraverser();
$this->traverser->addVisitor(new SudoVisitor());
@@ -112,7 +113,8 @@ HELP
$nodes = $this->traverser->traverse($this->parser->parse($code));
$sudoCode = $this->printer->prettyPrint($nodes);
$shell = $this->getApplication();
$shell = $this->getShell();
$shell->addCode($sudoCode, !$shell->hasCode());
return 0;

View File

@@ -29,8 +29,8 @@ use Symfony\Component\Console\Output\OutputInterface;
*/
class ThrowUpCommand extends Command
{
private $parser;
private $printer;
private CodeArgumentParser $parser;
private Printer $printer;
/**
* {@inheritdoc}
@@ -82,7 +82,7 @@ HELP
$throwStmt = new Expression(new Throw_(new New_(new FullyQualifiedName(ThrowUpException::class), $args)));
$throwCode = $this->printer->prettyPrint([$throwStmt]);
$shell = $this->getApplication();
$shell = $this->getShell();
$shell->addCode($throwCode, !$shell->hasCode());
return 0;
@@ -99,7 +99,7 @@ HELP
*
* @return Arg[]
*/
private function prepareArgs(string $code = null): array
private function prepareArgs(?string $code = null): array
{
if (!$code) {
// Default to last exception if nothing else was supplied

View File

@@ -28,12 +28,12 @@ class TimeitCommand extends Command
const AVG_RESULT_MSG = '<info>Command took %.6f seconds on average (%.6f median; %.6f total) to complete.</info>';
// All times stored as nanoseconds!
private static $start = null;
private static $times = [];
private static ?int $start = null;
private static array $times = [];
private $parser;
private $traverser;
private $printer;
private CodeArgumentParser $parser;
private NodeTraverser $traverser;
private Printer $printer;
/**
* {@inheritdoc}
@@ -42,6 +42,7 @@ class TimeitCommand extends Command
{
$this->parser = new CodeArgumentParser();
// @todo Pass visitor directly to once we drop support for PHP-Parser 4.x
$this->traverser = new NodeTraverser();
$this->traverser->addVisitor(new TimeitVisitor());
@@ -82,7 +83,8 @@ HELP
{
$code = $input->getArgument('code');
$num = (int) ($input->getOption('num') ?: 1);
$shell = $this->getApplication();
$shell = $this->getShell();
$instrumentedCode = $this->instrumentCode($code);

View File

@@ -31,7 +31,7 @@ use Psy\Command\TimeitCommand;
*/
class TimeitVisitor extends NodeVisitorAbstract
{
private $functionDepth;
private int $functionDepth = 0;
/**
* {@inheritdoc}
@@ -120,7 +120,7 @@ class TimeitVisitor extends NodeVisitorAbstract
*
* @param Expr|null $arg
*/
private function getEndCall(Expr $arg = null): StaticCall
private function getEndCall(?Expr $arg = null): StaticCall
{
if ($arg === null) {
$arg = NoReturnValue::create();

View File

@@ -92,7 +92,7 @@ HELP
*
* @return array Formatted stacktrace lines
*/
protected function getBacktrace(\Throwable $e, int $count = null, bool $includePsy = true): array
protected function getBacktrace(\Throwable $e, ?int $count = null, bool $includePsy = true): array
{
return TraceFormatter::formatTrace($e, $this->filter, $count, $includePsy);
}

View File

@@ -23,7 +23,7 @@ use Symfony\Component\Console\Output\OutputInterface;
*/
class WhereamiCommand extends Command
{
private $backtrace;
private array $backtrace;
public function __construct()
{

View File

@@ -25,12 +25,7 @@ use Symfony\Component\Console\Output\OutputInterface;
*/
class WtfCommand extends TraceCommand implements ContextAware
{
/**
* Context instance (for ContextAware interface).
*
* @var Context
*/
protected $context;
protected Context $context;
/**
* ContextAware interface.
@@ -96,8 +91,6 @@ HELP
$exception = $this->context->getLastException();
$count = $input->getOption('all') ? \PHP_INT_MAX : \max(3, \pow(2, \strlen($incredulity) + 1));
$shell = $this->getApplication();
if ($output instanceof ShellOutput) {
$output->startPaging();
}
@@ -113,7 +106,7 @@ HELP
$trace = $this->getBacktrace($exception, $showLines);
$moreLines = $traceCount - \count($trace);
$output->writeln($shell->formatException($exception));
$output->writeln($this->getShell()->formatException($exception));
$output->writeln('--');
$output->write($trace, true, ShellOutput::NUMBER_LINES);
$output->writeln('');

View File

@@ -16,10 +16,10 @@ namespace Psy;
*/
class ConfigPaths
{
private $configDir;
private $dataDir;
private $runtimeDir;
private $env;
private ?string $configDir = null;
private ?string $dataDir = null;
private ?string $runtimeDir = null;
private EnvInterface $env;
/**
* ConfigPaths constructor.
@@ -31,7 +31,7 @@ class ConfigPaths
* @param string[] $overrides Directory overrides
* @param EnvInterface $env
*/
public function __construct(array $overrides = [], EnvInterface $env = null)
public function __construct(array $overrides = [], ?EnvInterface $env = null)
{
$this->overrideDirs($overrides);
@@ -63,10 +63,8 @@ class ConfigPaths
/**
* Get the current home directory.
*
* @return string|null
*/
public function homeDir()
public function homeDir(): ?string
{
if ($homeDir = $this->getEnv('HOME') ?: $this->windowsHomeDir()) {
return \strtr($homeDir, '\\', '/');
@@ -75,7 +73,7 @@ class ConfigPaths
return null;
}
private function windowsHomeDir()
private function windowsHomeDir(): ?string
{
if (\defined('PHP_WINDOWS_VERSION_MAJOR')) {
$homeDrive = $this->getEnv('HOMEDRIVE');
@@ -88,13 +86,16 @@ class ConfigPaths
return null;
}
private function homeConfigDir()
private function homeConfigDir(): ?string
{
if ($homeConfigDir = $this->getEnv('XDG_CONFIG_HOME')) {
return $homeConfigDir;
}
$homeDir = $this->homeDir();
if ($homeDir === null) {
return null;
}
return $homeDir === '/' ? $homeDir.'.config' : $homeDir.'/.config';
}
@@ -130,7 +131,7 @@ class ConfigPaths
*
* @see self::homeConfigDir
*/
public function currentConfigDir(): string
public function currentConfigDir(): ?string
{
if ($this->configDir !== null) {
return $this->configDir;
@@ -144,7 +145,7 @@ class ConfigPaths
}
}
return $configDirs[0];
return $configDirs[0] ?? null;
}
/**
@@ -231,11 +232,13 @@ class ConfigPaths
* If $PATH is unset/empty it defaults to '/usr/sbin:/usr/bin:/sbin:/bin'.
*
* @param string $command the executable to locate
*
* @return string
*/
public function which($command)
public function which($command): ?string
{
if (!\is_string($command) || $command === '') {
return null;
}
foreach ($this->pathDirs() as $path) {
$fullpath = $path.\DIRECTORY_SEPARATOR.$command;
if (@\is_file($fullpath) && @\is_executable($fullpath)) {
@@ -259,6 +262,7 @@ class ConfigPaths
*/
private function allDirNames(array $baseDirs): array
{
$baseDirs = \array_filter($baseDirs);
$dirs = \array_map(function ($dir) {
return \strtr($dir, '\\', '/').'/psysh';
}, $baseDirs);
@@ -362,12 +366,12 @@ class ConfigPaths
return $file;
}
private function getEnv($key)
private function getEnv(string $key)
{
return $this->env->get($key);
}
private function getEnvArray($key)
private function getEnvArray(string $key)
{
if ($value = $this->getEnv($key)) {
return \explode(\PATH_SEPARATOR, $value);

View File

@@ -47,7 +47,7 @@ class Configuration
const VERBOSITY_VERY_VERBOSE = 'very_verbose';
const VERBOSITY_DEBUG = 'debug';
private static $AVAILABLE_OPTIONS = [
private const AVAILABLE_OPTIONS = [
'codeCleaner',
'colorMode',
'configDir',
@@ -80,58 +80,57 @@ class Configuration
'yolo',
];
private $defaultIncludes;
private $configDir;
private $dataDir;
private $runtimeDir;
private $configFile;
/** @var string|false */
private ?array $defaultIncludes = null;
private ?string $configDir = null;
private ?string $dataDir = null;
private ?string $runtimeDir = null;
private ?string $configFile = null;
/** @var string|false|null */
private $historyFile;
private $historySize;
private $eraseDuplicates;
private $manualDbFile;
private $hasReadline;
private $useReadline;
private $useBracketedPaste;
private $hasPcntl;
private $usePcntl;
private $newCommands = [];
private $pipedInput;
private $pipedOutput;
private $rawOutput = false;
private $requireSemicolons = false;
private $strictTypes = false;
private $useUnicode;
private $useTabCompletion;
private $newMatchers = [];
private $errorLoggingLevel = \E_ALL;
private $warnOnMultipleConfigs = false;
private $colorMode = self::COLOR_MODE_AUTO;
private $interactiveMode = self::INTERACTIVE_MODE_AUTO;
private $updateCheck;
private $startupMessage;
private $forceArrayIndexes = false;
private int $historySize = 0;
private ?bool $eraseDuplicates = null;
private ?string $manualDbFile = null;
private bool $hasReadline;
private ?bool $useReadline = null;
private bool $useBracketedPaste = false;
private bool $hasPcntl;
private ?bool $usePcntl = null;
private array $newCommands = [];
private ?bool $pipedInput = null;
private ?bool $pipedOutput = null;
private bool $rawOutput = false;
private bool $requireSemicolons = false;
private bool $strictTypes = false;
private ?bool $useUnicode = null;
private ?bool $useTabCompletion = null;
private array $newMatchers = [];
private int $errorLoggingLevel = \E_ALL;
private bool $warnOnMultipleConfigs = false;
private string $colorMode = self::COLOR_MODE_AUTO;
private string $interactiveMode = self::INTERACTIVE_MODE_AUTO;
private ?string $updateCheck = null;
private ?string $startupMessage = null;
private bool $forceArrayIndexes = false;
/** @deprecated */
private $formatterStyles = [];
private $verbosity = self::VERBOSITY_NORMAL;
private $yolo = false;
/** @var Theme */
private $theme;
private array $formatterStyles = [];
private string $verbosity = self::VERBOSITY_NORMAL;
private bool $yolo = false;
private ?Theme $theme = null;
// services
private $readline;
/** @var ShellOutput */
private $output;
private $shell;
private $cleaner;
private $pager;
private $manualDb;
private $presenter;
private $autoCompleter;
private $checker;
private ?Readline\Readline $readline = null;
private ?ShellOutput $output = null;
private ?Shell $shell = null;
private ?CodeCleaner $cleaner = null;
/** @var string|OutputPager|false|null */
private $pager = null;
private ?\PDO $manualDb = null;
private ?Presenter $presenter = null;
private ?AutoCompleter $autoCompleter = null;
private ?Checker $checker = null;
/** @deprecated */
private $prompt;
private $configPaths;
private ?string $prompt = null;
private ConfigPaths $configPaths;
/**
* Construct a Configuration instance.
@@ -310,6 +309,11 @@ class Configuration
return self::VERBOSITY_VERY_VERBOSE;
case '3':
case 'vv': // `-vvv`
case 'vvv':
case 'vvvv':
case 'vvvvv':
case 'vvvvvv':
case 'vvvvvvv':
return self::VERBOSITY_DEBUG;
default: // implicitly normal, config file default wins
return;
@@ -461,7 +465,7 @@ class Configuration
*/
public function loadConfig(array $options)
{
foreach (self::$AVAILABLE_OPTIONS as $option) {
foreach (self::AVAILABLE_OPTIONS as $option) {
if (isset($options[$option])) {
$method = 'set'.\ucfirst($option);
$this->$method($options[$option]);
@@ -618,17 +622,19 @@ class Configuration
/**
* Get the shell's temporary directory location.
*
* Defaults to `/psysh` inside the system's temp dir unless explicitly
* Defaults to `/psysh` inside the system's temp dir unless explicitly
* overridden.
*
* @throws RuntimeException if no temporary directory is set and it is not possible to create one
*
* @param bool $create False to suppress directory creation if it does not exist
*/
public function getRuntimeDir(): string
public function getRuntimeDir($create = true): string
{
$runtimeDir = $this->configPaths->runtimeDir();
if (!\is_dir($runtimeDir)) {
if (!@\mkdir($runtimeDir, 0700, true)) {
if ($create) {
if (!@ConfigPaths::ensureDir($runtimeDir)) {
throw new RuntimeException(\sprintf('Unable to create PsySH runtime directory. Make sure PHP is able to write to %s in order to continue.', \dirname($runtimeDir)));
}
}
@@ -652,7 +658,7 @@ class Configuration
* Defaults to `/history` inside the shell's base config dir unless
* explicitly overridden.
*/
public function getHistoryFile(): string
public function getHistoryFile(): ?string
{
if (isset($this->historyFile)) {
return $this->historyFile;
@@ -669,7 +675,12 @@ class Configuration
$this->setHistoryFile($files[0]);
} else {
// fallback: create our own history file
$this->setHistoryFile($this->configPaths->currentConfigDir().'/psysh_history');
$configDir = $this->configPaths->currentConfigDir();
if ($configDir === null) {
return null;
}
$this->setHistoryFile($configDir.'/psysh_history');
}
return $this->historyFile;
@@ -702,7 +713,7 @@ class Configuration
*/
public function setEraseDuplicates(bool $value)
{
$this->eraseDuplicates = (bool) $value;
$this->eraseDuplicates = $value;
}
/**
@@ -808,7 +819,7 @@ class Configuration
$this->readline = new $className(
$this->getHistoryFile(),
$this->getHistorySize(),
$this->getEraseDuplicates()
$this->getEraseDuplicates() ?? false
);
}
@@ -1018,7 +1029,11 @@ class Configuration
*/
public function setErrorLoggingLevel($errorLoggingLevel)
{
$this->errorLoggingLevel = (\E_ALL | \E_STRICT) & $errorLoggingLevel;
if (\PHP_VERSION_ID < 80400) {
$this->errorLoggingLevel = (\E_ALL | \E_STRICT) & $errorLoggingLevel;
} else {
$this->errorLoggingLevel = \E_ALL & $errorLoggingLevel;
}
}
/**
@@ -1253,6 +1268,18 @@ class Configuration
$this->pager = $pager;
} elseif ($less = $this->configPaths->which('less')) {
// check for the presence of less...
// n.b. The busybox less implementation is a bit broken, so
// let's not use it by default.
//
// See https://github.com/bobthecow/psysh/issues/778
if (@\is_link($less)) {
$link = @\readlink($less);
if ($link !== false && \strpos($link, 'busybox') !== false) {
return false;
}
}
$this->pager = $less.' -R -F -X';
}
}
@@ -1631,7 +1658,12 @@ class Configuration
*/
public function getUpdateCheckCacheFile()
{
return ConfigPaths::touchFileWithMkdir($this->configPaths->currentConfigDir().'/update_check.json');
$configDir = $this->configPaths->currentConfigDir();
if ($configDir === null) {
return false;
}
return ConfigPaths::touchFileWithMkdir($configDir.'/update_check.json');
}
/**

View File

@@ -19,29 +19,28 @@ namespace Psy;
*/
class Context
{
private static $specialNames = ['_', '_e', '__out', '__psysh__', 'this'];
private const SPECIAL_NAMES = ['_', '_e', '__out', '__psysh__', 'this'];
// Include a very limited number of command-scope magic variable names.
// This might be a bad idea, but future me can sort it out.
private static $commandScopeNames = [
private const COMMAND_SCOPE_NAMES = [
'__function', '__method', '__class', '__namespace', '__file', '__line', '__dir',
];
private $scopeVariables = [];
private $commandScopeVariables = [];
private $returnValue;
private $lastException;
private $lastStdout;
private $boundObject;
private $boundClass;
private array $scopeVariables = [];
private array $commandScopeVariables = [];
/** @var mixed */
private $returnValue = null;
private ?\Throwable $lastException = null;
private ?string $lastStdout = null;
private ?object $boundObject = null;
private ?string $boundClass = null;
/**
* Get a context variable.
*
* @throws \InvalidArgumentException If the variable is not found in the current context
*
* @param string $name
*
* @return mixed
*/
public function get(string $name)
@@ -127,16 +126,14 @@ class Context
*
* This method does *not* set any of the magic variables: $_, $_e, $__out,
* $__class, $__file, etc.
*
* @param array $vars
*/
public function setAll(array $vars)
{
foreach (self::$specialNames as $key) {
foreach (self::SPECIAL_NAMES as $key) {
unset($vars[$key]);
}
foreach (self::$commandScopeNames as $key) {
foreach (self::COMMAND_SCOPE_NAMES as $key) {
unset($vars[$key]);
}
@@ -191,8 +188,6 @@ class Context
/**
* Set the most recent output from evaluated code.
*
* @param string $lastStdout
*/
public function setLastStdout(string $lastStdout)
{
@@ -263,15 +258,13 @@ class Context
/**
* Set command-scope magic variables: $__class, $__file, etc.
*
* @param array $commandScopeVariables
*/
public function setCommandScopeVariables(array $commandScopeVariables)
{
$vars = [];
foreach ($commandScopeVariables as $key => $value) {
// kind of type check
if (\is_scalar($value) && \in_array($key, self::$commandScopeNames)) {
if (\is_scalar($value) && \in_array($key, self::COMMAND_SCOPE_NAMES)) {
$vars[$key] = $value;
}
}
@@ -297,16 +290,14 @@ class Context
*/
public function getUnusedCommandScopeVariableNames(): array
{
return \array_diff(self::$commandScopeNames, \array_keys($this->commandScopeVariables));
return \array_diff(self::COMMAND_SCOPE_NAMES, \array_keys($this->commandScopeVariables));
}
/**
* Check whether a variable name is a magic variable.
*
* @param string $name
*/
public static function isSpecialVariableName(string $name): bool
{
return \in_array($name, self::$specialNames) || \in_array($name, self::$commandScopeNames);
return \in_array($name, self::SPECIAL_NAMES) || \in_array($name, self::COMMAND_SCOPE_NAMES);
}
}

View File

@@ -16,12 +16,12 @@ namespace Psy\Exception;
*/
class BreakException extends \Exception implements Exception
{
private $rawMessage;
private string $rawMessage;
/**
* {@inheritdoc}
*/
public function __construct($message = '', $code = 0, \Throwable $previous = null)
public function __construct($message = '', $code = 0, ?\Throwable $previous = null)
{
$this->rawMessage = $message;
parent::__construct(\sprintf('Exit: %s', $message), $code, $previous);

View File

@@ -16,7 +16,7 @@ namespace Psy\Exception;
*/
class ErrorException extends \ErrorException implements Exception
{
private $rawMessage;
private string $rawMessage;
/**
* Construct a Psy ErrorException.
@@ -28,7 +28,7 @@ class ErrorException extends \ErrorException implements Exception
* @param int|null $lineno (default: null)
* @param \Throwable|null $previous (default: null)
*/
public function __construct($message = '', $code = 0, $severity = 1, $filename = null, $lineno = null, \Throwable $previous = null)
public function __construct($message = '', $code = 0, $severity = 1, $filename = null, $lineno = null, ?\Throwable $previous = null)
{
$this->rawMessage = $message;
@@ -37,10 +37,6 @@ class ErrorException extends \ErrorException implements Exception
}
switch ($severity) {
case \E_STRICT:
$type = 'Strict error';
break;
case \E_NOTICE:
case \E_USER_NOTICE:
$type = 'Notice';
@@ -63,6 +59,10 @@ class ErrorException extends \ErrorException implements Exception
break;
default:
if (\PHP_VERSION_ID < 80400 && $severity === \E_STRICT) {
$type = 'Strict error';
break;
}
$type = 'Error';
break;
}

View File

@@ -16,7 +16,7 @@ namespace Psy\Exception;
*/
class FatalErrorException extends \ErrorException implements Exception
{
private $rawMessage;
private string $rawMessage;
/**
* Create a fatal error.
@@ -28,7 +28,7 @@ class FatalErrorException extends \ErrorException implements Exception
* @param int|null $lineno (default: null)
* @param \Throwable|null $previous (default: null)
*/
public function __construct($message = '', $code = 0, $severity = 1, $filename = null, $lineno = null, \Throwable $previous = null)
public function __construct($message = '', $code = 0, $severity = 1, $filename = null, $lineno = null, ?\Throwable $previous = null)
{
// Since these are basically always PHP Parser Node line numbers, treat -1 as null.
if ($lineno === -1) {

View File

@@ -16,7 +16,7 @@ namespace Psy\Exception;
*/
class RuntimeException extends \RuntimeException implements Exception
{
private $rawMessage;
private string $rawMessage;
/**
* Make this bad boy.
@@ -25,7 +25,7 @@ class RuntimeException extends \RuntimeException implements Exception
* @param int $code (default: 0)
* @param \Throwable|null $previous (default: null)
*/
public function __construct(string $message = '', int $code = 0, \Throwable $previous = null)
public function __construct(string $message = '', int $code = 0, ?\Throwable $previous = null)
{
$this->rawMessage = $message;
parent::__construct($message, $code, $previous);

View File

@@ -13,6 +13,7 @@ namespace Psy\Exception;
class UnexpectedTargetException extends RuntimeException
{
/** @var mixed */
private $target;
/**
@@ -21,7 +22,7 @@ class UnexpectedTargetException extends RuntimeException
* @param int $code (default: 0)
* @param \Throwable|null $previous (default: null)
*/
public function __construct($target, string $message = '', int $code = 0, \Throwable $previous = null)
public function __construct($target, string $message = '', int $code = 0, ?\Throwable $previous = null)
{
$this->target = $target;
parent::__construct($message, $code, $previous);

View File

@@ -18,7 +18,7 @@ class ExecutionClosure
{
const NOOP_INPUT = 'return null;';
private $closure;
private \Closure $closure;
/**
* @param Shell $__psysh__

View File

@@ -23,10 +23,11 @@ use Psy\Shell;
*/
class ProcessForker extends AbstractListener
{
private $savegame;
private ?int $savegame = null;
/** @var resource */
private $up;
private static $pcntlFunctions = [
private const PCNTL_FUNCTIONS = [
'pcntl_fork',
'pcntl_signal_dispatch',
'pcntl_signal',
@@ -34,7 +35,7 @@ class ProcessForker extends AbstractListener
'pcntl_wexitstatus',
];
private static $posixFunctions = [
private const POSIX_FUNCTIONS = [
'posix_getpid',
'posix_kill',
];
@@ -52,7 +53,7 @@ class ProcessForker extends AbstractListener
*/
public static function isPcntlSupported(): bool
{
foreach (self::$pcntlFunctions as $func) {
foreach (self::PCNTL_FUNCTIONS as $func) {
if (!\function_exists($func)) {
return false;
}
@@ -66,7 +67,7 @@ class ProcessForker extends AbstractListener
*/
public static function disabledPcntlFunctions()
{
return self::checkDisabledFunctions(self::$pcntlFunctions);
return self::checkDisabledFunctions(self::PCNTL_FUNCTIONS);
}
/**
@@ -74,7 +75,7 @@ class ProcessForker extends AbstractListener
*/
public static function isPosixSupported(): bool
{
foreach (self::$posixFunctions as $func) {
foreach (self::POSIX_FUNCTIONS as $func) {
if (!\function_exists($func)) {
return false;
}
@@ -88,7 +89,7 @@ class ProcessForker extends AbstractListener
*/
public static function disabledPosixFunctions()
{
return self::checkDisabledFunctions(self::$posixFunctions);
return self::checkDisabledFunctions(self::POSIX_FUNCTIONS);
}
private static function checkDisabledFunctions(array $functions): array

View File

@@ -11,6 +11,7 @@
namespace Psy\ExecutionLoop;
use PhpParser\Parser;
use Psy\Exception\ParseErrorException;
use Psy\ParserFactory;
use Psy\Shell;
@@ -22,8 +23,8 @@ use Psy\Shell;
*/
class RunkitReloader extends AbstractListener
{
private $parser;
private $timestamps = [];
private Parser $parser;
private array $timestamps = [];
/**
* Only enabled if Runkit is installed.

View File

@@ -35,7 +35,7 @@ class CodeFormatter implements ReflectorFormatter
const HIGHLIGHT_COMMENT = 'code_comment';
const HIGHLIGHT_INLINE_HTML = 'inline_html';
private static $tokenMap = [
private const TOKEN_MAP = [
// Not highlighted
\T_OPEN_TAG => self::HIGHLIGHT_DEFAULT,
\T_OPEN_TAG_WITH_ECHO => self::HIGHLIGHT_DEFAULT,
@@ -103,7 +103,7 @@ class CodeFormatter implements ReflectorFormatter
*
* @return string formatted code
*/
public static function formatCode(string $code, int $startLine = 1, int $endLine = null, int $markLine = null): string
public static function formatCode(string $code, int $startLine = 1, ?int $endLine = null, ?int $markLine = null): string
{
$spans = self::tokenizeSpans($code);
$lines = self::splitLines($spans, $startLine, $endLine);
@@ -187,8 +187,8 @@ class CodeFormatter implements ReflectorFormatter
return $currentType;
}
if (\array_key_exists($token[0], self::$tokenMap)) {
return self::$tokenMap[$token[0]];
if (\array_key_exists($token[0], self::TOKEN_MAP)) {
return self::TOKEN_MAP[$token[0]];
}
}
@@ -206,7 +206,7 @@ class CodeFormatter implements ReflectorFormatter
*
* @return \Generator lines, each an array of [$spanType, $spanText] pairs
*/
private static function splitLines(\Generator $spans, int $startLine = 1, int $endLine = null): \Generator
private static function splitLines(\Generator $spans, int $startLine = 1, ?int $endLine = null): \Generator
{
$lineNum = 1;
$buffer = [];
@@ -273,7 +273,7 @@ class CodeFormatter implements ReflectorFormatter
*
* @return \Generator Numbered, formatted lines
*/
private static function numberLines(\Generator $lines, int $markLine = null): \Generator
private static function numberLines(\Generator $lines, ?int $markLine = null): \Generator
{
$lines = \iterator_to_array($lines);

View File

@@ -19,7 +19,7 @@ use Symfony\Component\Console\Formatter\OutputFormatter;
*/
class DocblockFormatter implements ReflectorFormatter
{
private static $vectorParamTemplates = [
private const VECTOR_PARAM_TEMPLATES = [
'type' => 'info',
'var' => 'strong',
];
@@ -134,11 +134,11 @@ class DocblockFormatter implements ReflectorFormatter
*/
private static function getVectorParamTemplate(string $type, int $max): string
{
if (!isset(self::$vectorParamTemplates[$type])) {
if (!isset(self::VECTOR_PARAM_TEMPLATES[$type])) {
return \sprintf('%%-%ds', $max);
}
return \sprintf('<%s>%%-%ds</%s>', self::$vectorParamTemplates[$type], $max, self::$vectorParamTemplates[$type]);
return \sprintf('<%s>%%-%ds</%s>', self::VECTOR_PARAM_TEMPLATES[$type], $max, self::VECTOR_PARAM_TEMPLATES[$type]);
}
/**

View File

@@ -29,7 +29,7 @@ class TraceFormatter
*
* @return string[] Formatted stacktrace lines
*/
public static function formatTrace(\Throwable $throwable, FilterOptions $filter = null, int $count = null, bool $includePsy = true): array
public static function formatTrace(\Throwable $throwable, ?FilterOptions $filter = null, ?int $count = null, bool $includePsy = true): array
{
if ($cwd = \getcwd()) {
$cwd = \rtrim($cwd, \DIRECTORY_SEPARATOR).\DIRECTORY_SEPARATOR;

View File

@@ -39,7 +39,7 @@ class CodeArgument extends InputArgument
*
* @throws \InvalidArgumentException When argument mode is not valid
*/
public function __construct(string $name, int $mode = null, string $description = '', $default = null)
public function __construct(string $name, ?int $mode = null, string $description = '', $default = null)
{
if ($mode & InputArgument::IS_ARRAY) {
throw new \InvalidArgumentException('Argument mode IS_ARRAY is not valid');

View File

@@ -21,10 +21,10 @@ use Symfony\Component\Console\Input\InputOption;
*/
class FilterOptions
{
private $filter = false;
private $pattern;
private $insensitive;
private $invert;
private bool $filter = false;
private ?string $pattern = null;
private bool $insensitive = false;
private bool $invert = false;
/**
* Get input option definitions for filtering.
@@ -85,7 +85,7 @@ class FilterOptions
* @param string $string
* @param array $matches
*/
public function match(string $string, array &$matches = null): bool
public function match(string $string, ?array &$matches = null): bool
{
return $this->filter === false || (\preg_match($this->pattern, $string, $matches) xor $this->invert);
}

View File

@@ -21,14 +21,14 @@ class ShellInput extends StringInput
{
public const REGEX_STRING = '([^\s]+?)(?:\s|(?<!\\\\)"|(?<!\\\\)\'|$)';
private $hasCodeArgument = false;
private bool $hasCodeArgument = false;
/**
* Unlike the parent implementation's tokens, this contains an array of
* token/rest pairs, so that code arguments can be handled while parsing.
*/
private $tokenPairs;
private $parsed;
private array $tokenPairs;
private array $parsed = [];
/**
* Constructor.

View File

@@ -20,7 +20,7 @@ namespace Psy\Input;
*/
class SilentInput
{
private $inputString;
private string $inputString;
/**
* Constructor.

View File

@@ -23,10 +23,13 @@ use Symfony\Component\Console\Output\StreamOutput;
*/
class ProcOutputPager extends StreamOutput implements OutputPager
{
private $proc;
private $pipe;
/** @var ?resource */
private $proc = null;
/** @var ?resource */
private $pipe = null;
/** @var resource */
private $stream;
private $cmd;
private string $cmd;
/**
* Constructor.

View File

@@ -22,13 +22,9 @@ class ShellOutput extends ConsoleOutput
{
const NUMBER_LINES = 128;
private $paging = 0;
/** @var OutputPager */
private $pager;
/** @var Theme */
private $theme;
private int $paging = 0;
private OutputPager $pager;
private Theme $theme;
/**
* Construct a ShellOutput instance.
@@ -38,7 +34,7 @@ class ShellOutput extends ConsoleOutput
* @param OutputFormatterInterface|null $formatter (default: null)
* @param string|OutputPager|null $pager (default: null)
*/
public function __construct($verbosity = self::VERBOSITY_NORMAL, $decorated = null, OutputFormatterInterface $formatter = null, $pager = null, $theme = null)
public function __construct($verbosity = self::VERBOSITY_NORMAL, $decorated = null, ?OutputFormatterInterface $formatter = null, $pager = null, $theme = null)
{
parent::__construct($verbosity, $decorated, $formatter);
@@ -156,7 +152,8 @@ class ShellOutput extends ConsoleOutput
*/
public function doWrite($message, $newline): void
{
if ($this->paging > 0) {
// @todo Update OutputPager interface to require doWrite
if ($this->paging > 0 && $this->pager instanceof ProcOutputPager) {
$this->pager->doWrite($message, $newline);
} else {
parent::doWrite($message, $newline);

View File

@@ -74,16 +74,16 @@ class Theme
const ERROR_STYLES = ['info', 'warning', 'error', 'whisper', 'class'];
private $compact = false;
private bool $compact = false;
private $prompt = '> ';
private $bufferPrompt = '. ';
private $replayPrompt = '- ';
private $returnValue = '= ';
private string $prompt = '> ';
private string $bufferPrompt = '. ';
private string $replayPrompt = '- ';
private string $returnValue = '= ';
private $grayFallback = 'blue';
private string $grayFallback = 'blue';
private $styles = [];
private array $styles = [];
/**
* @param string|array $config theme name or config options

View File

@@ -22,10 +22,9 @@ class GNUReadline implements Readline
{
/** @var string|false */
protected $historyFile;
/** @var int */
protected $historySize;
/** @var bool */
protected $eraseDups;
protected int $historySize;
// @todo better type for this
protected ?bool $eraseDups;
/**
* GNU Readline is supported iff `readline_list_history` is defined. PHP
@@ -105,7 +104,7 @@ class GNUReadline implements Readline
/**
* {@inheritdoc}
*/
public function readline(string $prompt = null)
public function readline(?string $prompt = null)
{
return \readline($prompt);
}

View File

@@ -62,8 +62,8 @@ class AutocompleterPath implements Autocompleter
* Constructor.
*/
public function __construct(
string $root = null,
\Closure $iteratorFactory = null
?string $root = null,
?\Closure $iteratorFactory = null
) {
if (null === $root) {
$root = static::PWD;

View File

@@ -154,7 +154,7 @@ class ConsoleCursor
* Move to the line X and the column Y.
* If null, use the current coordinate.
*/
public static function moveTo(int $x = null, int $y = null)
public static function moveTo(?int $x = null, ?int $y = null)
{
if (null === $x || null === $y) {
$position = static::getPosition();

View File

@@ -53,7 +53,7 @@ class ConsoleInput implements StreamIn
/**
* Wraps an `Hoa\Stream\IStream\In` stream.
*/
public function __construct(StreamIn $input = null)
public function __construct(?StreamIn $input = null)
{
if (null === $input) {
if (\defined('STDIN') &&

View File

@@ -57,7 +57,7 @@ class ConsoleOutput implements StreamOut
/**
* Wraps an `Hoa\Stream\IStream\Out` stream.
*/
public function __construct(StreamOut $output = null)
public function __construct(?StreamOut $output = null)
{
$this->_output = $output;

View File

@@ -245,10 +245,10 @@ class ConsoleProcessus extends Stream implements StreamIn, StreamOut, StreamPath
*/
public function __construct(
string $command,
array $options = null,
array $descriptors = null,
string $cwd = null,
array $environment = null,
?array $options = null,
?array $descriptors = null,
?string $cwd = null,
?array $environment = null,
int $timeout = 30
) {
$this->setCommand($command);
@@ -285,7 +285,7 @@ class ConsoleProcessus extends Stream implements StreamIn, StreamOut, StreamPath
/**
* Open the stream and return the associated resource.
*/
protected function &_open(string $streamName, StreamContext $context = null)
protected function &_open(string $streamName, ?StreamContext $context = null)
{
$out = @\proc_open(
$streamName,
@@ -527,7 +527,7 @@ class ConsoleProcessus extends Stream implements StreamIn, StreamOut, StreamPath
* Read an array.
* Alias of the $this->scanf() method.
*/
public function readArray(string $format = null, int $pipe = 1)
public function readArray(?string $format = null, int $pipe = 1)
{
return $this->scanf($format, $pipe);
}

View File

@@ -52,7 +52,7 @@ class Exception extends ExceptionIdle implements EventSource
string $message,
int $code = 0,
$arguments = [],
\Throwable $previous = null
?\Throwable $previous = null
) {
parent::__construct($message, $code, $arguments, $previous);

View File

@@ -79,7 +79,7 @@ class ExceptionIdle extends \Exception
string $message,
int $code = 0,
$arguments = [],
\Exception $previous = null
?\Exception $previous = null
) {
$this->_tmpArguments = $arguments;
parent::__construct($message, $code, $previous);

View File

@@ -105,7 +105,7 @@ abstract class File extends FileGeneric implements StreamBufferable, StreamLocka
public function __construct(
string $streamName,
string $mode,
string $context = null,
?string $context = null,
bool $wait = false
) {
$this->setMode($mode);
@@ -140,7 +140,7 @@ abstract class File extends FileGeneric implements StreamBufferable, StreamLocka
/**
* Open the stream and return the associated resource.
*/
protected function &_open(string $streamName, StreamContext $context = null)
protected function &_open(string $streamName, ?StreamContext $context = null)
{
if (\substr($streamName, 0, 4) === 'file' &&
false === \is_dir(\dirname($streamName))) {
@@ -181,7 +181,7 @@ abstract class File extends FileGeneric implements StreamBufferable, StreamLocka
* Start a new buffer.
* The callable acts like a light filter.
*/
public function newBuffer($callable = null, int $size = null): int
public function newBuffer($callable = null, ?int $size = null): int
{
$this->setStreamBuffer($size);

View File

@@ -66,7 +66,7 @@ class FileDirectory extends FileGeneric
public function __construct(
string $streamName,
string $mode = self::MODE_READ,
string $context = null,
?string $context = null,
bool $wait = false
) {
$this->setMode($mode);
@@ -78,7 +78,7 @@ class FileDirectory extends FileGeneric
/**
* Open the stream and return the associated resource.
*/
protected function &_open(string $streamName, StreamContext $context = null)
protected function &_open(string $streamName, ?StreamContext $context = null)
{
if (false === \is_dir($streamName)) {
if ($this->getMode() === self::MODE_READ) {
@@ -185,7 +185,7 @@ class FileDirectory extends FileGeneric
public static function create(
string $name,
string $mode = self::MODE_CREATE_RECURSIVE,
string $context = null
?string $context = null
): bool {
if (true === \is_dir($name)) {
return true;

View File

@@ -229,7 +229,7 @@ abstract class FileGeneric extends Stream implements StreamPathable, StreamStata
/**
* Set access and modification time of file.
*/
public function touch(int $time = null, int $atime = null): bool
public function touch(?int $time = null, ?int $atime = null): bool
{
if (null === $time) {
$time = \time();
@@ -333,7 +333,7 @@ abstract class FileGeneric extends Stream implements StreamPathable, StreamStata
/**
* Change the current umask.
*/
public static function umask(int $umask = null): int
public static function umask(?int $umask = null): int
{
if (null === $umask) {
return \umask();

View File

@@ -49,7 +49,7 @@ class FileLink extends File
public function __construct(
string $streamName,
string $mode,
string $context = null,
?string $context = null,
bool $wait = false
) {
if (!\is_link($streamName)) {

View File

@@ -57,7 +57,7 @@ class FileLinkRead extends FileLink implements StreamIn
public function __construct(
string $streamName,
string $mode = parent::MODE_READ,
string $context = null,
?string $context = null,
bool $wait = false
) {
parent::__construct($streamName, $mode, $context, $wait);
@@ -76,7 +76,7 @@ class FileLinkRead extends FileLink implements StreamIn
* @throws \Hoa\File\Exception\FileDoesNotExist
* @throws \Hoa\File\Exception
*/
protected function &_open(string $streamName, StreamContext $context = null)
protected function &_open(string $streamName, ?StreamContext $context = null)
{
static $createModes = [
parent::MODE_READ,
@@ -190,7 +190,7 @@ class FileLinkRead extends FileLink implements StreamIn
*
* @return array
*/
public function readArray(string $format = null)
public function readArray(?string $format = null)
{
return $this->scanf($format);
}

View File

@@ -49,7 +49,7 @@ class FileLinkReadWrite extends FileLink implements StreamIn, StreamOut
public function __construct(
string $streamName,
string $mode = parent::MODE_APPEND_READ_WRITE,
string $context = null,
?string $context = null,
bool $wait = false
) {
parent::__construct($streamName, $mode, $context, $wait);
@@ -60,7 +60,7 @@ class FileLinkReadWrite extends FileLink implements StreamIn, StreamOut
/**
* Open the stream and return the associated resource.
*/
protected function &_open(string $streamName, StreamContext $context = null)
protected function &_open(string $streamName, ?StreamContext $context = null)
{
static $createModes = [
parent::MODE_READ_WRITE,
@@ -150,7 +150,7 @@ class FileLinkReadWrite extends FileLink implements StreamIn, StreamOut
* Read an array.
* Alias of the $this->scanf() method.
*/
public function readArray(string $format = null)
public function readArray(?string $format = null)
{
return $this->scanf($format);
}

View File

@@ -49,7 +49,7 @@ class FileRead extends File implements StreamIn
public function __construct(
string $streamName,
string $mode = parent::MODE_READ,
string $context = null,
?string $context = null,
bool $wait = false
) {
parent::__construct($streamName, $mode, $context, $wait);
@@ -60,7 +60,7 @@ class FileRead extends File implements StreamIn
/**
* Open the stream and return the associated resource.
*/
protected function &_open(string $streamName, StreamContext $context = null)
protected function &_open(string $streamName, ?StreamContext $context = null)
{
static $createModes = [
parent::MODE_READ,
@@ -146,7 +146,7 @@ class FileRead extends File implements StreamIn
* Read an array.
* Alias of the $this->scanf() method.
*/
public function readArray(string $format = null)
public function readArray(?string $format = null)
{
return $this->scanf($format);
}

View File

@@ -49,7 +49,7 @@ class FileReadWrite extends File implements StreamIn, StreamOut
public function __construct(
string $streamName,
string $mode = parent::MODE_APPEND_READ_WRITE,
string $context = null,
?string $context = null,
bool $wait = false
) {
parent::__construct($streamName, $mode, $context, $wait);
@@ -60,7 +60,7 @@ class FileReadWrite extends File implements StreamIn, StreamOut
/**
* Open the stream and return the associated resource.
*/
protected function &_open(string $streamName, StreamContext $context = null)
protected function &_open(string $streamName, ?StreamContext $context = null)
{
static $createModes = [
parent::MODE_READ_WRITE,
@@ -150,7 +150,7 @@ class FileReadWrite extends File implements StreamIn, StreamOut
* Read an array.
* Alias of the $this->scanf() method.
*/
public function readArray(string $format = null)
public function readArray(?string $format = null)
{
return $this->scanf($format);
}

View File

@@ -53,7 +53,7 @@ class IteratorFileSystem extends \FilesystemIterator
* Please, see \FileSystemIterator::__construct() method.
* We add the $splFileInfoClass parameter.
*/
public function __construct(string $path, int $flags = null, string $splFileInfoClass = null)
public function __construct(string $path, ?int $flags = null, ?string $splFileInfoClass = null)
{
$this->_splFileInfoClass = $splFileInfoClass;

View File

@@ -58,7 +58,7 @@ class IteratorRecursiveDirectory extends \RecursiveDirectoryIterator
* Please, see \RecursiveDirectoryIterator::__construct() method.
* We add the $splFileInfoClass parameter.
*/
public function __construct(string $path, int $flags = null, string $splFileInfoClass = null)
public function __construct(string $path, ?int $flags = null, ?string $splFileInfoClass = null)
{
if (null === $flags) {
parent::__construct($path);

View File

@@ -56,7 +56,7 @@ class IteratorSplFileInfo extends \SplFileInfo
/**
* Construct.
*/
public function __construct(string $filename, string $relativePath = null)
public function __construct(string $filename, ?string $relativePath = null)
{
parent::__construct($filename);

View File

@@ -62,7 +62,7 @@ class ProtocolNode implements \ArrayAccess, \IteratorAggregate
* overload the `$_name` attribute), we can set the `$_name` attribute
* dynamically. This is useful to create a node on-the-fly.
*/
public function __construct(string $name = null, string $reach = null, array $children = [])
public function __construct(?string $name = null, ?string $reach = null, array $children = [])
{
if (null !== $name) {
$this->_name = $name;
@@ -133,7 +133,7 @@ class ProtocolNode implements \ArrayAccess, \IteratorAggregate
* Resolve a path, i.e. iterate the nodes tree and reach the queue of
* the path.
*/
protected function _resolve(string $path, &$accumulator, string $id = null)
protected function _resolve(string $path, &$accumulator, ?string $id = null)
{
if (\substr($path, 0, 6) === 'hoa://') {
$path = \substr($path, 6);
@@ -246,7 +246,7 @@ class ProtocolNode implements \ArrayAccess, \IteratorAggregate
* Queue of the node.
* Generic one. Must be overrided in children classes.
*/
public function reach(string $queue = null)
public function reach(?string $queue = null)
{
return empty($queue) ? $this->_reach : $queue;
}

View File

@@ -44,7 +44,7 @@ class ProtocolNodeLibrary extends ProtocolNode
/**
* Queue of the component.
*/
public function reach(string $queue = null)
public function reach(?string $queue = null)
{
$withComposer = \class_exists('Composer\Autoload\ClassLoader', false) ||
('cli' === \PHP_SAPI && \file_exists(__DIR__.\DIRECTORY_SEPARATOR.'..'.\DIRECTORY_SEPARATOR.'..'.\DIRECTORY_SEPARATOR.'..'.\DIRECTORY_SEPARATOR.'..'.\DIRECTORY_SEPARATOR.'autoload.php'));

View File

@@ -137,7 +137,7 @@ class Readline
/**
* Read a line from the input.
*/
public function readLine(string $prefix = null)
public function readLine(?string $prefix = null)
{
$input = Console::getInput();
@@ -270,7 +270,7 @@ class Readline
/**
* Add an entry in the history.
*/
public function addHistory(string $line = null)
public function addHistory(?string $line = null)
{
if (empty($line)) {
return;
@@ -294,7 +294,7 @@ class Readline
/**
* Get an entry in the history.
*/
public function getHistory(int $i = null)
public function getHistory(?int $i = null)
{
if (null === $i) {
$i = $this->_historyCurrent;

View File

@@ -110,7 +110,7 @@ abstract class Stream implements IStream, EventListenable
* If not exists in the register, try to call the
* `$this->_open()` method. Please, see the `self::_getStream()` method.
*/
public function __construct(string $streamName, string $context = null, bool $wait = false)
public function __construct(string $streamName, ?string $context = null, bool $wait = false)
{
$this->_streamName = $streamName;
$this->_context = $context;
@@ -150,7 +150,7 @@ abstract class Stream implements IStream, EventListenable
private static function &_getStream(
string $streamName,
self $handler,
string $context = null
?string $context = null
): array {
$name = \md5($streamName);
@@ -195,7 +195,7 @@ abstract class Stream implements IStream, EventListenable
* Note: This method is protected, but do not forget that it could be
* overloaded into a public context.
*/
abstract protected function &_open(string $streamName, StreamContext $context = null);
abstract protected function &_open(string $streamName, ?StreamContext $context = null);
/**
* Close the current stream.

View File

@@ -49,7 +49,7 @@ interface StreamBufferable extends IStream
* Start a new buffer.
* The callable acts like a light filter.
*/
public function newBuffer($callable = null, int $size = null): int;
public function newBuffer($callable = null, ?int $size = null): int;
/**
* Flush the buffer.

View File

@@ -106,5 +106,5 @@ interface StreamTouchable extends IStream
/**
* Change the current umask.
*/
public static function umask(int $umask = null): int;
public static function umask(?int $umask = null): int;
}

View File

@@ -25,7 +25,7 @@ use Psy\Util\Str;
*/
class Libedit extends GNUReadline
{
private $hasWarnedOwnership = false;
private bool $hasWarnedOwnership = false;
/**
* Let's emulate GNU Readline by manually reading and parsing the history file!
@@ -48,6 +48,10 @@ class Libedit extends GNUReadline
*/
public function listHistory(): array
{
if ($this->historyFile === false) {
return [];
}
$history = \file_get_contents($this->historyFile);
if (!$history) {
return [];
@@ -63,6 +67,7 @@ class Libedit extends GNUReadline
// decode the line
$history = \array_map([$this, 'parseHistoryLine'], $history);
// filter empty lines & comments
return \array_values(\array_filter($history));
}

View File

@@ -70,7 +70,7 @@ interface Readline
*
* @return false|string
*/
public function readline(string $prompt = null);
public function readline(?string $prompt = null);
/**
* Redraw readline to redraw the display.

View File

@@ -18,9 +18,10 @@ use Psy\Exception\BreakException;
*/
class Transient implements Readline
{
private $history;
private $historySize;
private $eraseDups;
private array $history;
private int $historySize;
private bool $eraseDups;
/** @var resource */
private $stdin;
/**
@@ -49,7 +50,7 @@ class Transient implements Readline
// don't do anything with the history file...
$this->history = [];
$this->historySize = $historySize;
$this->eraseDups = $eraseDups;
$this->eraseDups = $eraseDups ?? false;
}
/**
@@ -110,7 +111,7 @@ class Transient implements Readline
*
* @return false|string
*/
public function readline(string $prompt = null)
public function readline(?string $prompt = null)
{
echo $prompt;

View File

@@ -25,15 +25,11 @@ use Psy\Readline\Hoa\Ustring as HoaUstring;
*/
class Userland implements Readline
{
/** @var HoaReadline */
private $hoaReadline;
/** @var string|null */
private $lastPrompt;
private $tput;
private $input;
private $output;
private HoaReadline $hoaReadline;
private ?string $lastPrompt = null;
private HoaConsoleTput $tput;
private HoaConsoleInput $input;
private HoaConsoleOutput $output;
public static function isSupported(): bool
{
@@ -138,7 +134,7 @@ class Userland implements Readline
*
* @return string
*/
public function readline(string $prompt = null)
public function readline(?string $prompt = null)
{
$this->lastPrompt = $prompt;

View File

@@ -19,9 +19,10 @@ namespace Psy\Reflection;
class ReflectionConstant implements \Reflector
{
public $name;
/** @var mixed */
private $value;
private static $magicConstants = [
private const MAGIC_CONSTANTS = [
'__LINE__',
'__FILE__',
'__DIR__',
@@ -75,7 +76,7 @@ class ReflectionConstant implements \Reflector
public static function isMagicConstant($name)
{
return \in_array($name, self::$magicConstants);
return \in_array($name, self::MAGIC_CONSTANTS);
}
/**

Some files were not shown because too many files have changed in this diff Show More