This commit is contained in:
TiclemFR
2024-01-20 23:14:52 +01:00
parent a068f54957
commit 031f7071e6
881 changed files with 241469 additions and 247870 deletions

View File

@@ -13,12 +13,12 @@
}
],
"require": {
"php": "^8.0 || ^7.0.8",
"php": "^8.0 || ^7.4",
"ext-json": "*",
"ext-tokenizer": "*",
"symfony/console": "^6.0 || ^5.0 || ^4.0 || ^3.4",
"symfony/var-dumper": "^6.0 || ^5.0 || ^4.0 || ^3.4",
"nikic/php-parser": "^4.0 || ^3.1"
"nikic/php-parser": "^5.0 || ^4.0",
"symfony/console": "^7.0 || ^6.0 || ^5.0 || ^4.0 || ^3.4",
"symfony/var-dumper": "^7.0 || ^6.0 || ^5.0 || ^4.0 || ^3.4"
},
"require-dev": {
"bamarni/composer-bin-plugin": "^1.2"
@@ -26,7 +26,6 @@
"suggest": {
"ext-pcntl": "Enabling the PCNTL extension makes PsySH a lot happier :)",
"ext-posix": "If you have PCNTL, you'll want the POSIX extension as well.",
"ext-readline": "Enables support for arrow-key history navigation, and showing and manipulating command history.",
"ext-pdo-sqlite": "The doc command requires SQLite to work."
},
"autoload": {
@@ -48,7 +47,7 @@
},
"extra": {
"branch-alias": {
"dev-0.11": "0.11.x-dev"
"dev-main": "0.12.x-dev"
},
"bamarni-bin": {
"bin-links": false,

View File

@@ -25,7 +25,6 @@ use Psy\CodeCleaner\FinalClassPass;
use Psy\CodeCleaner\FunctionContextPass;
use Psy\CodeCleaner\FunctionReturnInWriteContextPass;
use Psy\CodeCleaner\ImplicitReturnPass;
use Psy\CodeCleaner\InstanceOfPass;
use Psy\CodeCleaner\IssetPass;
use Psy\CodeCleaner\LabelContextPass;
use Psy\CodeCleaner\LeavePsyshAlonePass;
@@ -71,12 +70,7 @@ class CodeCleaner
$this->yolo = $yolo;
$this->strictTypes = $strictTypes;
if ($parser === null) {
$parserFactory = new ParserFactory();
$parser = $parserFactory->createParser();
}
$this->parser = $parser;
$this->parser = $parser ?? (new ParserFactory())->createParser();
$this->printer = $printer ?: new Printer();
$this->traverser = $traverser ?: new NodeTraverser();
@@ -120,7 +114,6 @@ class CodeCleaner
new FinalClassPass(),
new FunctionContextPass(),
new FunctionReturnInWriteContextPass(),
new InstanceOfPass(),
new IssetPass(),
new LabelContextPass(),
new LeavePsyshAlonePass(),

View File

@@ -43,7 +43,7 @@ class AbstractClassPass extends CodeCleanerPass
if ($node->stmts !== null) {
$msg = \sprintf('Abstract function %s cannot contain body', $name);
throw new FatalErrorException($msg, 0, \E_ERROR, null, $node->getLine());
throw new FatalErrorException($msg, 0, \E_ERROR, null, $node->getStartLine());
}
}
}
@@ -68,7 +68,7 @@ class AbstractClassPass extends CodeCleanerPass
($count === 1) ? '' : 's',
\implode(', ', $this->abstractMethods)
);
throw new FatalErrorException($msg, 0, \E_ERROR, null, $node->getLine());
throw new FatalErrorException($msg, 0, \E_ERROR, null, $node->getStartLine());
}
}
}

View File

@@ -35,7 +35,7 @@ class AssignThisVariablePass extends CodeCleanerPass
public function enterNode(Node $node)
{
if ($node instanceof Assign && $node->var instanceof Variable && $node->var->name === 'this') {
throw new FatalErrorException('Cannot re-assign $this', 0, \E_ERROR, null, $node->getLine());
throw new FatalErrorException('Cannot re-assign $this', 0, \E_ERROR, null, $node->getStartLine());
}
}
}

View File

@@ -50,7 +50,7 @@ class CallTimePassByReferencePass extends CodeCleanerPass
}
if ($arg->byRef) {
throw new FatalErrorException(self::EXCEPTION_MESSAGE, 0, \E_ERROR, null, $node->getLine());
throw new FatalErrorException(self::EXCEPTION_MESSAGE, 0, \E_ERROR, null, $node->getStartLine());
}
}
}

View File

@@ -66,7 +66,7 @@ class CalledClassPass extends CodeCleanerPass
$name = \strtolower($node->name);
if (\in_array($name, ['get_class', 'get_called_class'])) {
$msg = \sprintf('%s() called without object from outside a class', $name);
throw new ErrorException($msg, 0, \E_USER_WARNING, null, $node->getLine());
throw new ErrorException($msg, 0, \E_USER_WARNING, null, $node->getStartLine());
}
}
}

View File

@@ -59,7 +59,7 @@ class EmptyArrayDimFetchPass extends CodeCleanerPass
if ($node instanceof ArrayDimFetch && $node->dim === null) {
if (!\in_array($node, $this->theseOnesAreFine)) {
throw new FatalErrorException(self::EXCEPTION_MESSAGE, $node->getLine());
throw new FatalErrorException(self::EXCEPTION_MESSAGE, $node->getStartLine());
}
}
}

View File

@@ -46,7 +46,7 @@ class FinalClassPass extends CodeCleanerPass
$extends = (string) $node->extends;
if ($this->isFinalClass($extends)) {
$msg = \sprintf('Class %s may not inherit from final class (%s)', $node->name, $extends);
throw new FatalErrorException($msg, 0, \E_ERROR, null, $node->getLine());
throw new FatalErrorException($msg, 0, \E_ERROR, null, $node->getStartLine());
}
}

View File

@@ -50,7 +50,7 @@ class FunctionContextPass extends CodeCleanerPass
// It causes fatal error.
if ($node instanceof Yield_) {
$msg = 'The "yield" expression can only be used inside a function';
throw new FatalErrorException($msg, 0, \E_ERROR, null, $node->getLine());
throw new FatalErrorException($msg, 0, \E_ERROR, null, $node->getStartLine());
}
}

View File

@@ -53,7 +53,7 @@ class FunctionReturnInWriteContextPass extends CodeCleanerPass
}
if ($item && $item->byRef && $this->isCallNode($item->value)) {
throw new FatalErrorException(self::EXCEPTION_MESSAGE, 0, \E_ERROR, null, $node->getLine());
throw new FatalErrorException(self::EXCEPTION_MESSAGE, 0, \E_ERROR, null, $node->getStartLine());
}
}
} elseif ($node instanceof Isset_ || $node instanceof Unset_) {
@@ -63,10 +63,10 @@ class FunctionReturnInWriteContextPass extends CodeCleanerPass
}
$msg = $node instanceof Isset_ ? self::ISSET_MESSAGE : self::EXCEPTION_MESSAGE;
throw new FatalErrorException($msg, 0, \E_ERROR, null, $node->getLine());
throw new FatalErrorException($msg, 0, \E_ERROR, null, $node->getStartLine());
}
} elseif ($node instanceof Assign && $this->isCallNode($node->var)) {
throw new FatalErrorException(self::EXCEPTION_MESSAGE, 0, \E_ERROR, null, $node->getLine());
throw new FatalErrorException(self::EXCEPTION_MESSAGE, 0, \E_ERROR, null, $node->getStartLine());
}
}

View File

@@ -77,15 +77,14 @@ class ImplicitReturnPass extends CodeCleanerPass
} elseif ($last instanceof Expr && !($last instanceof Exit_)) {
// @codeCoverageIgnoreStart
$nodes[\count($nodes) - 1] = new Return_($last, [
'startLine' => $last->getLine(),
'endLine' => $last->getLine(),
'startLine' => $last->getStartLine(),
'endLine' => $last->getEndLine(),
]);
// @codeCoverageIgnoreEnd
} elseif ($last instanceof Expression && !($last->expr instanceof Exit_)) {
// For PHP Parser 4.x
$nodes[\count($nodes) - 1] = new Return_($last->expr, [
'startLine' => $last->getLine(),
'endLine' => $last->getLine(),
'startLine' => $last->getStartLine(),
'endLine' => $last->getEndLine(),
]);
} elseif ($last instanceof Namespace_) {
$last->stmts = $this->addImplicitReturn($last->stmts);

View File

@@ -1,69 +0,0 @@
<?php
/*
* This file is part of Psy Shell.
*
* (c) 2012-2023 Justin Hileman
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Psy\CodeCleaner;
use PhpParser\Node;
use PhpParser\Node\Expr\Array_;
use PhpParser\Node\Expr\BinaryOp;
use PhpParser\Node\Expr\ClassConstFetch;
use PhpParser\Node\Expr\ConstFetch;
use PhpParser\Node\Expr\Instanceof_;
use PhpParser\Node\Scalar;
use PhpParser\Node\Scalar\Encapsed;
use Psy\Exception\FatalErrorException;
/**
* Validate that the instanceof statement does not receive a scalar value or a non-class constant.
*
* @author Martin Hasoň <martin.hason@gmail.com>
*/
class InstanceOfPass extends CodeCleanerPass
{
const EXCEPTION_MSG = 'instanceof expects an object instance, constant given';
private $atLeastPhp73;
public function __construct()
{
$this->atLeastPhp73 = \version_compare(\PHP_VERSION, '7.3', '>=');
}
/**
* Validate that the instanceof statement does not receive a scalar value or a non-class constant.
*
* @throws FatalErrorException if a scalar or a non-class constant is given
*
* @param Node $node
*
* @return int|Node|null Replacement node (or special return value)
*/
public function enterNode(Node $node)
{
// Basically everything is allowed in PHP 7.3 :)
if ($this->atLeastPhp73) {
return;
}
if (!$node instanceof Instanceof_) {
return;
}
if (($node->expr instanceof Scalar && !$node->expr instanceof Encapsed) ||
$node->expr instanceof BinaryOp ||
$node->expr instanceof Array_ ||
$node->expr instanceof ConstFetch ||
$node->expr instanceof ClassConstFetch
) {
throw new FatalErrorException(self::EXCEPTION_MSG, 0, \E_ERROR, null, $node->getLine());
}
}
}

View File

@@ -42,7 +42,7 @@ class IssetPass extends CodeCleanerPass
foreach ($node->vars as $var) {
if (!$var instanceof Variable && !$var instanceof ArrayDimFetch && !$var instanceof PropertyFetch && !$var instanceof NullsafePropertyFetch) {
throw new FatalErrorException(self::EXCEPTION_MSG, 0, \E_ERROR, null, $node->getLine());
throw new FatalErrorException(self::EXCEPTION_MSG, 0, \E_ERROR, null, $node->getStartLine());
}
}
}

