Create isset expression fixer
This commit is contained in:
parent
fb9dac3d95
commit
e33da7edc8
|
@ -3,6 +3,7 @@
|
|||
namespace Phabel;
|
||||
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Expr\Isset_;
|
||||
use SplStack;
|
||||
|
||||
/**
|
||||
|
@ -17,10 +18,42 @@ class Context
|
|||
* @var SplStack<Node>
|
||||
*/
|
||||
public SplStack $parents;
|
||||
/**
|
||||
* Whether we're inside an isset expression
|
||||
*/
|
||||
public bool $insideIsset = false;
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->parents = new SplStack;
|
||||
}
|
||||
/**
|
||||
* Push node
|
||||
*
|
||||
* @param Node $node Node
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function push(Node $node): void
|
||||
{
|
||||
$this->parents->push($node);
|
||||
if ($node instanceof Isset_) {
|
||||
$this->insideIsset = true;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Pop node
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function pop(): void
|
||||
{
|
||||
if ($this->parents->pop() instanceof Isset_) {
|
||||
$this->insideIsset = false;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Insert nodes before node.
|
||||
*
|
||||
|
|
|
@ -0,0 +1,181 @@
|
|||
<?php
|
||||
|
||||
namespace Phabel\Plugin;
|
||||
|
||||
use Phabel\Plugin;
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Expr;
|
||||
use PhpParser\Node\Expr\ArrayDimFetch;
|
||||
use PhpParser\Node\Expr\BinaryOp\Concat;
|
||||
use PhpParser\Node\Expr\ClassConstFetch;
|
||||
use PhpParser\Node\Expr\Isset_;
|
||||
use PhpParser\Node\Expr\PropertyFetch;
|
||||
use PhpParser\Node\Expr\StaticPropertyFetch;
|
||||
use PhpParser\Node\Expr\Ternary;
|
||||
use PhpParser\Node\Scalar\LNumber;
|
||||
use PhpParser\Node\Scalar\String_;
|
||||
use PhpParser\Node\VarLikeIdentifier;
|
||||
use ReflectionClass;
|
||||
use ReflectionClassConstant;
|
||||
use ReflectionException;
|
||||
use ReflectionProperty;
|
||||
|
||||
class IssetExpressionFixer extends Plugin
|
||||
{
|
||||
/**
|
||||
* Recursively extract bottom ArrayDimFetch
|
||||
*
|
||||
* @param Node $var
|
||||
* @return Node
|
||||
*/
|
||||
private static function &extractWorkVar(Node &$var): Node
|
||||
{
|
||||
if ($var instanceof ArrayDimFetch && $var->var instanceof ArrayDimFetch) {
|
||||
return self::extractWorkVar($var->var);
|
||||
}
|
||||
return $var;
|
||||
}
|
||||
/**
|
||||
* Wrap boolean isset check.
|
||||
*
|
||||
* @param Expr $node Node
|
||||
*
|
||||
* @return ArrayDimFetch
|
||||
*/
|
||||
private static function wrapBoolean(Expr $node): ArrayDimFetch
|
||||
{
|
||||
return new ArrayDimFetch(
|
||||
self::callPoly(
|
||||
'returnMe',
|
||||
new Ternary(
|
||||
$node,
|
||||
self::toLiteral([0]),
|
||||
self::toLiteral([]),
|
||||
)
|
||||
),
|
||||
new LNumber(0)
|
||||
);
|
||||
}
|
||||
|
||||
public function enter(Isset_ $isset): void
|
||||
{
|
||||
foreach ($isset->vars as $key => &$var) {
|
||||
/** @var array<string, array<class-string<Expr>, true>> */
|
||||
$subNodes = $this->getConfig(\get_class($var), false)
|
||||
if (!$subNodes) {
|
||||
continue;
|
||||
}
|
||||
$workVar = $this->extractWorkVar($var);
|
||||
$needsFixing = false;
|
||||
foreach ($subNodes as $key => $types) {
|
||||
if (isset($types[\get_class($workVar->{$key})])) {
|
||||
$needsFixing = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!$needsFixing) {
|
||||
continue;
|
||||
}
|
||||
switch (\get_class($workVar)) {
|
||||
case ArrayDimFetch::class:
|
||||
case PropertyFetch::class:
|
||||
$workVar->var = self::callPoly('returnMe', $workVar->var);
|
||||
break;
|
||||
case StaticPropertyFetch::class:
|
||||
$workVar = $this->wrapBoolean(self::callPoly(
|
||||
'staticExists',
|
||||
$workVar->class,
|
||||
$workVar->name instanceof VarLikeIdentifier ? new String_($workVar->name->name) : $workVar->name,
|
||||
new LNumber(1)
|
||||
));
|
||||
break;
|
||||
case ClassConstFetch::class:
|
||||
$workVar = $this->wrapBoolean(self::callPoly(
|
||||
'staticExists',
|
||||
$workVar->class,
|
||||
new String_($workVar->name->name),
|
||||
new LNumber(0)
|
||||
));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the data provided.
|
||||
*
|
||||
* @param mixed $data Data
|
||||
*
|
||||
* @return mixed
|
||||
*
|
||||
* @template T
|
||||
*
|
||||
* @psalm-param T $data data
|
||||
*
|
||||
* @psalm-return T
|
||||
*/
|
||||
public static function returnMe($data)
|
||||
{
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get name of class.
|
||||
*
|
||||
* @param class-string|object $class Class
|
||||
*
|
||||
* @return class-string
|
||||
*/
|
||||
public static function getClass($class): string
|
||||
{
|
||||
return \is_string($class) ? $class : \get_class($class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if static property is set
|
||||
*
|
||||
* @param class-string|object $class Class
|
||||
* @param string $property Property name
|
||||
* @param boolean $propertyOrConstant Whether to fetch the property or the constant
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public static function staticExists($class, string $property, bool $propertyOrConstant): bool
|
||||
{
|
||||
$reflectionClass = new ReflectionClass($class);
|
||||
$class = new self::getClass($class);
|
||||
if ($propertyOrConstant) {
|
||||
try {
|
||||
$reflection = $reflectionClass->getProperty($property);
|
||||
} catch (ReflectionException $e) {
|
||||
return false;
|
||||
}
|
||||
} else if (PHP_VERSION_ID >= 70100) {
|
||||
try {
|
||||
$reflection = new ReflectionClassConstant($class, $property);
|
||||
} catch (ReflectionException $e) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return isset($reflectionClass->getConstants()[$property];
|
||||
}
|
||||
|
||||
$classCaller = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2)[1]['class'] ?? '';
|
||||
$allowProtected = false;
|
||||
$allowPrivate = false;
|
||||
if ($classCaller) {
|
||||
if ($class === $classCaller) {
|
||||
$allowProtected = $allowPrivate = true;
|
||||
} else if ($reflectionClass->isSubclassOf($classCaller) || (new ReflectionClass($classCaller))->isSubclassOf($class)) {
|
||||
$allowProtected = true;
|
||||
}
|
||||
}
|
||||
if ($reflection->isPrivate()) {
|
||||
return $allowPrivate ? $reflection->getValue() !== null : false;
|
||||
}
|
||||
if ($reflection->isProtected()) {
|
||||
return $allowProtected ? $reflection->getValue() !== null : false;
|
||||
}
|
||||
return $reflection->getValue() !== null;
|
||||
}
|
||||
}
|
|
@ -1,241 +0,0 @@
|
|||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Phabel\Plugin;
|
||||
|
||||
use Phabel\Plugin;
|
||||
use PhpParser\ErrorHandler;
|
||||
use PhpParser\NameContext;
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Expr;
|
||||
use PhpParser\Node\Name;
|
||||
use PhpParser\Node\Name\FullyQualified;
|
||||
use PhpParser\Node\Stmt;
|
||||
|
||||
class NameResolver extends Plugin
|
||||
{
|
||||
/** @var NameContext Naming context */
|
||||
protected $nameContext;
|
||||
|
||||
/**
|
||||
* Constructs a name resolution visitor.
|
||||
*
|
||||
* Options:
|
||||
* * preserveOriginalNames (default false): An "originalName" attribute will be added to
|
||||
* all name nodes that underwent resolution.
|
||||
* * replaceNodes (default true): Resolved names are replaced in-place. Otherwise, a
|
||||
* resolvedName attribute is added. (Names that cannot be statically resolved receive a
|
||||
* namespacedName attribute, as usual.)
|
||||
*
|
||||
* @param ErrorHandler|null $errorHandler Error handler
|
||||
* @param array $options Options
|
||||
*/
|
||||
public function __construct(ErrorHandler $errorHandler = null, array $options = [])
|
||||
{
|
||||
$this->nameContext = new NameContext($errorHandler ?? new ErrorHandler\Throwing);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get name resolution context.
|
||||
*
|
||||
* @return NameContext
|
||||
*/
|
||||
public function getNameContext(): NameContext
|
||||
{
|
||||
return $this->nameContext;
|
||||
}
|
||||
|
||||
public function beforeTraverse(array $nodes)
|
||||
{
|
||||
$this->nameContext->startNamespace();
|
||||
return null;
|
||||
}
|
||||
|
||||
public function enterNode(Node $node)
|
||||
{
|
||||
if ($node instanceof Stmt\Namespace_) {
|
||||
$this->nameContext->startNamespace($node->name);
|
||||
} elseif ($node instanceof Stmt\Use_) {
|
||||
foreach ($node->uses as $use) {
|
||||
$this->addAlias($use, $node->type, null);
|
||||
}
|
||||
} elseif ($node instanceof Stmt\GroupUse) {
|
||||
foreach ($node->uses as $use) {
|
||||
$this->addAlias($use, $node->type, $node->prefix);
|
||||
}
|
||||
} elseif ($node instanceof Stmt\Class_) {
|
||||
if (null !== $node->extends) {
|
||||
$node->extends = $this->resolveClassName($node->extends);
|
||||
}
|
||||
|
||||
foreach ($node->implements as &$interface) {
|
||||
$interface = $this->resolveClassName($interface);
|
||||
}
|
||||
|
||||
if (null !== $node->name) {
|
||||
$this->addNamespacedName($node);
|
||||
}
|
||||
} elseif ($node instanceof Stmt\Interface_) {
|
||||
foreach ($node->extends as &$interface) {
|
||||
$interface = $this->resolveClassName($interface);
|
||||
}
|
||||
|
||||
$this->addNamespacedName($node);
|
||||
} elseif ($node instanceof Stmt\Trait_) {
|
||||
$this->addNamespacedName($node);
|
||||
} elseif ($node instanceof Stmt\Function_) {
|
||||
$this->addNamespacedName($node);
|
||||
$this->resolveSignature($node);
|
||||
} elseif ($node instanceof Stmt\ClassMethod
|
||||
|| $node instanceof Expr\Closure
|
||||
|| $node instanceof Expr\ArrowFunction
|
||||
) {
|
||||
$this->resolveSignature($node);
|
||||
} elseif ($node instanceof Stmt\Property) {
|
||||
if (null !== $node->type) {
|
||||
$node->type = $this->resolveType($node->type);
|
||||
}
|
||||
} elseif ($node instanceof Stmt\Const_) {
|
||||
foreach ($node->consts as $const) {
|
||||
$this->addNamespacedName($const);
|
||||
}
|
||||
} elseif ($node instanceof Expr\StaticCall
|
||||
|| $node instanceof Expr\StaticPropertyFetch
|
||||
|| $node instanceof Expr\ClassConstFetch
|
||||
|| $node instanceof Expr\New_
|
||||
|| $node instanceof Expr\Instanceof_
|
||||
) {
|
||||
if ($node->class instanceof Name) {
|
||||
$node->class = $this->resolveClassName($node->class);
|
||||
}
|
||||
} elseif ($node instanceof Stmt\Catch_) {
|
||||
foreach ($node->types as &$type) {
|
||||
$type = $this->resolveClassName($type);
|
||||
}
|
||||
} elseif ($node instanceof Expr\FuncCall) {
|
||||
if ($node->name instanceof Name) {
|
||||
$node->name = $this->resolveName($node->name, Stmt\Use_::TYPE_FUNCTION);
|
||||
}
|
||||
} elseif ($node instanceof Expr\ConstFetch) {
|
||||
$node->name = $this->resolveName($node->name, Stmt\Use_::TYPE_CONSTANT);
|
||||
} elseif ($node instanceof Stmt\TraitUse) {
|
||||
foreach ($node->traits as &$trait) {
|
||||
$trait = $this->resolveClassName($trait);
|
||||
}
|
||||
|
||||
foreach ($node->adaptations as $adaptation) {
|
||||
if (null !== $adaptation->trait) {
|
||||
$adaptation->trait = $this->resolveClassName($adaptation->trait);
|
||||
}
|
||||
|
||||
if ($adaptation instanceof Stmt\TraitUseAdaptation\Precedence) {
|
||||
foreach ($adaptation->insteadof as &$insteadof) {
|
||||
$insteadof = $this->resolveClassName($insteadof);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private function addAlias(Stmt\UseUse $use, $type, Name $prefix = null)
|
||||
{
|
||||
// Add prefix for group uses
|
||||
$name = $prefix ? Name::concat($prefix, $use->name) : $use->name;
|
||||
// Type is determined either by individual element or whole use declaration
|
||||
$type |= $use->type;
|
||||
|
||||
$this->nameContext->addAlias(
|
||||
$name,
|
||||
(string) $use->getAlias(),
|
||||
$type,
|
||||
$use->getAttributes()
|
||||
);
|
||||
}
|
||||
|
||||
/** @param Stmt\Function_|Stmt\ClassMethod|Expr\Closure $node */
|
||||
private function resolveSignature($node)
|
||||
{
|
||||
foreach ($node->params as $param) {
|
||||
$param->type = $this->resolveType($param->type);
|
||||
}
|
||||
$node->returnType = $this->resolveType($node->returnType);
|
||||
}
|
||||
|
||||
private function resolveType($node)
|
||||
{
|
||||
if ($node instanceof Name) {
|
||||
return $this->resolveClassName($node);
|
||||
}
|
||||
if ($node instanceof Node\NullableType) {
|
||||
$node->type = $this->resolveType($node->type);
|
||||
return $node;
|
||||
}
|
||||
if ($node instanceof Node\UnionType) {
|
||||
foreach ($node->types as &$type) {
|
||||
$type = $this->resolveType($type);
|
||||
}
|
||||
return $node;
|
||||
}
|
||||
return $node;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve name, according to name resolver options.
|
||||
*
|
||||
* @param Name $name Function or constant name to resolve
|
||||
* @param int $type One of Stmt\Use_::TYPE_*
|
||||
*
|
||||
* @return Name Resolved name, or original name with attribute
|
||||
*/
|
||||
protected function resolveName(Name $name, int $type): Name
|
||||
{
|
||||
if (!$this->replaceNodes) {
|
||||
$resolvedName = $this->nameContext->getResolvedName($name, $type);
|
||||
if (null !== $resolvedName) {
|
||||
$name->setAttribute('resolvedName', $resolvedName);
|
||||
} else {
|
||||
$name->setAttribute('namespacedName', FullyQualified::concat(
|
||||
$this->nameContext->getNamespace(),
|
||||
$name,
|
||||
$name->getAttributes()
|
||||
));
|
||||
}
|
||||
return $name;
|
||||
}
|
||||
|
||||
if ($this->preserveOriginalNames) {
|
||||
// Save the original name
|
||||
$originalName = $name;
|
||||
$name = clone $originalName;
|
||||
$name->setAttribute('originalName', $originalName);
|
||||
}
|
||||
|
||||
$resolvedName = $this->nameContext->getResolvedName($name, $type);
|
||||
if (null !== $resolvedName) {
|
||||
return $resolvedName;
|
||||
}
|
||||
|
||||
// unqualified names inside a namespace cannot be resolved at compile-time
|
||||
// add the namespaced version of the name as an attribute
|
||||
$name->setAttribute('namespacedName', FullyQualified::concat(
|
||||
$this->nameContext->getNamespace(),
|
||||
$name,
|
||||
$name->getAttributes()
|
||||
));
|
||||
return $name;
|
||||
}
|
||||
|
||||
protected function resolveClassName(Name $name)
|
||||
{
|
||||
return $this->resolveName($name, Stmt\Use_::TYPE_NORMAL);
|
||||
}
|
||||
|
||||
protected function addNamespacedName(Node $node)
|
||||
{
|
||||
$node->namespacedName = Name::concat(
|
||||
$this->nameContext->getNamespace(),
|
||||
(string) $node->name
|
||||
);
|
||||
}
|
||||
}
|
|
@ -125,7 +125,7 @@ class Traverser
|
|||
public function traverseAst(Node &$node, SplQueue $pluginQueue = null): void
|
||||
{
|
||||
$context = new Context;
|
||||
$context->parents->push($node);
|
||||
$context->push($node);
|
||||
foreach ($pluginQueue ?? $this->packageQueue ?? $this->queue as $queue) {
|
||||
$this->traverseNode($ast, $queue, $context);
|
||||
}
|
||||
|
@ -158,7 +158,7 @@ class Traverser
|
|||
}
|
||||
}
|
||||
}
|
||||
$context->parents->push($node);
|
||||
$context->push($node);
|
||||
foreach ($node->getSubNodeNames() as $name) {
|
||||
$node->setAttribute('currentNode', $name);
|
||||
|
||||
|
@ -177,7 +177,7 @@ class Traverser
|
|||
$this->traverseNode($subNode, $plugins, $context);
|
||||
}
|
||||
}
|
||||
$context->parents->pop();
|
||||
$context->pop();
|
||||
foreach ($plugins as $plugin) {
|
||||
foreach (PluginCache::leaveMethods(\get_class($plugin)) as $type => $methods) {
|
||||
if (!$node instanceof $type) {
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
<?php
|
||||
|
||||
// This file generates an array containing all possible expression nodes, generated using default parameters.
|
||||
// Then, for each expression that accepts another expression as subnode, it tries to use all the expressions generated in the previous step,
|
||||
// for each subnode, to test compatibility with various versions of the PHP lexer.
|
||||
|
||||
use HaydenPierce\ClassFinder\ClassFinder;
|
||||
use PhpParser\Builder\Class_;
|
||||
use PhpParser\Builder\Method;
|
||||
|
@ -30,7 +34,7 @@ function format(Node $code): string
|
|||
->addStmt($code)
|
||||
->getNode()
|
||||
)->getNode();
|
||||
$prettyPrinter = new PhpParser\PrettyPrinter\Standard;
|
||||
$prettyPrinter = new PhpParser\PrettyPrinter\Standard(['shortArraySyntax' => true]);
|
||||
return $prettyPrinter->prettyPrintFile([$code]);
|
||||
}
|
||||
function readUntilPrompt($resource): string
|
||||
|
@ -63,11 +67,10 @@ function checkSyntaxVersion(int $version, string $code): bool
|
|||
\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));
|
||||
}
|
||||
//var_dump($code, "Result for $version is: " .trim($result));
|
||||
$result = \trim($result);
|
||||
return \strlen($result) === 0;
|
||||
}
|
||||
|
@ -150,12 +153,12 @@ foreach ($expressions as $expr) {
|
|||
$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 Name::class:
|
||||
$arguments[] = new Name('self');
|
||||
break;
|
||||
case 'array':
|
||||
if (\in_array('Expr[]', $types[$param->getName()] ?? [])) {
|
||||
$argTypes[$key] = [true, [Expr::class]];
|
||||
|
@ -209,7 +212,7 @@ foreach ($instanceArgTypes as $class => $argTypes) {
|
|||
foreach ($types as $type) {
|
||||
switch ($type) {
|
||||
case Expr::class:
|
||||
$possibleValues = array_merge($possibleValues, $exprInstances);
|
||||
$possibleValues = \array_merge($possibleValues, $exprInstances);
|
||||
break;
|
||||
case Name::class:
|
||||
$possibleValues[] = new Name('self');
|
||||
|
@ -217,7 +220,10 @@ foreach ($instanceArgTypes as $class => $argTypes) {
|
|||
case Variable::class:
|
||||
$possibleValues[] = new Variable("test");
|
||||
break;
|
||||
case Identifier::class;
|
||||
case Identifier::class:
|
||||
$possibleValues[] = new Identifier('test');
|
||||
break;
|
||||
case VarLikeIdentifier::class:
|
||||
$possibleValues[] = new Identifier('test');
|
||||
break;
|
||||
case StmtClass_::class:
|
||||
|
@ -227,12 +233,15 @@ foreach ($instanceArgTypes as $class => $argTypes) {
|
|||
case 'string':
|
||||
$possibleValues[] = 'test';
|
||||
break;
|
||||
case 'null':
|
||||
$possibleValues[] = null;
|
||||
break;
|
||||
default:
|
||||
throw new Exception($type);
|
||||
}
|
||||
}
|
||||
foreach ($possibleValues as $arg) {
|
||||
$subVersion = \max(is_object($arg) ? $versionMap[\get_class($arg)] : 0, $versionMap[$class]);
|
||||
$subVersion = \max($versionMap[\get_debug_type($arg)] ?? 0, $versionMap[$class]);
|
||||
|
||||
$arguments = $baseArgs;
|
||||
$arguments[$key] = $isArray ? [$arg] : $arg;
|
||||
|
@ -240,46 +249,27 @@ foreach ($instanceArgTypes as $class => $argTypes) {
|
|||
$code = format($prev = new $class(...$arguments));
|
||||
$curVersion = checkSyntax($code, $subVersion);
|
||||
if ($curVersion && $curVersion !== $subVersion) {
|
||||
$result['main'][$curVersion][$class][$name][$arg] = true;
|
||||
$result['main'][$curVersion][$class][$name][\get_debug_type($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;
|
||||
$result['isset'][$curVersion][$class][$name][\get_debug_type($arg)] = 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";
|
||||
}
|
||||
}
|
||||
}
|
||||
$keys = [];
|
||||
foreach ($result['isset'] as $version) {
|
||||
$keys = array_merge_recursive($keys, $version);
|
||||
}
|
||||
foreach ($keys as &$values) {
|
||||
$values = array_keys($values);
|
||||
}
|
||||
|
||||
$allClassesKeys = \array_fill_keys($allClasses, true);
|
||||
var_dump($keys);
|
||||
|
||||
\file_put_contents('result.php', '<?php $result = '.\var_export($result, true).";");
|
||||
|
|
Loading…
Reference in New Issue