Skip to content

Commit

Permalink
+phpstan generics
Browse files Browse the repository at this point in the history
  • Loading branch information
rudiedirkx committed Oct 28, 2024
1 parent 6cefaa9 commit 0dbb803
Show file tree
Hide file tree
Showing 7 changed files with 107 additions and 1 deletion.
5 changes: 5 additions & 0 deletions phpstan-extension.neon
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
services:
-
class: Kris\LaravelFormBuilder\PhpStan\FormGetFieldExtension
tags:
- phpstan.broker.dynamicMethodReturnTypeExtension
8 changes: 8 additions & 0 deletions src/Kris/LaravelFormBuilder/Fields/ChildFormType.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,17 @@

use Kris\LaravelFormBuilder\Form;

/**
* @template TFormType of Form
*
* @extends ParentType<FormField>
*/
class ChildFormType extends ParentType
{

/**
* @var Form
* @phpstan-var TFormType
*/
protected $form;

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

/**
* @return Form
* @phpstan-return TFormType
*/
public function getForm()
{
Expand Down Expand Up @@ -96,6 +103,7 @@ protected function createChildren()

/**
* @return Form
* @phpstan-return TFormType
* @throws \Exception
*/
protected function getClassFromOptions()
Expand Down
5 changes: 4 additions & 1 deletion src/Kris/LaravelFormBuilder/Fields/ChoiceType.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@

use Illuminate\Support\Arr;

/**
* @extends ParentType<FormField>
*/
class ChoiceType extends ParentType
{
/**
Expand Down Expand Up @@ -76,7 +79,7 @@ protected function createChildren()
if (($data_override = $this->getOption('data_override')) && $data_override instanceof \Closure) {
$this->options['choices'] = $data_override($this->options['choices'], $this);
}

$this->children = [];
$this->determineChoiceField();

Expand Down
7 changes: 7 additions & 0 deletions src/Kris/LaravelFormBuilder/Fields/CollectionType.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,18 @@
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Collection;

/**
* @template TType of FormField
*
* @extends ParentType<TType>
*/
class CollectionType extends ParentType
{
/**
* Contains template for a collection element.
*
* @var FormField
* @phpstan-var TType
*/
protected $proto;

Expand Down Expand Up @@ -49,6 +55,7 @@ protected function getDefaults()
* Get the prototype object.
*
* @return FormField
* @phpstan-return TType
* @throws \Exception
*/
public function prototype()
Expand Down
7 changes: 7 additions & 0 deletions src/Kris/LaravelFormBuilder/Fields/ParentType.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,15 @@
use Illuminate\Support\Arr;
use Kris\LaravelFormBuilder\Form;

/**
* @template TChildType of FormField
*/
abstract class ParentType extends FormField
{

/**
* @var FormField[]
* @phpstan-var TChildType[]
*/
protected $children;

Expand Down Expand Up @@ -64,6 +68,7 @@ public function render(array $options = [], $showLabel = true, $showField = true
* Get all children of the choice field.
*
* @return mixed
* @phpstan-return TChildType[]
*/
public function getChildren()
{
Expand All @@ -74,6 +79,7 @@ public function getChildren()
* Get a child of the choice field.
*
* @return mixed
* @phpstan-return ?TChildType
*/
public function getChild($key)
{
Expand Down Expand Up @@ -145,6 +151,7 @@ public function isRendered()
*
* @param string $name
* @return FormField
* @phpstan-return TChildType
*/
public function __get($name)
{
Expand Down
3 changes: 3 additions & 0 deletions src/Kris/LaravelFormBuilder/Fields/RepeatedType.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@

use Illuminate\Support\Arr;

/**
* @extends ParentType<FormField>
*/
class RepeatedType extends ParentType
{

Expand Down
73 changes: 73 additions & 0 deletions src/Kris/LaravelFormBuilder/PhpStan/FormGetFieldExtension.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
<?php

namespace Kris\LaravelFormBuilder\PhpStan;

use Kris\LaravelFormBuilder\Fields\ChildFormType;
use Kris\LaravelFormBuilder\Form;
use Kris\LaravelFormBuilder\FormHelper;
use Larastan\Larastan\Concerns\HasContainer;
use PHPStan\Analyser\Scope;
use PHPStan\Reflection\Annotations\AnnotationPropertyReflection;
use PHPStan\Reflection\MethodReflection;
use PHPStan\Reflection\ReflectionProvider;
use PHPStan\Type\DynamicMethodReturnTypeExtension;
use PHPStan\Type\ObjectType;
use PHPStan\Type\Type;
use PHPStan\Type\TypeWithClassName;
use PhpParser\Node\Expr;
use PhpParser\Node\Expr\Array_;
use PhpParser\Node\Expr\ClassConstFetch;
use PhpParser\Node\Expr\MethodCall;
use PhpParser\Node\Scalar\String_;

class FormGetFieldExtension implements DynamicMethodReturnTypeExtension
{

public function __construct(
protected ReflectionProvider $reflectionProvider,
) {}

public function getClass(): string {
return Form::class;
}

public function isMethodSupported(MethodReflection $methodReflection): bool {
return $methodReflection->getName() == 'getField';
}

public function getTypeFromMethodCall(MethodReflection $methodReflection, MethodCall $methodCall, Scope $scope): ?Type {
return $this->getTypeFromMethodCallGetField($methodReflection, $methodCall, $scope);
}

protected function getTypeFromMethodCallGetField(MethodReflection $methodReflection, MethodCall $methodCall, Scope $scope): ?Type
{
if (count($methodCall->getArgs()) < 1) {
return null;
}

$fieldNameArg = $methodCall->getArgs()[0]->value;
if (!($fieldNameArg instanceof String_)) {
return null;
}

$fieldName = $fieldNameArg->value;

$calledOnType = $scope->getType($methodCall->var);
assert($calledOnType instanceof TypeWithClassName);
$formClass = $calledOnType->getClassName();

$formClassReflection = $this->reflectionProvider->getClass($formClass);

if (!$formClassReflection->hasProperty($fieldName)) {
return null;
}

$formClassFieldProperty = $formClassReflection->getProperty($fieldName, $scope);
if (!($formClassFieldProperty instanceof AnnotationPropertyReflection)) {
return null;
}

return $formClassFieldProperty->getReadableType();
}

}

0 comments on commit 0dbb803

Please sign in to comment.