rector/vendor/rector/rector-laravel/src/NodeFactory/ModelFactoryNodeFactory.php
Tomas Votruba 785f5e3b06 Updated Rector to commit 74f6b181e82f191c1e471d446a029a06dff16619
74f6b181e8 [DX] Remove upgrade RectorConfig set, as last 2 version use only PHP (#2852)
2022-08-29 21:45:23 +00:00

164 lines
5.9 KiB
PHP

<?php
declare (strict_types=1);
namespace Rector\Laravel\NodeFactory;
use PhpParser\Builder\Method;
use PhpParser\Builder\Property;
use PhpParser\Node;
use PhpParser\Node\Arg;
use PhpParser\Node\Expr;
use PhpParser\Node\Expr\Closure;
use PhpParser\Node\Expr\MethodCall;
use PhpParser\Node\Expr\PropertyFetch;
use PhpParser\Node\Expr\Variable;
use PhpParser\Node\Name\FullyQualified;
use PhpParser\Node\Param;
use PhpParser\Node\Stmt\Class_;
use PhpParser\Node\Stmt\ClassMethod;
use PhpParser\Node\Stmt\Return_;
use PhpParser\NodeTraverser;
use PhpParser\NodeVisitor\NameResolver;
use Rector\Core\PhpParser\Node\NodeFactory;
use Rector\Core\PhpParser\Node\Value\ValueResolver;
use Rector\NodeNameResolver\NodeNameResolver;
use Rector\PhpDocParser\NodeTraverser\SimpleCallableNodeTraverser;
final class ModelFactoryNodeFactory
{
/**
* @var string
*/
private const THIS = 'this';
/**
* @readonly
* @var \Rector\NodeNameResolver\NodeNameResolver
*/
private $nodeNameResolver;
/**
* @readonly
* @var \Rector\Core\PhpParser\Node\NodeFactory
*/
private $nodeFactory;
/**
* @readonly
* @var \Rector\Core\PhpParser\Node\Value\ValueResolver
*/
private $valueResolver;
/**
* @readonly
* @var \Rector\PhpDocParser\NodeTraverser\SimpleCallableNodeTraverser
*/
private $simpleCallableNodeTraverser;
public function __construct(NodeNameResolver $nodeNameResolver, NodeFactory $nodeFactory, ValueResolver $valueResolver, SimpleCallableNodeTraverser $simpleCallableNodeTraverser)
{
$this->nodeNameResolver = $nodeNameResolver;
$this->nodeFactory = $nodeFactory;
$this->valueResolver = $valueResolver;
$this->simpleCallableNodeTraverser = $simpleCallableNodeTraverser;
}
public function createEmptyFactory(string $name, Expr $expr) : Class_
{
$class = new Class_($name . 'Factory');
$class->extends = new FullyQualified('Illuminate\\Database\\Eloquent\\Factories\\Factory');
$propertyBuilder = new Property('model');
$propertyBuilder->makeProtected();
$propertyBuilder->setDefault($expr);
$property = $propertyBuilder->getNode();
$class->stmts[] = $property;
// decorate with namespaced names
$nameResolver = new NameResolver(null, ['replaceNodes' => \false, 'preserveOriginalNames' => \true]);
$nodeTraverser = new NodeTraverser();
$nodeTraverser->addVisitor($nameResolver);
$nodeTraverser->traverse([$class]);
return $class;
}
public function createDefinition(Closure $closure) : ClassMethod
{
if (isset($closure->params[0])) {
$this->fakerVariableToPropertyFetch($closure->stmts, $closure->params[0]);
}
return $this->createPublicMethod('definition', $closure->stmts);
}
public function createStateMethod(MethodCall $methodCall) : ?ClassMethod
{
if (!isset($methodCall->args[2])) {
return null;
}
if (!$methodCall->args[2] instanceof Arg) {
return null;
}
$thirdArgValue = $methodCall->args[2]->value;
// the third argument may be closure or array
if ($thirdArgValue instanceof Closure && isset($thirdArgValue->params[0])) {
$this->fakerVariableToPropertyFetch($thirdArgValue->stmts, $thirdArgValue->params[0]);
unset($thirdArgValue->params[0]);
}
$expr = $this->nodeFactory->createMethodCall(self::THIS, 'state', [$methodCall->args[2]]);
$return = new Return_($expr);
if (!isset($methodCall->args[1])) {
return null;
}
if (!$methodCall->args[1] instanceof Arg) {
return null;
}
$methodName = $this->valueResolver->getValue($methodCall->args[1]->value);
if (!\is_string($methodName)) {
return null;
}
return $this->createPublicMethod($methodName, [$return]);
}
public function createEmptyConfigure() : ClassMethod
{
$return = new Return_(new Variable(self::THIS));
return $this->createPublicMethod('configure', [$return]);
}
public function appendConfigure(ClassMethod $classMethod, string $name, Closure $closure) : void
{
$this->simpleCallableNodeTraverser->traverseNodesWithCallable((array) $classMethod->stmts, function (Node $node) use($closure, $name) : ?Return_ {
if (!$node instanceof Return_) {
return null;
}
if ($node->expr === null) {
return null;
}
if (isset($closure->params[1])) {
$this->fakerVariableToPropertyFetch($closure->stmts, $closure->params[1]);
// remove argument $faker
unset($closure->params[1]);
}
$node->expr = $this->nodeFactory->createMethodCall($node->expr, $name, [$closure]);
return $node;
});
}
/**
* @param Node\Stmt[] $stmts
*/
private function fakerVariableToPropertyFetch(array $stmts, Param $param) : void
{
$this->simpleCallableNodeTraverser->traverseNodesWithCallable($stmts, function (Node $node) use($param) : ?PropertyFetch {
if (!$node instanceof Variable) {
return null;
}
$name = $this->nodeNameResolver->getName($param->var);
if ($name === null) {
return null;
}
if (!$this->nodeNameResolver->isName($node, $name)) {
return null;
}
// use $this->faker instead of $faker
return $this->nodeFactory->createPropertyFetch(self::THIS, 'faker');
});
}
/**
* @param Node\Stmt[] $stmts
*/
private function createPublicMethod(string $name, array $stmts) : ClassMethod
{
$method = new Method($name);
$method->makePublic();
$method->addStmts($stmts);
return $method->getNode();
}
}