Skip to content

Commit

Permalink
Fix psalm on form builder
Browse files Browse the repository at this point in the history
  • Loading branch information
vincent4vx committed Jan 4, 2022
1 parent 0134a2b commit 7f53887
Show file tree
Hide file tree
Showing 7 changed files with 143 additions and 20 deletions.
1 change: 1 addition & 0 deletions psalm.xml
Expand Up @@ -8,6 +8,7 @@
>
<projectFiles>
<directory name="src" />
<directory name="tests/StaticAnalysis" />
<ignoreFiles>
<directory name="vendor" />
</ignoreFiles>
Expand Down
15 changes: 9 additions & 6 deletions src/Aggregate/ArrayElementBuilder.php
Expand Up @@ -126,10 +126,12 @@ public function value($value)
* });
* </code>
*
* @param class-string<ElementInterface<RT>> $element The element class name
* @param callable(ElementBuilderInterface<ElementInterface<RT>>):void|null $configurator Callback for configure the inner element builder. Takes as parameter the element builder
* @param class-string<E> $element The element class name
* @param callable(EB):void|null $configurator Callback for configure the inner element builder. Takes as parameter the element builder
*
* @template RT
* @template E as ElementInterface<RT>
* @template EB as ElementBuilderInterface<E>
*
* @return ArrayElementBuilder<RT>
*/
Expand All @@ -140,6 +142,7 @@ public function element(string $element, ?callable $configurator = null): ArrayE
$this->element = $this->registry->elementBuilder($element);

if ($configurator) {
/** @psalm-suppress InvalidArgument */
$configurator($this->element);
}