View File

@@ -68,9 +68,9 @@ class LabelContextPass extends CodeCleanerPass
}
if ($node instanceof Goto_) {
$this->labelGotos[\strtolower($node->name)] = $node->getLine();
$this->labelGotos[\strtolower($node->name)] = $node->getStartLine();
} elseif ($node instanceof Label) {
$this->labelDeclarations[\strtolower($node->name)] = $node->getLine();
$this->labelDeclarations[\strtolower($node->name)] = $node->getStartLine();
}
}

View File

@@ -12,7 +12,6 @@
namespace Psy\CodeCleaner;
use PhpParser\Node;
use PhpParser\Node\Expr;
use PhpParser\Node\Expr\Array_;
use PhpParser\Node\Expr\ArrayDimFetch;
use PhpParser\Node\Expr\ArrayItem;
@@ -29,13 +28,6 @@ use Psy\Exception\ParseErrorException;
*/
class ListPass extends CodeCleanerPass
{
private $atLeastPhp71;
public function __construct()
{
$this->atLeastPhp71 = \version_compare(\PHP_VERSION, '7.1', '>=');
}
/**
* Validate use of list assignment.
*
@@ -55,16 +47,11 @@ class ListPass extends CodeCleanerPass
return;
}
if (!$this->atLeastPhp71 && $node->var instanceof Array_) {
$msg = "syntax error, unexpected '='";
throw new ParseErrorException($msg, $node->expr->getLine());
}
// Polyfill for PHP-Parser 2.x
$items = isset($node->var->items) ? $node->var->items : $node->var->vars;
if ($items === [] || $items === [null]) {
throw new ParseErrorException('Cannot use empty list', $node->var->getLine());
throw new ParseErrorException('Cannot use empty list', ['startLine' => $node->var->getStartLine(), 'endLine' => $node->var->getEndLine()]);
}
$itemFound = false;
@@ -75,15 +62,9 @@ class ListPass extends CodeCleanerPass
$itemFound = true;
// List_->$vars in PHP-Parser 2.x is Variable instead of ArrayItem.
if (!$this->atLeastPhp71 && $item instanceof ArrayItem && $item->key !== null) {
$msg = 'Syntax error, unexpected T_CONSTANT_ENCAPSED_STRING, expecting \',\' or \')\'';
throw new ParseErrorException($msg, $item->key->getLine());
}
if (!self::isValidArrayItem($item)) {
$msg = 'Assignments can only happen to writable values';
throw new ParseErrorException($msg, $item->getLine());
throw new ParseErrorException($msg, ['startLine' => $item->getStartLine(), 'endLine' => $item->getEndLine()]);
}
}
@@ -95,9 +76,9 @@ class ListPass extends CodeCleanerPass
/**
* Validate whether a given item in an array is valid for short assignment.
*
* @param Expr $item
* @param Node $item
*/
private static function isValidArrayItem(Expr $item): bool
private static function isValidArrayItem(Node $item): bool
{
$value = ($item instanceof ArrayItem) ? $item->value : $item;

View File

@@ -67,23 +67,23 @@ class LoopContextPass extends CodeCleanerPass
if ($this->loopDepth === 0) {
$msg = \sprintf("'%s' not in the 'loop' or 'switch' context", $operator);
throw new FatalErrorException($msg, 0, \E_ERROR, null, $node->getLine());
throw new FatalErrorException($msg, 0, \E_ERROR, null, $node->getStartLine());
}
if ($node->num instanceof LNumber || $node->num instanceof DNumber) {
$num = $node->num->value;
if ($node->num instanceof DNumber || $num < 1) {
$msg = \sprintf("'%s' operator accepts only positive numbers", $operator);
throw new FatalErrorException($msg, 0, \E_ERROR, null, $node->getLine());
throw new FatalErrorException($msg, 0, \E_ERROR, null, $node->getStartLine());
}
if ($num > $this->loopDepth) {
$msg = \sprintf("Cannot '%s' %d levels", $operator, $num);
throw new FatalErrorException($msg, 0, \E_ERROR, null, $node->getLine());
throw new FatalErrorException($msg, 0, \E_ERROR, null, $node->getStartLine());
}
} elseif ($node->num) {
$msg = \sprintf("'%s' operator with non-constant operand is no longer supported", $operator);
throw new FatalErrorException($msg, 0, \E_ERROR, null, $node->getLine());
throw new FatalErrorException($msg, 0, \E_ERROR, null, $node->getStartLine());
}
break;
}

View File

@@ -49,7 +49,7 @@ abstract class NamespaceAwarePass extends CodeCleanerPass
public function enterNode(Node $node)
{
if ($node instanceof Namespace_) {
$this->namespace = isset($node->name) ? $node->name->parts : [];
$this->namespace = isset($node->name) ? $this->getParts($node->name) : [];
}
}
@@ -61,13 +61,25 @@ abstract class NamespaceAwarePass extends CodeCleanerPass
protected function getFullyQualifiedName($name): string
{
if ($name instanceof FullyQualifiedName) {
return \implode('\\', $name->parts);
} elseif ($name instanceof Name) {
$name = $name->parts;
return \implode('\\', $this->getParts($name));
}
if ($name instanceof Name) {
$name = $this->getParts($name);
} elseif (!\is_array($name)) {
$name = [$name];
}
return \implode('\\', \array_merge($this->namespace, $name));
}
/**
* Backwards compatibility shim for PHP-Parser 4.x.
*
* At some point we might want to make $namespace a plain string, to match how Name works?
*/
protected function getParts(Name $name): array
{
return \method_exists($name, 'getParts') ? $name->getParts() : $name->parts;
}
}

View File

@@ -86,6 +86,16 @@ class NamespacePass extends CodeCleanerPass
private function setNamespace($namespace)
{
$this->namespace = $namespace;
$this->cleaner->setNamespace($namespace === null ? null : $namespace->parts);
$this->cleaner->setNamespace($namespace === null ? null : $this->getParts($namespace));
}
/**
* Backwards compatibility shim for PHP-Parser 4.x.
*
* At some point we might want to make the namespace a plain string, to match how Name works?
*/
protected function getParts(Name $name): array
{
return \method_exists($name, 'getParts') ? $name->getParts() : $name->parts;
}
}

View File

@@ -63,7 +63,7 @@ class PassableByReferencePass extends CodeCleanerPass
if (\array_key_exists($key, $node->args)) {
$arg = $node->args[$key];
if ($param->isPassedByReference() && !$this->isPassableByReference($arg)) {
throw new FatalErrorException(self::EXCEPTION_MESSAGE, 0, \E_ERROR, null, $node->getLine());
throw new FatalErrorException(self::EXCEPTION_MESSAGE, 0, \E_ERROR, null, $node->getStartLine());
}
}
}
@@ -112,7 +112,7 @@ class PassableByReferencePass extends CodeCleanerPass
} elseif (++$nonPassable > 2) {
// There can be *at most* two non-passable-by-reference args in a row. This is about
// as close as we can get to validating the arguments for this function :-/
throw new FatalErrorException(self::EXCEPTION_MESSAGE, 0, \E_ERROR, null, $node->getLine());
throw new FatalErrorException(self::EXCEPTION_MESSAGE, 0, \E_ERROR, null, $node->getStartLine());
}
}
}

View File

