Skip to content

Commit 0dbb803

Browse files
committed
+phpstan generics
1 parent 6cefaa9 commit 0dbb803

File tree

7 files changed

+107
-1
lines changed

7 files changed

+107
-1
lines changed

phpstan-extension.neon

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
services:
2+
-
3+
class: Kris\LaravelFormBuilder\PhpStan\FormGetFieldExtension
4+
tags:
5+
- phpstan.broker.dynamicMethodReturnTypeExtension

src/Kris/LaravelFormBuilder/Fields/ChildFormType.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,17 @@
44

55
use Kris\LaravelFormBuilder\Form;
66

7+
/**
8+
* @template TFormType of Form
9+
*
10+
* @extends ParentType<FormField>
11+
*/
712
class ChildFormType extends ParentType
813
{
914

1015
/**
1116
* @var Form
17+
* @phpstan-var TFormType
1218
*/
1319
protected $form;
1420

@@ -22,6 +28,7 @@ protected function getTemplate()
2228

2329
/**
2430
* @return Form
31+
* @phpstan-return TFormType
2532
*/
2633
public function getForm()
2734
{
@@ -96,6 +103,7 @@ protected function createChildren()
96103

97104
/**
98105
* @return Form
106+
* @phpstan-return TFormType
99107
* @throws \Exception
100108
*/
101109
protected function getClassFromOptions()

src/Kris/LaravelFormBuilder/Fields/ChoiceType.php

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@
44

55
use Illuminate\Support\Arr;
66

7+
/**
8+
* @extends ParentType<FormField>
9+
*/
710
class ChoiceType extends ParentType
811
{
912
/**
@@ -76,7 +79,7 @@ protected function createChildren()
7679
if (($data_override = $this->getOption('data_override')) && $data_override instanceof \Closure) {
7780
$this->options['choices'] = $data_override($this->options['choices'], $this);
7881
}
79-
82+
8083
$this->children = [];
8184
$this->determineChoiceField();
8285

src/Kris/LaravelFormBuilder/Fields/CollectionType.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,18 @@
55
use Illuminate\Database\Eloquent\Model;
66
use Illuminate\Support\Collection;
77

8+
/**
9+
* @template TType of FormField
10+
*
11+
* @extends ParentType<TType>
12+
*/
813
class CollectionType extends ParentType
914
{
1015
/**
1116
* Contains template for a collection element.
1217
*
1318
* @var FormField
19+
* @phpstan-var TType
1420
*/
1521
protected $proto;
1622

@@ -49,6 +55,7 @@ protected function getDefaults()
4955
* Get the prototype object.
5056
*
5157
* @return FormField
58+
* @phpstan-return TType
5259
* @throws \Exception
5360
*/
5461
public function prototype()

src/Kris/LaravelFormBuilder/Fields/ParentType.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,15 @@
55
use Illuminate\Support\Arr;
66
use Kris\LaravelFormBuilder\Form;
77

8+
/**
9+
* @template TChildType of FormField
10+
*/
811
abstract class ParentType extends FormField
912
{
1013

1114
/**
1215
* @var FormField[]
16+
* @phpstan-var TChildType[]
1317
*/
1418
protected $children;
1519

@@ -64,6 +68,7 @@ public function render(array $options = [], $showLabel = true, $showField = true
6468
* Get all children of the choice field.
6569
*
6670
* @return mixed
71+
* @phpstan-return TChildType[]
6772
*/
6873
public function getChildren()
6974
{
@@ -74,6 +79,7 @@ public function getChildren()
7479
* Get a child of the choice field.
7580
*
7681
* @return mixed
82+
* @phpstan-return ?TChildType
7783
*/
7884
public function getChild($key)
7985
{
@@ -145,6 +151,7 @@ public function isRendered()
145151
*
146152
* @param string $name
147153
* @return FormField
154+
* @phpstan-return TChildType
148155
*/
149156
public function __get($name)
150157
{

src/Kris/LaravelFormBuilder/Fields/RepeatedType.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@
44

55
use Illuminate\Support\Arr;
66

7+
/**
8+
* @extends ParentType<FormField>
9+
*/
710
class RepeatedType extends ParentType
811
{
912

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
<?php
2+
3+
namespace Kris\LaravelFormBuilder\PhpStan;
4+
5+
use Kris\LaravelFormBuilder\Fields\ChildFormType;
6+
use Kris\LaravelFormBuilder\Form;
7+
use Kris\LaravelFormBuilder\FormHelper;
8+
use Larastan\Larastan\Concerns\HasContainer;
9+
use PHPStan\Analyser\Scope;
10+
use PHPStan\Reflection\Annotations\AnnotationPropertyReflection;
11+
use PHPStan\Reflection\MethodReflection;
12+
use PHPStan\Reflection\ReflectionProvider;
13+
use PHPStan\Type\DynamicMethodReturnTypeExtension;
14+
use PHPStan\Type\ObjectType;
15+
use PHPStan\Type\Type;
16+
use PHPStan\Type\TypeWithClassName;
17+
use PhpParser\Node\Expr;
18+
use PhpParser\Node\Expr\Array_;
19+
use PhpParser\Node\Expr\ClassConstFetch;
20+
use PhpParser\Node\Expr\MethodCall;
21+
use PhpParser\Node\Scalar\String_;
22+
23+
class FormGetFieldExtension implements DynamicMethodReturnTypeExtension
24+
{
25+
26+
public function __construct(
27+
protected ReflectionProvider $reflectionProvider,
28+
) {}
29+
30+
public function getClass(): string {
31+
return Form::class;
32+
}
33+
34+
public function isMethodSupported(MethodReflection $methodReflection): bool {
35+
return $methodReflection->getName() == 'getField';
36+
}
37+
38+
public function getTypeFromMethodCall(MethodReflection $methodReflection, MethodCall $methodCall, Scope $scope): ?Type {
39+
return $this->getTypeFromMethodCallGetField($methodReflection, $methodCall, $scope);
40+
}
41+
42+
protected function getTypeFromMethodCallGetField(MethodReflection $methodReflection, MethodCall $methodCall, Scope $scope): ?Type
43+
{
44+
if (count($methodCall->getArgs()) < 1) {
45+
return null;
46+
}
47+
48+
$fieldNameArg = $methodCall->getArgs()[0]->value;
49+
if (!($fieldNameArg instanceof String_)) {
50+
return null;
51+
}
52+
53+
$fieldName = $fieldNameArg->value;
54+
55+
$calledOnType = $scope->getType($methodCall->var);
56+
assert($calledOnType instanceof TypeWithClassName);
57+
$formClass = $calledOnType->getClassName();
58+
59+
$formClassReflection = $this->reflectionProvider->getClass($formClass);
60+
61+
if (!$formClassReflection->hasProperty($fieldName)) {
62+
return null;
63+
}
64+
65+
$formClassFieldProperty = $formClassReflection->getProperty($fieldName, $scope);
66+
if (!($formClassFieldProperty instanceof AnnotationPropertyReflection)) {
67+
return null;
68+
}
69+
70+
return $formClassFieldProperty->getReadableType();
71+
}
72+
73+
}

0 commit comments

Comments
 (0)