Add expression generator
This commit is contained in:
parent
5228af041e
commit
fb9dac3d95
@ -9,7 +9,8 @@
|
|||||||
"require-dev": {
|
"require-dev": {
|
||||||
"phpunit/phpunit": "^7 | ^8 | ^9",
|
"phpunit/phpunit": "^7 | ^8 | ^9",
|
||||||
"amphp/php-cs-fixer-config": "dev-master",
|
"amphp/php-cs-fixer-config": "dev-master",
|
||||||
"composer/composer": "^1|^2"
|
"composer/composer": "^1|^2",
|
||||||
|
"haydenpierce/class-finder": "^0.4.2"
|
||||||
},
|
},
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"authors": [{
|
"authors": [{
|
||||||
|
@ -1,32 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Spatie\Php7to5\NodeVisitors;
|
|
||||||
|
|
||||||
use PhpParser\Node;
|
|
||||||
use PhpParser\NodeVisitorAbstract;
|
|
||||||
|
|
||||||
class YieldReturnDetector extends NodeVisitorAbstract
|
|
||||||
{
|
|
||||||
protected $hasYield = [];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritdoc}
|
|
||||||
*/
|
|
||||||
public function enterNode(Node $node)
|
|
||||||
{
|
|
||||||
if ($node instanceof Node\FunctionLike) {
|
|
||||||
$this->hasYield []= $node;
|
|
||||||
}
|
|
||||||
if ($node instanceof Node\Expr\Yield_ ||
|
|
||||||
$node instanceof Node\Expr\YieldFrom
|
|
||||||
) {
|
|
||||||
\end($this->hasYield)->hasYield = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public function leaveNode(Node $node)
|
|
||||||
{
|
|
||||||
if ($node instanceof Node\FunctionLike) {
|
|
||||||
\array_pop($this->hasYield);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,55 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Spatie\Php7to5\NodeVisitors;
|
|
||||||
|
|
||||||
use PhpParser\Node;
|
|
||||||
use PhpParser\NodeVisitorAbstract;
|
|
||||||
|
|
||||||
class YieldReturnReplacer extends NodeVisitorAbstract
|
|
||||||
{
|
|
||||||
protected $functions = [];
|
|
||||||
/**
|
|
||||||
* {@inheritdoc}
|
|
||||||
*/
|
|
||||||
public function enterNode(Node $node)
|
|
||||||
{
|
|
||||||
if ($node instanceof Node\FunctionLike) {
|
|
||||||
$this->functions[] = $node;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritdoc}
|
|
||||||
*/
|
|
||||||
public function leaveNode(Node $node)
|
|
||||||
{
|
|
||||||
if ($node instanceof Node\FunctionLike) {
|
|
||||||
\array_pop($this->functions);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!$node instanceof Node\Stmt\Return_) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if ($node->expr === null) {
|
|
||||||
return new Node\Stmt\Return_();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!(\end($this->functions)->hasYield ?? false)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$value = $node->expr;
|
|
||||||
|
|
||||||
$newReturn = new Node\Expr\Yield_(
|
|
||||||
new Node\Expr\New_(
|
|
||||||
new Node\Expr\ConstFetch(
|
|
||||||
new Node\Name('\YieldReturnValue')
|
|
||||||
),
|
|
||||||
[$value]
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
$stmts = [$newReturn, new Node\Stmt\Return_()];
|
|
||||||
return $stmts;
|
|
||||||
}
|
|
||||||
}
|
|
@ -3,17 +3,27 @@
|
|||||||
namespace Phabel\Plugin;
|
namespace Phabel\Plugin;
|
||||||
|
|
||||||
use Phabel\Plugin;
|
use Phabel\Plugin;
|
||||||
|
use Phabel\Traverser;
|
||||||
use PhpParser\Builder\FunctionLike;
|
use PhpParser\Builder\FunctionLike;
|
||||||
|
|
||||||
class ReGenerator extends Plugin
|
class ReGenerator extends Plugin
|
||||||
{
|
{
|
||||||
const SHOULD_ATTRIBUTE = 'shouldRegenerate';
|
const SHOULD_ATTRIBUTE = 'shouldRegenerate';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Custom traverser.
|
||||||
|
*/
|
||||||
|
private Traverser $traverser;
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->traverser = Traverser::fromPlugin(new ReGeneratorInternal);
|
||||||
|
}
|
||||||
public function enter(FunctionLike $function)
|
public function enter(FunctionLike $function)
|
||||||
{
|
{
|
||||||
if (!$function->getAttribute(self::SHOULD_ATTRIBUTE, false)) {
|
if (!$function->getAttribute(self::SHOULD_ATTRIBUTE, false)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
$this->traverser->traverseAst($function);
|
||||||
}
|
}
|
||||||
public function runAfter(): array
|
public function runAfter(): array
|
||||||
{
|
{
|
||||||
|
@ -12,62 +12,55 @@ class ReGenerator implements \Iterator
|
|||||||
*
|
*
|
||||||
* @var array
|
* @var array
|
||||||
*/
|
*/
|
||||||
private $variables = [];
|
public $variables = [];
|
||||||
/**
|
/**
|
||||||
* Return value.
|
* Return value.
|
||||||
*
|
*
|
||||||
* @var mixed
|
* @var mixed
|
||||||
*/
|
*/
|
||||||
private $returnValue;
|
public $returnValue;
|
||||||
/**
|
/**
|
||||||
* Yield key.
|
* Yield key.
|
||||||
*
|
*
|
||||||
* @var mixed
|
* @var mixed
|
||||||
*/
|
*/
|
||||||
private $yieldKey;
|
public $yieldKey;
|
||||||
/**
|
/**
|
||||||
* Yield value.
|
* Yield value.
|
||||||
*
|
*
|
||||||
* @var mixed
|
* @var mixed
|
||||||
*/
|
*/
|
||||||
private $yieldValue;
|
public $yieldValue;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Value sent from the outside.
|
* Value sent from the outside.
|
||||||
*
|
*
|
||||||
* @var mixed
|
* @var mixed
|
||||||
*/
|
*/
|
||||||
private $sentValue;
|
public $sentValue;
|
||||||
/**
|
/**
|
||||||
* Exception sent from the outside.
|
* Exception sent from the outside.
|
||||||
*/
|
*/
|
||||||
private ?\Throwable $sentException = null;
|
public ?\Throwable $sentException = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Current state of state machine.
|
* Current state of state machine.
|
||||||
*/
|
*/
|
||||||
private int $state = 0;
|
public int $state = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether the generator has returned.
|
* Whether the generator has returned.
|
||||||
*/
|
*/
|
||||||
private bool $returned = false;
|
public bool $returned = false;
|
||||||
/**
|
/**
|
||||||
* Whether the generator was started.
|
* Whether the generator was started.
|
||||||
*/
|
*/
|
||||||
private bool $started = false;
|
public bool $started = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Actual generator function.
|
* Actual generator function.
|
||||||
*/
|
*/
|
||||||
private \Closure $generator;
|
public \Closure $generator;
|
||||||
|
|
||||||
/**
|
|
||||||
* Yielded from (re)generator.
|
|
||||||
*
|
|
||||||
* @var \Generator|self|null
|
|
||||||
*/
|
|
||||||
private $yieldedFrom;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Construct regenerator.
|
* Construct regenerator.
|
||||||
@ -97,35 +90,26 @@ class ReGenerator implements \Iterator
|
|||||||
private function start(): void
|
private function start(): void
|
||||||
{
|
{
|
||||||
if (!$this->started) {
|
if (!$this->started) {
|
||||||
($this->generator)($this->state, $this->variables, $this->yieldKey, $this->yieldValue, $this->sentValue, $this->sentException, $this->returnValue, $this->returned, $this->yieldedFrom);
|
($this->generator)($this->state, $this->variables, $this->yieldKey, $this->yieldValue, $this->sentValue, $this->sentException, $this->returnValue, $this->returned);
|
||||||
$this->started = true;
|
$this->started = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send value into generator
|
||||||
|
*
|
||||||
|
* @param mixed $value Value
|
||||||
|
*
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
public function send($value)
|
public function send($value)
|
||||||
{
|
{
|
||||||
$this->start();
|
$this->start();
|
||||||
if ($this->yieldedFrom) {
|
|
||||||
try {
|
|
||||||
$result = $this->yieldedFrom->send($value);
|
|
||||||
} catch (\Throwable $exception) {
|
|
||||||
$e = $exception;
|
|
||||||
}
|
|
||||||
if (!$this->yieldedFrom->valid()) { // Returned from yield from
|
|
||||||
$returnValue = \method_exists($this->yieldedFrom, 'getReturn') ? $this->yieldedFrom->getReturn() : null;
|
|
||||||
$this->yieldedFrom = null;
|
|
||||||
if (isset($e)) {
|
|
||||||
return $this->throw($e);
|
|
||||||
}
|
|
||||||
return $this->send($returnValue);
|
|
||||||
}
|
|
||||||
return $result;
|
|
||||||
}
|
|
||||||
$value = $this->yieldValue;
|
$value = $this->yieldValue;
|
||||||
if (!$this->returned) {
|
if (!$this->returned) {
|
||||||
$this->sentValue = $value;
|
$this->sentValue = $value;
|
||||||
try {
|
try {
|
||||||
($this->generator)($this->state, $this->variables, $this->yieldKey, $this->yieldValue, $this->sentValue, $this->sentException, $this->returnValue, $this->returned, $this->yieldedFrom);
|
($this->generator)($this->state, $this->variables, $this->yieldKey, $this->yieldValue, $this->sentValue, $this->sentException, $this->returnValue, $this->returned);
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
$this->returned = true;
|
$this->returned = true;
|
||||||
throw $e;
|
throw $e;
|
||||||
@ -135,30 +119,21 @@ class ReGenerator implements \Iterator
|
|||||||
}
|
}
|
||||||
return $value;
|
return $value;
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* Throw value into generator
|
||||||
|
*
|
||||||
|
* @param \Throwable $throwable Excpeption
|
||||||
|
*
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
public function throw(\Throwable $throwable)
|
public function throw(\Throwable $throwable)
|
||||||
{
|
{
|
||||||
$this->start();
|
$this->start();
|
||||||
if ($this->yieldedFrom) {
|
|
||||||
try {
|
|
||||||
$result = $this->yieldedFrom->throw($throwable);
|
|
||||||
} catch (\Throwable $exception) {
|
|
||||||
$e = $exception;
|
|
||||||
}
|
|
||||||
if (!$this->yieldedFrom->valid()) { // Returned from yield from
|
|
||||||
$returnValue = \method_exists($this->yieldedFrom, 'getReturn') ? $this->yieldedFrom->getReturn() : null;
|
|
||||||
$this->yieldedFrom = null;
|
|
||||||
if (isset($e)) {
|
|
||||||
return $this->throw($e);
|
|
||||||
}
|
|
||||||
return $this->send($returnValue);
|
|
||||||
}
|
|
||||||
return $result;
|
|
||||||
}
|
|
||||||
$value = $this->yieldValue;
|
$value = $this->yieldValue;
|
||||||
if (!$this->returned) {
|
if (!$this->returned) {
|
||||||
$this->sentException = $value;
|
$this->sentException = $value;
|
||||||
try {
|
try {
|
||||||
($this->generator)($this->variables, $this->yieldKey, $this->yieldValue, $this->sentValue, $this->sentException, $this->returnValue, $this->returned, $this->yieldedFrom);
|
($this->generator)($this->state, $this->variables, $this->yieldKey, $this->yieldValue, $this->sentValue, $this->sentException, $this->returnValue, $this->returned);
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
$this->returned = true;
|
$this->returned = true;
|
||||||
throw $e;
|
throw $e;
|
||||||
@ -169,31 +144,46 @@ class ReGenerator implements \Iterator
|
|||||||
return $value;
|
return $value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get current value
|
||||||
|
*
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
public function current()
|
public function current()
|
||||||
{
|
{
|
||||||
if ($this->yieldedFrom) {
|
|
||||||
return $this->yieldedFrom->current();
|
|
||||||
}
|
|
||||||
$this->start();
|
$this->start();
|
||||||
return $this->yieldValue;
|
return $this->yieldValue;
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* Get current key
|
||||||
|
*
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
public function key()
|
public function key()
|
||||||
{
|
{
|
||||||
if ($this->yieldedFrom) {
|
|
||||||
return $this->yieldedFrom->key();
|
|
||||||
}
|
|
||||||
$this->start();
|
$this->start();
|
||||||
return $this->yieldKey;
|
return $this->yieldKey;
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* Advance generator
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
public function next(): void
|
public function next(): void
|
||||||
{
|
{
|
||||||
$this->send(null);
|
$this->send(null);
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* Rewind generator
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
public function rewind(): void
|
public function rewind(): void
|
||||||
{
|
{
|
||||||
if ($this->started && !$this->returned) {
|
if ($this->started && !$this->returned) {
|
||||||
throw new \Exception('Cannot rewind a generator that was already run');
|
throw new \Exception('Cannot rewind a generator that was already run');
|
||||||
}
|
}
|
||||||
|
$this->state = 0;
|
||||||
$this->started = false;
|
$this->started = false;
|
||||||
$this->returned = false;
|
$this->returned = false;
|
||||||
$this->returnValue = null;
|
$this->returnValue = null;
|
||||||
@ -201,10 +191,14 @@ class ReGenerator implements \Iterator
|
|||||||
$this->yieldValue = null;
|
$this->yieldValue = null;
|
||||||
$this->sentValue = null;
|
$this->sentValue = null;
|
||||||
$this->sentException = null;
|
$this->sentException = null;
|
||||||
$this->yieldedFrom = null;
|
|
||||||
$this->variables = [];
|
$this->variables = [];
|
||||||
$this->start();
|
$this->start();
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* Check if generator is valid
|
||||||
|
*
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
public function valid(): bool
|
public function valid(): bool
|
||||||
{
|
{
|
||||||
return !$this->returned;
|
return !$this->returned;
|
||||||
|
46
src/Plugin/ReGeneratorInternal.php
Normal file
46
src/Plugin/ReGeneratorInternal.php
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Phabel\Plugin;
|
||||||
|
|
||||||
|
use Phabel\Plugin;
|
||||||
|
use PhpParser\Node;
|
||||||
|
use PhpParser\Node\FunctionLike;
|
||||||
|
use SplQueue;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal regenerator traversor.
|
||||||
|
*/
|
||||||
|
class ReGeneratorInternal extends Plugin
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* List of nodes for each case.
|
||||||
|
*
|
||||||
|
* @var SplQueue<SplQueue<Node>>
|
||||||
|
*/
|
||||||
|
private SplQueue $states;
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->states = new SplQueue;
|
||||||
|
$this->states->enqueue(new SplQueue);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Push node to current case.
|
||||||
|
*
|
||||||
|
* @param Node $node Node
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
private function pushNode(Node $node): void
|
||||||
|
{
|
||||||
|
$this->states->top()->enqueue($node);
|
||||||
|
}
|
||||||
|
private function pushState(): int
|
||||||
|
{
|
||||||
|
$this->states->enqueue(new SplQueue);
|
||||||
|
return $this->states->count()-1;
|
||||||
|
}
|
||||||
|
public function enterRoot(FunctionLike $func)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -57,12 +57,14 @@ class TypeHintStripper extends Plugin
|
|||||||
* @var SplStack<T>
|
* @var SplStack<T>
|
||||||
*/
|
*/
|
||||||
private SplStack $stack;
|
private SplStack $stack;
|
||||||
|
private ArrowClosure $converter;
|
||||||
/**
|
/**
|
||||||
* Constructor.
|
* Constructor.
|
||||||
*/
|
*/
|
||||||
public function __construct()
|
public function __construct()
|
||||||
{
|
{
|
||||||
$this->stack = new SplStack;
|
$this->stack = new SplStack;
|
||||||
|
$this->converter = new ArrowClosure;
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Convert a function to a closure.
|
* Convert a function to a closure.
|
||||||
@ -70,7 +72,7 @@ class TypeHintStripper extends Plugin
|
|||||||
private function toClosure(FunctionLike &$func): void
|
private function toClosure(FunctionLike &$func): void
|
||||||
{
|
{
|
||||||
if ($func instanceof ArrowFunction) {
|
if ($func instanceof ArrowFunction) {
|
||||||
$func = ArrowClosure::enterClosure($func);
|
$func = $this->converter->enterClosure($func);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
|
@ -4,10 +4,13 @@ namespace Phabel\Target\Php70;
|
|||||||
|
|
||||||
use Phabel\Plugin;
|
use Phabel\Plugin;
|
||||||
use PhpParser\Node;
|
use PhpParser\Node;
|
||||||
|
use PhpParser\Node\Expr;
|
||||||
use PhpParser\Node\Expr\ArrayDimFetch;
|
use PhpParser\Node\Expr\ArrayDimFetch;
|
||||||
use PhpParser\Node\Expr\ClassConstFetch;
|
use PhpParser\Node\Expr\ClassConstFetch;
|
||||||
|
use PhpParser\Node\Expr\FuncCall;
|
||||||
use PhpParser\Node\Expr\Isset_;
|
use PhpParser\Node\Expr\Isset_;
|
||||||
use PhpParser\Node\Expr\MethodCall;
|
use PhpParser\Node\Expr\MethodCall;
|
||||||
|
use PhpParser\Node\Expr\Variable;
|
||||||
use PhpParser\Node\Expr\Yield_;
|
use PhpParser\Node\Expr\Yield_;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -25,13 +28,11 @@ class CompoundAccess extends Plugin
|
|||||||
public function enterIsset(Isset_ $node): void
|
public function enterIsset(Isset_ $node): void
|
||||||
{
|
{
|
||||||
foreach ($node->vars as &$var) {
|
foreach ($node->vars as &$var) {
|
||||||
if (!$var instanceof ArrayDimFetch) {
|
if ($var instanceof ArrayDimFetch
|
||||||
continue;
|
&& $var->var instanceof Expr
|
||||||
|
&& !($var->var instanceof Variable || $var->var instanceof FuncCall) {
|
||||||
|
$var->var = self::callPoly('returnMe', $var->var);
|
||||||
}
|
}
|
||||||
if (!$var->var instanceof ClassConstFetch) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
$var->var = self::callPoly('returnMe', $var->var);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
namespace Phabel\Target\Php71;
|
namespace Phabel\Target\Php71;
|
||||||
|
|
||||||
use Phabel\Plugin;
|
use Phabel\Plugin;
|
||||||
|
use PhpParser\BuilderHelpers;
|
||||||
use PhpParser\Node\Expr\Array_;
|
use PhpParser\Node\Expr\Array_;
|
||||||
use PhpParser\Node\Expr\Assign;
|
use PhpParser\Node\Expr\Assign;
|
||||||
use PhpParser\Node\Expr\List_;
|
use PhpParser\Node\Expr\List_;
|
||||||
@ -81,7 +82,7 @@ class ListKey extends Plugin
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
/** @var Array_ */
|
/** @var Array_ */
|
||||||
$keys = self::toLiteral($keys);
|
$keys = BuilderHelpers::normalizeValue($keys);
|
||||||
return [new List_($newList), $keys];
|
return [new List_($newList), $keys];
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
namespace Phabel\Target;
|
namespace Phabel\Target;
|
||||||
|
|
||||||
use Phabel\Plugin;
|
use Phabel\Plugin;
|
||||||
|
use Phabel\Target\Php72\ObjectTypeHintReplacer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Makes changes necessary to polyfill PHP 7.2 and run on PHP 7.1 and below.
|
* Makes changes necessary to polyfill PHP 7.2 and run on PHP 7.1 and below.
|
||||||
@ -16,4 +17,11 @@ class Php72 extends Plugin
|
|||||||
{
|
{
|
||||||
return ['symfony/polyfill-php72' => '*'];
|
return ['symfony/polyfill-php72' => '*'];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function runWithAfter(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
ObjectTypeHintReplacer::class
|
||||||
|
];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
23
src/Target/Php72/ObjectTypeHintReplacer.php
Normal file
23
src/Target/Php72/ObjectTypeHintReplacer.php
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Phabel\Target\Php72;
|
||||||
|
|
||||||
|
use Phabel\Plugin;
|
||||||
|
use Phabel\Plugin\TypeHintStripper;
|
||||||
|
|
||||||
|
class ObjectTypeHintReplacer extends Plugin
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Alias.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public static function runAfter(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
TypeHintStripper::class => [
|
||||||
|
'types' => ['object']
|
||||||
|
]
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
43
src/Target/Php74/ArrayUnpack.php
Normal file
43
src/Target/Php74/ArrayUnpack.php
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Phabel\Target\Php74;
|
||||||
|
|
||||||
|
use Phabel\Plugin;
|
||||||
|
use PhpParser\Node\Arg;
|
||||||
|
use PhpParser\Node\Expr\Array_;
|
||||||
|
use PhpParser\Node\Expr\ArrayItem;
|
||||||
|
use PhpParser\Node\Expr\FuncCall;
|
||||||
|
|
||||||
|
class ArrayUnpack extends Plugin
|
||||||
|
{
|
||||||
|
public function enter(Array_ $array): ?FuncCall
|
||||||
|
{
|
||||||
|
$hasUnpack = false;
|
||||||
|
foreach ($array->items as $item) {
|
||||||
|
if ($item->unpack) {
|
||||||
|
$hasUnpack = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!$hasUnpack) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$args = [];
|
||||||
|
$array = new Array_();
|
||||||
|
foreach ($array->items as $item) {
|
||||||
|
if ($item->unpack) {
|
||||||
|
if ($array->items) {
|
||||||
|
$args []= new Arg($array);
|
||||||
|
$array = new Array_();
|
||||||
|
}
|
||||||
|
$args []= new Arg($item->value);
|
||||||
|
} else {
|
||||||
|
$array->items []= $item;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($array->items) {
|
||||||
|
$args []= new Arg($array);
|
||||||
|
}
|
||||||
|
return Plugin::call("array_merge", ...$args);
|
||||||
|
}
|
||||||
|
}
|
@ -2,14 +2,47 @@
|
|||||||
|
|
||||||
namespace Phabel\Target\Php74;
|
namespace Phabel\Target\Php74;
|
||||||
|
|
||||||
|
use Phabel\Plugin;
|
||||||
|
use Phabel\Traverser;
|
||||||
use PhpParser\Node\Expr\ArrowFunction;
|
use PhpParser\Node\Expr\ArrowFunction;
|
||||||
use PhpParser\Node\Expr\Closure;
|
use PhpParser\Node\Expr\Closure;
|
||||||
|
use PhpParser\Node\Expr\ClosureUse;
|
||||||
|
use PhpParser\Node\Expr\Variable;
|
||||||
|
use PhpParser\Node\Param;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Turn an arrow function into a closure.
|
* Turn an arrow function into a closure.
|
||||||
*/
|
*/
|
||||||
class ArrowClosure
|
class ArrowClosure
|
||||||
{
|
{
|
||||||
|
/**
|
||||||
|
* Traverser.
|
||||||
|
*/
|
||||||
|
private Traverser $traverser;
|
||||||
|
/**
|
||||||
|
* Finder plugin.
|
||||||
|
*/
|
||||||
|
private Plugin $finderPlugin;
|
||||||
|
/**
|
||||||
|
* Constructor.
|
||||||
|
*/
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->finderPlugin = new class extends Plugin {
|
||||||
|
private array $found = [];
|
||||||
|
public function enter(Variable $var)
|
||||||
|
{
|
||||||
|
$this->found[$var->name]= new ClosureUse($var, true);
|
||||||
|
}
|
||||||
|
public function getFound(): array
|
||||||
|
{
|
||||||
|
$found = $this->found;
|
||||||
|
$this->found = [];
|
||||||
|
return $found;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
$this->traverser = Traverser::fromPlugin($this->finderPlugin);
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* Enter arrow function.
|
* Enter arrow function.
|
||||||
*
|
*
|
||||||
@ -17,12 +50,24 @@ class ArrowClosure
|
|||||||
*
|
*
|
||||||
* @return Closure
|
* @return Closure
|
||||||
*/
|
*/
|
||||||
public static function enterClosure(ArrowFunction $func): Closure
|
public function enterClosure(ArrowFunction $func): Closure
|
||||||
{
|
{
|
||||||
$nodes = [];
|
$nodes = [];
|
||||||
foreach ($func->getSubNodeNames() as $node) {
|
foreach ($func->getSubNodeNames() as $node) {
|
||||||
$nodes[$node] = $func->{$node};
|
$nodes[$node] = $func->{$node};
|
||||||
}
|
}
|
||||||
|
$params = [];
|
||||||
|
foreach ($nodes['params'] ?? [] as $param) {
|
||||||
|
$params[$param->var->name] = true;
|
||||||
|
}
|
||||||
|
$this->traverser->traverseAst($func);
|
||||||
|
$nodes['uses'] = \array_merge(
|
||||||
|
\array_values(\array_diff_key(
|
||||||
|
$this->finderPlugin->getFound(),
|
||||||
|
$params
|
||||||
|
)),
|
||||||
|
$nodes['use'] ?? []
|
||||||
|
);
|
||||||
return new Closure($nodes, $func->getAttributes());
|
return new Closure($nodes, $func->getAttributes());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
16
src/Target/Php74/NullCoalesceAssignment.php
Normal file
16
src/Target/Php74/NullCoalesceAssignment.php
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Phabel\Target\Php74;
|
||||||
|
|
||||||
|
use Phabel\Plugin;
|
||||||
|
use PhpParser\Node\Expr\Assign;
|
||||||
|
use PhpParser\Node\Expr\AssignOp\Coalesce;
|
||||||
|
use PhpParser\Node\Expr\BinaryOp\Coalesce as BinaryOpCoalesce;
|
||||||
|
|
||||||
|
class NullCoalesceAssignment extends Plugin
|
||||||
|
{
|
||||||
|
public function enter(Coalesce $coalesce): Assign
|
||||||
|
{
|
||||||
|
return new Assign($coalesce->var, new BinaryOpCoalesce($coalesce->var, $coalesce->expr), $coalesce->getAttributes());
|
||||||
|
}
|
||||||
|
}
|
13
src/Target/Php74/TypedProperty.php
Normal file
13
src/Target/Php74/TypedProperty.php
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Phabel\Target\Php74;
|
||||||
|
|
||||||
|
use Phabel\Plugin;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
class TypedProperty extends Plugin
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
@ -28,6 +28,23 @@ class Traverser
|
|||||||
* @return SplQueue<SplQueue<Plugin>>
|
* @return SplQueue<SplQueue<Plugin>>
|
||||||
*/
|
*/
|
||||||
private SplQueue $packageQueue;
|
private SplQueue $packageQueue;
|
||||||
|
/**
|
||||||
|
* Generate traverser from basic plugin instances.
|
||||||
|
*
|
||||||
|
* @param Plugin ...$plugin Plugins
|
||||||
|
*
|
||||||
|
* @return self
|
||||||
|
*/
|
||||||
|
public static function fromPlugin(Plugin ...$plugin): self
|
||||||
|
{
|
||||||
|
$queue = new SplQueue;
|
||||||
|
foreach ($plugin as $p) {
|
||||||
|
$queue->enqueue($p);
|
||||||
|
}
|
||||||
|
$final = new SplQueue;
|
||||||
|
$final->enqueue($queue);
|
||||||
|
return new self($final);
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* AST traverser.
|
* AST traverser.
|
||||||
*
|
*
|
||||||
@ -68,7 +85,7 @@ class Traverser
|
|||||||
/**
|
/**
|
||||||
* Traverse AST of file.
|
* Traverse AST of file.
|
||||||
*
|
*
|
||||||
* @param string $file File
|
* @param string $file File
|
||||||
*
|
*
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
@ -95,9 +112,21 @@ class Traverser
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
$ast = new RootNode($this->parser->parse(\file_get_contents($file)));
|
$ast = new RootNode($this->parser->parse(\file_get_contents($file)));
|
||||||
|
$this->traverseAst($ast, $reducedQueue);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Traverse AST.
|
||||||
|
*
|
||||||
|
* @param Node $node Initial node
|
||||||
|
* @param SplQueue $pluginQueue Plugin queue (optional)
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function traverseAst(Node &$node, SplQueue $pluginQueue = null): void
|
||||||
|
{
|
||||||
$context = new Context;
|
$context = new Context;
|
||||||
$context->parents->push($ast);
|
$context->parents->push($node);
|
||||||
foreach ($reducedQueue as $queue) {
|
foreach ($pluginQueue ?? $this->packageQueue ?? $this->queue as $queue) {
|
||||||
$this->traverseNode($ast, $queue, $context);
|
$this->traverseNode($ast, $queue, $context);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
use PhpParser\ParserFactory;
|
use PhpParser\ParserFactory;
|
||||||
|
use PhpParser\PrettyPrinter\Standard;
|
||||||
|
|
||||||
require 'vendor/autoload.php';
|
require 'vendor/autoload.php';
|
||||||
|
|
||||||
@ -10,5 +11,7 @@ if ($argc < 2) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
$parser = (new ParserFactory)->create(ParserFactory::PREFER_PHP7);
|
$parser = (new ParserFactory)->create(ParserFactory::PREFER_PHP7);
|
||||||
|
//$parser = (new ParserFactory)->create(ParserFactory::ONLY_PHP5);
|
||||||
|
|
||||||
\var_dump($parser->parse(\file_get_contents($argv[1])));
|
\var_dump($a = $parser->parse(\file_get_contents($argv[1])));
|
||||||
|
var_dumP((new Standard())->prettyPrint($a));
|
||||||
|
285
test/exprGen.php
Normal file
285
test/exprGen.php
Normal file
@ -0,0 +1,285 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use HaydenPierce\ClassFinder\ClassFinder;
|
||||||
|
use PhpParser\Builder\Class_;
|
||||||
|
use PhpParser\Builder\Method;
|
||||||
|
use PhpParser\Internal\PrintableNewAnonClassNode;
|
||||||
|
use PhpParser\Node;
|
||||||
|
use PhpParser\Node\Expr;
|
||||||
|
use PhpParser\Node\Expr\ArrayItem;
|
||||||
|
use PhpParser\Node\Expr\ArrowFunction;
|
||||||
|
use PhpParser\Node\Expr\Error;
|
||||||
|
use PhpParser\Node\Expr\Exit_;
|
||||||
|
use PhpParser\Node\Expr\Isset_;
|
||||||
|
use PhpParser\Node\Expr\List_;
|
||||||
|
use PhpParser\Node\Expr\Variable;
|
||||||
|
use PhpParser\Node\Identifier;
|
||||||
|
use PhpParser\Node\Name;
|
||||||
|
use PhpParser\Node\Scalar\EncapsedStringPart;
|
||||||
|
use PhpParser\Node\Stmt\Class_ as StmtClass_;
|
||||||
|
use PhpParser\Node\VarLikeIdentifier;
|
||||||
|
|
||||||
|
require 'vendor/autoload.php';
|
||||||
|
|
||||||
|
function format(Node $code): string
|
||||||
|
{
|
||||||
|
static $count = 0;
|
||||||
|
$count++;
|
||||||
|
$code = (new Class_("lmao$count"))->addStmt(
|
||||||
|
(new Method("te"))
|
||||||
|
->addStmt($code)
|
||||||
|
->getNode()
|
||||||
|
)->getNode();
|
||||||
|
$prettyPrinter = new PhpParser\PrettyPrinter\Standard;
|
||||||
|
return $prettyPrinter->prettyPrintFile([$code]);
|
||||||
|
}
|
||||||
|
function readUntilPrompt($resource): string
|
||||||
|
{
|
||||||
|
$data = '';
|
||||||
|
while (\substr($data, -6) !== 'php > ') {
|
||||||
|
$data .= \fread($resource, 1);
|
||||||
|
}
|
||||||
|
return \substr($data, 0, -6);
|
||||||
|
}
|
||||||
|
function checkSyntaxVersion(int $version, string $code): bool
|
||||||
|
{
|
||||||
|
$hasPrompt = $version < 80;
|
||||||
|
$code = \str_replace(["\n", '<?php'], '', $code)."\n";
|
||||||
|
static $processes = [];
|
||||||
|
static $pipes = [];
|
||||||
|
if (!isset($processes[$version])) {
|
||||||
|
$cmd = "php$version -a 2>&1";
|
||||||
|
$processes[$version] = \proc_open($cmd, [0 => ['pipe', 'r'], 1 => ['pipe', 'w']], $pipes[$version]);
|
||||||
|
if ($hasPrompt) {
|
||||||
|
readUntilPrompt($pipes[$version][1]);
|
||||||
|
} else {
|
||||||
|
\fgets($pipes[$version][1]);
|
||||||
|
\fgets($pipes[$version][1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!$hasPrompt) {
|
||||||
|
$code .= "echo 'php > ';\n";
|
||||||
|
}
|
||||||
|
\fputs($pipes[$version][0], $code);
|
||||||
|
if ($hasPrompt) {
|
||||||
|
$result = \str_replace(['{', '}'], '', \substr(\preg_replace('#\\x1b[[][^A-Za-z]*[A-Za-z]#', '', readUntilPrompt($pipes[$version][1])), \strlen($code)));
|
||||||
|
//var_dump($code, "REsult for $version is: " .trim($result));
|
||||||
|
} else {
|
||||||
|
$result = readUntilPrompt($pipes[$version][1]);
|
||||||
|
//var_dump($code, trim($result));
|
||||||
|
}
|
||||||
|
$result = \trim($result);
|
||||||
|
return \strlen($result) === 0;
|
||||||
|
}
|
||||||
|
function checkSyntax(string $code, int $startFrom = 56): int
|
||||||
|
{
|
||||||
|
if (!$startFrom) {
|
||||||
|
return $startFrom;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ([56, 70, 73, 74, 80] as $version) {
|
||||||
|
if ($version < $startFrom) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (checkSyntaxVersion($version, $code)) {
|
||||||
|
return $version;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @var ReflectionClass[] */
|
||||||
|
$expressions = [];
|
||||||
|
foreach ($allClasses = ClassFinder::getClassesInNamespace('PhpParser', ClassFinder::RECURSIVE_MODE) as $class) {
|
||||||
|
$class = new ReflectionClass($class);
|
||||||
|
if ($class->isSubclassOf(Expr::class) && !$class->isAbstract()
|
||||||
|
&& $class->getName() !== PrintableNewAnonClassNode::class
|
||||||
|
&& $class->getName() !== ArrowFunction::class
|
||||||
|
&& $class->getName() !== Error::class
|
||||||
|
&& $class->getName() !== List_::class
|
||||||
|
&& $class->getName() !== ArrayItem::class
|
||||||
|
&& $class->getName() !== EncapsedStringPart::class
|
||||||
|
&& $class->getName() !== Exit_::class
|
||||||
|
&& $class->getName() !== List_::class) {
|
||||||
|
$expressions []= $class;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$instanceArgs = [];
|
||||||
|
$instanceArgNames = [];
|
||||||
|
$instanceArgTypes = [];
|
||||||
|
|
||||||
|
$exprInstances = [];
|
||||||
|
foreach ($expressions as $expr) {
|
||||||
|
$class = $expr->getName();
|
||||||
|
$method = $expr->getMethod('__construct');
|
||||||
|
if ($method->getNumberOfParameters() === 1) {
|
||||||
|
$exprInstances[$class] = $expr->newInstance();
|
||||||
|
continue; // Is a magic constant or such
|
||||||
|
}
|
||||||
|
\preg_match_all('/@param (?<type>\S+) +\$(?<name>\S+)/', $method->getDocComment(), $matches);
|
||||||
|
$types = \array_combine($matches['name'], $matches['type']);
|
||||||
|
foreach ($types as &$type) {
|
||||||
|
$type = \explode("|", $type);
|
||||||
|
foreach ($type as $key => &$subtype) {
|
||||||
|
if (str_starts_with($subtype, 'Node')) {
|
||||||
|
$subtype = 'PhpParser\\'.$subtype;
|
||||||
|
} elseif ($subtype === 'Error') {
|
||||||
|
unset($type[$key]);
|
||||||
|
} elseif ($subtype === 'Identifier') {
|
||||||
|
$subtype = Identifier::class;
|
||||||
|
} elseif ($subtype === 'Name') {
|
||||||
|
$subtype = Name::class;
|
||||||
|
} elseif ($subtype === 'Expr') {
|
||||||
|
$subtype = Expr::class;
|
||||||
|
} elseif ($subtype === 'VarLikeIdentifier') {
|
||||||
|
$subtype = VarLikeIdentifier::class;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$params = $method->getParameters();
|
||||||
|
$hasExpr = false;
|
||||||
|
$arguments = [];
|
||||||
|
$argNames = [];
|
||||||
|
$argTypes = [];
|
||||||
|
foreach ($params as $key => $param) {
|
||||||
|
$paramStr = (string) $param->getType();
|
||||||
|
$argNames[] = $param->getName();
|
||||||
|
switch ($paramStr) {
|
||||||
|
case Expr::class:
|
||||||
|
$argTypes[$key] = [false, [Expr::class]];
|
||||||
|
$arguments[] = new Variable("test");
|
||||||
|
break;
|
||||||
|
case Name::class:
|
||||||
|
$arguments[] = new Name('self');
|
||||||
|
break;
|
||||||
|
case Variable::class:
|
||||||
|
$arguments[] = new Variable("test");
|
||||||
|
break;
|
||||||
|
case 'array':
|
||||||
|
if (\in_array('Expr[]', $types[$param->getName()] ?? [])) {
|
||||||
|
$argTypes[$key] = [true, [Expr::class]];
|
||||||
|
$arguments[] = [new Variable('test')];
|
||||||
|
} else {
|
||||||
|
$arguments[] = [];
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'bool':
|
||||||
|
$arguments[] = false;
|
||||||
|
break;
|
||||||
|
case 'float':
|
||||||
|
case 'int':
|
||||||
|
$arguments[] = 0;
|
||||||
|
break;
|
||||||
|
case 'string':
|
||||||
|
$arguments[] = 'test';
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
$argTypes[$key] = [false, $types[$param->getName()]];
|
||||||
|
$arguments[] = new Variable("test");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$exprInstances[$class] = $expr->newInstanceArgs($arguments);
|
||||||
|
if (\count($argTypes)) {
|
||||||
|
$instanceArgs[$class] = $arguments;
|
||||||
|
$instanceArgNames[$class] = $argNames;
|
||||||
|
$instanceArgTypes[$class] = $argTypes;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$versionMap = [];
|
||||||
|
|
||||||
|
$result = [
|
||||||
|
'main' => [], // Needs adaptation for nested expressions
|
||||||
|
'isset' => [], // Needs adaptation for nested expressions in isset
|
||||||
|
];
|
||||||
|
|
||||||
|
$newInstances = [];
|
||||||
|
foreach ($exprInstances as $class => $instance) {
|
||||||
|
$version = checkSyntax(format($prev = $instance));
|
||||||
|
$versionMap[$class] = $version ?: 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($instanceArgTypes as $class => $argTypes) {
|
||||||
|
$baseArgs = $instanceArgs[$class];
|
||||||
|
foreach ($argTypes as $key => [$isArray, $types]) {
|
||||||
|
$name = $instanceArgNames[$class][$key];
|
||||||
|
$possibleValues = [];
|
||||||
|
foreach ($types as $type) {
|
||||||
|
switch ($type) {
|
||||||
|
case Expr::class:
|
||||||
|
$possibleValues = array_merge($possibleValues, $exprInstances);
|
||||||
|
break;
|
||||||
|
case Name::class:
|
||||||
|
$possibleValues[] = new Name('self');
|
||||||
|
break;
|
||||||
|
case Variable::class:
|
||||||
|
$possibleValues[] = new Variable("test");
|
||||||
|
break;
|
||||||
|
case Identifier::class;
|
||||||
|
$possibleValues[] = new Identifier('test');
|
||||||
|
break;
|
||||||
|
case StmtClass_::class:
|
||||||
|
// Avoid using anonymous classes
|
||||||
|
//$possibleValues[] = new StmtClass_(null);
|
||||||
|
break;
|
||||||
|
case 'string':
|
||||||
|
$possibleValues[] = 'test';
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new Exception($type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
foreach ($possibleValues as $arg) {
|
||||||
|
$subVersion = \max(is_object($arg) ? $versionMap[\get_class($arg)] : 0, $versionMap[$class]);
|
||||||
|
|
||||||
|
$arguments = $baseArgs;
|
||||||
|
$arguments[$key] = $isArray ? [$arg] : $arg;
|
||||||
|
|
||||||
|
$code = format($prev = new $class(...$arguments));
|
||||||
|
$curVersion = checkSyntax($code, $subVersion);
|
||||||
|
if ($curVersion && $curVersion !== $subVersion) {
|
||||||
|
$result['main'][$curVersion][$class][$name][$arg] = true;
|
||||||
|
echo "Min $curVersion for $code\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
$code = format(new Isset_([$prev]));
|
||||||
|
$curVersion = checkSyntax($code, $subVersion);
|
||||||
|
if ($curVersion && $curVersion !== $subVersion) {
|
||||||
|
$result['isset'][$curVersion][$class][$name][\get_class($expr)] = true;
|
||||||
|
echo "Min $curVersion for $code\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
foreach ($instanceArgs as $class => $argumentsOld) {
|
||||||
|
foreach ($argumentsOld as $key => $argument) {
|
||||||
|
$name = $instanceArgNames[$class][$key];
|
||||||
|
if ($argument instanceof Expr || (\is_array($argument) && \count($argument))) {
|
||||||
|
foreach ($exprInstances as $expr) {
|
||||||
|
$subVersion = \max($versionMap[\get_class($expr)], $versionMap[$class]);
|
||||||
|
$arguments = $argumentsOld;
|
||||||
|
$arguments[$key] = \is_array($argument) ? [$expr] : $expr;
|
||||||
|
|
||||||
|
$code = format($prev = new $class(...$arguments));
|
||||||
|
$curVersion = checkSyntax($code, $subVersion);
|
||||||
|
if ($curVersion && $curVersion !== $subVersion) {
|
||||||
|
$result['main'][$curVersion][$class][$name][\get_class($expr)] = true;
|
||||||
|
echo "Min $curVersion for $code\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
$code = format(new Isset_([$prev]));
|
||||||
|
$curVersion = checkSyntax($code, $subVersion);
|
||||||
|
if ($curVersion && $curVersion !== $subVersion) {
|
||||||
|
$result['isset'][$curVersion][$class][$name][\get_class($expr)] = true;
|
||||||
|
echo "Min $curVersion for $code\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$allClassesKeys = \array_fill_keys($allClasses, true);
|
||||||
|
|
||||||
|
\file_put_contents('result.php', '<?php $result = '.\var_export($result, true).";");
|
Loading…
Reference in New Issue
Block a user