Skip to content

Commit bd76cbc

Browse files
committed
refactor: use new BuilderHelper::getBuilderTypeForModels method
1 parent adc5db2 commit bd76cbc

5 files changed

+48
-61
lines changed

src/Methods/ModelForwardsCallsExtension.php

+12-10
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,8 @@
1717
use PHPStan\Reflection\ParametersAcceptor;
1818
use PHPStan\Reflection\ParametersAcceptorSelector;
1919
use PHPStan\Reflection\Php\DummyParameter;
20-
use PHPStan\Reflection\ReflectionProvider;
2120
use PHPStan\ShouldNotHappenException;
2221
use PHPStan\TrinaryLogic;
23-
use PHPStan\Type\Generic\GenericObjectType;
2422
use PHPStan\Type\ObjectType;
2523
use PHPStan\Type\StaticType;
2624
use PHPStan\Type\Type;
@@ -36,7 +34,7 @@ final class ModelForwardsCallsExtension implements MethodsClassReflectionExtensi
3634
/** @var array<string, MethodReflection> */
3735
private array $cache = [];
3836

39-
public function __construct(private BuilderHelper $builderHelper, private ReflectionProvider $reflectionProvider, private EloquentBuilderForwardsCallsExtension $eloquentBuilderForwardsCallsExtension)
37+
public function __construct(private BuilderHelper $builderHelper, private EloquentBuilderForwardsCallsExtension $eloquentBuilderForwardsCallsExtension)
4038
{
4139
}
4240

@@ -169,17 +167,21 @@ public function hasSideEffects(): TrinaryLogic
169167
};
170168
}
171169

172-
$builderReflection = $this->reflectionProvider->getClass($builderName)->withTypes([new ObjectType($classReflection->getName())]);
173-
$genericBuilderAndModelType = new GenericObjectType($builderName, [new ObjectType($classReflection->getName())]);
170+
$builderType = $this->builderHelper->getBuilderTypeForModels($classReflection->getName());
171+
$builderReflection = $builderType->getClassReflection();
172+
173+
if ($builderReflection === null) {
174+
return null;
175+
}
174176