@@ -52,7 +52,7 @@ class RequirePass extends CodeCleanerPass
$node->expr = new StaticCall(
new FullyQualifiedName(self::class),
'resolve',
[new Arg($origNode->expr), new Arg(new LNumber($origNode->getLine()))],
[new Arg($origNode->expr), new Arg(new LNumber($origNode->getStartLine()))],
$origNode->getAttributes()
);
@@ -71,11 +71,11 @@ class RequirePass extends CodeCleanerPass
* @throws ErrorException if $file is empty and E_WARNING is included in error_reporting level
*
* @param string $file
* @param int $lineNumber Line number of the original require expression
* @param int $startLine Line number of the original require expression
*
* @return string Exactly the same as $file, unless $file collides with a path in the currently running phar
*/
public static function resolve($file, $lineNumber = null): string
public static function resolve($file, $startLine = null): string
{
$file = (string) $file;
@@ -84,7 +84,7 @@ class RequirePass extends CodeCleanerPass
// fake the file and line number, but we can't call it statically.
// So we're duplicating some of the logics here.
if (\E_WARNING & \error_reporting()) {
ErrorException::throwException(\E_WARNING, 'Filename cannot be empty', null, $lineNumber);
ErrorException::throwException(\E_WARNING, 'Filename cannot be empty', null, $startLine);
}
// @todo trigger an error as fallback? this is pretty ugly…
// trigger_error('Filename cannot be empty', E_USER_WARNING);
@@ -93,7 +93,7 @@ class RequirePass extends CodeCleanerPass
$resolvedPath = \stream_resolve_include_path($file);
if ($file === '' || !$resolvedPath) {
$msg = \sprintf("Failed opening required '%s'", $file);
throw new FatalErrorException($msg, 0, \E_ERROR, null, $lineNumber);
throw new FatalErrorException($msg, 0, \E_ERROR, null, $startLine);
}
// Special case: if the path is not already relative or absolute, and it would resolve to

View File

@@ -32,14 +32,8 @@ 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 $atLeastPhp71;
private $returnTypeStack = [];
public function __construct()
{
$this->atLeastPhp71 = \version_compare(\PHP_VERSION, '7.1', '>=');
}
/**
* {@inheritdoc}
*
@@ -47,10 +41,6 @@ class ReturnTypePass extends CodeCleanerPass
*/
public function enterNode(Node $node)
{
if (!$this->atLeastPhp71) {
return; // @codeCoverageIgnore
}
if ($this->isFunctionNode($node)) {
$this->returnTypeStack[] = $node->returnType;
@@ -82,7 +72,7 @@ class ReturnTypePass extends CodeCleanerPass
}
if ($msg !== null) {
throw new FatalErrorException($msg, 0, \E_ERROR, null, $node->getLine());
throw new FatalErrorException($msg, 0, \E_ERROR, null, $node->getStartLine());
}
}
}
@@ -94,10 +84,6 @@ class ReturnTypePass extends CodeCleanerPass
*/
public function leaveNode(Node $node)
{
if (!$this->atLeastPhp71) {
return; // @codeCoverageIgnore
}
if (!empty($this->returnTypeStack) && $this->isFunctionNode($node)) {
\array_pop($this->returnTypeStack);
}

View File

@@ -12,7 +12,6 @@
namespace Psy\CodeCleaner;
use PhpParser\Node;
use PhpParser\Node\Identifier;
use PhpParser\Node\Scalar\LNumber;
use PhpParser\Node\Stmt\Declare_;
use PhpParser\Node\Stmt\DeclareDeclare;
@@ -61,12 +60,10 @@ class StrictTypesPass extends CodeCleanerPass
foreach ($nodes as $node) {
if ($node instanceof Declare_) {
foreach ($node->declares as $declare) {
// For PHP Parser 4.x
$declareKey = $declare->key instanceof Identifier ? $declare->key->toString() : $declare->key;
if ($declareKey === 'strict_types') {
if ($declare->key->toString() === 'strict_types') {
$value = $declare->value;
if (!$value instanceof LNumber || ($value->value !== 0 && $value->value !== 1)) {
throw new FatalErrorException(self::EXCEPTION_MESSAGE, 0, \E_ERROR, null, $node->getLine());
throw new FatalErrorException(self::EXCEPTION_MESSAGE, 0, \E_ERROR, null, $node->getStartLine());
}
$this->strictTypes = $value->value === 1;

View File

@@ -17,6 +17,7 @@ use PhpParser\Node\Name\FullyQualified as FullyQualifiedName;
use PhpParser\Node\Stmt\GroupUse;
use PhpParser\Node\Stmt\Namespace_;
use PhpParser\Node\Stmt\Use_;
use PhpParser\Node\Stmt\UseItem;
use PhpParser\Node\Stmt\UseUse;
use PhpParser\NodeTraverser;
@@ -72,9 +73,8 @@ class UseStatementPass extends CodeCleanerPass
{
// Store a reference to every "use" statement, because we'll need them in a bit.
if ($node instanceof Use_) {
foreach ($node->uses as $use) {
$alias = $use->alias ?: \end($use->name->parts);
$this->aliases[\strtolower($alias)] = $use->name;
foreach ($node->uses as $useItem) {
$this->aliases[\strtolower($useItem->getAlias())] = $useItem->name;
}
return NodeTraverser::REMOVE_NODE;
@@ -82,11 +82,10 @@ class UseStatementPass extends CodeCleanerPass
// Expand every "use" statement in the group into a full, standalone "use" and store 'em with the others.
if ($node instanceof GroupUse) {
foreach ($node->uses as $use) {
$alias = $use->alias ?: \end($use->name->parts);
$this->aliases[\strtolower($alias)] = Name::concat($node->prefix, $use->name, [
foreach ($node->uses as $useItem) {
$this->aliases[\strtolower($useItem->getAlias())] = Name::concat($node->prefix, $useItem->name, [
'startLine' => $node->prefix->getAttribute('startLine'),
'endLine' => $use->name->getAttribute('endLine'),
'endLine' => $useItem->name->getAttribute('endLine'),
]);
}
@@ -102,8 +101,9 @@ class UseStatementPass extends CodeCleanerPass
return;
}
// Do nothing with UseUse; this an entry in the list of uses in the use statement.
if ($node instanceof UseUse) {
// Do nothing with UseItem; this an entry in the list of uses in the use statement.
// @todo Remove UseUse once we drop support for PHP-Parser 4.x
if ($node instanceof UseUse || $node instanceof UseItem) {
return;
}

View File

@@ -79,8 +79,6 @@ class ValidClassNamePass extends NamespaceAwarePass
{
if (self::isConditional($node)) {
$this->conditionalScopes--;
return;
}
}
@@ -261,30 +259,6 @@ class ValidClassNamePass extends NamespaceAwarePass
}
}
/**
* Get a symbol type key for storing in the scope name cache.
*
* @deprecated No longer used. Scope type should be passed into ensureCanDefine directly.
*
* @codeCoverageIgnore
*
* @throws FatalErrorException
*
* @param Stmt $stmt
*/
protected function getScopeType(Stmt $stmt): string
{
if ($stmt instanceof Class_) {
return self::CLASS_TYPE;
} elseif ($stmt instanceof Interface_) {
return self::INTERFACE_TYPE;
} elseif ($stmt instanceof Trait_) {
return self::TRAIT_TYPE;
}
throw $this->createError('Unsupported statement type', $stmt);
}
/**
* Check whether a class exists, or has been defined in the current code snippet.
*
@@ -347,6 +321,6 @@ class ValidClassNamePass extends NamespaceAwarePass
*/
protected function createError(string $msg, Stmt $stmt): FatalErrorException
{
return new FatalErrorException($msg, 0, \E_ERROR, null, $stmt->getLine());
return new FatalErrorException($msg, 0, \E_ERROR, null, $stmt->getStartLine());
}
}

View File

@@ -12,7 +12,7 @@
namespace Psy\CodeCleaner;
use PhpParser\Node;
use PhpParser\Node\Identifier;
use PhpParser\Node\Name;
use PhpParser\Node\Stmt\Class_;
use PhpParser\Node\Stmt\ClassMethod;
use PhpParser\Node\Stmt\Namespace_;
@@ -56,7 +56,7 @@ class ValidConstructorPass extends CodeCleanerPass
public function enterNode(Node $node)
{
if ($node instanceof Namespace_) {
$this->namespace = isset($node->name) ? $node->name->parts : [];
$this->namespace = isset($node->name) ? $this->getParts($node->name) : [];
} elseif ($node instanceof Class_) {
$constructor = null;
foreach ($node->stmts as $stmt) {
@@ -91,27 +91,31 @@ class ValidConstructorPass extends CodeCleanerPass
private function validateConstructor(Node $constructor, Node $classNode)
{
if ($constructor->isStatic()) {
// For PHP Parser 4.x
$className = $classNode->name instanceof Identifier ? $classNode->name->toString() : $classNode->name;
$msg = \sprintf(
'Constructor %s::%s() cannot be static',
\implode('\\', \array_merge($this->namespace, (array) $className)),
\implode('\\', \array_merge($this->namespace, (array) $classNode->name->toString())),
$constructor->name
);
throw new FatalErrorException($msg, 0, \E_ERROR, null, $classNode->getLine());
throw new FatalErrorException($msg, 0, \E_ERROR, null, $classNode->getStartLine());
}
if (\method_exists($constructor, 'getReturnType') && $constructor->getReturnType()) {
// For PHP Parser 4.x
$className = $classNode->name instanceof Identifier ? $classNode->name->toString() : $classNode->name;
$msg = \sprintf(
'Constructor %s::%s() cannot declare a return type',
\implode('\\', \array_merge($this->namespace, (array) $className)),
\implode('\\', \array_merge($this->namespace, (array) $classNode->name->toString())),
$constructor->name
);
throw new FatalErrorException($msg, 0, \E_ERROR, null, $classNode->getLine());
throw new FatalErrorException($msg, 0, \E_ERROR, null, $classNode->getStartLine());
}
}
/**
* Backwards compatibility shim for PHP-Parser 4.x.
*
* At some point we might want to make $namespace a plain string, to match how Name works?
*/
protected function getParts(Name $name): array
{
return \method_exists($name, 'getParts') ? $name->getParts() : $name->parts;
}
}

View File

@@ -53,7 +53,7 @@ class ValidFunctionNamePass extends NamespaceAwarePass
if (\function_exists($name) ||
isset($this->currentScope[\strtolower($name)])) {
$msg = \sprintf('Cannot redeclare %s()', $name);
throw new FatalErrorException($msg, 0, \E_ERROR, null, $node->getLine());
throw new FatalErrorException($msg, 0, \E_ERROR, null, $node->getStartLine());
}
}

View File

@@ -50,7 +50,7 @@ HELP
*
* @return int 0 if everything went fine, or an exit code
*/
protected function execute(InputInterface $input, OutputInterface $output)
protected function execute(InputInterface $input, OutputInterface $output): int
{
$app = $this->getApplication();
if (!$app instanceof \Psy\Shell) {

View File

@@ -44,7 +44,7 @@ HELP
*
* @return int 0 if everything went fine, or an exit code
*/
protected function execute(InputInterface $input, OutputInterface $output)
protected function execute(InputInterface $input, OutputInterface $output): int
{
$output->write(\sprintf('%c[2J%c[0;0f', 27, 27));

View File

@@ -30,13 +30,13 @@ abstract class Command extends BaseCommand
*
* @api
*/
public function setApplication(Application $application = null)
public function setApplication(Application $application = null): void
{
if ($application !== null && !$application instanceof Shell) {
throw new \InvalidArgumentException('PsySH Commands require an instance of Psy\Shell');
}
return parent::setApplication($application);
parent::setApplication($application);
}
/**

View File

@@ -15,8 +15,7 @@ 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\ReflectionConstant;
use Psy\Reflection\ReflectionLanguageConstruct;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
@@ -63,7 +62,7 @@ HELP
*
* @return int 0 if everything went fine, or an exit code
*/
protected function execute(InputInterface $input, OutputInterface $output)
protected function execute(InputInterface $input, OutputInterface $output): int
{
$value = $input->getArgument('target');
if (ReflectionLanguageConstruct::isLanguageConstruct($value)) {
@@ -146,14 +145,13 @@ HELP
break;
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;
break;
case ReflectionConstant_::class:
case ReflectionConstant::class:
$id = $reflector->name;
break;

View File

@@ -69,7 +69,7 @@ HELP
*
* @return int 0 if everything went fine, or an exit code
*/
protected function execute(InputInterface $input, OutputInterface $output)
protected function execute(InputInterface $input, OutputInterface $output): int
{
$depth = $input->getOption('depth');
$target = $this->resolveCode($input->getArgument('target'));
@@ -81,18 +81,4 @@ HELP
return 0;
}
/**
* @deprecated Use `resolveCode` instead
*
* @param string $name
*
* @return mixed
*/
protected function resolveTarget(string $name)
{
@\trigger_error('`resolveTarget` is deprecated; use `resolveCode` instead.', \E_USER_DEPRECATED);
return $this->resolveCode($name);
}
}

View File

@@ -79,7 +79,7 @@ class EditCommand extends Command implements ContextAware
* @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
*/
protected function execute(InputInterface $input, OutputInterface $output)
protected function execute(InputInterface $input, OutputInterface $output): int
{
if ($input->getOption('exec') &&
$input->getOption('no-exec')) {

View File

@@ -47,7 +47,7 @@ HELP
*
* @return int 0 if everything went fine, or an exit code
*/
protected function execute(InputInterface $input, OutputInterface $output)
protected function execute(InputInterface $input, OutputInterface $output): int
{
throw new BreakException('Goodbye');
}

View File

@@ -55,7 +55,7 @@ class HelpCommand extends Command
*
* @return int 0 if everything went fine, or an exit code
*/
protected function execute(InputInterface $input, OutputInterface $output)
protected function execute(InputInterface $input, OutputInterface $output): int
{
if ($this->command !== null) {
// help for an individual command

View File

@@ -93,7 +93,7 @@ HELP
*
* @return int 0 if everything went fine, or an exit code
*/
protected function execute(InputInterface $input, OutputInterface $output)
protected function execute(InputInterface $input, OutputInterface $output): int
{
$this->validateOnlyOne($input, ['show', 'head', 'tail']);
$this->validateOnlyOne($input, ['save', 'replay', 'clear']);

View File

@@ -114,7 +114,7 @@ HELP
*
* @return int 0 if everything went fine, or an exit code
*/
protected function execute(InputInterface $input, OutputInterface $output)
protected function execute(InputInterface $input, OutputInterface $output): int
{
$this->validateInput($input);
$this->initEnumerators();

View File

@@ -11,7 +11,6 @@
namespace Psy\Command\ListCommand;
use Psy\Reflection\ReflectionClassConstant;
use Symfony\Component\Console\Input\InputInterface;
/**
@@ -67,7 +66,7 @@ class ClassConstantEnumerator extends Enumerator
$constants = [];
foreach ($reflector->getConstants() as $name => $constant) {
$constReflector = ReflectionClassConstant::create($reflector->name, $name);
$constReflector = new \ReflectionClassConstant($reflector->name, $name);
if ($noInherit && $constReflector->getDeclaringClass()->getName() !== $className) {
continue;

View File

@@ -12,7 +12,6 @@
namespace Psy\Command;
use PhpParser\Node;
use PhpParser\Parser;
use Psy\Context;
use Psy\ContextAware;
use Psy\Input\CodeArgument;
@@ -37,16 +36,14 @@ class ParseCommand extends Command implements ContextAware, PresenterAware
protected $context;
private $presenter;
private $parserFactory;
private $parsers;
private $parser;
/**
* {@inheritdoc}
*/
public function __construct($name = null)
{
$this->parserFactory = new ParserFactory();
$this->parsers = [];
$this->parser = (new ParserFactory())->createParser();
parent::__construct($name);
}
@@ -90,16 +87,11 @@ class ParseCommand extends Command implements ContextAware, PresenterAware
*/
protected function configure()
{
$kindMsg = 'One of PhpParser\\ParserFactory constants: '
.\implode(', ', ParserFactory::getPossibleKinds())
." (default is based on current interpreter's version).";
$this
->setName('parse')
->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(
@@ -119,31 +111,17 @@ HELP
/**
* {@inheritdoc}
*/
protected function execute(InputInterface $input, OutputInterface $output)
protected function execute(InputInterface $input, OutputInterface $output): int
{
$code = $input->getArgument('code');
$parserKind = $input->getOption('kind');
$depth = $input->getOption('depth');
$nodes = $this->getParser($parserKind)->parse($code);
$nodes = $this->parser->parse($code);
$output->page($this->presenter->present($nodes, $depth));
$this->context->setReturnValue($nodes);
return 0;
}
/**
* Get (or create) the Parser instance.
*
* @param string|null $kind One of Psy\ParserFactory constants (only for PHP parser 2.0 and above)
*/
private function getParser(string $kind = null): CodeArgumentParser
{
if (!\array_key_exists($kind, $this->parsers)) {
$this->parsers[$kind] = new CodeArgumentParser($this->parserFactory->createParser($kind));
}
return $this->parsers[$kind];
}
}

View File

@@ -34,7 +34,7 @@ class PsyVersionCommand extends Command
/**
* {@inheritdoc}
*/
protected function execute(InputInterface $input, OutputInterface $output)
protected function execute(InputInterface $input, OutputInterface $output): int
{
$output->writeln($this->getApplication()->getVersion());

View File

@@ -19,8 +19,7 @@ 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\Reflection\ReflectionConstant;
use Psy\Sudo\SudoVisitor;
use Psy\Util\Mirror;
@@ -227,20 +226,6 @@ abstract class ReflectingCommand extends Command implements ContextAware
return $value;
}
/**
* @deprecated Use `resolveCode` instead
*
* @param string $name
*
* @return mixed Variable instance
*/
protected function resolveInstance(string $name)
{
@\trigger_error('`resolveInstance` is deprecated; use `resolveCode` instead.', \E_USER_DEPRECATED);
return $this->resolveCode($name);
}
/**
* Get a variable from the current shell scope.
*
@@ -314,7 +299,6 @@ abstract class ReflectingCommand extends Command implements ContextAware
case \ReflectionProperty::class:
case \ReflectionClassConstant::class:
case ReflectionClassConstant::class:
$classReflector = $reflector->getDeclaringClass();
$vars['__class'] = $classReflector->name;
if ($classReflector->inNamespace()) {
@@ -327,7 +311,7 @@ abstract class ReflectingCommand extends Command implements ContextAware
}
break;
case ReflectionConstant_::class:
case ReflectionConstant::class:
if ($reflector->inNamespace()) {
$vars['__namespace'] = $reflector->getNamespaceName();
}

View File

@@ -29,14 +29,6 @@ class ShowCommand extends ReflectingCommand
private $lastException;
private $lastExceptionIndex;
/**
* @param string|null $colorMode (deprecated and ignored)
*/
public function __construct($colorMode = null)
{
parent::__construct();
}
/**
* {@inheritdoc}
*/
@@ -72,7 +64,7 @@ HELP
*
* @return int 0 if everything went fine, or an exit code
*/
protected function execute(InputInterface $input, OutputInterface $output)
protected function execute(InputInterface $input, OutputInterface $output): int
{
// n.b. As far as I can tell, InputInterface doesn't want to tell me
// whether an option with an optional value was actually passed. If you

View File

@@ -96,7 +96,7 @@ HELP
*
* @return int 0 if everything went fine, or an exit code
*/
protected function execute(InputInterface $input, OutputInterface $output)
protected function execute(InputInterface $input, OutputInterface $output): int
{
$code = $input->getArgument('code');

View File

@@ -13,13 +13,12 @@ namespace Psy\Command;
use PhpParser\Node\Arg;
use PhpParser\Node\Expr\New_;
use PhpParser\Node\Expr\Throw_;
use PhpParser\Node\Expr\Variable;
use PhpParser\Node\Name\FullyQualified as FullyQualifiedName;
use PhpParser\Node\Scalar\String_;
use PhpParser\Node\Stmt\Throw_;
use PhpParser\Node\Stmt\Expression;
use PhpParser\PrettyPrinter\Standard as Printer;
use Psy\Context;
use Psy\ContextAware;
use Psy\Exception\ThrowUpException;
use Psy\Input\CodeArgument;
use Symfony\Component\Console\Input\InputInterface;
@@ -28,7 +27,7 @@ use Symfony\Component\Console\Output\OutputInterface;
/**
* Throw an exception or error out of the Psy Shell.
*/
class ThrowUpCommand extends Command implements ContextAware
class ThrowUpCommand extends Command
{
private $parser;
private $printer;
@@ -44,16 +43,6 @@ class ThrowUpCommand extends Command implements ContextAware
parent::__construct($name);
}
/**
* @deprecated throwUp no longer needs to be ContextAware
*
* @param Context $context
*/
public function setContext(Context $context)
{
// Do nothing
}
/**
* {@inheritdoc}
*/
@@ -87,10 +76,10 @@ HELP
*
* @throws \InvalidArgumentException if there is no exception to throw
*/
protected function execute(InputInterface $input, OutputInterface $output)
protected function execute(InputInterface $input, OutputInterface $output): int
{
$args = $this->prepareArgs($input->getArgument('exception'));
$throwStmt = new Throw_(new New_(new FullyQualifiedName(ThrowUpException::class), $args));
$throwStmt = new Expression(new Throw_(new New_(new FullyQualifiedName(ThrowUpException::class), $args)));
$throwCode = $this->printer->prettyPrint([$throwStmt]);
$shell = $this->getApplication();
@@ -123,9 +112,7 @@ HELP
}
$node = $nodes[0];
// Make this work for PHP Parser v3.x
$expr = isset($node->expr) ? $node->expr : $node;
$expr = $node->expr;
$args = [new Arg($expr, false, false, $node->getAttributes())];

View File

@@ -28,7 +28,6 @@ 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 $useHrtime;
private static $start = null;
private static $times = [];
@@ -41,9 +40,6 @@ class TimeitCommand extends Command
*/
public function __construct($name = null)
{
// @todo Remove microtime use after we drop support for PHP < 7.3
self::$useHrtime = \function_exists('hrtime');
$this->parser = new CodeArgumentParser();
$this->traverser = new NodeTraverser();
@@ -82,7 +78,7 @@ HELP
*
* @return int 0 if everything went fine, or an exit code
*/
protected function execute(InputInterface $input, OutputInterface $output)
protected function execute(InputInterface $input, OutputInterface $output): int
{
$code = $input->getArgument('code');
$num = (int) ($input->getOption('num') ?: 1);
@@ -124,7 +120,7 @@ HELP
*/
public static function markStart()
{
self::$start = self::$useHrtime ? \hrtime(true) : (\microtime(true) * 1e+6);
self::$start = \hrtime(true);
}
/**
@@ -143,7 +139,7 @@ HELP
*/
public static function markEnd($ret = null)
{
self::$times[] = (self::$useHrtime ? \hrtime(true) : (\microtime(true) * 1e+6)) - self::$start;
self::$times[] = \hrtime(true) - self::$start;
self::$start = null;
return $ret;

View File

@@ -84,7 +84,7 @@ class TimeitVisitor extends NodeVisitorAbstract
public function afterTraverse(array $nodes)
{
// prepend a `markStart` call
\array_unshift($nodes, $this->maybeExpression($this->getStartCall()));
\array_unshift($nodes, new Expression($this->getStartCall(), []));
// append a `markEnd` call (wrapping the final node, if it's an expression)
$last = $nodes[\count($nodes) - 1];
@@ -97,7 +97,7 @@ class TimeitVisitor extends NodeVisitorAbstract
} elseif ($last instanceof Return_) {
// nothing to do here, we're already ending with a return call
} else {
$nodes[] = $this->maybeExpression($this->getEndCall());
$nodes[] = new Expression($this->getEndCall(), []);
}
return $nodes;
@@ -128,19 +128,4 @@ class TimeitVisitor extends NodeVisitorAbstract
return new StaticCall(new FullyQualifiedName(TimeitCommand::class), 'markEnd', [new Arg($arg)]);
}
/**
* Compatibility shim for PHP Parser 3.x.
*
* Wrap $expr in a PhpParser\Node\Stmt\Expression if the class exists.
*
* @param \PhpParser\Node $expr
* @param array $attrs
*
* @return \PhpParser\Node\Expr|\PhpParser\Node\Stmt\Expression
*/
private function maybeExpression(Node $expr, array $attrs = [])
{
return \class_exists(Expression::class) ? new Expression($expr, $attrs) : $expr;
}
}

View File

@@ -71,7 +71,7 @@ HELP
*
* @return int 0 if everything went fine, or an exit code
*/
protected function execute(InputInterface $input, OutputInterface $output)
protected function execute(InputInterface $input, OutputInterface $output): int
{
$this->filter->bind($input);
$trace = $this->getBacktrace(new \Exception(), $input->getOption('num'), $input->getOption('include-psy'));

View File

@@ -25,10 +25,7 @@ class WhereamiCommand extends Command
{
private $backtrace;
/**
* @param string|null $colorMode (deprecated and ignored)
*/
public function __construct($colorMode = null)
public function __construct()
{
$this->backtrace = \debug_backtrace(\DEBUG_BACKTRACE_IGNORE_ARGS);
@@ -112,7 +109,7 @@ HELP
*
* @return int 0 if everything went fine, or an exit code
*/
protected function execute(InputInterface $input, OutputInterface $output)
protected function execute(InputInterface $input, OutputInterface $output): int
{
$info = $this->fileInfo();
$num = $input->getOption('num');

View File

@@ -84,7 +84,7 @@ HELP
*
* @return int 0 if everything went fine, or an exit code
*/
protected function execute(InputInterface $input, OutputInterface $output)
protected function execute(InputInterface $input, OutputInterface $output): int
{
$this->filter->bind($input);

View File

@@ -120,32 +120,6 @@ class ConfigPaths
return $this->allDirNames(\array_merge([$this->homeConfigDir()], $configDirs));
}
/**
* @deprecated
*/
public static function getConfigDirs(): array
{
return (new self())->configDirs();
}
/**
* Get potential home config directory paths.
*
* Returns `~/.psysh`, `%APPDATA%/PsySH` (when on Windows), and the
* XDG Base Directory home config directory:
*
* http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html
*
* @deprecated
*
* @return string[]
*/
public static function getHomeConfigDirs(): array
{
// Not quite the same, but this is deprecated anyway /shrug
return self::getConfigDirs();
}
/**
* Get the current home config directory.
*
@@ -173,14 +147,6 @@ class ConfigPaths
return $configDirs[0];
}
/**
* @deprecated
*/
public static function getCurrentConfigDir(): string
{
return (new self())->currentConfigDir();
}
/**
* Find real config files in config directories.
*
@@ -193,14 +159,6 @@ class ConfigPaths
return $this->allRealFiles($this->configDirs(), $names);
}
/**
* @deprecated
*/
public static function getConfigFiles(array $names, $configDir = null): array
{
return (new self(['configDir' => $configDir]))->configFiles($names);
}
/**
* Get potential data directory paths.
*
@@ -225,14 +183,6 @@ class ConfigPaths
return $this->allDirNames(\array_merge([$homeDataDir], $dataDirs));
}
/**
* @deprecated
*/
public static function getDataDirs(): array
{
return (new self())->dataDirs();
}
/**
* Find real data files in config directories.
*
@@ -245,14 +195,6 @@ class ConfigPaths
return $this->allRealFiles($this->dataDirs(), $names);
}
/**
* @deprecated
*/
public static function getDataFiles(array $names, $dataDir = null): array
{
return (new self(['dataDir' => $dataDir]))->dataFiles($names);
}
/**
* Get a runtime directory.
*
@@ -270,14 +212,6 @@ class ConfigPaths
return \strtr($runtimeDir, '\\', '/').'/psysh';
}
/**
* @deprecated
*/
public static function getRuntimeDir(): string
{
return (new self())->runtimeDir();
}
/**
* Get a list of directories in PATH.
*

View File

@@ -1097,6 +1097,8 @@ class Configuration
*/
public function setTabCompletion(bool $useTabCompletion)
{
@\trigger_error('`setTabCompletion` is deprecated; call `setUseTabCompletion` instead.', \E_USER_DEPRECATED);
$this->setUseTabCompletion($useTabCompletion);
}
@@ -1118,6 +1120,8 @@ class Configuration
*/
public function getTabCompletion(): bool
{
@\trigger_error('`getTabCompletion` is deprecated; call `useTabCompletion` instead.', \E_USER_DEPRECATED);
return $this->useTabCompletion();
}
@@ -1283,6 +1287,8 @@ class Configuration
*/
public function getTabCompletionMatchers(): array
{
@\trigger_error('`getTabCompletionMatchers` is no longer used.', \E_USER_DEPRECATED);
return [];
}
@@ -1323,6 +1329,8 @@ class Configuration
*/
public function addTabCompletionMatchers(array $matchers)
{
@\trigger_error('`addTabCompletionMatchers` is deprecated; call `addMatchers` instead.', \E_USER_DEPRECATED);
$this->addMatchers($matchers);
}

View File

@@ -105,8 +105,8 @@ class ErrorException extends \ErrorException implements Exception
*
* @param \Error $e
*/
public static function fromError(\Error $e): self
public static function fromError(\Error $e)
{
return new self($e->getMessage(), $e->getCode(), 1, $e->getFile(), $e->getLine(), $e);
@\trigger_error('PsySH no longer wraps Errors', \E_USER_DEPRECATED);
}
}

View File

@@ -19,13 +19,19 @@ class ParseErrorException extends \PhpParser\Error implements Exception
/**
* Constructor!
*
* @param string $message (default: "")
* @param int $line (default: -1)
* @param string $message (default: '')
* @param array|int $attributes Attributes of node/token where error occurred
* (or start line of error -- deprecated)
*/
public function __construct(string $message = '', int $line = -1)
public function __construct(string $message = '', $attributes = [])
{
$message = \sprintf('PHP Parse error: %s', $message);
parent::__construct($message, $line);
if (!\is_array($attributes)) {
$attributes = ['startLine' => $attributes];
}
parent::__construct($message, $attributes);
}
/**
@@ -35,6 +41,6 @@ class ParseErrorException extends \PhpParser\Error implements Exception
*/
public static function fromParseError(\PhpParser\Error $e): self
{
return new self($e->getRawMessage(), $e->getStartLine());
return new self($e->getRawMessage(), $e->getAttributes());
}
}

View File

@@ -40,16 +40,8 @@ class ThrowUpException extends \Exception implements Exception
*
* @param \Throwable $throwable
*/
public static function fromThrowable($throwable): self
public static function fromThrowable($throwable)
{
if ($throwable instanceof \Error) {
$throwable = ErrorException::fromError($throwable);
}
if (!$throwable instanceof \Exception) {
throw new \InvalidArgumentException('throw-up can only throw Exceptions and Errors');
}
return new self($throwable);
@\trigger_error('PsySH no longer wraps Throwables', \E_USER_DEPRECATED);
}
}

View File

@@ -1,56 +0,0 @@
<?php
/*
* This file is part of Psy Shell.
*
* (c) 2012-2023 Justin Hileman
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Psy\Exception;
/**
* A "type error" Exception for Psy.
*/
class TypeErrorException extends \Exception implements Exception
{
private $rawMessage;
/**
* Constructor!
*
* @deprecated PsySH no longer wraps TypeErrors
*
* @param string $message (default: "")
* @param int $code (default: 0)
* @param \Throwable|null $previous (default: null)
*/
public function __construct(string $message = '', int $code = 0, \Throwable $previous = null)
{
$this->rawMessage = $message;
$message = \preg_replace('/, called in .*?: eval\\(\\)\'d code/', '', $message);
parent::__construct(\sprintf('TypeError: %s', $message), $code, $previous);
}
/**
* Get the raw (unformatted) message for this error.
*/
public function getRawMessage(): string
{
return $this->rawMessage;
}
/**
* Create a TypeErrorException from a TypeError.
*
* @deprecated PsySH no longer wraps TypeErrors
*
* @param \TypeError $e
*/
public static function fromTypeError(\TypeError $e): self
{
return new self($e->getMessage(), $e->getCode(), $e);
}
}

View File

@@ -36,13 +36,10 @@ class RunkitReloader extends AbstractListener
/**
* Construct a Runkit Reloader.
*
* @todo Pass in Parser Factory instance for dependency injection?
*/
public function __construct()
{
$parserFactory = new ParserFactory();
$this->parser = $parserFactory->createParser();
$this->parser = (new ParserFactory())->createParser();
}
/**

View File

@@ -17,8 +17,7 @@ use Psy\Exception\ThrowUpException;
/**
* The Psy Shell's execution loop scope.
*
* @todo Once we're on PHP 5.5, we can switch ExecutionClosure to a generator
* and get rid of the duplicate closure implementations :)
* @todo Switch ExecutionClosure to a generator and get rid of the duplicate closure implementations?
*/
class ExecutionLoopClosure extends ExecutionClosure
{

View File

@@ -76,12 +76,11 @@ class CodeFormatter implements ReflectorFormatter
/**
* Format the code represented by $reflector for shell output.
*
* @param \Reflector $reflector
* @param string|null $colorMode (deprecated and ignored)
* @param \Reflector $reflector
*
* @return string formatted code
*/
public static function format(\Reflector $reflector, string $colorMode = null): string
public static function format(\Reflector $reflector): string
{
if (self::isReflectable($reflector)) {
if ($code = @\file_get_contents($reflector->getFileName())) {

View File

@@ -1,21 +0,0 @@
<?php
/*
* This file is part of Psy Shell.
*
* (c) 2012-2023 Justin Hileman
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Psy\Formatter;
/**
* Formatter interface.
*
* @deprecated this interface only exists for backwards compatibility. Use ReflectorFormatter.
*/
interface Formatter extends ReflectorFormatter
{
}

View File

@@ -11,8 +11,7 @@
namespace Psy\Formatter;
use Psy\Reflection\ReflectionClassConstant;
use Psy\Reflection\ReflectionConstant_;
use Psy\Reflection\ReflectionConstant;
use Psy\Reflection\ReflectionLanguageConstruct;
use Psy\Util\Json;
use Symfony\Component\Console\Formatter\OutputFormatter;
@@ -42,7 +41,6 @@ class SignatureFormatter implements ReflectorFormatter
// this case also covers \ReflectionObject
return self::formatClass($reflector);
case $reflector instanceof ReflectionClassConstant:
case $reflector instanceof \ReflectionClassConstant:
return self::formatClassConstant($reflector);
@@ -52,7 +50,7 @@ class SignatureFormatter implements ReflectorFormatter
case $reflector instanceof \ReflectionProperty:
return self::formatProperty($reflector);
case $reflector instanceof ReflectionConstant_:
case $reflector instanceof ReflectionConstant:
return self::formatConstant($reflector);
default:
@@ -63,7 +61,7 @@ class SignatureFormatter implements ReflectorFormatter
/**
* Print the signature name.
*
* @param \ReflectionClass|ReflectionClassConstant|\ReflectionClassConstant|\ReflectionFunctionAbstract $reflector
* @param \ReflectionClass|\ReflectionClassConstant|\ReflectionFunctionAbstract $reflector
*
* @return string Formatted name
*/
@@ -130,7 +128,7 @@ class SignatureFormatter implements ReflectorFormatter
/**
* Format a constant signature.
*
* @param ReflectionClassConstant|\ReflectionClassConstant $reflector
* @param \ReflectionClassConstant $reflector
*
* @return string Formatted signature
*/
@@ -151,11 +149,11 @@ class SignatureFormatter implements ReflectorFormatter
/**
* Format a constant signature.
*
* @param ReflectionConstant_ $reflector
* @param ReflectionConstant $reflector
*
* @return string Formatted signature
*/
private static function formatConstant(ReflectionConstant_ $reflector): string
private static function formatConstant(ReflectionConstant $reflector): string
{
$value = $reflector->getValue();
$style = self::getTypeStyle($value);
@@ -234,7 +232,7 @@ class SignatureFormatter implements ReflectorFormatter
return '';
}
return \sprintf(': %s', self::formatReflectionType($reflector->getReturnType()));
return \sprintf(': %s', self::formatReflectionType($reflector->getReturnType(), true));
}
/**
@@ -267,7 +265,9 @@ class SignatureFormatter implements ReflectorFormatter
$hint = '';
try {
if (\method_exists($param, 'getType')) {
$hint = self::formatReflectionType($param->getType());
// Only include the inquisitive nullable type iff param default value is not null.
$defaultIsNull = $param->isOptional() && $param->isDefaultValueAvailable() && $param->getDefaultValue() === null;
$hint = self::formatReflectionType($param->getType(), !$defaultIsNull);
} else {
if ($param->isArray()) {
$hint = '<keyword>array</keyword>';
@@ -323,25 +323,36 @@ class SignatureFormatter implements ReflectorFormatter
*
* @param \ReflectionType $type
*/
private static function formatReflectionType(\ReflectionType $type = null): string
private static function formatReflectionType(?\ReflectionType $type, bool $indicateNullable): string
{
if ($type === null) {
return '';
}
$types = $type instanceof \ReflectionUnionType ? $type->getTypes() : [$type];
$formattedTypes = [];
foreach ($types as $type) {
$typeStyle = $type->isBuiltin() ? 'keyword' : 'class';
// PHP 7.0 didn't have `getName` on reflection types, so wheee!
$typeName = \method_exists($type, 'getName') ? $type->getName() : (string) $type;
// @todo Do we want to include the ? for nullable types? Maybe only sometimes?
$formattedTypes[] = \sprintf('<%s>%s</%s>', $typeStyle, OutputFormatter::escape($typeName), $typeStyle);
if ($type instanceof \ReflectionUnionType) {
$delimeter = '|';
} elseif ($type instanceof \ReflectionIntersectionType) {
$delimeter = '&';
} else {
return self::formatReflectionNamedType($type, $indicateNullable);
}
return \implode('|', $formattedTypes);
$formattedTypes = [];
foreach ($type->getTypes() as $namedType) {
$formattedTypes[] = self::formatReflectionNamedType($namedType, $indicateNullable);
}
return \implode($delimeter, $formattedTypes);
}
/**
* Print a single named type.
*/
private static function formatReflectionNamedType(\ReflectionNamedType $type, bool $indicateNullable): string
{
$typeStyle = $type->isBuiltin() ? 'keyword' : 'class';
$nullable = $indicateNullable && $type->allowsNull() ? '?' : '';
return \sprintf('<%s>%s%s</%s>', $typeStyle, $nullable, OutputFormatter::escape($type->getName()), $typeStyle);
}
}

View File

@@ -19,6 +19,8 @@ use Symfony\Component\Console\Input\StringInput;
*/
class ShellInput extends StringInput
{
public const REGEX_STRING = '([^\s]+?)(?:\s|(?<!\\\\)"|(?<!\\\\)\'|$)';
private $hasCodeArgument = false;
/**
@@ -45,7 +47,7 @@ class ShellInput extends StringInput
*
* @throws \InvalidArgumentException if $definition has CodeArgument before the final argument position
*/
public function bind(InputDefinition $definition)
public function bind(InputDefinition $definition): void
{
$hasCodeArgument = false;
@@ -66,7 +68,7 @@ class ShellInput extends StringInput
$this->hasCodeArgument = $hasCodeArgument;
return parent::bind($definition);
parent::bind($definition);
}
/**
@@ -98,7 +100,7 @@ class ShellInput extends StringInput
\stripcslashes(\substr($match[0], 1, \strlen($match[0]) - 2)),
\stripcslashes(\substr($input, $cursor)),
];
} elseif (\preg_match('/'.StringInput::REGEX_STRING.'/A', $input, $match, 0, $cursor)) {
} elseif (\preg_match('/'.self::REGEX_STRING.'/A', $input, $match, 0, $cursor)) {
$tokens[] = [
\stripcslashes($match[1]),
\stripcslashes(\substr($input, $cursor)),
@@ -119,7 +121,7 @@ class ShellInput extends StringInput
/**
* Same as parent, but with some bonus handling for code arguments.
*/
protected function parse()
protected function parse(): void
{
$parseOptions = true;
$this->parsed = $this->tokenPairs;

View File

@@ -48,7 +48,7 @@ class ProcOutputPager extends StreamOutput implements OutputPager
*
* @throws \RuntimeException When unable to write output (should never happen)
*/
public function doWrite($message, $newline)
public function doWrite($message, $newline): void
{
$pipe = $this->getPipe();
if (false === @\fwrite($pipe, $message.($newline ? \PHP_EOL : ''))) {

View File

@@ -119,7 +119,7 @@ class ShellOutput extends ConsoleOutput
* @param bool $newline Whether to add a newline or not
* @param int $type The type of output
*/
public function write($messages, $newline = false, $type = 0)
public function write($messages, $newline = false, $type = 0): void
{
if ($this->getVerbosity() === self::VERBOSITY_QUIET) {
return;
@@ -154,7 +154,7 @@ class ShellOutput extends ConsoleOutput
* @param string $message A message to write to the output
* @param bool $newline Whether to add a newline or not
*/
public function doWrite($message, $newline)
public function doWrite($message, $newline): void
{
if ($this->paging > 0) {
$this->pager->doWrite($message, $newline);

View File

@@ -15,52 +15,21 @@ use PhpParser\Parser;
use PhpParser\ParserFactory as OriginalParserFactory;
/**
* Parser factory to abstract over PHP parser library versions.
* Parser factory to abstract over PHP Parser library versions.
*/
class ParserFactory
{
const ONLY_PHP5 = 'ONLY_PHP5';
const ONLY_PHP7 = 'ONLY_PHP7';
const PREFER_PHP5 = 'PREFER_PHP5';
const PREFER_PHP7 = 'PREFER_PHP7';
/**
* Possible kinds of parsers for the factory, from PHP parser library.
*
* @return string[]
* New parser instance.
*/
public static function getPossibleKinds(): array
public function createParser(): Parser
{
return ['ONLY_PHP5', 'ONLY_PHP7', 'PREFER_PHP5', 'PREFER_PHP7'];
}
$factory = new OriginalParserFactory();
/**
* Default kind (if supported, based on current interpreter's version).
*
* @return string|null
*/
public function getDefaultKind()
{
return static::ONLY_PHP7;
}
/**
* New parser instance with given kind.
*
* @param string|null $kind One of class constants (only for PHP parser 2.0 and above)
*/
public function createParser($kind = null): Parser
{
$originalFactory = new OriginalParserFactory();
$kind = $kind ?: $this->getDefaultKind();
if (!\in_array($kind, static::getPossibleKinds())) {
throw new \InvalidArgumentException('Unknown parser kind');
if (!\method_exists($factory, 'createForHostVersion')) {
return $factory->create(OriginalParserFactory::PREFER_PHP7);
}
$parser = $originalFactory->create(\constant(OriginalParserFactory::class.'::'.$kind));
return $parser;
return $factory->createForHostVersion();
}
}

View File

@@ -128,11 +128,7 @@ abstract class File extends FileGeneric implements StreamBufferable, StreamLocka
default:
if (true === \ctype_digit($streamName)) {
if (\PHP_VERSION_ID >= 50306) {
$streamName = 'php://fd/'.$streamName;
} else {
throw new FileException('You need PHP5.3.6 to use a file descriptor '.'other than 0, 1 or 2 (tried %d with PHP%s).', 0, [$streamName, \PHP_VERSION]);
}
$streamName = 'php://fd/'.$streamName;
}
}
@@ -189,7 +185,7 @@ abstract class File extends FileGeneric implements StreamBufferable, StreamLocka
{
$this->setStreamBuffer($size);
// @TODO manage $callable as a filter?
// @todo manage $callable as a filter?
return 1;
}

View File

@@ -1,21 +0,0 @@
<?php
/*
* This file is part of Psy Shell.
*
* (c) 2012-2023 Justin Hileman
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Psy\Readline;
/**
* Hoa\Console Readline implementation.
*
* @deprecated, use Userland readline
*/
class HoaConsole extends Userland
{
}

View File

@@ -1,220 +0,0 @@
<?php
/*
* This file is part of Psy Shell.
*
* (c) 2012-2023 Justin Hileman
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Psy\Reflection;
/**
* Somehow the standard reflection library didn't include class constants until 7.1.
*
* ReflectionClassConstant corrects that omission.
*/
class ReflectionClassConstant implements \Reflector
{
public $class;
public $name;
private $value;
/**
* Construct a ReflectionClassConstant object.
*
* @param string|object $class
* @param string $name
*/
public function __construct($class, string $name)
{
if (!$class instanceof \ReflectionClass) {
$class = new \ReflectionClass($class);
}
$this->class = $class;
$this->name = $name;
$constants = $class->getConstants();
if (!\array_key_exists($name, $constants)) {
throw new \InvalidArgumentException('Unknown constant: '.$name);
}
$this->value = $constants[$name];
}
/**
* Exports a reflection.
*
* @param string|object $class
* @param string $name
* @param bool $return pass true to return the export, as opposed to emitting it
*
* @return string|null
*/
public static function export($class, string $name, bool $return = false)
{
$refl = new self($class, $name);
$value = $refl->getValue();
$str = \sprintf('Constant [ public %s %s ] { %s }', \gettype($value), $refl->getName(), $value);
if ($return) {
return $str;
}
echo $str."\n";
}
/**
* Gets the declaring class.
*/
public function getDeclaringClass(): \ReflectionClass
{
$parent = $this->class;
// Since we don't have real reflection constants, we can't see where
// it's actually defined. Let's check for a constant that is also
// available on the parent class which has exactly the same value.
//
// While this isn't _technically_ correct, it's prolly close enough.
do {
$class = $parent;
$parent = $class->getParentClass();
} while ($parent && $parent->hasConstant($this->name) && $parent->getConstant($this->name) === $this->value);
return $class;
}
/**
* Get the constant's docblock.
*
* @return false
*/
public function getDocComment(): bool
{
return false;
}
/**
* Gets the class constant modifiers.
*
* Since this is only used for PHP < 7.1, we can just return "public". All
* the fancier modifiers are only available on PHP versions which have their
* own ReflectionClassConstant class :)
*/
public function getModifiers(): int
{
return \ReflectionMethod::IS_PUBLIC;
}
/**
* Gets the constant name.
*/
public function getName(): string
{
return $this->name;
}
/**
* Gets the value of the constant.
*
* @return mixed
*/
public function getValue()
{
return $this->value;
}
/**
* Checks if class constant is private.
*
* @return bool false
*/
public function isPrivate(): bool
{
return false;
}
/**
* Checks if class constant is protected.
*
* @return bool false
*/
public function isProtected(): bool
{
return false;
}
/**
* Checks if class constant is public.
*
* @return bool true
*/
public function isPublic(): bool
{
return true;
}
/**
* To string.
*/
public function __toString(): string
{
return $this->getName();
}
/**
* Gets the constant's file name.
*
* Currently returns null, because if it returns a file name the signature
* formatter will barf.
*/
public function getFileName()
{
return;
// return $this->class->getFileName();
}
/**
* Get the code start line.
*
* @throws \RuntimeException
*/
public function getStartLine()
{
throw new \RuntimeException('Not yet implemented because it\'s unclear what I should do here :)');
}
/**
* Get the code end line.
*
* @throws \RuntimeException
*/
public function getEndLine()
{
return $this->getStartLine();
}
/**
* Get a ReflectionClassConstant instance.
*
* In PHP >= 7.1, this will return a \ReflectionClassConstant from the
* standard reflection library. For older PHP, it will return this polyfill.
*
* @param string|object $class
* @param string $name
*
* @return ReflectionClassConstant|\ReflectionClassConstant
*/
public static function create($class, string $name)
{
if (\class_exists(\ReflectionClassConstant::class)) {
return new \ReflectionClassConstant($class, $name);
}
return new self($class, $name);
}
}

View File

@@ -12,19 +12,159 @@
namespace Psy\Reflection;
/**
* @deprecated ReflectionConstant is now ReflectionClassConstant. This class
* name will be reclaimed in the next stable release, to be used for
* ReflectionConstant_ :)
* Somehow the standard reflection library doesn't include constants.
*
* ReflectionConstant corrects that omission.
*/
class ReflectionConstant extends ReflectionClassConstant
class ReflectionConstant implements \Reflector
{
/**
* {inheritDoc}.
*/
public function __construct($class, $name)
{
@\trigger_error('ReflectionConstant is now ReflectionClassConstant', \E_USER_DEPRECATED);
public $name;
private $value;
parent::__construct($class, $name);
private static $magicConstants = [
'__LINE__',
'__FILE__',
'__DIR__',
'__FUNCTION__',
'__CLASS__',
'__TRAIT__',
'__METHOD__',
'__NAMESPACE__',
'__COMPILER_HALT_OFFSET__',
];
/**
* Construct a ReflectionConstant object.
*
* @param string $name
*/
public function __construct(string $name)
{
$this->name = $name;
if (!\defined($name) && !self::isMagicConstant($name)) {
throw new \InvalidArgumentException('Unknown constant: '.$name);
}
if (!self::isMagicConstant($name)) {
$this->value = @\constant($name);
}
}
/**
* Exports a reflection.
*
* @param string $name
* @param bool $return pass true to return the export, as opposed to emitting it
*
* @return string|null
*/
public static function export(string $name, bool $return = false)
{
$refl = new self($name);
$value = $refl->getValue();
$str = \sprintf('Constant [ %s %s ] { %s }', \gettype($value), $refl->getName(), $value);
if ($return) {
return $str;
}
echo $str."\n";
}
public static function isMagicConstant($name)
{
return \in_array($name, self::$magicConstants);
}
/**
* Get the constant's docblock.
*
* @return false
*/
public function getDocComment(): bool
{
return false;
}
/**
* Gets the constant name.
*/
public function getName(): string
{
return $this->name;
}
/**
* Gets the namespace name.
*
* Returns '' when the constant is not namespaced.
*/
public function getNamespaceName(): string
{
if (!$this->inNamespace()) {
return '';
}
return \preg_replace('/\\\\[^\\\\]+$/', '', $this->name);
}
/**
* Gets the value of the constant.
*
* @return mixed
*/
public function getValue()
{
return $this->value;
}
/**
* Checks if this constant is defined in a namespace.
*/
public function inNamespace(): bool
{
return \strpos($this->name, '\\') !== false;
}
/**
* To string.
*/
public function __toString(): string
{
return $this->getName();
}
/**
* Gets the constant's file name.
*
* Currently returns null, because if it returns a file name the signature
* formatter will barf.
*/
public function getFileName()
{
return;
// return $this->class->getFileName();
}
/**
* Get the code start line.
*
* @throws \RuntimeException
*/
public function getStartLine()
{
throw new \RuntimeException('Not yet implemented because it\'s unclear what I should do here :)');
}
/**
* Get the code end line.
*
* @throws \RuntimeException
*/
public function getEndLine()
{
return $this->getStartLine();
}
}

View File

@@ -1,174 +0,0 @@
<?php
/*
* This file is part of Psy Shell.
*
* (c) 2012-2023 Justin Hileman
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Psy\Reflection;
/**
* Somehow the standard reflection library doesn't include constants.
*
* ReflectionConstant_ corrects that omission.
*
* Note: For backwards compatibility reasons, this class is named
* ReflectionConstant_ rather than ReflectionConstant. It will be renamed in
* v0.10.0.
*/
class ReflectionConstant_ implements \Reflector
{
public $name;
private $value;
private static $magicConstants = [
'__LINE__',
'__FILE__',
'__DIR__',
'__FUNCTION__',
'__CLASS__',
'__TRAIT__',
'__METHOD__',
'__NAMESPACE__',
'__COMPILER_HALT_OFFSET__',
];
/**
* Construct a ReflectionConstant_ object.
*
* @param string $name
*/
public function __construct(string $name)
{
$this->name = $name;
if (!\defined($name) && !self::isMagicConstant($name)) {
throw new \InvalidArgumentException('Unknown constant: '.$name);
}
if (!self::isMagicConstant($name)) {
$this->value = @\constant($name);
}
}
/**
* Exports a reflection.
*
* @param string $name
* @param bool $return pass true to return the export, as opposed to emitting it
*
* @return string|null
*/
public static function export(string $name, bool $return = false)
{
$refl = new self($name);
$value = $refl->getValue();
$str = \sprintf('Constant [ %s %s ] { %s }', \gettype($value), $refl->getName(), $value);
if ($return) {
return $str;
}
echo $str."\n";
}
public static function isMagicConstant($name)
{
return \in_array($name, self::$magicConstants);
}
/**
* Get the constant's docblock.
*
* @return false
*/
public function getDocComment(): bool
{
return false;
}
/**
* Gets the constant name.
*/
public function getName(): string
{
return $this->name;
}
/**
* Gets the namespace name.
*
* Returns '' when the constant is not namespaced.
*/
public function getNamespaceName(): string
{
if (!$this->inNamespace()) {
return '';
}
return \preg_replace('/\\\\[^\\\\]+$/', '', $this->name);
}
/**
* Gets the value of the constant.
*
* @return mixed
*/
public function getValue()
{
return $this->value;
}
/**
* Checks if this constant is defined in a namespace.
*/
public function inNamespace(): bool
{
return \strpos($this->name, '\\') !== false;
}
/**
* To string.
*/
public function __toString(): string
{
return $this->getName();
}
/**
* Gets the constant's file name.
*
* Currently returns null, because if it returns a file name the signature
* formatter will barf.
*/
public function getFileName()
{
return;
// return $this->class->getFileName();
}
/**
* Get the code start line.
*
* @throws \RuntimeException
*/
public function getStartLine()
{
throw new \RuntimeException('Not yet implemented because it\'s unclear what I should do here :)');
}
/**
* Get the code end line.
*
* @throws \RuntimeException
*/
public function getEndLine()
{
return $this->getStartLine();
}
}

View File

@@ -31,13 +31,10 @@ class ReflectionLanguageConstructParameter extends \ReflectionParameter
/**
* No class here.
*
* @todo remove \ReturnTypeWillChange attribute after dropping support for PHP 7.0 (when we can use nullable types)
*/
#[\ReturnTypeWillChange]
public function getClass()
public function getClass(): ?\ReflectionClass
{
return;
return null;
}
/**

View File

@@ -50,16 +50,7 @@ use Symfony\Component\Console\Output\OutputInterface;
*/
class Shell extends Application
{
const VERSION = 'v0.11.22';
/** @deprecated */
const PROMPT = '>>> ';
/** @deprecated */
const BUFF_PROMPT = '... ';
/** @deprecated */
const REPLAY = '--> ';
/** @deprecated */
const RETVAL = '=> ';
const VERSION = 'v0.12.0';
private $config;
private $cleaner;
@@ -151,6 +142,8 @@ class Shell extends Application
*/
public static function debug(array $vars = [], $bindTo = null): array
{
@\trigger_error('`Psy\\Shell::debug` is deprecated; call `Psy\\debug` instead.', \E_USER_DEPRECATED);
return \Psy\debug($vars, $bindTo);
}
@@ -255,14 +248,6 @@ class Shell extends Application
];
}
/**
* @deprecated Nothing should use this anymore
*/
protected function getTabCompletionMatchers()
{
@\trigger_error('getTabCompletionMatchers is no longer used', \E_USER_DEPRECATED);
}
/**
* Gets the default command loop listeners.
*
@@ -304,6 +289,8 @@ class Shell extends Application
*/
public function addTabCompletionMatchers(array $matchers)
{
@\trigger_error('`addTabCompletionMatchers` is deprecated; call `addMatchers` instead.', \E_USER_DEPRECATED);
$this->addMatchers($matchers);
}
@@ -449,7 +436,7 @@ class Shell extends Application
/**
* Configures the input and output instances based on the user arguments and options.
*/
protected function configureIO(InputInterface $input, OutputInterface $output)
protected function configureIO(InputInterface $input, OutputInterface $output): void
{
// @todo overrides via environment variables (or should these happen in config? ... probably config)
$input->setInteractive($this->config->getInputInteractive());
@@ -667,7 +654,7 @@ class Shell extends Application
*
* @param bool $includeBoundObject Pass false to exclude 'this'. If you're
* passing the scope variables to `extract`
* in PHP 7.1+, you _must_ exclude 'this'
* you _must_ exclude 'this'
*
* @return array Associative array of scope variables
*/
@@ -687,7 +674,7 @@ class Shell extends Application
*
* @param bool $includeBoundObject Pass false to exclude 'this'. If you're
* passing the scope variables to `extract`
* in PHP 7.1+, you _must_ exclude 'this'
* you _must_ exclude 'this'
*
* @return array Associative array of magic scope variables
*/
@@ -1532,6 +1519,8 @@ class Shell extends Application
*/
public function getVersion(): string
{
@\trigger_error('`getVersion` is deprecated; call `self::getVersionHeader` instead.', \E_USER_DEPRECATED);
return self::getVersionHeader($this->config->useUnicode());
}
@@ -1557,14 +1546,6 @@ class Shell extends Application
return $this->config->getManualDb();
}
/**
* @deprecated Tab completion is provided by the AutoCompleter service
*/
protected function autocomplete($text)
{
@\trigger_error('Tab completion is provided by the AutoCompleter service', \E_USER_DEPRECATED);
}
/**
* Initialize tab completion matchers.
*

View File

@@ -97,7 +97,7 @@ class Sudo
$prop = self::getProperty(new \ReflectionClass($class), $property);
$refl = $prop->getDeclaringClass();
if (\version_compare(\PHP_VERSION, '7.4', '>=') && \method_exists($refl, 'setStaticPropertyValue')) {
if (\method_exists($refl, 'setStaticPropertyValue')) {
$refl->setStaticPropertyValue($property, $value);
} else {
$prop->setValue($value);

View File

@@ -214,10 +214,8 @@ class Docblock
*
* @return array|null
*/
public function tag(string $tag)
public function tag(string $tag): ?array
{
// TODO: Add proper null-type return values once the lowest PHP version supported is 7.1
return $this->hasTag($tag) ? $this->tags[$tag] : null;
}

View File

@@ -12,8 +12,7 @@
namespace Psy\Util;
use Psy\Exception\RuntimeException;
use Psy\Reflection\ReflectionClassConstant;
use Psy\Reflection\ReflectionConstant_;
use Psy\Reflection\ReflectionConstant;
use Psy\Reflection\ReflectionNamespace;
/**
@@ -48,8 +47,8 @@ class Mirror
if ($member === null && \is_string($value)) {
if (\function_exists($value)) {
return new \ReflectionFunction($value);
} elseif (\defined($value) || ReflectionConstant_::isMagicConstant($value)) {
return new ReflectionConstant_($value);
} elseif (\defined($value) || ReflectionConstant::isMagicConstant($value)) {
return new ReflectionConstant($value);
}
}
@@ -58,7 +57,7 @@ class Mirror
if ($member === null) {
return $class;
} elseif ($filter & self::CONSTANT && $class->hasConstant($member)) {
return ReflectionClassConstant::create($value, $member);
return new \ReflectionClassConstant($value, $member);
} elseif ($filter & self::METHOD && $class->hasMethod($member)) {
return $class->getMethod($member);
} elseif ($filter & self::PROPERTY && $class->hasProperty($member)) {

View File

@@ -23,9 +23,9 @@ class Dumper extends CliDumper
private $formatter;
private $forceArrayIndexes;
protected static $onlyControlCharsRx = '/^[\x00-\x1F\x7F]+$/';
protected static $controlCharsRx = '/([\x00-\x1F\x7F]+)/';
protected static $controlCharsMap = [
private const ONLY_CONTROL_CHARS = '/^[\x00-\x1F\x7F]+$/';
private const CONTROL_CHARS = '/([\x00-\x1F\x7F]+)/';
private const CONTROL_CHARS_MAP = [
"\0" => '\0',
"\t" => '\t',
"\n" => '\n',
@@ -46,7 +46,7 @@ class Dumper extends CliDumper
/**
* {@inheritdoc}
*/
public function enterHash(Cursor $cursor, $type, $class, $hasChild)
public function enterHash(Cursor $cursor, $type, $class, $hasChild): void
{
if (Cursor::HASH_INDEXED === $type || Cursor::HASH_ASSOC === $type) {
$class = 0;
@@ -57,7 +57,7 @@ class Dumper extends CliDumper
/**
* {@inheritdoc}
*/
protected function dumpKey(Cursor $cursor)
protected function dumpKey(Cursor $cursor): void
{
if ($this->forceArrayIndexes || Cursor::HASH_INDEXED !== $cursor->hashType) {
parent::dumpKey($cursor);
@@ -71,16 +71,15 @@ class Dumper extends CliDumper
}
$styled = '';
$map = self::$controlCharsMap;
$cchr = $this->styles['cchr'];
$chunks = \preg_split(self::$controlCharsRx, $value, -1, \PREG_SPLIT_NO_EMPTY | \PREG_SPLIT_DELIM_CAPTURE);
$chunks = \preg_split(self::CONTROL_CHARS, $value, -1, \PREG_SPLIT_NO_EMPTY | \PREG_SPLIT_DELIM_CAPTURE);
foreach ($chunks as $chunk) {
if (\preg_match(self::$onlyControlCharsRx, $chunk)) {
if (\preg_match(self::ONLY_CONTROL_CHARS, $chunk)) {
$chars = '';
$i = 0;
do {
$chars .= isset($map[$chunk[$i]]) ? $map[$chunk[$i]] : \sprintf('\x%02X', \ord($chunk[$i]));
$chars .= isset(self::CONTROL_CHARS_MAP[$chunk[$i]]) ? self::CONTROL_CHARS_MAP[$chunk[$i]] : \sprintf('\x%02X', \ord($chunk[$i]));
} while (isset($chunk[++$i]));
$chars = $this->formatter->escape($chars);
@@ -98,7 +97,7 @@ class Dumper extends CliDumper
/**
* {@inheritdoc}
*/
protected function dumpLine($depth, $endOfValue = false)
protected function dumpLine($depth, $endOfValue = false): void
{
if ($endOfValue && 0 < $depth) {
$this->line .= ',';

View File

@@ -233,7 +233,7 @@ if (!\function_exists('Psy\\info')) {
];
$theme = $config->theme();
// TODO: show styles (but only if they're different than default?)
// @todo show styles (but only if they're different than default?)
$output['theme'] = [
'compact' => $theme->compact(),
'prompt' => $theme->prompt(),
@@ -351,8 +351,8 @@ if (!\function_exists('Psy\\bin')) {
exit(1);
}
if (\PHP_VERSION_ID < 70000) {
\fwrite(\STDERR, 'PHP 7.0.0 or higher is required. You can set the environment variable PSYSH_IGNORE_ENV=1 to override this restriction and proceed anyway.'.\PHP_EOL);
if (\PHP_VERSION_ID < 70400) {
\fwrite(\STDERR, 'PHP 7.4.0 or higher is required. You can set the environment variable PSYSH_IGNORE_ENV=1 to override this restriction and proceed anyway.'.\PHP_EOL);
exit(1);
}