Expand Down Expand Up @@ -171,7 +174,7 @@ public function getElementBuilder(): ElementBuilderInterface
* });
* </code>
*
* @param callable(ElementBuilderInterface<ElementInterface<string>>):void|null $configurator Callback for configure the inner element builder
* @param callable(ElementBuilderInterface<StringElement>):void|null $configurator Callback for configure the inner element builder
*
* @return ArrayElementBuilder<string>
*/
Expand All @@ -189,7 +192,7 @@ public function string(?callable $configurator = null): ArrayElementBuilder
* });
* </code>
*
* @param callable(ElementBuilderInterface<ElementInterface<int>>):void|null $configurator Callback for configure the inner element builder
* @param callable(ElementBuilderInterface<IntegerElement>):void|null $configurator Callback for configure the inner element builder
*
* @return ArrayElementBuilder<int>
*/
Expand All @@ -207,7 +210,7 @@ public function integer(?callable $configurator = null): ArrayElementBuilder
* });
* </code>
*
* @param callable(ElementBuilderInterface<ElementInterface<float>>):void|null $configurator Callback for configure the inner element builder
* @param callable(ElementBuilderInterface<FloatElement>):void|null $configurator Callback for configure the inner element builder
*
* @return ArrayElementBuilder<float>
*/
Expand All @@ -223,7 +226,7 @@ public function float(?callable $configurator = null): ArrayElementBuilder
* $builder->array('flags')->boolean();
* </code>
*
* @param callable(ElementBuilderInterface<ElementInterface<bool>>):void|null $configurator Callback for configure the inner element builder
* @param callable(ElementBuilderInterface<BooleanElement>):void|null $configurator Callback for configure the inner element builder
*
* @return ArrayElementBuilder<bool>
*/
Expand Down
62 changes: 56 additions & 6 deletions src/Aggregate/FormBuilder.php
Expand Up @@ -7,17 +7,26 @@
use Bdf\Form\Aggregate\Value\ValueGenerator;
use Bdf\Form\Aggregate\Value\ValueGeneratorInterface;
use Bdf\Form\Button\ButtonBuilderInterface;
use Bdf\Form\Child\ChildBuilder;
use Bdf\Form\Child\ChildBuilderInterface;
use Bdf\Form\Csrf\CsrfElement;
use Bdf\Form\Csrf\CsrfElementBuilder;
use Bdf\Form\Custom\CustomForm;
use Bdf\Form\ElementBuilderInterface;
use Bdf\Form\ElementInterface;
use Bdf\Form\Leaf\BooleanElement;
use Bdf\Form\Leaf\BooleanElementBuilder;
use Bdf\Form\Leaf\Date\DateTimeChildBuilder;
use Bdf\Form\Leaf\Date\DateTimeElement;
use Bdf\Form\Leaf\FloatElement;
use Bdf\Form\Leaf\FloatElementBuilder;
use Bdf\Form\Leaf\Helper\EmailElement;
use Bdf\Form\Leaf\Helper\UrlElement;
use Bdf\Form\Leaf\IntegerElement;
use Bdf\Form\Leaf\IntegerElementBuilder;
use Bdf\Form\Leaf\StringElement;
use Bdf\Form\Leaf\StringElementBuilder;
use Bdf\Form\Phone\PhoneChildBuilder;
use Bdf\Form\Phone\PhoneElement;
use Bdf\Form\Registry\RegistryInterface;
use Bdf\Form\RootElementInterface;
Expand Down Expand Up @@ -56,7 +65,7 @@
class FormBuilder extends AbstractElementBuilder implements FormBuilderInterface
{
/**
* @var array<non-empty-string, ChildBuilderInterface>
* @var array<non-empty-string, ChildBuilder>
*/
private $children = [];

Expand Down Expand Up @@ -93,6 +102,17 @@ public function __construct(?RegistryInterface $registry = null)

/**
* {@inheritdoc}
*
* @psalm-param non-empty-string $name
* @psalm-param class-string<E> $element
*
* @template E as ElementInterface
*
* @psalm-return ChildBuilder<ElementBuilderInterface<E>>
*
* @psalm-suppress MoreSpecificReturnType
* @psalm-suppress LessSpecificReturnStatement
* @psalm-suppress PropertyTypeCoercion
*/
public function add(string $name, string $element): ChildBuilderInterface
{
Expand All @@ -102,6 +122,9 @@ public function add(string $name, string $element): ChildBuilderInterface
/**
* {@inheritdoc}
*
* @psalm-param non-empty-string $name
* @psalm-return ChildBuilder<StringElementBuilder>
*
* @psalm-suppress MoreSpecificReturnType
* @psalm-suppress LessSpecificReturnStatement
*/
Expand All @@ -113,6 +136,9 @@ public function string(string $name, ?string $default = null): ChildBuilderInter
/**
* {@inheritdoc}
*
* @psalm-param non-empty-string $name
* @psalm-return ChildBuilder<IntegerElementBuilder>
*
* @psalm-suppress MoreSpecificReturnType
* @psalm-suppress LessSpecificReturnStatement
*/
Expand All @@ -124,6 +150,9 @@ public function integer(string $name, ?int $default = null): ChildBuilderInterfa
/**
* {@inheritdoc}
*
* @psalm-param non-empty-string $name
* @psalm-return ChildBuilder<FloatElementBuilder>
*
* @psalm-suppress MoreSpecificReturnType
* @psalm-suppress LessSpecificReturnStatement
*/
Expand All @@ -135,6 +164,9 @@ public function float(string $name, ?float $default = null): ChildBuilderInterfa
/**
* {@inheritdoc}
*
* @psalm-param non-empty-string $name
* @psalm-return ChildBuilder<BooleanElementBuilder>
*
* @psalm-suppress MoreSpecificReturnType
* @psalm-suppress LessSpecificReturnStatement
*/
Expand All @@ -146,6 +178,9 @@ public function boolean(string $name): ChildBuilderInterface
/**
* {@inheritdoc}
*
* @psalm-param non-empty-string $name
* @psalm-return DateTimeChildBuilder
*
* @psalm-suppress MoreSpecificReturnType
* @psalm-suppress LessSpecificReturnStatement
*/
Expand All @@ -157,6 +192,9 @@ public function dateTime(string $name): ChildBuilderInterface
/**
* {@inheritdoc}
*
* @psalm-param non-empty-string $name
* @psalm-return PhoneChildBuilder
*
* @psalm-suppress MoreSpecificReturnType
* @psalm-suppress LessSpecificReturnStatement
*/
Expand All @@ -168,6 +206,9 @@ public function phone(string $name): ChildBuilderInterface
/**
* {@inheritdoc}
*
* @psalm-param non-empty-string $name
* @psalm-return ChildBuilder<CsrfElementBuilder>
*
* @psalm-suppress MoreSpecificReturnType
* @psalm-suppress LessSpecificReturnStatement
*/
Expand All @@ -188,8 +229,8 @@ public function csrf(string $name = '_token'): ChildBuilderInterface
*
* @param non-empty-string $name The name of the input
*
* @return ChildBuilderInterface|\Bdf\Form\Leaf\Helper\EmailElementBuilder
* @psalm-return ChildBuilderInterface<\Bdf\Form\Leaf\Helper\EmailElementBuilder>
* @return ChildBuilder|\Bdf\Form\Leaf\Helper\EmailElementBuilder
* @psalm-return ChildBuilder<\Bdf\Form\Leaf\Helper\EmailElementBuilder>
*
* @psalm-suppress MoreSpecificReturnType
* @psalm-suppress LessSpecificReturnStatement
Expand All @@ -209,8 +250,8 @@ public function email(string $name): ChildBuilderInterface
*
* @param non-empty-string $name The name of the input
*
* @return ChildBuilderInterface|\Bdf\Form\Leaf\Helper\UrlElementBuilder
* @psalm-return ChildBuilderInterface<\Bdf\Form\Leaf\Helper\UrlElementBuilder>
* @return ChildBuilder|\Bdf\Form\Leaf\Helper\UrlElementBuilder
* @psalm-return ChildBuilder<\Bdf\Form\Leaf\Helper\UrlElementBuilder>
*
* @psalm-suppress MoreSpecificReturnType
* @psalm-suppress LessSpecificReturnStatement
Expand All @@ -223,6 +264,10 @@ public function url(string $name): ChildBuilderInterface
/**
* {@inheritdoc}
*
* @psalm-param non-empty-string $name
* @psalm-param callable|null $configurator
* @psalm-return ChildBuilder<FormBuilder>
*
* @psalm-suppress MoreSpecificReturnType
* @psalm-suppress LessSpecificReturnStatement
*/
Expand All @@ -240,12 +285,17 @@ public function embedded(string $name, ?callable $configurator = null): ChildBui
/**
* {@inheritdoc}
*
* @psalm-param non-empty-string $name
* @psalm-param class-string<ElementInterface>|null $elementType
* @psalm-param callable|null $elementConfigurator
* @psalm-return ArrayChildBuilder&ChildBuilderInterface<ArrayElementBuilder>
*
* @psalm-suppress MoreSpecificReturnType
* @psalm-suppress LessSpecificReturnStatement
*/
public function array(string $name, ?string $elementType = null, ?callable $elementConfigurator = null): ChildBuilderInterface
{
/** @var ChildBuilderInterface<ArrayElementBuilder> $builder */
/** @var ChildBuilderInterface<ArrayElementBuilder>&ArrayChildBuilder $builder */
$builder = $this->add($name, ArrayElement::class);

if ($elementType) {
Expand Down
13 changes: 7 additions & 6 deletions src/Aggregate/FormBuilderInterface.php
Expand Up @@ -4,6 +4,7 @@

use Bdf\Form\Aggregate\Value\ValueGeneratorInterface;
use Bdf\Form\Button\ButtonBuilderInterface;
use Bdf\Form\Child\ChildBuilder;
use Bdf\Form\Child\ChildBuilderInterface;
use Bdf\Form\Csrf\CsrfElementBuilder;
use Bdf\Form\ElementBuilderInterface;
Expand Down Expand Up @@ -65,7 +66,7 @@ public function add(string $name, string $element): ChildBuilderInterface;
* @param non-empty-string $name The child name
* @param string|null $default Default value to submit
*
* @return ChildBuilderInterface|StringElementBuilder
* @return ChildBuilder|StringElementBuilder
* @psalm-return ChildBuilderInterface<StringElementBuilder>
*/
public function string(string $name, ?string $default = null): ChildBuilderInterface;
Expand All @@ -80,7 +81,7 @@ public function string(string $name, ?string $default = null): ChildBuilderInter
* @param non-empty-string $name The child name
* @param integer|null $default Default value to submit
*
* @return ChildBuilderInterface|IntegerElementBuilder
* @return ChildBuilder|IntegerElementBuilder
* @psalm-return ChildBuilderInterface<IntegerElementBuilder>
*/
public function integer(string $name, ?int $default = null): ChildBuilderInterface;
Expand All @@ -95,7 +96,7 @@ public function integer(string $name, ?int $default = null): ChildBuilderInterfa
* @param non-empty-string $name The child name
* @param float|null $default Default value to submit
*
* @return ChildBuilderInterface|FloatElementBuilder
* @return ChildBuilder|FloatElementBuilder
* @psalm-return ChildBuilderInterface<FloatElementBuilder>
*/
public function float(string $name, ?float $default = null): ChildBuilderInterface;
Expand All @@ -109,7 +110,7 @@ public function float(string $name, ?float $default = null): ChildBuilderInterfa
*
* @param non-empty-string $name The child name
*
* @return ChildBuilderInterface|BooleanElementBuilder
* @return ChildBuilder|BooleanElementBuilder
* @psalm-return ChildBuilderInterface<BooleanElementBuilder>
*/
public function boolean(string $name): ChildBuilderInterface;
Expand Down Expand Up @@ -158,7 +159,7 @@ public function phone(string $name): ChildBuilderInterface;
*
* @param non-empty-string $name The child name
*
* @return ChildBuilderInterface|CsrfElementBuilder
* @return ChildBuilder|CsrfElementBuilder
* @psalm-return ChildBuilderInterface<CsrfElementBuilder>
*/
public function csrf(string $name = '_token'): ChildBuilderInterface;
Expand All @@ -184,7 +185,7 @@ public function csrf(string $name = '_token'): ChildBuilderInterface;
* @param non-empty-string $name The child name
* @param callable|null $configurator Configure the embedded form
*
* @return ChildBuilderInterface|FormBuilder
* @return ChildBuilder|FormBuilder
* @psalm-return ChildBuilderInterface<FormBuilder>
*/
public function embedded(string $name, ?callable $configurator = null): ChildBuilderInterface;
Expand Down
5 changes: 3 additions & 2 deletions src/Custom/CustomForm.php
Expand Up @@ -67,7 +67,7 @@ abstract class CustomForm implements FormInterface
*/
public function __construct(?FormBuilderInterface $builder = null)
{
$this->builder = $builder ?: new FormBuilder();
$this->builder = $builder ?? new FormBuilder();
}

/**
Expand Down Expand Up @@ -230,7 +230,7 @@ public function view(?HttpFieldPath $field = null): ElementViewInterface
/**
* Configure the form using the builder
*
* @param FormBuilderInterface $builder
* @param FormBuilder $builder
*/
abstract protected function configure(FormBuilderInterface $builder): void;

Expand Down Expand Up @@ -272,6 +272,7 @@ final protected function form(): FormInterface
return $this->form;
}

/** @psalm-suppress ArgumentTypeCoercion */
$this->configure($this->builder);

$form = $this->form = $this->builder->buildElement();
Expand Down
19 changes: 19 additions & 0 deletions tests/StaticAnalysis/ArrayElementConfigure.php
@@ -0,0 +1,19 @@
<?php

namespace Bdf\Form\StaticAnalysis;

use Bdf\Form\Aggregate\ArrayElement;
use Bdf\Form\Aggregate\ArrayElementBuilder;
use Bdf\Form\Aggregate\FormBuilderInterface;
use Bdf\Form\Custom\CustomForm;

class ArrayElementConfigure extends CustomForm
{
/**
* {@inheritdoc}
*/
protected function configure(FormBuilderInterface $builder): void
{
$builder->array('values')->element(ArrayElement::class, function (ArrayElementBuilder $builder) {});
}
}
48 changes: 48 additions & 0 deletions tests/StaticAnalysis/MyCustomForm.php
@@ -0,0 +1,48 @@
<?php

namespace Bdf\Form\StaticAnalysis;

use Bdf\Form\Aggregate\FormBuilderInterface;
use Bdf\Form\Custom\CustomForm;

/**
* @extends CustomForm<MyGeneratedEntity>
*/
class MyCustomForm extends CustomForm
{
/**
* {@inheritdoc}
*/
protected function configure(FormBuilderInterface $builder): void
{
$builder->generates(MyGeneratedEntity::class);

$builder->string('foo')->length(['min' => 1])->getter()->setter();
$builder->string('bar')->required()->getter()->setter();
}

public static function test()
{
$form = new MyCustomForm();
$form->submit(['foo' => 'a', 'bar' => 'b']);
self::check($form->value());
}

public static function check(?MyGeneratedEntity $entity): void
{

}
}

class MyGeneratedEntity
{
/**
* @var string
*/
public $foo;

/**
* @var string
*/
public $bar;
}

0 comments on commit 7f53887

Please sign in to comment.