175177
if ($builderReflection->hasNativeMethod($methodName)) {
176178
$reflection = $builderReflection->getNativeMethod($methodName);
177179

178-
$parametersAcceptor = ParametersAcceptorSelector::selectSingle($this->transformStaticParameters($reflection, $genericBuilderAndModelType));
180+
$parametersAcceptor = ParametersAcceptorSelector::selectSingle($this->transformStaticParameters($reflection, $builderType));
179181

180-
$returnType = TypeTraverser::map($parametersAcceptor->getReturnType(), static function (Type $type, callable $traverse) use ($genericBuilderAndModelType) {
182+
$returnType = TypeTraverser::map($parametersAcceptor->getReturnType(), static function (Type $type, callable $traverse) use ($builderType) {
181183
if ($type instanceof TypeWithClassName && $type->getClassName() === Builder::class) {
182-
return $genericBuilderAndModelType;
184+
return $builderType;
183185
}
184186

185187
return $traverse($type);
@@ -202,7 +204,7 @@ public function hasSideEffects(): TrinaryLogic
202204
}
203205

204206
/** @return ParametersAcceptor[] */
205-
private function transformStaticParameters(MethodReflection $method, GenericObjectType $builder): array
207+
private function transformStaticParameters(MethodReflection $method, ObjectType $builder): array
206208
{
207209
return array_map(function (ParametersAcceptor $acceptor) use ($builder): ParametersAcceptor {
208210
return new FunctionVariant($acceptor->getTemplateTypeMap(), $acceptor->getResolvedTemplateTypeMap(), array_map(function (
@@ -220,7 +222,7 @@ private function transformStaticParameters(MethodReflection $method, GenericObje
220222
}, $method->getVariants());
221223
}
222224

223-
private function transformStaticType(Type $type, GenericObjectType $builder): Type
225+
private function transformStaticType(Type $type, ObjectType $builder): Type
224226
{
225227
return TypeTraverser::map($type, static function (Type $type, callable $traverse) use ($builder): Type {
226228
if ($type instanceof StaticType) {

src/Methods/RelationForwardsCallsExtension.php

+5-2
Original file line numberDiff line numberDiff line change
@@ -76,9 +76,12 @@ private function findMethod(ClassReflection $classReflection, string $methodName
7676
$modelReflection = $this->reflectionProvider->getClass(Model::class);
7777
}
7878

79-
$builderName = $this->builderHelper->determineBuilderName($modelReflection->getName());
79+
$builderReflection = $this->builderHelper->getBuilderTypeForModels($modelReflection->getName())
80+
->getClassReflection();
8081

81-
$builderReflection = $this->reflectionProvider->getClass($builderName)->withTypes([$relatedModel]);
82+
if ($builderReflection === null) {
83+
return null;
84+
}
8285

8386
if ($builderReflection->hasNativeMethod($methodName)) {
8487
$reflection = $builderReflection->getNativeMethod($methodName);

src/ReturnTypes/ModelDynamicStaticMethodReturnTypeExtension.php

+19-27
Original file line numberDiff line numberDiff line change
@@ -16,16 +16,16 @@
1616
use PhpParser\Node\Name;
1717
use PHPStan\Analyser\Scope;
1818
use PHPStan\Reflection\MethodReflection;
19-
use PHPStan\Reflection\MissingMethodFromReflectionException;
2019
use PHPStan\Reflection\ParametersAcceptorSelector;
2120
use PHPStan\Reflection\ReflectionProvider;
2221
use PHPStan\Type\DynamicStaticMethodReturnTypeExtension;
23-
use PHPStan\Type\Generic\GenericObjectType;
24-
use PHPStan\Type\ObjectType;
22+
use PHPStan\Type\StaticType;
23+
use PHPStan\Type\ThisType;
2524
use PHPStan\Type\Type;
2625
use PHPStan\Type\TypeCombinator;
2726

2827
use function array_intersect;
28+
use function collect;
2929
use function count;
3030
use function in_array;
3131

@@ -84,34 +84,26 @@ public function getTypeFromStaticMethodCall(
8484

8585
if (count(array_intersect([EloquentBuilder::class], $returnType->getReferencedClasses())) > 0) {
8686
if ($methodCall->class instanceof Name) {
87-
$returnType = new GenericObjectType(
88-
$this->builderHelper->determineBuilderName($scope->resolveName($methodCall->class)),
89-
[new ObjectType($scope->resolveName($methodCall->class))],
90-
);
91-
} elseif ($methodCall->class instanceof Expr) {
92-
$type = $scope->getType($methodCall->class);
87+
$type = $scope->resolveTypeByName($methodCall->class);
9388

94-
$classNames = $type->getObjectClassNames();
95-
96-
$types = [];
89+
if ($type instanceof ThisType) {
90+
$type = new StaticType($type->getClassReflection());
91+
}
9792

98-
foreach ($classNames as $className) {
99-
if (! $this->reflectionProvider->hasClass($className)) {
100-
continue;
101-
}
93+
$returnType = $this->builderHelper->getBuilderTypeForModels($type);
94+
} elseif ($methodCall->class instanceof Expr) {
95+
$type = $scope->getType($methodCall->class);
10296

103-
try {
104-
$types[] = new GenericObjectType(
105-
$this->builderHelper->determineBuilderName($className),
106-
[new ObjectType($className)],
107-
);
108-
} catch (MissingMethodFromReflectionException) {
109-
}
110-
}
97+
$returnType = collect($type->getObjectClassReflections())
98+
->filter(static fn ($r) => $r->is(Model::class))
99+
->map(static fn ($r) => $r->getName())
100+
->pipe(function ($models) use ($returnType) {
101+
if ($models->isEmpty()) {
102+
return $returnType;
103+
}
111104

112-
if ($types !== []) {
113-
$returnType = TypeCombinator::union(...$types);
114-
}
105+
return $this->builderHelper->getBuilderTypeForModels($models->all());
106+
});
115107
}
116108
}
117109

src/ReturnTypes/NewModelQueryDynamicMethodReturnTypeExtension.php

+11-21
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,9 @@
1010
use PHPStan\Analyser\Scope;
1111
use PHPStan\Reflection\MethodReflection;
1212
use PHPStan\Type\DynamicMethodReturnTypeExtension;
13-
use PHPStan\Type\Generic\GenericObjectType;
14-
use PHPStan\Type\ObjectType;
1513
use PHPStan\Type\Type;
16-
use PHPStan\Type\TypeCombinator;
1714

15+
use function collect;
1816
use function in_array;
1917

2018
class NewModelQueryDynamicMethodReturnTypeExtension implements DynamicMethodReturnTypeExtension
@@ -45,30 +43,22 @@ public function getTypeFromMethodCall(
4543
MethodCall $methodCall,
4644
Scope $scope,
4745
): Type|null {
48-
$calledOnType = $scope->getType($methodCall->var);
49-
46+
$calledOnType = $scope->getType($methodCall->var);
5047
$classReflections = $calledOnType->getObjectClassReflections();
5148

5249
if ($classReflections === []) {
5350
return null;
5451
}
5552

56-
$types = [];
57-
58-
foreach ($classReflections as $classReflection) {
59-
if (! $classReflection->isSubclassOf(Model::class)) {
60-
continue;
61-
}
62-
63-
$builderName = $this->builderHelper->determineBuilderName($classReflection->getName());
64-
65-
$types[] = new GenericObjectType($builderName, [new ObjectType($classReflection->getName())]);
66-
}
67-
68-
if ($types === []) {
69-
return null;
70-
}
53+
return collect($classReflections)
54+
->filter(static fn ($r) => $r->is(Model::class))
55+
->map(static fn ($r) => $r->getName())
56+
->pipe(function ($models) {
57+
if ($models->isEmpty()) {
58+
return null;
59+
}
7160

72-
return TypeCombinator::union(...$types);
61+
return $this->builderHelper->getBuilderTypeForModels($models->all());
62+
});
7363
}
7464
}

tests/Type/data/eloquent-builder.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -344,7 +344,7 @@ class TestModel extends Model
344344
public function test(): void
345345
{
346346
assertType('Illuminate\Database\Eloquent\Collection<int, EloquentBuilder\TestModel>', $this->where('email', 1)->get());
347-
assertType('Illuminate\Database\Eloquent\Builder<EloquentBuilder\TestModel>', static::query()->where('email', 'bar'));
347+
assertType('Illuminate\Database\Eloquent\Builder<static(EloquentBuilder\TestModel)>', static::query()->where('email', 'bar'));
348348
assertType('Illuminate\Database\Eloquent\Builder<EloquentBuilder\TestModel>', $this->where('email', 'bar'));
349349
}
350350
}

0 commit comments

Comments
 (0)