Aggiornato Composer

This commit is contained in:
Paolo A
2024-05-17 12:24:19 +00:00
parent 4ac62108b5
commit ec201d75b2
2238 changed files with 38684 additions and 59785 deletions

View File

@@ -1,6 +1,6 @@
The MIT License (MIT)
Copyright (c) 2012-2022 Justin Hileman
Copyright (c) 2012-2023 Justin Hileman
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View File

@@ -7,7 +7,7 @@ PsySH is a runtime developer console, interactive debugger and [REPL](https://en
[![Monthly downloads](http://img.shields.io/packagist/dm/psy/psysh.svg?style=flat-square)](https://packagist.org/packages/psy/psysh)
[![Made out of awesome](https://img.shields.io/badge/made_out_of_awesome-✓-brightgreen.svg?style=flat-square)](http://psysh.org)
[![Build status](https://img.shields.io/github/workflow/status/bobthecow/psysh/Tests/main.svg?style=flat-square)](https://github.com/bobthecow/psysh/actions?query=branch:main)
[![Build status](https://img.shields.io/github/actions/workflow/status/bobthecow/psysh/tests.yml?branch=main&style=flat-square)](https://github.com/bobthecow/psysh/actions?query=branch:main)
[![StyleCI](https://styleci.io/repos/4549925/shield)](https://styleci.io/repos/4549925)
@@ -30,6 +30,7 @@ PsySH is a runtime developer console, interactive debugger and [REPL](https://en
### [🛠 Configuration](https://github.com/bobthecow/psysh/wiki/Configuration)
* [🎛 Config options](https://github.com/bobthecow/psysh/wiki/Config-options)
* [🎨 Themes](https://github.com/bobthecow/psysh/wiki/Themes)
* [📄 Sample config file](https://github.com/bobthecow/psysh/wiki/Sample-config)
### [🔌 Integrations](https://github.com/bobthecow/psysh/wiki/Integrations)

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

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

View File

@@ -13,23 +13,20 @@
}
],
"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": {
"hoa/console": "3.17.05.02",
"bamarni/composer-bin-plugin": "^1.2"
},
"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.",
"hoa/console": "A pure PHP readline implementation. You'll want this if your PHP install doesn't already support readline or libedit."
"ext-pdo-sqlite": "The doc command requires SQLite to work."
},
"autoload": {
"files": ["src/functions.php"],
@@ -50,7 +47,11 @@
},
"extra": {
"branch-alias": {
"dev-main": "0.11.x-dev"
"dev-main": "0.12.x-dev"
},
"bamarni-bin": {
"bin-links": false,
"forward-command": false
}
},
"conflict": {

View File

@@ -3,7 +3,7 @@
/*
* This file is part of Psy Shell.
*
* (c) 2012-2022 Justin Hileman
* (c) 2012-2023 Justin Hileman
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
@@ -18,13 +18,13 @@ use Psy\CodeCleaner\AbstractClassPass;
use Psy\CodeCleaner\AssignThisVariablePass;
use Psy\CodeCleaner\CalledClassPass;
use Psy\CodeCleaner\CallTimePassByReferencePass;
use Psy\CodeCleaner\CodeCleanerPass;
use Psy\CodeCleaner\EmptyArrayDimFetchPass;
use Psy\CodeCleaner\ExitPass;
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;
@@ -49,6 +49,8 @@ use Psy\Exception\ParseErrorException;
class CodeCleaner
{
private $yolo = false;
private $strictTypes = false;
private $parser;
private $printer;
private $traverser;
@@ -57,21 +59,18 @@ class CodeCleaner
/**
* CodeCleaner constructor.
*
* @param Parser|null $parser A PhpParser Parser instance. One will be created if not explicitly supplied
* @param Printer|null $printer A PhpParser Printer instance. One will be created if not explicitly supplied
* @param NodeTraverser|null $traverser A PhpParser NodeTraverser instance. One will be created if not explicitly supplied
* @param bool $yolo run without input validation
* @param Parser|null $parser A PhpParser Parser instance. One will be created if not explicitly supplied
* @param Printer|null $printer A PhpParser Printer instance. One will be created if not explicitly supplied
* @param NodeTraverser|null $traverser A PhpParser NodeTraverser instance. One will be created if not explicitly supplied
* @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)
public function __construct(?Parser $parser = null, ?Printer $printer = null, ?NodeTraverser $traverser = null, bool $yolo = false, bool $strictTypes = false)
{
$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();
@@ -82,8 +81,6 @@ class CodeCleaner
/**
* Check whether this CodeCleaner is in YOLO mode.
*
* @return bool
*/
public function yolo(): bool
{
@@ -93,7 +90,7 @@ class CodeCleaner
/**
* Get default CodeCleaner passes.
*
* @return array
* @return CodeCleanerPass[]
*/
private function getDefaultPasses(): array
{
@@ -117,7 +114,6 @@ class CodeCleaner
new FinalClassPass(),
new FunctionContextPass(),
new FunctionReturnInWriteContextPass(),
new InstanceOfPass(),
new IssetPass(),
new LabelContextPass(),
new LeavePsyshAlonePass(),
@@ -135,7 +131,7 @@ class CodeCleaner
new MagicConstantsPass(),
$namespacePass, // must run after the implicit return pass
new RequirePass(),
new StrictTypesPass(),
new StrictTypesPass($this->strictTypes),
// Namespace-aware validation (which depends on aforementioned shenanigans)
new ValidClassNamePass(),
@@ -150,7 +146,7 @@ class CodeCleaner
* This list should stay in sync with the "rewriting shenanigans" in
* getDefaultPasses above.
*
* @return array
* @return CodeCleanerPass[]
*/
private function getYoloPasses(): array
{
@@ -169,7 +165,7 @@ class CodeCleaner
new MagicConstantsPass(),
$namespacePass, // must run after the implicit return pass
new RequirePass(),
new StrictTypesPass(),
new StrictTypesPass($this->strictTypes),
];
}
@@ -201,6 +197,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);
@@ -240,8 +237,6 @@ class CodeCleaner
* Check whether a given backtrace frame is a call to Psy\debug.
*
* @param array $stackFrame
*
* @return bool
*/
private static function isDebugCall(array $stackFrame): bool
{
@@ -288,10 +283,8 @@ class CodeCleaner
* Set the current local namespace.
*
* @param array|null $namespace (default: null)
*
* @return array|null
*/
public function setNamespace(array $namespace = null)
public function setNamespace(?array $namespace = null)
{
$this->namespace = $namespace;
}
@@ -369,8 +362,6 @@ class CodeCleaner
*
* @param \PhpParser\Error $e
* @param string $code
*
* @return bool
*/
private function parseErrorIsUnclosedString(\PhpParser\Error $e, string $code): bool
{

View File

@@ -3,7 +3,7 @@
/*
* This file is part of Psy Shell.
*
* (c) 2012-2022 Justin Hileman
* (c) 2012-2023 Justin Hileman
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
@@ -28,6 +28,8 @@ class AbstractClassPass extends CodeCleanerPass
* @throws FatalErrorException if the node is an abstract function with a body
*
* @param Node $node
*
* @return int|Node|null Replacement node (or special return value)
*/
public function enterNode(Node $node)
{
@@ -41,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());
}
}
}
@@ -51,6 +53,8 @@ class AbstractClassPass extends CodeCleanerPass
* @throws FatalErrorException if the node is a non-abstract class with abstract methods
*
* @param Node $node
*
* @return int|Node|Node[]|null Replacement node (or special return value)
*/
public function leaveNode(Node $node)
{
@@ -64,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

@@ -3,7 +3,7 @@
/*
* This file is part of Psy Shell.
*
* (c) 2012-2022 Justin Hileman
* (c) 2012-2023 Justin Hileman
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
@@ -29,11 +29,13 @@ class AssignThisVariablePass extends CodeCleanerPass
* @throws FatalErrorException if the user assign the `$this` variable
*
* @param Node $node
*
* @return int|Node|null Replacement node (or special return value)
*/
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

@@ -3,7 +3,7 @@
/*
* This file is part of Psy Shell.
*
* (c) 2012-2022 Justin Hileman
* (c) 2012-2023 Justin Hileman
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
@@ -35,6 +35,8 @@ class CallTimePassByReferencePass extends CodeCleanerPass
* @throws FatalErrorException if the user used call-time pass-by-reference
*
* @param Node $node
*
* @return int|Node|null Replacement node (or special return value)
*/
public function enterNode(Node $node)
{
@@ -48,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

@@ -3,7 +3,7 @@
/*
* This file is part of Psy Shell.
*
* (c) 2012-2022 Justin Hileman
* (c) 2012-2023 Justin Hileman
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
@@ -30,6 +30,8 @@ class CalledClassPass extends CodeCleanerPass
/**
* @param array $nodes
*
* @return Node[]|null Array of nodes
*/
public function beforeTraverse(array $nodes)
{
@@ -40,6 +42,8 @@ class CalledClassPass extends CodeCleanerPass
* @throws ErrorException if get_class or get_called_class is called without an object from outside a class
*
* @param Node $node
*
* @return int|Node|null Replacement node (or special return value)
*/
public function enterNode(Node $node)
{
@@ -62,13 +66,15 @@ 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());
}
}
}
/**
* @param Node $node
*
* @return int|Node|Node[]|null Replacement node (or special return value)
*/
public function leaveNode(Node $node)
{

View File

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

View File

@@ -3,7 +3,7 @@
/*
* This file is part of Psy Shell.
*
* (c) 2012-2022 Justin Hileman
* (c) 2012-2023 Justin Hileman
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
@@ -14,6 +14,8 @@ namespace Psy\CodeCleaner;
use PhpParser\Node;
use PhpParser\Node\Expr\ArrayDimFetch;
use PhpParser\Node\Expr\Assign;
use PhpParser\Node\Expr\AssignRef;
use PhpParser\Node\Stmt\Foreach_;
use Psy\Exception\FatalErrorException;
/**
@@ -25,25 +27,39 @@ class EmptyArrayDimFetchPass extends CodeCleanerPass
private $theseOnesAreFine = [];
/**
* @return Node[]|null Array of nodes
*/
public function beforeTraverse(array $nodes)
{
$this->theseOnesAreFine = [];
}
/**
* @throws FatalErrorException if the user used empty empty array dim fetch outside of assignment
* @throws FatalErrorException if the user used empty array dim fetch outside of assignment
*
* @param Node $node
*
* @return int|Node|null Replacement node (or special return value)
*/
public function enterNode(Node $node)
{
if ($node instanceof Assign && $node->var instanceof ArrayDimFetch) {
$this->theseOnesAreFine[] = $node->var;
} elseif ($node instanceof AssignRef && $node->expr instanceof ArrayDimFetch) {
$this->theseOnesAreFine[] = $node->expr;
} elseif ($node instanceof Foreach_ && $node->valueVar instanceof ArrayDimFetch) {
$this->theseOnesAreFine[] = $node->valueVar;
} elseif ($node instanceof ArrayDimFetch && $node->var instanceof ArrayDimFetch) {
// $a[]['b'] = 'c'
if (\in_array($node, $this->theseOnesAreFine)) {
$this->theseOnesAreFine[] = $node->var;
}
}
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

@@ -3,7 +3,7 @@
/*
* This file is part of Psy Shell.
*
* (c) 2012-2022 Justin Hileman
* (c) 2012-2023 Justin Hileman
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
@@ -23,6 +23,8 @@ class ExitPass extends CodeCleanerPass
* Converts exit calls to BreakExceptions.
*
* @param \PhpParser\Node $node
*
* @return int|Node|Node[]|null Replacement node (or special return value)
*/
public function leaveNode(Node $node)
{

View File

@@ -3,7 +3,7 @@
/*
* This file is part of Psy Shell.
*
* (c) 2012-2022 Justin Hileman
* (c) 2012-2023 Justin Hileman
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
@@ -24,6 +24,8 @@ class FinalClassPass extends CodeCleanerPass
/**
* @param array $nodes
*
* @return Node[]|null Array of nodes
*/
public function beforeTraverse(array $nodes)
{
@@ -34,6 +36,8 @@ class FinalClassPass extends CodeCleanerPass
* @throws FatalErrorException if the node is a class that extends a final class
*
* @param Node $node
*
* @return int|Node|null Replacement node (or special return value)
*/
public function enterNode(Node $node)
{
@@ -42,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());
}
}
@@ -54,8 +58,6 @@ class FinalClassPass extends CodeCleanerPass
/**
* @param string $name Class name
*
* @return bool
*/
private function isFinalClass(string $name): bool
{

View File

@@ -3,7 +3,7 @@
/*
* This file is part of Psy Shell.
*
* (c) 2012-2022 Justin Hileman
* (c) 2012-2023 Justin Hileman
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
@@ -23,12 +23,17 @@ class FunctionContextPass extends CodeCleanerPass
/**
* @param array $nodes
*
* @return Node[]|null Array of nodes
*/
public function beforeTraverse(array $nodes)
{
$this->functionDepth = 0;
}
/**
* @return int|Node|null Replacement node (or special return value)
*/
public function enterNode(Node $node)
{
if ($node instanceof FunctionLike) {
@@ -45,12 +50,14 @@ 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());
}
}
/**
* @param \PhpParser\Node $node
*
* @return int|Node|Node[]|null Replacement node (or special return value)
*/
public function leaveNode(Node $node)
{

View File

@@ -3,7 +3,7 @@
/*
* This file is part of Psy Shell.
*
* (c) 2012-2022 Justin Hileman
* (c) 2012-2023 Justin Hileman
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
@@ -40,6 +40,8 @@ class FunctionReturnInWriteContextPass extends CodeCleanerPass
* @throws FatalErrorException if a value is assigned to a function
*
* @param Node $node
*
* @return int|Node|null Replacement node (or special return value)
*/
public function enterNode(Node $node)
{
@@ -51,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_) {
@@ -61,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

@@ -3,7 +3,7 @@
/*
* This file is part of Psy Shell.
*
* (c) 2012-2022 Justin Hileman
* (c) 2012-2023 Justin Hileman
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
@@ -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);
@@ -115,8 +114,6 @@ class ImplicitReturnPass extends CodeCleanerPass
* we'll exclude them here.
*
* @param Node $node
*
* @return bool
*/
private static function isNonExpressionStmt(Node $node): bool
{

View File

@@ -1,67 +0,0 @@
<?php
/*
* This file is part of Psy Shell.
*
* (c) 2012-2022 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
*/
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

@@ -3,7 +3,7 @@
/*
* This file is part of Psy Shell.
*
* (c) 2012-2022 Justin Hileman
* (c) 2012-2023 Justin Hileman
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
@@ -31,6 +31,8 @@ class IssetPass extends CodeCleanerPass
* @throws FatalErrorException
*
* @param Node $node
*
* @return int|Node|null Replacement node (or special return value)
*/
public function enterNode(Node $node)
{
@@ -40,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

@@ -3,7 +3,7 @@
/*
* This file is part of Psy Shell.
*
* (c) 2012-2022 Justin Hileman
* (c) 2012-2023 Justin Hileman
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
@@ -41,6 +41,8 @@ class LabelContextPass extends CodeCleanerPass
/**
* @param array $nodes
*
* @return Node[]|null Array of nodes
*/
public function beforeTraverse(array $nodes)
{
@@ -49,6 +51,9 @@ class LabelContextPass extends CodeCleanerPass
$this->labelGotos = [];
}
/**
* @return int|Node|null Replacement node (or special return value)
*/
public function enterNode(Node $node)
{
if ($node instanceof FunctionLike) {
@@ -63,14 +68,16 @@ 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();
}
}
/**
* @param \PhpParser\Node $node
*
* @return int|Node|Node[]|null Replacement node (or special return value)
*/
public function leaveNode(Node $node)
{
@@ -79,6 +86,9 @@ class LabelContextPass extends CodeCleanerPass
}
}
/**
* @return Node[]|null Array of nodes
*/
public function afterTraverse(array $nodes)
{
foreach ($this->labelGotos as $name => $line) {

View File

@@ -3,7 +3,7 @@
/*
* This file is part of Psy Shell.
*
* (c) 2012-2022 Justin Hileman
* (c) 2012-2023 Justin Hileman
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
@@ -26,6 +26,8 @@ class LeavePsyshAlonePass extends CodeCleanerPass
* @throws RuntimeException if the user is messing with $__psysh__
*
* @param Node $node
*
* @return int|Node|null Replacement node (or special return value)
*/
public function enterNode(Node $node)
{

View File

@@ -3,7 +3,7 @@
/*
* This file is part of Psy Shell.
*
* (c) 2012-2022 Justin Hileman
* (c) 2012-2023 Justin Hileman
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
@@ -12,9 +12,9 @@
namespace Psy\CodeCleaner;
use PhpParser\Node;
use PhpParser\Node\Expr;
use PhpParser\Node\Expr\Array_;
use PhpParser\Node\Expr\ArrayDimFetch;
// @todo Switch to PhpParser\Node\ArrayItem once we drop support for PHP-Parser 4.x
use PhpParser\Node\Expr\ArrayItem;
use PhpParser\Node\Expr\Assign;
use PhpParser\Node\Expr\FuncCall;
@@ -29,19 +29,14 @@ 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.
*
* @throws ParseErrorException if the user used empty with anything but a variable
*
* @param Node $node
*
* @return int|Node|null Replacement node (or special return value)
*/
public function enterNode(Node $node)
{
@@ -53,16 +48,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;
@@ -73,15 +63,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()]);
}
}
@@ -93,11 +77,9 @@ class ListPass extends CodeCleanerPass
/**
* Validate whether a given item in an array is valid for short assignment.
*
* @param Expr $item
*
* @return bool
* @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

@@ -3,7 +3,7 @@
/*
* This file is part of Psy Shell.
*
* (c) 2012-2022 Justin Hileman
* (c) 2012-2023 Justin Hileman
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
@@ -32,6 +32,8 @@ class LoopContextPass extends CodeCleanerPass
/**
* {@inheritdoc}
*
* @return Node[]|null Array of nodes
*/
public function beforeTraverse(array $nodes)
{
@@ -45,6 +47,8 @@ class LoopContextPass extends CodeCleanerPass
* @throws FatalErrorException if the node is a break or continue and has an argument less than 1
*
* @param Node $node
*
* @return int|Node|null Replacement node (or special return value)
*/
public function enterNode(Node $node)
{
@@ -63,23 +67,24 @@ 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());
}
// @todo Rename to Int_ and Float_ once we drop support for PHP-Parser 4.x
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;
}
@@ -87,6 +92,8 @@ class LoopContextPass extends CodeCleanerPass
/**
* @param Node $node
*
* @return int|Node|Node[]|null Replacement node (or special return value)
*/
public function leaveNode(Node $node)
{

View File

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

View File

@@ -3,7 +3,7 @@
/*
* This file is part of Psy Shell.
*
* (c) 2012-2022 Justin Hileman
* (c) 2012-2023 Justin Hileman
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
@@ -29,6 +29,8 @@ abstract class NamespaceAwarePass extends CodeCleanerPass
* use afterTraverse or call parent::beforeTraverse() when overloading.
*
* Reset the namespace and the current scope before beginning analysis
*
* @return Node[]|null Array of nodes
*/
public function beforeTraverse(array $nodes)
{
@@ -41,11 +43,13 @@ abstract class NamespaceAwarePass extends CodeCleanerPass
* leaveNode or call parent::enterNode() when overloading
*
* @param Node $node
*
* @return int|Node|null Replacement node (or special return value)
*/
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) : [];
}
}
@@ -53,19 +57,29 @@ abstract class NamespaceAwarePass extends CodeCleanerPass
* Get a fully-qualified name (class, function, interface, etc).
*
* @param mixed $name
*
* @return string
*/
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

@@ -3,7 +3,7 @@
/*
* This file is part of Psy Shell.
*
* (c) 2012-2022 Justin Hileman
* (c) 2012-2023 Justin Hileman
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
@@ -11,6 +11,7 @@
namespace Psy\CodeCleaner;
use PhpParser\Node;
use PhpParser\Node\Name;
use PhpParser\Node\Stmt\Namespace_;
use Psy\CodeCleaner;
@@ -46,6 +47,8 @@ class NamespacePass extends CodeCleanerPass
* is encountered.
*
* @param array $nodes
*
* @return Node[]|null Array of nodes
*/
public function beforeTraverse(array $nodes)
{
@@ -83,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

@@ -3,7 +3,7 @@
/*
* This file is part of Psy Shell.
*
* (c) 2012-2022 Justin Hileman
* (c) 2012-2023 Justin Hileman
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
@@ -25,8 +25,6 @@ class NoReturnValue
{
/**
* Get PhpParser AST expression for creating a new NoReturnValue.
*
* @return New_
*/
public static function create(): New_
{

View File

@@ -3,7 +3,7 @@
/*
* This file is part of Psy Shell.
*
* (c) 2012-2022 Justin Hileman
* (c) 2012-2023 Justin Hileman
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
@@ -34,6 +34,8 @@ class PassableByReferencePass extends CodeCleanerPass
* @throws FatalErrorException if non-variables are passed by reference
*
* @param Node $node
*
* @return int|Node|null Replacement node (or special return value)
*/
public function enterNode(Node $node)
{
@@ -61,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());
}
}
}
@@ -110,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

@@ -3,7 +3,7 @@
/*
* This file is part of Psy Shell.
*
* (c) 2012-2022 Justin Hileman
* (c) 2012-2023 Justin Hileman
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
@@ -29,6 +29,8 @@ class RequirePass extends CodeCleanerPass
/**
* {@inheritdoc}
*
* @return int|Node|null Replacement node (or special return value)
*/
public function enterNode(Node $origNode)
{
@@ -47,10 +49,11 @@ class RequirePass extends CodeCleanerPass
*
* $foo = require \Psy\CodeCleaner\RequirePass::resolve($bar)
*/
// @todo Rename LNumber to Int_ once we drop support for PHP-Parser 4.x
$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()
);
@@ -69,11 +72,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;
@@ -82,7 +85,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);
@@ -91,7 +94,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

@@ -3,7 +3,7 @@
/*
* This file is part of Psy Shell.
*
* (c) 2012-2022 Justin Hileman
* (c) 2012-2023 Justin Hileman
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
@@ -18,6 +18,7 @@ use PhpParser\Node\Identifier;
use PhpParser\Node\NullableType;
use PhpParser\Node\Stmt\Function_;
use PhpParser\Node\Stmt\Return_;
use PhpParser\Node\UnionType;
use Psy\Exception\FatalErrorException;
/**
@@ -31,23 +32,15 @@ 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}
*
* @return int|Node|null Replacement node (or special return value)
*/
public function enterNode(Node $node)
{
if (!$this->atLeastPhp71) {
return; // @codeCoverageIgnore
}
if ($this->isFunctionNode($node)) {
$this->returnTypeStack[] = $node->returnType;
@@ -79,20 +72,18 @@ 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());
}
}
}
/**
* {@inheritdoc}
*
* @return int|Node|Node[]|null Replacement node (or special return value)
*/
public function leaveNode(Node $node)
{
if (!$this->atLeastPhp71) {
return; // @codeCoverageIgnore
}
if (!empty($this->returnTypeStack) && $this->isFunctionNode($node)) {
\array_pop($this->returnTypeStack);
}
@@ -105,6 +96,10 @@ class ReturnTypePass extends CodeCleanerPass
private function typeName(Node $node): string
{
if ($node instanceof UnionType) {
return \implode('|', \array_map([$this, 'typeName'], $node->types));
}
if ($node instanceof NullableType) {
return \strtolower($node->type->name);
}

View File

@@ -3,7 +3,7 @@
/*
* This file is part of Psy Shell.
*
* (c) 2012-2022 Justin Hileman
* (c) 2012-2023 Justin Hileman
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
@@ -11,7 +11,7 @@
namespace Psy\CodeCleaner;
use PhpParser\Node\Identifier;
use PhpParser\Node;
use PhpParser\Node\Scalar\LNumber;
use PhpParser\Node\Stmt\Declare_;
use PhpParser\Node\Stmt\DeclareDeclare;
@@ -33,6 +33,14 @@ class StrictTypesPass extends CodeCleanerPass
private $strictTypes = false;
/**
* @param bool $strictTypes enforce strict types by default
*/
public function __construct(bool $strictTypes = false)
{
$this->strictTypes = $strictTypes;
}
/**
* If this is a standalone strict types declaration, remember it for later.
*
@@ -42,6 +50,8 @@ class StrictTypesPass extends CodeCleanerPass
* @throws FatalErrorException if an invalid `strict_types` declaration is found
*
* @param array $nodes
*
* @return Node[]|null Array of nodes
*/
public function beforeTraverse(array $nodes)
{
@@ -50,12 +60,11 @@ 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;
// @todo Rename LNumber to Int_ once we drop support for PHP-Parser 4.x
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;
@@ -67,6 +76,8 @@ class StrictTypesPass extends CodeCleanerPass
if ($prependStrictTypes) {
$first = \reset($nodes);
if (!$first instanceof Declare_) {
// @todo Switch to PhpParser\Node\DeclareItem once we drop support for PHP-Parser 4.x
// @todo Rename LNumber to Int_ once we drop support for PHP-Parser 4.x
$declare = new Declare_([new DeclareDeclare('strict_types', new LNumber(1))]);
\array_unshift($nodes, $declare);
}

View File

@@ -3,7 +3,7 @@
/*
* This file is part of Psy Shell.
*
* (c) 2012-2022 Justin Hileman
* (c) 2012-2023 Justin Hileman
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
@@ -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;
@@ -44,6 +45,8 @@ class UseStatementPass extends CodeCleanerPass
* work like you'd expect.
*
* @param Node $node
*
* @return int|Node|null Replacement node (or special return value)
*/
public function enterNode(Node $node)
{
@@ -63,29 +66,31 @@ class UseStatementPass extends CodeCleanerPass
* remembered aliases to the code.
*
* @param Node $node
*
* @return int|Node|Node[]|null Replacement node (or special return value)
*/
public function leaveNode(Node $node)
{
// 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;
}
// @todo Rename to Node_Visitor::REMOVE_NODE once we drop support for PHP-Parser 4.x
return NodeTraverser::REMOVE_NODE;
}
// 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'),
]);
}
// @todo Rename to Node_Visitor::REMOVE_NODE once we drop support for PHP-Parser 4.x
return NodeTraverser::REMOVE_NODE;
}
@@ -98,8 +103,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

@@ -3,7 +3,7 @@
/*
* This file is part of Psy Shell.
*
* (c) 2012-2022 Justin Hileman
* (c) 2012-2023 Justin Hileman
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
@@ -46,6 +46,8 @@ class ValidClassNamePass extends NamespaceAwarePass
* trait methods.
*
* @param Node $node
*
* @return int|Node|null Replacement node (or special return value)
*/
public function enterNode(Node $node)
{
@@ -70,13 +72,13 @@ class ValidClassNamePass extends NamespaceAwarePass
/**
* @param Node $node
*
* @return int|Node|Node[]|null Replacement node (or special return value)
*/
public function leaveNode(Node $node)
{
if (self::isConditional($node)) {
$this->conditionalScopes--;
return;
}
}
@@ -257,35 +259,12 @@ 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
*
* @param Stmt $stmt
*
* @return string
*/
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;
}
}
/**
* Check whether a class exists, or has been defined in the current code snippet.
*
* Gives `self`, `static` and `parent` a free pass.
*
* @param string $name
*
* @return bool
*/
protected function classExists(string $name): bool
{
@@ -303,8 +282,6 @@ class ValidClassNamePass extends NamespaceAwarePass
* Check whether an interface exists, or has been defined in the current code snippet.
*
* @param string $name
*
* @return bool
*/
protected function interfaceExists(string $name): bool
{
@@ -315,8 +292,6 @@ class ValidClassNamePass extends NamespaceAwarePass
* Check whether a trait exists, or has been defined in the current code snippet.
*
* @param string $name
*
* @return bool
*/
protected function traitExists(string $name): bool
{
@@ -343,11 +318,9 @@ class ValidClassNamePass extends NamespaceAwarePass
*
* @param string $msg
* @param Stmt $stmt
*
* @return FatalErrorException
*/
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

@@ -3,7 +3,7 @@
/*
* This file is part of Psy Shell.
*
* (c) 2012-2022 Justin Hileman
* (c) 2012-2023 Justin Hileman
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
@@ -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_;
@@ -35,6 +35,9 @@ class ValidConstructorPass extends CodeCleanerPass
{
private $namespace;
/**
* @return Node[]|null Array of nodes
*/
public function beforeTraverse(array $nodes)
{
$this->namespace = [];
@@ -47,11 +50,13 @@ class ValidConstructorPass extends CodeCleanerPass
* @throws FatalErrorException the constructor function has a return type
*
* @param Node $node
*
* @return int|Node|null Replacement node (or special return value)
*/
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) {
@@ -86,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

@@ -3,7 +3,7 @@
/*
* This file is part of Psy Shell.
*
* (c) 2012-2022 Justin Hileman
* (c) 2012-2023 Justin Hileman
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
@@ -35,6 +35,8 @@ class ValidFunctionNamePass extends NamespaceAwarePass
* @throws FatalErrorException if a function is redefined in a non-conditional scope
*
* @param Node $node
*
* @return int|Node|null Replacement node (or special return value)
*/
public function enterNode(Node $node)
{
@@ -51,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());
}
}
@@ -61,6 +63,8 @@ class ValidFunctionNamePass extends NamespaceAwarePass
/**
* @param Node $node
*
* @return int|Node|Node[]|null Replacement node (or special return value)
*/
public function leaveNode(Node $node)
{

View File

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

View File

@@ -3,7 +3,7 @@
/*
* This file is part of Psy Shell.
*
* (c) 2012-2022 Justin Hileman
* (c) 2012-2023 Justin Hileman
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
@@ -41,8 +41,10 @@ HELP
/**
* {@inheritdoc}
*
* @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

@@ -3,7 +3,7 @@
/*
* This file is part of Psy Shell.
*
* (c) 2012-2022 Justin Hileman
* (c) 2012-2023 Justin Hileman
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
@@ -15,7 +15,6 @@ use Psy\Shell;
use Symfony\Component\Console\Application;
use Symfony\Component\Console\Command\Command as BaseCommand;
use Symfony\Component\Console\Helper\Table;
use Symfony\Component\Console\Helper\TableHelper;
use Symfony\Component\Console\Helper\TableStyle;
use Symfony\Component\Console\Output\OutputInterface;
@@ -31,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);
}
/**
@@ -86,7 +85,7 @@ abstract class Command extends BaseCommand
/**
* These arguments will be excluded from help output.
*
* @return array
* @return string[]
*/
protected function getHiddenArguments(): array
{
@@ -108,7 +107,7 @@ abstract class Command extends BaseCommand
/**
* These options will be excluded from help output.
*
* @return array
* @return string[]
*/
protected function getHiddenOptions(): array
{
@@ -117,8 +116,6 @@ abstract class Command extends BaseCommand
/**
* Format command aliases as text..
*
* @return string
*/
private function aliasesAsText(): string
{
@@ -127,8 +124,6 @@ abstract class Command extends BaseCommand
/**
* Format command arguments as text.
*
* @return string
*/
private function argumentsAsText(): string
{
@@ -147,7 +142,7 @@ abstract class Command extends BaseCommand
$description = \str_replace("\n", "\n".\str_pad('', $max + 2, ' '), $argument->getDescription());
$messages[] = \sprintf(" <info>%-${max}s</info> %s%s", $argument->getName(), $description, $default);
$messages[] = \sprintf(" <info>%-{$max}s</info> %s%s", $argument->getName(), $description, $default);
}
$messages[] = '';
@@ -158,8 +153,6 @@ abstract class Command extends BaseCommand
/**
* Format options as text.
*
* @return string
*/
private function optionsAsText(): string
{
@@ -182,7 +175,7 @@ abstract class Command extends BaseCommand
$optionMax = $max - \strlen($option->getName()) - 2;
$messages[] = \sprintf(
" <info>%s</info> %-${optionMax}s%s%s%s",
" <info>%s</info> %-{$optionMax}s%s%s%s",
'--'.$option->getName(),
$option->getShortcut() ? \sprintf('(-%s) ', $option->getShortcut()) : '',
$description,
@@ -199,8 +192,6 @@ abstract class Command extends BaseCommand
/**
* Calculate the maximum padding width for a set of lines.
*
* @return int
*/
private function getMaxWidth(): int
{
@@ -226,8 +217,6 @@ abstract class Command extends BaseCommand
* Format an option default as text.
*
* @param mixed $default
*
* @return string
*/
private function formatDefaultValue($default): string
{
@@ -241,16 +230,10 @@ abstract class Command extends BaseCommand
/**
* Get a Table instance.
*
* Falls back to legacy TableHelper.
*
* @return Table|TableHelper
* @return Table
*/
protected function getTable(OutputInterface $output)
{
if (!\class_exists(Table::class)) {
return $this->getTableHelper();
}
$style = new TableStyle();
// Symfony 4.1 deprecated single-argument style setters.
@@ -270,20 +253,4 @@ abstract class Command extends BaseCommand
->setRows([])
->setStyle($style);
}
/**
* Legacy fallback for getTable.
*
* @return TableHelper
*/
protected function getTableHelper(): TableHelper
{
$table = $this->getApplication()->getHelperSet()->get('table');
return $table
->setRows([])
->setLayout(TableHelper::LAYOUT_BORDERLESS)
->setHorizontalBorderChar('')
->setCrossingChar('');
}
}

View File

@@ -3,7 +3,7 @@
/*
* This file is part of Psy Shell.
*
* (c) 2012-2022 Justin Hileman
* (c) 2012-2023 Justin Hileman
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
@@ -14,8 +14,8 @@ namespace Psy\Command;
use Psy\Formatter\DocblockFormatter;
use Psy\Formatter\SignatureFormatter;
use Psy\Input\CodeArgument;
use Psy\Reflection\ReflectionClassConstant;
use Psy\Reflection\ReflectionConstant_;
use Psy\Output\ShellOutput;
use Psy\Reflection\ReflectionConstant;
use Psy\Reflection\ReflectionLanguageConstruct;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
@@ -59,8 +59,10 @@ HELP
/**
* {@inheritdoc}
*
* @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)) {
@@ -143,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;
@@ -242,9 +243,10 @@ HELP
private function getManualDocById($id)
{
if ($db = $this->getApplication()->getManualDb()) {
return $db
->query(\sprintf('SELECT doc FROM php_manual WHERE id = %s', $db->quote($id)))
->fetchColumn(0);
$result = $db->query(\sprintf('SELECT doc FROM php_manual WHERE id = %s', $db->quote($id)));
if ($result !== false) {
return $result->fetchColumn(0);
}
}
}
}

View File

@@ -3,7 +3,7 @@
/*
* This file is part of Psy Shell.
*
* (c) 2012-2022 Justin Hileman
* (c) 2012-2023 Justin Hileman
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
@@ -66,8 +66,10 @@ HELP
/**
* {@inheritdoc}
*
* @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'));
@@ -79,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

@@ -3,7 +3,7 @@
/*
* This file is part of Psy Shell.
*
* (c) 2012-2022 Justin Hileman
* (c) 2012-2023 Justin Hileman
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
@@ -74,10 +74,12 @@ class EditCommand extends Command implements ContextAware
* @param InputInterface $input
* @param OutputInterface $output
*
* @return int 0 if everything went fine, or an exit code
*
* @throws \InvalidArgumentException when both exec and no-exec flags are given or if a given variable is not found in the current context
* @throws \UnexpectedValueException if file_get_contents on the edited file returns false instead of a string
*/
protected function execute(InputInterface $input, OutputInterface $output)
protected function execute(InputInterface $input, OutputInterface $output): int
{
if ($input->getOption('exec') &&
$input->getOption('no-exec')) {
@@ -112,10 +114,8 @@ class EditCommand extends Command implements ContextAware
* @param bool $execOption
* @param bool $noExecOption
* @param string|null $filePath
*
* @return bool
*/
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 +136,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 &&
@@ -152,8 +152,6 @@ class EditCommand extends Command implements ContextAware
* @param string $filePath
* @param bool $shouldRemoveFile
*
* @return string
*
* @throws \UnexpectedValueException if file_get_contents on $filePath returns false instead of a string
*/
private function editFile(string $filePath, bool $shouldRemoveFile): string

View File

@@ -3,7 +3,7 @@
/*
* This file is part of Psy Shell.
*
* (c) 2012-2022 Justin Hileman
* (c) 2012-2023 Justin Hileman
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
@@ -44,8 +44,10 @@ HELP
/**
* {@inheritdoc}
*
* @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

@@ -3,7 +3,7 @@
/*
* This file is part of Psy Shell.
*
* (c) 2012-2022 Justin Hileman
* (c) 2012-2023 Justin Hileman
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
@@ -12,7 +12,6 @@
namespace Psy\Command;
use Psy\Output\ShellOutput;
use Symfony\Component\Console\Helper\TableHelper;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
@@ -53,8 +52,10 @@ class HelpCommand extends Command
/**
* {@inheritdoc}
*
* @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
@@ -91,11 +92,7 @@ class HelpCommand extends Command
$output->startPaging();
}
if ($table instanceof TableHelper) {
$table->render($output);
} else {
$table->render();
}
$table->render();
if ($output instanceof ShellOutput) {
$output->stopPaging();

View File

@@ -3,7 +3,7 @@
/*
* This file is part of Psy Shell.
*
* (c) 2012-2022 Justin Hileman
* (c) 2012-2023 Justin Hileman
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
@@ -90,8 +90,10 @@ HELP
/**
* {@inheritdoc}
*
* @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

@@ -3,7 +3,7 @@
/*
* This file is part of Psy Shell.
*
* (c) 2012-2022 Justin Hileman
* (c) 2012-2023 Justin Hileman
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
@@ -26,7 +26,6 @@ use Psy\Output\ShellOutput;
use Psy\VarDumper\Presenter;
use Psy\VarDumper\PresenterAware;
use Symfony\Component\Console\Formatter\OutputFormatter;
use Symfony\Component\Console\Helper\TableHelper;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
@@ -112,8 +111,10 @@ HELP
/**
* {@inheritdoc}
*
* @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();
@@ -211,11 +212,7 @@ HELP
$table->addRow([$this->formatItemName($item), $item['value']]);
}
if ($table instanceof TableHelper) {
$table->render($output);
} else {
$table->render();
}
$table->render();
}
}
@@ -223,8 +220,6 @@ HELP
* Format an item name given its visibility.
*
* @param array $item
*
* @return string
*/
private function formatItemName(array $item): string
{

View File

@@ -3,7 +3,7 @@
/*
* This file is part of Psy Shell.
*
* (c) 2012-2022 Justin Hileman
* (c) 2012-2023 Justin Hileman
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
@@ -11,7 +11,6 @@
namespace Psy\Command\ListCommand;
use Psy\Reflection\ReflectionClassConstant;
use Symfony\Component\Console\Input\InputInterface;
/**
@@ -22,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) {
@@ -56,18 +55,18 @@ class ClassConstantEnumerator extends Enumerator
/**
* Get defined constants for the given class or object Reflector.
*
* @param \Reflector $reflector
* @param bool $noInherit Exclude inherited constants
* @param \ReflectionClass $reflector
* @param bool $noInherit Exclude inherited constants
*
* @return array
*/
protected function getConstants(\Reflector $reflector, bool $noInherit = false): array
protected function getConstants(\ReflectionClass $reflector, bool $noInherit = false): array
{
$className = $reflector->getName();
$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;
@@ -110,8 +109,6 @@ class ClassConstantEnumerator extends Enumerator
* Get a label for the particular kind of "class" represented.
*
* @param \ReflectionClass $reflector
*
* @return string
*/
protected function getKindLabel(\ReflectionClass $reflector): string
{

View File

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

@@ -3,7 +3,7 @@
/*
* This file is part of Psy Shell.
*
* (c) 2012-2022 Justin Hileman
* (c) 2012-2023 Justin Hileman
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
@@ -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) {
@@ -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();
@@ -133,7 +133,7 @@ class ConstantEnumerator extends Enumerator
if ($category === 'internal') {
unset($consts['user']);
return \call_user_func_array('array_merge', \array_values($consts));
return \array_merge(...\array_values($consts));
}
foreach ($consts as $key => $value) {

View File

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

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

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

@@ -3,7 +3,7 @@
/*
* This file is part of Psy Shell.
*
* (c) 2012-2022 Justin Hileman
* (c) 2012-2023 Justin Hileman
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
@@ -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) {
@@ -55,13 +55,13 @@ class MethodEnumerator extends Enumerator
/**
* Get defined methods for the given class or object Reflector.
*
* @param bool $showAll Include private and protected methods
* @param \Reflector $reflector
* @param bool $noInherit Exclude inherited methods
* @param bool $showAll Include private and protected methods
* @param \ReflectionClass $reflector
* @param bool $noInherit Exclude inherited methods
*
* @return array
*/
protected function getMethods(bool $showAll, \Reflector $reflector, bool $noInherit = false): array
protected function getMethods(bool $showAll, \ReflectionClass $reflector, bool $noInherit = false): array
{
$className = $reflector->getName();
@@ -112,8 +112,6 @@ class MethodEnumerator extends Enumerator
* Get a label for the particular kind of "class" represented.
*
* @param \ReflectionClass $reflector
*
* @return string
*/
protected function getKindLabel(\ReflectionClass $reflector): string
{
@@ -130,8 +128,6 @@ class MethodEnumerator extends Enumerator
* Get output style for the given method's visibility.
*
* @param \ReflectionMethod $method
*
* @return string
*/
private function getVisibilityStyle(\ReflectionMethod $method): string
{

View File

@@ -3,7 +3,7 @@
/*
* This file is part of Psy Shell.
*
* (c) 2012-2022 Justin Hileman
* (c) 2012-2023 Justin Hileman
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
@@ -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.
@@ -56,13 +56,13 @@ class PropertyEnumerator extends Enumerator
/**
* Get defined properties for the given class or object Reflector.
*
* @param bool $showAll Include private and protected properties
* @param \Reflector $reflector
* @param bool $noInherit Exclude inherited properties
* @param bool $showAll Include private and protected properties
* @param \ReflectionClass $reflector
* @param bool $noInherit Exclude inherited properties
*
* @return array
*/
protected function getProperties(bool $showAll, \Reflector $reflector, bool $noInherit = false): array
protected function getProperties(bool $showAll, \ReflectionClass $reflector, bool $noInherit = false): array
{
$className = $reflector->getName();
@@ -112,8 +112,6 @@ class PropertyEnumerator extends Enumerator
* Get a label for the particular kind of "class" represented.
*
* @param \ReflectionClass $reflector
*
* @return string
*/
protected function getKindLabel(\ReflectionClass $reflector): string
{
@@ -128,8 +126,6 @@ class PropertyEnumerator extends Enumerator
* Get output style for the given property's visibility.
*
* @param \ReflectionProperty $property
*
* @return string
*/
private function getVisibilityStyle(\ReflectionProperty $property): string
{
@@ -147,8 +143,6 @@ class PropertyEnumerator extends Enumerator
*
* @param \ReflectionProperty $property
* @param mixed $target
*
* @return string
*/
protected function presentValue(\ReflectionProperty $property, $target): string
{

View File

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

View File

@@ -3,7 +3,7 @@
/*
* This file is part of Psy Shell.
*
* (c) 2012-2022 Justin Hileman
* (c) 2012-2023 Justin Hileman
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
@@ -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,58 +111,17 @@ HELP
/**
* {@inheritdoc}
*/
protected function execute(InputInterface $input, OutputInterface $output)
protected function execute(InputInterface $input, OutputInterface $output): int
{
$code = $input->getArgument('code');
if (\strpos($code, '<?') === false) {
$code = '<?php '.$code;
}
$parserKind = $input->getOption('kind');
$depth = $input->getOption('depth');
$nodes = $this->parse($this->getParser($parserKind), $code);
$nodes = $this->parser->parse($code);
$output->page($this->presenter->present($nodes, $depth));
$this->context->setReturnValue($nodes);
return 0;
}
/**
* Lex and parse a string of code into statements.
*
* @param Parser $parser
* @param string $code
*
* @return array Statements
*/
private function parse(Parser $parser, string $code): array
{
try {
return $parser->parse($code);
} catch (\PhpParser\Error $e) {
if (\strpos($e->getMessage(), 'unexpected EOF') === false) {
throw $e;
}
// If we got an unexpected EOF, let's try it again with a semicolon.
return $parser->parse($code.';');
}
}
/**
* Get (or create) the Parser instance.
*
* @param string|null $kind One of Psy\ParserFactory constants (only for PHP parser 2.0 and above)
*
* @return Parser
*/
private function getParser(string $kind = null): Parser
{
if (!\array_key_exists($kind, $this->parsers)) {
$this->parsers[$kind] = $this->parserFactory->createParser($kind);
}
return $this->parsers[$kind];
}
}

View File

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

@@ -3,7 +3,7 @@
/*
* This file is part of Psy Shell.
*
* (c) 2012-2022 Justin Hileman
* (c) 2012-2023 Justin Hileman
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
@@ -11,14 +11,16 @@
namespace Psy\Command;
use PhpParser\NodeTraverser;
use PhpParser\PrettyPrinter\Standard as Printer;
use Psy\CodeCleaner\NoReturnValue;
use Psy\Context;
use Psy\ContextAware;
use Psy\Exception\ErrorException;
use Psy\Exception\RuntimeException;
use Psy\Exception\UnexpectedTargetException;
use Psy\Reflection\ReflectionClassConstant;
use Psy\Reflection\ReflectionConstant_;
use Psy\Reflection\ReflectionConstant;
use Psy\Sudo\SudoVisitor;
use Psy\Util\Mirror;
/**
@@ -38,6 +40,26 @@ abstract class ReflectingCommand extends Command implements ContextAware
*/
protected $context;
private $parser;
private $traverser;
private $printer;
/**
* {@inheritdoc}
*/
public function __construct($name = null)
{
$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());
$this->printer = new Printer();
parent::__construct($name);
}
/**
* ContextAware interface.
*
@@ -92,8 +114,6 @@ abstract class ReflectingCommand extends Command implements ContextAware
*
* @param string $name
* @param bool $includeFunctions (default: false)
*
* @return string
*/
protected function resolveName(string $name, bool $includeFunctions = false): string
{
@@ -172,7 +192,10 @@ abstract class ReflectingCommand extends Command implements ContextAware
protected function resolveCode(string $code)
{
try {
$value = $this->getApplication()->execute($code, true);
// 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);
} catch (\Throwable $e) {
// Swallow all exceptions?
}
@@ -204,20 +227,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.
*
@@ -291,7 +300,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()) {
@@ -304,7 +312,7 @@ abstract class ReflectingCommand extends Command implements ContextAware
}
break;
case ReflectionConstant_::class:
case ReflectionConstant::class:
if ($reflector->inNamespace()) {
$vars['__namespace'] = $reflector->getNamespaceName();
}

View File

@@ -3,7 +3,7 @@
/*
* This file is part of Psy Shell.
*
* (c) 2012-2022 Justin Hileman
* (c) 2012-2023 Justin Hileman
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
@@ -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}
*/
@@ -69,8 +61,10 @@ HELP
/**
* {@inheritdoc}
*
* @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

@@ -3,7 +3,7 @@
/*
* This file is part of Psy Shell.
*
* (c) 2012-2022 Justin Hileman
* (c) 2012-2023 Justin Hileman
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
@@ -14,7 +14,6 @@ namespace Psy\Command;
use PhpParser\NodeTraverser;
use PhpParser\PrettyPrinter\Standard as Printer;
use Psy\Input\CodeArgument;
use Psy\ParserFactory;
use Psy\Readline\Readline;
use Psy\Sudo\SudoVisitor;
use Symfony\Component\Console\Input\InputInterface;
@@ -35,9 +34,9 @@ class SudoCommand extends Command
*/
public function __construct($name = null)
{
$parserFactory = new ParserFactory();
$this->parser = $parserFactory->createParser();
$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());
@@ -95,8 +94,10 @@ HELP
/**
* {@inheritdoc}
*
* @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');
@@ -109,11 +110,7 @@ HELP
$code = $history[\count($history) - 2];
}
if (\strpos($code, '<?') === false) {
$code = '<?php '.$code;
}
$nodes = $this->traverser->traverse($this->parse($code));
$nodes = $this->traverser->traverse($this->parser->parse($code));
$sudoCode = $this->printer->prettyPrint($nodes);
$shell = $this->getApplication();
@@ -121,25 +118,4 @@ HELP
return 0;
}
/**
* Lex and parse a string of code into statements.
*
* @param string $code
*
* @return array Statements
*/
private function parse(string $code): array
{
try {
return $this->parser->parse($code);
} catch (\PhpParser\Error $e) {
if (\strpos($e->getMessage(), 'unexpected EOF') === false) {
throw $e;
}
// If we got an unexpected EOF, let's try it again with a semicolon.
return $this->parser->parse($code.';');
}
}
}

View File

@@ -3,7 +3,7 @@
/*
* This file is part of Psy Shell.
*
* (c) 2012-2022 Justin Hileman
* (c) 2012-2023 Justin Hileman
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
@@ -13,24 +13,21 @@ namespace Psy\Command;
use PhpParser\Node\Arg;
use PhpParser\Node\Expr\New_;
use PhpParser\Node\Expr\StaticCall;
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 Psy\ParserFactory;
use Symfony\Component\Console\Input\InputInterface;
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;
@@ -40,24 +37,12 @@ class ThrowUpCommand extends Command implements ContextAware
*/
public function __construct($name = null)
{
$parserFactory = new ParserFactory();
$this->parser = $parserFactory->createParser();
$this->parser = new CodeArgumentParser();
$this->printer = new Printer();
parent::__construct($name);
}
/**
* @deprecated throwUp no longer needs to be ContextAware
*
* @param Context $context
*/
public function setContext(Context $context)
{
// Do nothing
}
/**
* {@inheritdoc}
*/
@@ -87,12 +72,14 @@ HELP
/**
* {@inheritdoc}
*
* @return int 0 if everything went fine, or an exit code
*
* @throws \InvalidArgumentException if there is no exception to throw
*/
protected function execute(InputInterface $input, OutputInterface $output)
protected function execute(InputInterface $input, OutputInterface $output): int
{
$args = $this->prepareArgs($input->getArgument('exception'));
$throwStmt = new Throw_(new StaticCall(new FullyQualifiedName(ThrowUpException::class), 'fromThrowable', $args));
$throwStmt = new Expression(new Throw_(new New_(new FullyQualifiedName(ThrowUpException::class), $args)));
$throwCode = $this->printer->prettyPrint([$throwStmt]);
$shell = $this->getApplication();
@@ -112,26 +99,20 @@ 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
return [new Arg(new Variable('_e'))];
}
if (\strpos($code, '<?') === false) {
$code = '<?php '.$code;
}
$nodes = $this->parse($code);
$nodes = $this->parser->parse($code);
if (\count($nodes) !== 1) {
throw new \InvalidArgumentException('No idea how to throw this');
}
$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())];
@@ -142,25 +123,4 @@ HELP
return $args;
}
/**
* Lex and parse a string of code into statements.
*
* @param string $code
*
* @return array Statements
*/
private function parse(string $code): array
{
try {
return $this->parser->parse($code);
} catch (\PhpParser\Error $e) {
if (\strpos($e->getMessage(), 'unexpected EOF') === false) {
throw $e;
}
// If we got an unexpected EOF, let's try it again with a semicolon.
return $this->parser->parse($code.';');
}
}
}

View File

@@ -3,7 +3,7 @@
/*
* This file is part of Psy Shell.
*
* (c) 2012-2022 Justin Hileman
* (c) 2012-2023 Justin Hileman
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
@@ -15,7 +15,6 @@ use PhpParser\NodeTraverser;
use PhpParser\PrettyPrinter\Standard as Printer;
use Psy\Command\TimeitCommand\TimeitVisitor;
use Psy\Input\CodeArgument;
use Psy\ParserFactory;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
@@ -28,6 +27,7 @@ class TimeitCommand extends Command
const RESULT_MSG = '<info>Command took %.6f seconds to complete.</info>';
const AVG_RESULT_MSG = '<info>Command took %.6f seconds on average (%.6f median; %.6f total) to complete.</info>';
// All times stored as nanoseconds!
private static $start = null;
private static $times = [];
@@ -40,9 +40,9 @@ class TimeitCommand extends Command
*/
public function __construct($name = null)
{
$parserFactory = new ParserFactory();
$this->parser = $parserFactory->createParser();
$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());
@@ -76,21 +76,23 @@ HELP
/**
* {@inheritdoc}
*
* @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 = $input->getOption('num') ?: 1;
$num = (int) ($input->getOption('num') ?: 1);
$shell = $this->getApplication();
$instrumentedCode = $this->instrumentCode($code);
self::$times = [];
for ($i = 0; $i < $num; $i++) {
do {
$_ = $shell->execute($instrumentedCode);
$this->ensureEndMarked();
}
} while (\count(self::$times) < $num);
$shell->writeReturnValue($_);
@@ -98,13 +100,13 @@ HELP
self::$times = [];
if ($num === 1) {
$output->writeln(\sprintf(self::RESULT_MSG, $times[0]));
$output->writeln(\sprintf(self::RESULT_MSG, $times[0] / 1e+9));
} else {
$total = \array_sum($times);
\rsort($times);
$median = $times[\round($num / 2)];
$output->writeln(\sprintf(self::AVG_RESULT_MSG, $total / $num, $median, $total));
$output->writeln(\sprintf(self::AVG_RESULT_MSG, ($total / $num) / 1e+9, $median / 1e+9, $total / 1e+9));
}
return 0;
@@ -119,7 +121,7 @@ HELP
*/
public static function markStart()
{
self::$start = \microtime(true);
self::$start = \hrtime(true);
}
/**
@@ -138,7 +140,7 @@ HELP
*/
public static function markEnd($ret = null)
{
self::$times[] = \microtime(true) - self::$start;
self::$times[] = \hrtime(true) - self::$start;
self::$start = null;
return $ret;
@@ -162,36 +164,9 @@ HELP
*
* This inserts `markStart` and `markEnd` calls to ensure that (reasonably)
* accurate times are recorded for just the code being executed.
*
* @param string $code
*
* @return string
*/
private function instrumentCode(string $code): string
{
return $this->printer->prettyPrint($this->traverser->traverse($this->parse($code)));
}
/**
* Lex and parse a string of code into statements.
*
* @param string $code
*
* @return array Statements
*/
private function parse(string $code): array
{
$code = '<?php '.$code;
try {
return $this->parser->parse($code);
} catch (\PhpParser\Error $e) {
if (\strpos($e->getMessage(), 'unexpected EOF') === false) {
throw $e;
}
// If we got an unexpected EOF, let's try it again with a semicolon.
return $this->parser->parse($code.';');
}
return $this->printer->prettyPrint($this->traverser->traverse($this->parser->parse($code)));
}
}

View File

@@ -3,7 +3,7 @@
/*
* This file is part of Psy Shell.
*
* (c) 2012-2022 Justin Hileman
* (c) 2012-2023 Justin Hileman
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
@@ -35,6 +35,8 @@ class TimeitVisitor extends NodeVisitorAbstract
/**
* {@inheritdoc}
*
* @return Node[]|null Array of nodes
*/
public function beforeTraverse(array $nodes)
{
@@ -43,6 +45,8 @@ class TimeitVisitor extends NodeVisitorAbstract
/**
* {@inheritdoc}
*
* @return int|Node|null Replacement node (or special return value)
*/
public function enterNode(Node $node)
{
@@ -62,6 +66,8 @@ class TimeitVisitor extends NodeVisitorAbstract
/**
* {@inheritdoc}
*
* @return int|Node|Node[]|null Replacement node (or special return value)
*/
public function leaveNode(Node $node)
{
@@ -72,11 +78,13 @@ class TimeitVisitor extends NodeVisitorAbstract
/**
* {@inheritdoc}
*
* @return Node[]|null Array of nodes
*/
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];
@@ -89,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;
@@ -111,10 +119,8 @@ class TimeitVisitor extends NodeVisitorAbstract
* Optionally pass in a return value.
*
* @param Expr|null $arg
*
* @return \PhpParser\Node\Expr\StaticCall
*/
private function getEndCall(Expr $arg = null): StaticCall
private function getEndCall(?Expr $arg = null): StaticCall
{
if ($arg === null) {
$arg = NoReturnValue::create();
@@ -122,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

@@ -3,7 +3,7 @@
/*
* This file is part of Psy Shell.
*
* (c) 2012-2022 Justin Hileman
* (c) 2012-2023 Justin Hileman
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
@@ -68,8 +68,10 @@ HELP
/**
* {@inheritdoc}
*
* @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'));
@@ -79,18 +81,18 @@ HELP
}
/**
* Get a backtrace for an exception.
* Get a backtrace for an exception or error.
*
* Optionally limit the number of rows to include with $count, and exclude
* Psy from the trace.
*
* @param \Exception $e The exception with a backtrace
* @param \Throwable $e The exception or error with a backtrace
* @param int $count (default: PHP_INT_MAX)
* @param bool $includePsy (default: true)
*
* @return array Formatted stacktrace lines
*/
protected function getBacktrace(\Exception $e, 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

@@ -3,7 +3,7 @@
/*
* This file is part of Psy Shell.
*
* (c) 2012-2022 Justin Hileman
* (c) 2012-2023 Justin Hileman
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
@@ -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);
@@ -109,8 +106,10 @@ HELP
/**
* {@inheritdoc}
*
* @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');
@@ -142,8 +141,6 @@ HELP
* Replace the given directory from the start of a filepath.
*
* @param string $file
*
* @return string
*/
private function replaceCwd(string $file): string
{

View File

@@ -3,7 +3,7 @@
/*
* This file is part of Psy Shell.
*
* (c) 2012-2022 Justin Hileman
* (c) 2012-2023 Justin Hileman
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
@@ -81,8 +81,10 @@ HELP
/**
* {@inheritdoc}
*
* @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

@@ -3,7 +3,7 @@
/*
* This file is part of Psy Shell.
*
* (c) 2012-2022 Justin Hileman
* (c) 2012-2023 Justin Hileman
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
@@ -31,10 +31,11 @@ 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);
$this->env = $env ?: new SuperglobalsEnv();
$this->env = $env ?: (\PHP_SAPI === 'cli-server' ? new SystemEnv() : new SuperglobalsEnv());
}
/**
@@ -119,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.
*
@@ -154,8 +129,6 @@ class ConfigPaths
* everywhere else).
*
* @see self::homeConfigDir
*
* @return string
*/
public function currentConfigDir(): string
{
@@ -174,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.
*
@@ -194,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.
*
@@ -226,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.
*
@@ -246,20 +195,10 @@ 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.
*
* Defaults to `/psysh` inside the system's temp dir.
*
* @return string
*/
public function runtimeDir(): string
{
@@ -274,11 +213,37 @@ class ConfigPaths
}
/**
* @deprecated
* Get a list of directories in PATH.
*
* If $PATH is unset/empty it defaults to '/usr/sbin:/usr/bin:/sbin:/bin'.
*
* @return string[]
*/
public static function getRuntimeDir(): string
public function pathDirs(): array
{
return (new self())->runtimeDir();
return $this->getEnvArray('PATH') ?: ['/usr/sbin', '/usr/bin', '/sbin', '/bin'];
}
/**
* Locate a command (an executable) in $PATH.
*
* Behaves like 'command -v COMMAND' or 'which COMMAND'.
* 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)
{
foreach ($this->pathDirs() as $path) {
$fullpath = $path.\DIRECTORY_SEPARATOR.$command;
if (@\is_file($fullpath) && @\is_executable($fullpath)) {
return $fullpath;
}
}
return null;
}
/**
@@ -405,7 +370,7 @@ class ConfigPaths
private function getEnvArray($key)
{
if ($value = $this->getEnv($key)) {
return \explode(':', $value);
return \explode(\PATH_SEPARATOR, $value);
}
return null;

View File

@@ -3,7 +3,7 @@
/*
* This file is part of Psy Shell.
*
* (c) 2012-2022 Justin Hileman
* (c) 2012-2023 Justin Hileman
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
@@ -16,6 +16,7 @@ use Psy\Exception\RuntimeException;
use Psy\ExecutionLoop\ProcessForker;
use Psy\Output\OutputPager;
use Psy\Output\ShellOutput;
use Psy\Output\Theme;
use Psy\TabCompletion\AutoCompleter;
use Psy\VarDumper\Presenter;
use Psy\VersionUpdater\Checker;
@@ -66,6 +67,8 @@ class Configuration
'requireSemicolons',
'runtimeDir',
'startupMessage',
'strictTypes',
'theme',
'updateCheck',
'useBracketedPaste',
'usePcntl',
@@ -97,6 +100,7 @@ class Configuration
private $pipedOutput;
private $rawOutput = false;
private $requireSemicolons = false;
private $strictTypes = false;
private $useUnicode;
private $useTabCompletion;
private $newMatchers = [];
@@ -107,12 +111,16 @@ class Configuration
private $updateCheck;
private $startupMessage;
private $forceArrayIndexes = false;
/** @deprecated */
private $formatterStyles = [];
private $verbosity = self::VERBOSITY_NORMAL;
private $yolo = false;
/** @var Theme */
private $theme;
// services
private $readline;
/** @var ShellOutput */
private $output;
private $shell;
private $cleaner;
@@ -121,6 +129,7 @@ class Configuration
private $presenter;
private $autoCompleter;
private $checker;
/** @deprecated */
private $prompt;
private $configPaths;
@@ -140,6 +149,8 @@ class Configuration
$this->configFile = $config['configFile'];
} elseif (isset($_SERVER['PSYSH_CONFIG']) && $_SERVER['PSYSH_CONFIG']) {
$this->configFile = $_SERVER['PSYSH_CONFIG'];
} elseif (\PHP_SAPI === 'cli-server' && ($configFile = \getenv('PSYSH_CONFIG'))) {
$this->configFile = $configFile;
}
// legacy baseDir option
@@ -171,8 +182,6 @@ class Configuration
* @throws \InvalidArgumentException
*
* @param InputInterface $input
*
* @return self
*/
public static function fromInput(InputInterface $input): self
{
@@ -197,6 +206,11 @@ class Configuration
$config->setInteractiveMode(self::INTERACTIVE_MODE_DISABLED);
}
// Handle --compact
if (self::getOptionFromInput($input, ['compact'])) {
$config->setTheme('compact');
}
// Handle --raw-output
// @todo support raw output with interactive input?
if (!$config->getInputInteractive()) {
@@ -346,6 +360,7 @@ class Configuration
new InputOption('quiet', 'q', InputOption::VALUE_NONE, 'Shhhhhh.'),
new InputOption('verbose', 'v|vv|vvv', InputOption::VALUE_OPTIONAL, 'Increase the verbosity of messages.', '0'),
new InputOption('compact', null, InputOption::VALUE_NONE, 'Run PsySH with compact output.'),
new InputOption('interactive', 'i|a', InputOption::VALUE_NONE, 'Force PsySH to run in interactive mode.'),
new InputOption('no-interactive', 'n', InputOption::VALUE_NONE, 'Run PsySH without interactive input. Requires input from stdin.'),
// --interaction and --no-interaction aliases for compatibility with Symfony, Composer, etc
@@ -353,6 +368,8 @@ class Configuration
new InputOption('no-interaction', null, InputOption::VALUE_NONE, 'Run PsySH without interactive input. Requires input from stdin.'),
new InputOption('raw-output', 'r', InputOption::VALUE_NONE, 'Print var_export-style return values (for non-interactive input)'),
new InputOption('self-update', 'u', InputOption::VALUE_NONE, 'Update to the latest version'),
new InputOption('yolo', null, InputOption::VALUE_NONE, 'Run PsySH with minimal input validation. You probably don\'t want this.'),
];
}
@@ -523,7 +540,7 @@ class Configuration
/**
* Get files to be included by default at the start of each shell session.
*
* @return array
* @return string[]
*/
public function getDefaultIncludes(): array
{
@@ -605,8 +622,6 @@ class Configuration
* overridden.
*
* @throws RuntimeException if no temporary directory is set and it is not possible to create one
*
* @return string
*/
public function getRuntimeDir(): string
{
@@ -636,8 +651,6 @@ class Configuration
*
* Defaults to `/history` inside the shell's base config dir unless
* explicitly overridden.
*
* @return string
*/
public function getHistoryFile(): string
{
@@ -806,8 +819,6 @@ class Configuration
* Get the appropriate Readline implementation class name.
*
* @see self::getReadline
*
* @return string
*/
private function getReadlineClass(): string
{
@@ -816,11 +827,13 @@ class Configuration
return Readline\GNUReadline::class;
} elseif (Readline\Libedit::isSupported()) {
return Readline\Libedit::class;
} elseif (Readline\HoaConsole::isSupported()) {
return Readline\HoaConsole::class;
}
}
if (Readline\Userland::isSupported()) {
return Readline\Userland::class;
}
return Readline\Transient::class;
}
@@ -891,7 +904,17 @@ class Configuration
*/
public function usePcntl(): bool
{
return isset($this->usePcntl) ? ($this->hasPcntl && $this->usePcntl) : $this->hasPcntl;
if (!isset($this->usePcntl)) {
// Unless pcntl is explicitly *enabled*, don't use it while XDebug is debugging.
// See https://github.com/bobthecow/psysh/issues/742
if (\function_exists('xdebug_is_debugger_active') && \xdebug_is_debugger_active()) {
return false;
}
return $this->hasPcntl;
}
return $this->hasPcntl && $this->usePcntl;
}
/**
@@ -935,14 +958,28 @@ class Configuration
* By default, PsySH will automatically insert semicolons at the end of
* statements if they're missing. To strictly require semicolons, set
* `requireSemicolons` to true.
*
* @return bool
*/
public function requireSemicolons(): bool
{
return $this->requireSemicolons;
}
/**
* Enable or disable strict types enforcement.
*/
public function setStrictTypes($strictTypes)
{
$this->strictTypes = (bool) $strictTypes;
}
/**
* Check whether to enforce strict types.
*/
public function strictTypes(): bool
{
return $this->strictTypes;
}
/**
* Enable or disable Unicode in PsySH specific output.
*
@@ -961,8 +998,6 @@ class Configuration
*
* Note that this does not disable Unicode output in general, it just makes
* it so PsySH won't output any itself.
*
* @return bool
*/
public function useUnicode(): bool
{
@@ -997,8 +1032,6 @@ class Configuration
* level.
*
* http://php.net/manual/en/function.error-reporting.php
*
* @return int
*/
public function errorLoggingLevel(): int
{
@@ -1019,13 +1052,11 @@ class Configuration
* Get a CodeCleaner service instance.
*
* If none has been explicitly defined, this will create a new instance.
*
* @return CodeCleaner
*/
public function getCodeCleaner(): CodeCleaner
{
if (!isset($this->cleaner)) {
$this->cleaner = new CodeCleaner(null, null, null, $this->yolo());
$this->cleaner = new CodeCleaner(null, null, null, $this->yolo(), $this->strictTypes());
}
return $this->cleaner;
@@ -1066,6 +1097,8 @@ class Configuration
*/
public function setTabCompletion(bool $useTabCompletion)
{
@\trigger_error('`setTabCompletion` is deprecated; call `setUseTabCompletion` instead.', \E_USER_DEPRECATED);
$this->setUseTabCompletion($useTabCompletion);
}
@@ -1084,11 +1117,11 @@ class Configuration
/**
* @deprecated Call `useTabCompletion` instead
*
* @return bool
*/
public function getTabCompletion(): bool
{
@\trigger_error('`getTabCompletion` is deprecated; call `useTabCompletion` instead.', \E_USER_DEPRECATED);
return $this->useTabCompletion();
}
@@ -1101,6 +1134,11 @@ class Configuration
{
$this->output = $output;
$this->pipedOutput = null; // Reset cached pipe info
if (isset($this->theme)) {
$output->setTheme($this->theme);
}
$this->applyFormatterStyles();
}
@@ -1112,8 +1150,6 @@ class Configuration
*
* @see self::verbosity
* @see self::getPager
*
* @return ShellOutput
*/
public function getOutput(): ShellOutput
{
@@ -1122,7 +1158,8 @@ class Configuration
$this->getOutputVerbosity(),
null,
null,
$this->getPager()
$this->getPager() ?: null,
$this->theme()
));
// This is racy because `getOutputDecorated` needs access to the
@@ -1145,29 +1182,29 @@ class Configuration
public function getOutputDecorated()
{
switch ($this->colorMode()) {
case self::COLOR_MODE_AUTO:
return $this->outputIsPiped() ? false : null;
case self::COLOR_MODE_FORCED:
return true;
case self::COLOR_MODE_DISABLED:
return false;
case self::COLOR_MODE_AUTO:
default:
return $this->outputIsPiped() ? false : null;
}
}
/**
* Get the interactive setting for shell input.
*
* @return bool
*/
public function getInputInteractive(): bool
{
switch ($this->interactiveMode()) {
case self::INTERACTIVE_MODE_AUTO:
return !$this->inputIsPiped();
case self::INTERACTIVE_MODE_FORCED:
return true;
case self::INTERACTIVE_MODE_DISABLED:
return false;
case self::INTERACTIVE_MODE_AUTO:
default:
return !$this->inputIsPiped();
}
}
@@ -1177,13 +1214,19 @@ class Configuration
* If a string is supplied, a ProcOutputPager will be used which shells out
* to the specified command.
*
* `cat` is special-cased to use the PassthruPager directly.
*
* @throws \InvalidArgumentException if $pager is not a string or OutputPager instance
*
* @param string|OutputPager $pager
* @param string|OutputPager|false $pager
*/
public function setPager($pager)
{
if ($pager && !\is_string($pager) && !$pager instanceof OutputPager) {
if ($pager === null || $pager === false || $pager === 'cat') {
$pager = false;
}
if ($pager !== false && !\is_string($pager) && !$pager instanceof OutputPager) {
throw new \InvalidArgumentException('Unexpected pager instance');
}
@@ -1196,17 +1239,31 @@ class Configuration
* If no Pager has been explicitly provided, and Pcntl is available, this
* will default to `cli.pager` ini value, falling back to `which less`.
*
* @return string|OutputPager
* @return string|OutputPager|false
*/
public function getPager()
{
if (!isset($this->pager) && $this->usePcntl()) {
if (\getenv('TERM') === 'dumb') {
return false;
}
if ($pager = \ini_get('cli.pager')) {
// use the default pager
$this->pager = $pager;
} elseif ($less = \exec('which less 2>/dev/null')) {
} elseif ($less = $this->configPaths->which('less')) {
// check for the presence of less...
$this->pager = $less.' -R -S -F -X';
// 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
$link = @\readlink($less);
if ($link !== false && \strpos($link, 'busybox') !== false) {
return false;
}
$this->pager = $less.' -R -F -X';
}
}
@@ -1225,8 +1282,6 @@ class Configuration
/**
* Get an AutoCompleter service instance.
*
* @return AutoCompleter
*/
public function getAutoCompleter(): AutoCompleter
{
@@ -1239,11 +1294,11 @@ class Configuration
/**
* @deprecated Nothing should be using this anymore
*
* @return array
*/
public function getTabCompletionMatchers(): array
{
@\trigger_error('`getTabCompletionMatchers` is no longer used.', \E_USER_DEPRECATED);
return [];
}
@@ -1284,6 +1339,8 @@ class Configuration
*/
public function addTabCompletionMatchers(array $matchers)
{
@\trigger_error('`addTabCompletionMatchers` is deprecated; call `addMatchers` instead.', \E_USER_DEPRECATED);
$this->addMatchers($matchers);
}
@@ -1401,8 +1458,6 @@ class Configuration
/**
* Get the Presenter service.
*
* @return Presenter
*/
public function getPresenter(): Presenter
{
@@ -1434,8 +1489,6 @@ class Configuration
* are found.
*
* This will default to true in a future release, but is false for now.
*
* @return bool
*/
public function warnOnMultipleConfigs(): bool
{
@@ -1445,6 +1498,8 @@ class Configuration
/**
* Set the current color mode.
*
* @throws \InvalidArgumentException if the color mode isn't auto, forced or disabled
*
* @param string $colorMode
*/
public function setColorMode(string $colorMode)
@@ -1464,8 +1519,6 @@ class Configuration
/**
* Get the current color mode.
*
* @return string
*/
public function colorMode(): string
{
@@ -1496,8 +1549,6 @@ class Configuration
/**
* Get the current interactive mode.
*
* @return string
*/
public function interactiveMode(): string
{
@@ -1518,8 +1569,6 @@ class Configuration
* Get an update checker service instance.
*
* If none has been explicitly defined, this will create a new instance.
*
* @return Checker
*/
public function getChecker(): Checker
{
@@ -1555,8 +1604,6 @@ class Configuration
*
* One of 'always', 'daily', 'weekly', 'monthly' or 'never'. If none is
* explicitly set, default to 'weekly'.
*
* @return string
*/
public function getUpdateCheck(): string
{
@@ -1620,11 +1667,19 @@ class Configuration
/**
* Set the prompt.
*
* @param string $prompt
* @deprecated The `prompt` configuration has been replaced by Themes and support will
* eventually be removed. In the meantime, prompt is applied first by the Theme, then overridden
* by any explicitly defined prompt.
*
* Note that providing a prompt but not a theme config will implicitly use the `classic` theme.
*/
public function setPrompt(string $prompt)
{
$this->prompt = $prompt;
if (isset($this->theme)) {
$this->theme->setPrompt($prompt);
}
}
/**
@@ -1639,8 +1694,6 @@ class Configuration
/**
* Get the force array indexes.
*
* @return bool
*/
public function forceArrayIndexes(): bool
{
@@ -1657,6 +1710,46 @@ class Configuration
$this->forceArrayIndexes = $forceArrayIndexes;
}
/**
* Set the current output Theme.
*
* @param Theme|string|array $theme Theme (or Theme config)
*/
public function setTheme($theme)
{
if (!$theme instanceof Theme) {
$theme = new Theme($theme);
}
$this->theme = $theme;
if (isset($this->prompt)) {
$this->theme->setPrompt($this->prompt);
}
if (isset($this->output)) {
$this->output->setTheme($theme);
$this->applyFormatterStyles();
}
}
/**
* Get the current output Theme.
*/
public function theme(): Theme
{
if (!isset($this->theme)) {
// If a prompt is explicitly set, and a theme is not, base it on the `classic` theme.
$this->theme = $this->prompt ? new Theme('classic') : new Theme();
}
if (isset($this->prompt)) {
$this->theme->setPrompt($this->prompt);
}
return $this->theme;
}
/**
* Set the shell output formatter styles.
*
@@ -1669,15 +1762,14 @@ class Configuration
*
* Foreground, background or options can be null, or even omitted entirely.
*
* @see ShellOutput::initFormatters
*
* @param array $formatterStyles
* @deprecated The `formatterStyles` configuration has been replaced by Themes and support will
* eventually be removed. In the meantime, styles are applied first by the Theme, then
* overridden by any explicitly defined formatter styles.
*/
public function setFormatterStyles(array $formatterStyles)
{
foreach ($formatterStyles as $name => $style) {
list($fg, $bg, $opts) = \array_pad($style, 3, null);
$this->formatterStyles[$name] = new OutputFormatterStyle($fg ?: null, $bg ?: null, $opts ?: []);
$this->formatterStyles[$name] = new OutputFormatterStyle(...$style);
}
if (isset($this->output)) {
@@ -1690,6 +1782,10 @@ class Configuration
*
* This is called on initialization of the shell output, and again if the
* formatter styles config is updated.
*
* @deprecated The `formatterStyles` configuration has been replaced by Themes and support will
* eventually be removed. In the meantime, styles are applied first by the Theme, then
* overridden by any explicitly defined formatter styles.
*/
private function applyFormatterStyles()
{
@@ -1697,12 +1793,17 @@ class Configuration
foreach ($this->formatterStyles as $name => $style) {
$formatter->setStyle($name, $style);
}
$errorFormatter = $this->output->getErrorOutput()->getFormatter();
foreach (Theme::ERROR_STYLES as $name) {
if (isset($this->formatterStyles[$name])) {
$errorFormatter->setStyle($name, $this->formatterStyles[$name]);
}
}
}
/**
* Get the configured output verbosity.
*
* @return string
*/
public function verbosity(): string
{
@@ -1765,13 +1866,11 @@ class Configuration
* Guess whether stdin is piped.
*
* This is mostly useful for deciding whether to use non-interactive mode.
*
* @return bool
*/
public function inputIsPiped(): bool
{
if ($this->pipedInput === null) {
$this->pipedInput = \defined('STDIN') && static::looksLikeAPipe(\STDIN);
$this->pipedInput = \defined('STDIN') && self::looksLikeAPipe(\STDIN);
}
return $this->pipedInput;
@@ -1781,13 +1880,11 @@ class Configuration
* Guess whether shell output is piped.
*
* This is mostly useful for deciding whether to use non-decorated output.
*
* @return bool
*/
public function outputIsPiped(): bool
{
if ($this->pipedOutput === null) {
$this->pipedOutput = static::looksLikeAPipe($this->getOutput()->getStream());
$this->pipedOutput = self::looksLikeAPipe($this->getOutput()->getStream());
}
return $this->pipedOutput;
@@ -1797,8 +1894,6 @@ class Configuration
* Guess whether an input or output stream is piped.
*
* @param resource|int $stream
*
* @return bool
*/
private static function looksLikeAPipe($stream): bool
{

View File

@@ -1,39 +0,0 @@
<?php
/*
* This file is part of Psy Shell.
*
* (c) 2012-2022 Justin Hileman
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Psy;
use JakubOnderka\PhpConsoleColor\ConsoleColor;
/**
* @deprecated Nothing should use this anymore
*/
class ConsoleColorFactory
{
/**
* @param string $colorMode
*/
public function __construct(string $colorMode)
{
// Nothing to see here
}
/**
* Get a `ConsoleColor` instance configured according to the given color
* mode.
*
* @return ConsoleColor
*/
public function getConsoleColor(): ConsoleColor
{
return new ConsoleColor(); // /shrug
}
}

View File

@@ -3,7 +3,7 @@
/*
* This file is part of Psy Shell.
*
* (c) 2012-2022 Justin Hileman
* (c) 2012-2023 Justin Hileman
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
@@ -92,8 +92,6 @@ class Context
/**
* Get all defined variables.
*
* @return array
*/
public function getAll(): array
{
@@ -102,8 +100,6 @@ class Context
/**
* Get all defined magic variables: $_, $_e, $__out, $__class, $__file, etc.
*
* @return array
*/
public function getSpecialVariables(): array
{
@@ -168,21 +164,21 @@ class Context
}
/**
* Set the most recent Exception.
* Set the most recent Exception or Error.
*
* @param \Exception $e
* @param \Throwable $e
*/
public function setLastException(\Exception $e)
public function setLastException(\Throwable $e)
{
$this->lastException = $e;
}
/**
* Get the most recent Exception.
* Get the most recent Exception or Error.
*
* @throws \InvalidArgumentException If no Exception has been caught
*
* @return \Exception|null
* @return \Throwable|null
*/
public function getLastException()
{
@@ -285,8 +281,6 @@ class Context
/**
* Get command-scope magic variables: $__class, $__file, etc.
*
* @return array
*/
public function getCommandScopeVariables(): array
{
@@ -310,8 +304,6 @@ class Context
* Check whether a variable name is a magic variable.
*
* @param string $name
*
* @return bool
*/
public static function isSpecialVariableName(string $name): bool
{

View File

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

View File

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

View File

@@ -3,7 +3,7 @@
/*
* This file is part of Psy Shell.
*
* (c) 2012-2022 Justin Hileman
* (c) 2012-2023 Justin Hileman
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
@@ -21,7 +21,7 @@ class BreakException extends \Exception implements Exception
/**
* {@inheritdoc}
*/
public function __construct($message = '', $code = 0, \Exception $previous = null)
public function __construct($message = '', $code = 0, ?\Throwable $previous = null)
{
$this->rawMessage = $message;
parent::__construct(\sprintf('Exit: %s', $message), $code, $previous);
@@ -29,8 +29,6 @@ class BreakException extends \Exception implements Exception
/**
* Return a raw (unformatted) version of the error message.
*
* @return string
*/
public function getRawMessage(): string
{

View File

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

View File

@@ -3,7 +3,7 @@
/*
* This file is part of Psy Shell.
*
* (c) 2012-2022 Justin Hileman
* (c) 2012-2023 Justin Hileman
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
@@ -21,14 +21,14 @@ class ErrorException extends \ErrorException implements Exception
/**
* Construct a Psy ErrorException.
*
* @param string $message (default: "")
* @param int $code (default: 0)
* @param int $severity (default: 1)
* @param string|null $filename (default: null)
* @param int|null $lineno (default: null)
* @param Exception|null $previous (default: null)
* @param string $message (default: "")
* @param int $code (default: 0)
* @param int $severity (default: 1)
* @param string|null $filename (default: null)
* @param int|null $lineno (default: null)
* @param \Throwable|null $previous (default: null)
*/
public function __construct($message = '', $code = 0, $severity = 1, $filename = null, $lineno = null, $previous = null)
public function __construct($message = '', $code = 0, $severity = 1, $filename = null, $lineno = null, ?\Throwable $previous = null)
{
$this->rawMessage = $message;
@@ -73,8 +73,6 @@ class ErrorException extends \ErrorException implements Exception
/**
* Get the raw (unformatted) message for this error.
*
* @return string
*/
public function getRawMessage(): string
{
@@ -103,12 +101,12 @@ class ErrorException extends \ErrorException implements Exception
/**
* Create an ErrorException from an Error.
*
* @param \Error $e
* @deprecated PsySH no longer wraps Errors
*
* @return self
* @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

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

View File

@@ -3,7 +3,7 @@
/*
* This file is part of Psy Shell.
*
* (c) 2012-2022 Justin Hileman
* (c) 2012-2023 Justin Hileman
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
@@ -26,9 +26,9 @@ class FatalErrorException extends \ErrorException implements Exception
* @param int $severity (default: 1)
* @param string|null $filename (default: null)
* @param int|null $lineno (default: null)
* @param \Exception|null $previous (default: null)
* @param \Throwable|null $previous (default: null)
*/
public function __construct($message = '', $code = 0, $severity = 1, $filename = null, $lineno = null, $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) {
@@ -42,8 +42,6 @@ class FatalErrorException extends \ErrorException implements Exception
/**
* Return a raw (unformatted) version of the error message.
*
* @return string
*/
public function getRawMessage(): string
{

View File

@@ -3,7 +3,7 @@
/*
* This file is part of Psy Shell.
*
* (c) 2012-2022 Justin Hileman
* (c) 2012-2023 Justin Hileman
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
@@ -19,24 +19,28 @@ 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);
}
/**
* Create a ParseErrorException from a PhpParser Error.
*
* @param \PhpParser\Error $e
*
* @return self
*/
public static function fromParseError(\PhpParser\Error $e): self
{
return new self($e->getRawMessage(), $e->getStartLine());
return new self($e->getRawMessage(), $e->getAttributes());
}
}

View File

@@ -3,7 +3,7 @@
/*
* This file is part of Psy Shell.
*
* (c) 2012-2022 Justin Hileman
* (c) 2012-2023 Justin Hileman
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
@@ -23,9 +23,9 @@ class RuntimeException extends \RuntimeException implements Exception
*
* @param string $message (default: "")
* @param int $code (default: 0)
* @param \Exception|null $previous (default: null)
* @param \Throwable|null $previous (default: null)
*/
public function __construct(string $message = '', int $code = 0, \Exception $previous = null)
public function __construct(string $message = '', int $code = 0, ?\Throwable $previous = null)
{
$this->rawMessage = $message;
parent::__construct($message, $code, $previous);
@@ -33,8 +33,6 @@ class RuntimeException extends \RuntimeException implements Exception
/**
* Return a raw (unformatted) version of the error message.
*
* @return string
*/
public function getRawMessage(): string
{

View File

@@ -3,7 +3,7 @@
/*
* This file is part of Psy Shell.
*
* (c) 2012-2022 Justin Hileman
* (c) 2012-2023 Justin Hileman
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
@@ -19,16 +19,14 @@ class ThrowUpException extends \Exception implements Exception
/**
* {@inheritdoc}
*/
public function __construct(\Exception $exception)
public function __construct(\Throwable $throwable)
{
$message = \sprintf("Throwing %s with message '%s'", \get_class($exception), $exception->getMessage());
parent::__construct($message, $exception->getCode(), $exception);
$message = \sprintf("Throwing %s with message '%s'", \get_class($throwable), $throwable->getMessage());
parent::__construct($message, $throwable->getCode(), $throwable);
}
/**
* Return a raw (unformatted) version of the error message.
*
* @return string
*/
public function getRawMessage(): string
{
@@ -38,20 +36,12 @@ class ThrowUpException extends \Exception implements Exception
/**
* Create a ThrowUpException from a Throwable.
*
* @param \Throwable $throwable
* @deprecated PsySH no longer wraps Throwables
*
* @return self
* @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,55 +0,0 @@
<?php
/*
* This file is part of Psy Shell.
*
* (c) 2012-2022 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!
*
* @param string $message (default: "")
* @param int $code (default: 0)
*/
public function __construct(string $message = '', int $code = 0)
{
$this->rawMessage = $message;
$message = \preg_replace('/, called in .*?: eval\\(\\)\'d code/', '', $message);
parent::__construct(\sprintf('TypeError: %s', $message), $code);
}
/**
* Get the raw (unformatted) message for this error.
*
* @return string
*/
public function getRawMessage(): string
{
return $this->rawMessage;
}
/**
* Create a TypeErrorException from a TypeError.
*
* @param \TypeError $e
*
* @return self
*/
public static function fromTypeError(\TypeError $e): self
{
return new self($e->getMessage(), $e->getCode());
}
}

View File

@@ -3,7 +3,7 @@
/*
* This file is part of Psy Shell.
*
* (c) 2012-2022 Justin Hileman
* (c) 2012-2023 Justin Hileman
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
@@ -19,9 +19,9 @@ class UnexpectedTargetException extends RuntimeException
* @param mixed $target
* @param string $message (default: "")
* @param int $code (default: 0)
* @param \Exception|null $previous (default: null)
* @param \Throwable|null $previous (default: null)
*/
public function __construct($target, string $message = '', int $code = 0, \Exception $previous = null)
public function __construct($target, string $message = '', int $code = 0, ?\Throwable $previous = null)
{
$this->target = $target;
parent::__construct($message, $code, $previous);

View File

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

View File

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

View File

@@ -3,7 +3,7 @@
/*
* This file is part of Psy Shell.
*
* (c) 2012-2022 Justin Hileman
* (c) 2012-2023 Justin Hileman
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
@@ -20,8 +20,6 @@ interface Listener
{
/**
* Determines whether this listener should be active.
*
* @return bool
*/
public static function isSupported(): bool;

View File

@@ -3,7 +3,7 @@
/*
* This file is part of Psy Shell.
*
* (c) 2012-2022 Justin Hileman
* (c) 2012-2023 Justin Hileman
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
@@ -41,8 +41,6 @@ class ProcessForker extends AbstractListener
/**
* Process forker is supported if pcntl and posix extensions are available.
*
* @return bool
*/
public static function isSupported(): bool
{
@@ -250,8 +248,6 @@ class ProcessForker extends AbstractListener
* we can.
*
* @param array $return
*
* @return string
*/
private function serializeReturn(array $return): string
{

View File

@@ -3,7 +3,7 @@
/*
* This file is part of Psy Shell.
*
* (c) 2012-2022 Justin Hileman
* (c) 2012-2023 Justin Hileman
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
@@ -17,6 +17,8 @@ use Psy\Shell;
/**
* A runkit-based code reloader, which is pretty much magic.
*
* @todo Remove RunkitReloader once we drop support for PHP 7.x :(
*/
class RunkitReloader extends AbstractListener
{
@@ -25,8 +27,6 @@ class RunkitReloader extends AbstractListener
/**
* Only enabled if Runkit is installed.
*
* @return bool
*/
public static function isSupported(): bool
{
@@ -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();
}
/**
@@ -127,8 +124,6 @@ class RunkitReloader extends AbstractListener
* Use PHP-Parser to ensure that the file is valid PHP.
*
* @param string $file
*
* @return bool
*/
private function lintFile(string $file): bool
{

View File

@@ -3,7 +3,7 @@
/*
* This file is part of Psy Shell.
*
* (c) 2012-2022 Justin Hileman
* (c) 2012-2023 Justin Hileman
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
@@ -12,15 +12,12 @@
namespace Psy;
use Psy\Exception\BreakException;
use Psy\Exception\ErrorException;
use Psy\Exception\ThrowUpException;
use Psy\Exception\TypeErrorException;
/**
* 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
{
@@ -80,11 +77,7 @@ class ExecutionLoopClosure extends ExecutionClosure
$__psysh__->writeException($_e);
throw $_e;
} catch (\TypeError $_e) {
$__psysh__->writeException(TypeErrorException::fromTypeError($_e));
} catch (\Error $_e) {
$__psysh__->writeException(ErrorException::fromError($_e));
} catch (\Exception $_e) {
} catch (\Throwable $_e) {
$__psysh__->writeException($_e);
}

View File

@@ -3,7 +3,7 @@
/*
* This file is part of Psy Shell.
*
* (c) 2012-2022 Justin Hileman
* (c) 2012-2023 Justin Hileman
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
@@ -32,7 +32,7 @@ class CodeFormatter implements ReflectorFormatter
const HIGHLIGHT_CONST = 'const';
const HIGHLIGHT_NUMBER = 'number';
const HIGHLIGHT_STRING = 'string';
const HIGHLIGHT_COMMENT = 'comment';
const HIGHLIGHT_COMMENT = 'code_comment';
const HIGHLIGHT_INLINE_HTML = 'inline_html';
private static $tokenMap = [
@@ -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())) {
@@ -104,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);
@@ -122,8 +121,6 @@ class CodeFormatter implements ReflectorFormatter
* This is typehinted as \Reflector but we've narrowed the input via self::isReflectable already.
*
* @param \ReflectionClass|\ReflectionFunctionAbstract $reflector
*
* @return int
*/
private static function getStartLine(\Reflector $reflector): int
{
@@ -209,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 = [];
@@ -276,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);
@@ -302,16 +299,16 @@ class CodeFormatter implements ReflectorFormatter
$mark = ($markLine === $lineNum) ? self::LINE_MARKER : self::NO_LINE_MARKER;
}
yield \sprintf("%s<aside>%${pad}s</aside>: %s", $mark, $lineNum, $line);
yield \sprintf("%s<aside>%{$pad}s</aside>: %s", $mark, $lineNum, $line);
}
}
/**
* Check whether a Reflector instance is reflectable by this formatter.
*
* @param \Reflector $reflector
* @phpstan-assert-if-true \ReflectionClass|\ReflectionFunctionAbstract $reflector
*
* @return bool
* @param \Reflector $reflector
*/
private static function isReflectable(\Reflector $reflector): bool
{

View File

@@ -3,7 +3,7 @@
/*
* This file is part of Psy Shell.
*
* (c) 2012-2022 Justin Hileman
* (c) 2012-2023 Justin Hileman
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
@@ -68,8 +68,6 @@ class DocblockFormatter implements ReflectorFormatter
*
* @param array $vector
* @param array $lines
*
* @return string
*/
private static function formatVector(array $vector, array $lines): string
{
@@ -133,8 +131,6 @@ class DocblockFormatter implements ReflectorFormatter
*
* @param string $type Vector type
* @param int $max Pad width
*
* @return string
*/
private static function getVectorParamTemplate(string $type, int $max): string
{
@@ -150,8 +146,6 @@ class DocblockFormatter implements ReflectorFormatter
*
* @param string $text String to indent
* @param string $indent (default: ' ')
*
* @return string
*/
private static function indent(string $text, string $indent = ' '): string
{
@@ -162,8 +156,6 @@ class DocblockFormatter implements ReflectorFormatter
* Convert underscored or whitespace separated words into sentence case.
*
* @param string $text
*
* @return string
*/
private static function inflect(string $text): string
{

View File

@@ -1,21 +0,0 @@
<?php
/*
* This file is part of Psy Shell.
*
* (c) 2012-2022 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

@@ -3,7 +3,7 @@
/*
* This file is part of Psy Shell.
*
* (c) 2012-2022 Justin Hileman
* (c) 2012-2023 Justin Hileman
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
@@ -18,8 +18,6 @@ interface ReflectorFormatter
{
/**
* @param \Reflector $reflector
*
* @return string
*/
public static function format(\Reflector $reflector): string;
}

View File

@@ -3,7 +3,7 @@
/*
* This file is part of Psy Shell.
*
* (c) 2012-2022 Justin Hileman
* (c) 2012-2023 Justin Hileman
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
@@ -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;
@@ -38,11 +37,10 @@ class SignatureFormatter implements ReflectorFormatter
case $reflector instanceof ReflectionLanguageConstruct:
return self::formatFunction($reflector);
// this case also covers \ReflectionObject:
case $reflector instanceof \ReflectionClass:
// 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 \Reflector $reflector
* @param \ReflectionClass|\ReflectionClassConstant|\ReflectionFunctionAbstract $reflector
*
* @return string Formatted name
*/
@@ -75,7 +73,7 @@ class SignatureFormatter implements ReflectorFormatter
/**
* Print the method, property or class modifiers.
*
* @param \Reflector $reflector
* @param \ReflectionMethod|\ReflectionProperty|\ReflectionClass $reflector
*
* @return string Formatted modifiers
*/
@@ -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);
@@ -173,8 +171,6 @@ class SignatureFormatter implements ReflectorFormatter
* Helper for getting output style for a given value's type.
*
* @param mixed $value
*
* @return string
*/
private static function getTypeStyle($value): string
{
@@ -236,7 +232,7 @@ class SignatureFormatter implements ReflectorFormatter
return '';
}
return \sprintf(': %s', self::formatReflectionType($reflector->getReturnType()));
return \sprintf(': %s', self::formatReflectionType($reflector->getReturnType(), true));
}
/**
@@ -269,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>';
@@ -324,28 +322,37 @@ class SignatureFormatter implements ReflectorFormatter
* Print function param or return type(s).
*
* @param \ReflectionType $type
*
* @return string
*/
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

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

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

@@ -3,7 +3,7 @@
/*
* This file is part of Psy Shell.
*
* (c) 2012-2022 Justin Hileman
* (c) 2012-2023 Justin Hileman
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
@@ -73,8 +73,6 @@ class FilterOptions
/**
* Check whether the bound input has filter options.
*
* @return bool
*/
public function hasFilter(): bool
{
@@ -86,10 +84,8 @@ class FilterOptions
*
* @param string $string
* @param array $matches
*
* @return bool
*/
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);
}
@@ -116,8 +112,6 @@ class FilterOptions
* Check whether a string appears to be a regular expression.
*
* @param string $string
*
* @return bool
*/
private function stringIsRegex(string $string): bool
{

View File

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

@@ -3,7 +3,7 @@
/*
* This file is part of Psy Shell.
*
* (c) 2012-2022 Justin Hileman
* (c) 2012-2023 Justin Hileman
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
@@ -34,8 +34,6 @@ class SilentInput
/**
* To. String.
*
* @return string
*/
public function __toString(): string
{

View File

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

View File

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

View File

@@ -3,7 +3,7 @@
/*
* This file is part of Psy Shell.
*
* (c) 2012-2022 Justin Hileman
* (c) 2012-2023 Justin Hileman
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
@@ -32,9 +32,9 @@ class ProcOutputPager extends StreamOutput implements OutputPager
* Constructor.
*
* @param StreamOutput $output
* @param string $cmd Pager process command (default: 'less -R -S -F -X')
* @param string $cmd Pager process command (default: 'less -R -F -X')
*/
public function __construct(StreamOutput $output, string $cmd = 'less -R -S -F -X')
public function __construct(StreamOutput $output, string $cmd = 'less -R -F -X')
{
$this->stream = $output->getStream();
$this->cmd = $cmd;
@@ -48,12 +48,13 @@ 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 : ''))) {
// @codeCoverageIgnoreStart
// should never happen
$this->close();
throw new \RuntimeException('Unable to write output');
// @codeCoverageIgnoreEnd
}

View File

@@ -3,7 +3,7 @@
/*
* This file is part of Psy Shell.
*
* (c) 2012-2022 Justin Hileman
* (c) 2012-2023 Justin Hileman
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
@@ -13,7 +13,6 @@ namespace Psy\Output;
use Symfony\Component\Console\Formatter\OutputFormatter;
use Symfony\Component\Console\Formatter\OutputFormatterInterface;
use Symfony\Component\Console\Formatter\OutputFormatterStyle;
use Symfony\Component\Console\Output\ConsoleOutput;
/**
@@ -24,8 +23,13 @@ class ShellOutput extends ConsoleOutput
const NUMBER_LINES = 128;
private $paging = 0;
/** @var OutputPager */
private $pager;
/** @var Theme */
private $theme;
/**
* Construct a ShellOutput instance.
*
@@ -34,10 +38,11 @@ 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)
public function __construct($verbosity = self::VERBOSITY_NORMAL, $decorated = null, ?OutputFormatterInterface $formatter = null, $pager = null, $theme = null)
{
parent::__construct($verbosity, $decorated, $formatter);
$this->theme = $theme ?? new Theme('modern');
$this->initFormatters();
if ($pager === null) {
@@ -114,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;
@@ -149,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);
@@ -158,6 +163,15 @@ class ShellOutput extends ConsoleOutput
}
}
/**
* Set the output Theme.
*/
public function setTheme(Theme $theme)
{
$this->theme = $theme;
$this->initFormatters();
}
/**
* Flush and close the output pager.
*/
@@ -173,38 +187,22 @@ class ShellOutput extends ConsoleOutput
*/
private function initFormatters()
{
$formatter = $this->getFormatter();
$useGrayFallback = !$this->grayExists();
$this->theme->applyStyles($this->getFormatter(), $useGrayFallback);
$this->theme->applyErrorStyles($this->getErrorOutput()->getFormatter(), $useGrayFallback);
}
$formatter->setStyle('warning', new OutputFormatterStyle('black', 'yellow'));
$formatter->setStyle('error', new OutputFormatterStyle('white', 'red', ['bold']));
$formatter->setStyle('aside', new OutputFormatterStyle('blue'));
$formatter->setStyle('strong', new OutputFormatterStyle(null, null, ['bold']));
$formatter->setStyle('return', new OutputFormatterStyle('cyan'));
$formatter->setStyle('urgent', new OutputFormatterStyle('red'));
$formatter->setStyle('hidden', new OutputFormatterStyle('black'));
/**
* Checks if the "gray" color exists on the output.
*/
private function grayExists(): bool
{
try {
$this->write('<fg=gray></>');
} catch (\InvalidArgumentException $e) {
return false;
}
// Visibility
$formatter->setStyle('public', new OutputFormatterStyle(null, null, ['bold']));
$formatter->setStyle('protected', new OutputFormatterStyle('yellow'));
$formatter->setStyle('private', new OutputFormatterStyle('red'));
$formatter->setStyle('global', new OutputFormatterStyle('cyan', null, ['bold']));
$formatter->setStyle('const', new OutputFormatterStyle('cyan'));
$formatter->setStyle('class', new OutputFormatterStyle('blue', null, ['underscore']));
$formatter->setStyle('function', new OutputFormatterStyle(null));
$formatter->setStyle('default', new OutputFormatterStyle(null));
// Types
$formatter->setStyle('number', new OutputFormatterStyle('magenta'));
$formatter->setStyle('integer', new OutputFormatterStyle('magenta'));
$formatter->setStyle('float', new OutputFormatterStyle('yellow'));
$formatter->setStyle('string', new OutputFormatterStyle('green'));
$formatter->setStyle('bool', new OutputFormatterStyle('cyan'));
$formatter->setStyle('keyword', new OutputFormatterStyle('yellow'));
$formatter->setStyle('comment', new OutputFormatterStyle('blue'));
$formatter->setStyle('object', new OutputFormatterStyle('blue'));
$formatter->setStyle('resource', new OutputFormatterStyle('yellow'));
// Code-specific formatting
$formatter->setStyle('inline_html', new OutputFormatterStyle('cyan'));
return true;
}
}

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