Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make FormElements and FormElement generic #134

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 9 additions & 49 deletions phpstan-baseline.neon
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,7 @@ parameters:
path: src/Form.php

-
message: "#^Parameter \\#1 \\$attributes of method ipl\\\\Html\\\\Contract\\\\FormElement\\:\\:addAttributes\\(\\) expects iterable, mixed given\\.$#"
message: "#^Parameter \\#1 \\$attributes of method ipl\\\\Html\\\\Contract\\\\FormElement\\<mixed\\>\\:\\:addAttributes\\(\\) expects iterable, mixed given\\.$#"
count: 1
path: src/Form.php

Expand All @@ -250,21 +250,6 @@ parameters:
count: 1
path: src/Form.php

-
message: "#^Parameter \\#3 \\$postfix of method ipl\\\\Html\\\\Form\\:\\:addPluginLoader\\(\\) expects string, string\\|null given\\.$#"
count: 2
path: src/Form.php

-
message: "#^Property ipl\\\\Html\\\\Form\\:\\:\\$defaultElementDecorator \\(ipl\\\\Html\\\\Contract\\\\FormElementDecorator\\|null\\) does not accept ipl\\\\Html\\\\Contract\\\\FormElementDecorator\\|ipl\\\\Html\\\\FormDecorator\\\\DecoratorInterface\\.$#"
count: 1
path: src/Form.php

-
message: "#^Property ipl\\\\Html\\\\Form\\:\\:\\$submitButton \\(ipl\\\\Html\\\\Contract\\\\FormSubmitElement\\) does not accept null\\.$#"
count: 1
path: src/Form.php

-
message: "#^Method ipl\\\\Html\\\\FormDecorator\\\\CallbackDecorator\\:\\:assemble\\(\\) has no return type specified\\.$#"
count: 1
Expand Down Expand Up @@ -326,7 +311,7 @@ parameters:
path: src/FormDecorator/DivDecorator.php

-
message: "#^Call to an undefined method ipl\\\\Html\\\\Contract\\\\FormElement\\:\\:ensureAssembled\\(\\)\\.$#"
message: "#^Call to an undefined method ipl\\\\Html\\\\Contract\\\\FormElement\\<mixed\\>\\:\\:ensureAssembled\\(\\)\\.$#"
count: 1
path: src/FormDecorator/DivDecorator.php

Expand Down Expand Up @@ -362,7 +347,7 @@ parameters:

-
message: "#^Cannot access offset 'name' on mixed\\.$#"
count: 2
count: 1
path: src/FormElement/BaseFormElement.php

-
Expand Down Expand Up @@ -405,6 +390,11 @@ parameters:
count: 1
path: src/FormElement/BaseFormElement.php

-
message: "#^Parameter \\#1 \\$validators of method ipl\\\\Validator\\\\ValidatorChain\\:\\:addValidators\\(\\) expects Traversable\\<int\\|string, mixed\\>, iterable given\\.$#"
count: 2
path: src/FormElement/BaseFormElement.php

-
message: "#^Trying to invoke mixed but it's not a callable\\.$#"
count: 1
Expand Down Expand Up @@ -436,25 +426,10 @@ parameters:
path: src/FormElement/FieldsetElement.php

-
message: "#^Parameter \\#1 \\$attributes of method ipl\\\\Html\\\\Contract\\\\FormElement\\:\\:addAttributes\\(\\) expects iterable, mixed given\\.$#"
count: 1
path: src/FormElement/FieldsetElement.php

-
message: "#^Parameter \\#3 \\$postfix of method ipl\\\\Html\\\\FormElement\\\\FieldsetElement\\:\\:addPluginLoader\\(\\) expects string, string\\|null given\\.$#"
count: 2
path: src/FormElement/FieldsetElement.php

-
message: "#^Property ipl\\\\Html\\\\FormElement\\\\FieldsetElement\\:\\:\\$defaultElementDecorator \\(ipl\\\\Html\\\\Contract\\\\FormElementDecorator\\|null\\) does not accept ipl\\\\Html\\\\Contract\\\\FormElementDecorator\\|ipl\\\\Html\\\\FormDecorator\\\\DecoratorInterface\\.$#"
message: "#^Parameter \\#1 \\$attributes of method ipl\\\\Html\\\\Contract\\\\FormElement\\<mixed\\>\\:\\:addAttributes\\(\\) expects iterable, mixed given\\.$#"
count: 1
path: src/FormElement/FieldsetElement.php

-
message: "#^Cannot access offset 0 on mixed\\.$#"
count: 1
path: src/FormElement/FileElement.php

-
message: "#^Method ipl\\\\Html\\\\FormElement\\\\FileElement\\:\\:assemble\\(\\) has no return type specified\\.$#"
count: 1
Expand Down Expand Up @@ -530,16 +505,6 @@ parameters:
count: 1
path: src/FormElement/SelectElement.php

-
message: "#^Method ipl\\\\Html\\\\FormElement\\\\SelectElement\\:\\:getValue\\(\\) return type has no value type specified in iterable type array\\.$#"
count: 1
path: src/FormElement/SelectElement.php

-
message: "#^Method ipl\\\\Html\\\\FormElement\\\\SelectElement\\:\\:getValue\\(\\) should return array\\|string\\|null but returns mixed\\.$#"
count: 2
path: src/FormElement/SelectElement.php

-
message: "#^Method ipl\\\\Html\\\\FormElement\\\\SelectElement\\:\\:makeOption\\(\\) has parameter \\$label with no value type specified in iterable type array\\.$#"
count: 1
Expand All @@ -565,11 +530,6 @@ parameters:
count: 1
path: src/FormElement/SelectElement.php

-
message: "#^Property ipl\\\\Html\\\\FormElement\\\\SelectElement\\:\\:\\$value type has no value type specified in iterable type array\\.$#"
count: 1
path: src/FormElement/SelectElement.php

-
message: "#^Method ipl\\\\Html\\\\FormElement\\\\SelectOption\\:\\:assemble\\(\\) has no return type specified\\.$#"
count: 1
Expand Down
10 changes: 7 additions & 3 deletions src/Contract/FormElement.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@

/**
* Representation of form elements
*
* @template TValue of mixed
*/
interface FormElement extends Wrappable
{
Expand Down Expand Up @@ -67,20 +69,22 @@ public function getName();
* Get whether the element has a value
*
* @return bool False if the element's value is null, the empty string or the empty array; true otherwise
*
* @phpstan-assert-if-false null|''|array{} $this->getValue()
*/
public function hasValue();

/**
* Get the value of the element
*
* @return mixed
* @return ?TValue
*/
public function getValue();

/**
* Set the value of the element
*
* @param mixed $value
* @param ?TValue $value
*
* @return $this
*/
Expand Down Expand Up @@ -124,7 +128,7 @@ public function validate();
/**
* Handler which is called after this element has been registered
*
* @param Form $form
* @param Form<array<string, FormElement<mixed>>> $form
*
* @return void
*/
Expand Down
2 changes: 1 addition & 1 deletion src/Contract/FormElementDecorator.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ interface FormElementDecorator extends ValidHtml
* }
* ```
*
* @param FormElement $formElement
* @param FormElement<mixed> $formElement
*/
public function decorate(FormElement $formElement);
}
1 change: 1 addition & 0 deletions src/Contract/FormSubmitElement.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace ipl\Html\Contract;

/** @extends FormElement<string> */
interface FormSubmitElement extends FormElement
{
/**
Expand Down
5 changes: 3 additions & 2 deletions src/Contract/ValueCandidates.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,20 @@

namespace ipl\Html\Contract;

/** @template TValue of mixed */
interface ValueCandidates
{
/**
* Get value candidates of this element
*
* @return array<int, mixed>
* @return array<int, TValue>
*/
public function getValueCandidates();

/**
* Set value candidates of this element
*
* @param array<int, mixed> $values
* @param array<int, TValue> $values
*
* @return $this
*/
Expand Down
5 changes: 4 additions & 1 deletion src/Form.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,10 @@
use ipl\Stdlib\Messages;
use Psr\Http\Message\ServerRequestInterface;

/** @template TFormElements of array<string, FormElement<mixed>> */
class Form extends BaseHtmlElement
{
/** @use FormElements<TFormElements> */
use FormElements {
FormElements::remove as private removeElement;
}
Expand All @@ -29,7 +31,7 @@ class Form extends BaseHtmlElement
/** @var string HTTP method to submit the form with */
protected $method = 'POST';

/** @var FormSubmitElement Primary submit button */
/** @var ?FormSubmitElement Primary submit button */
protected $submitButton;

/** @var FormSubmitElement[] Other elements that may submit the form */
Expand Down Expand Up @@ -380,6 +382,7 @@ protected function onSuccess()
// $this->redirectOnSuccess();
}

/** @param value-of<TFormElements> $element */
protected function onElementRegistered(FormElement $element)
{
if ($element instanceof FormSubmitElement) {
Expand Down
2 changes: 1 addition & 1 deletion src/FormDecorator/CallbackDecorator.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ class CallbackDecorator extends HtmlDocument implements FormElementDecorator
/** @var Closure The decorating callback */
protected $callback;

/** @var FormElement The decorated form element */
/** @var FormElement<mixed> The decorated form element */
protected $formElement;

/**
Expand Down
4 changes: 2 additions & 2 deletions src/FormDecorator/DdDtDecorator.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,13 @@ class DdDtDecorator extends BaseHtmlElement implements DecoratorInterface

protected $dd;

/** @var BaseFormElement */
/** @var BaseFormElement<mixed> */
protected $wrappedElement;

protected $ready = false;

/**
* @param BaseFormElement $element
* @param BaseFormElement<mixed> $element
* @return static
*/
public function decorate(BaseFormElement $element)
Expand Down
2 changes: 1 addition & 1 deletion src/FormDecorator/DecoratorInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ interface DecoratorInterface
/**
* Set the form element to decorate
*
* @param BaseFormElement $formElement
* @param BaseFormElement<mixed> $formElement
*
* @return static
*/
Expand Down
2 changes: 1 addition & 1 deletion src/FormDecorator/DivDecorator.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ class DivDecorator extends BaseHtmlElement implements FormElementDecorator
/** @var string CSS class to set on the decorator if the element has errors */
public const ERROR_HINT_CLASS = 'has-error';

/** @var FormElement The decorated form element */
/** @var FormElement<mixed> The decorated form element */
protected $formElement;

protected $tag = 'div';
Expand Down
9 changes: 7 additions & 2 deletions src/FormElement/BaseFormElement.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@
use ipl\Validator\ValidatorChain;
use ReflectionProperty;

/**
* @template TValue of mixed
* @implements FormElement<TValue>
* @implements ValueCandidates<TValue>
*/
abstract class BaseFormElement extends BaseHtmlElement implements FormElement, ValueCandidates
{
use Messages;
Expand All @@ -39,10 +44,10 @@ abstract class BaseFormElement extends BaseHtmlElement implements FormElement, V
/** @var ValidatorChain Registered validators */
protected $validators;

/** @var mixed Value of the element */
/** @var ?TValue Value of the element */
protected $value;

/** @var array<int, mixed> Value candidates of the element */
/** @var array<int, TValue> Value candidates of the element */
protected $valueCandidates = [];

/**
Expand Down
1 change: 1 addition & 0 deletions src/FormElement/ButtonElement.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace ipl\Html\FormElement;

/** @extends BaseFormElement<string> */
class ButtonElement extends BaseFormElement
{
protected $tag = 'button';
Expand Down
1 change: 1 addition & 0 deletions src/FormElement/CheckboxElement.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

use ipl\Html\Attributes;

/** @extends InputElement<string> */
class CheckboxElement extends InputElement
{
/** @var bool Whether the checkbox is checked */
Expand Down
1 change: 1 addition & 0 deletions src/FormElement/ColorElement.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
use ipl\Validator\HexColorValidator;
use ipl\Validator\ValidatorChain;

/** @extends InputElement<string> */
class ColorElement extends InputElement
{
protected $type = 'color';
Expand Down
1 change: 1 addition & 0 deletions src/FormElement/DateElement.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace ipl\Html\FormElement;

/** @extends InputElement<string> */
class DateElement extends InputElement
{
protected $type = 'date';
Expand Down
6 changes: 6 additions & 0 deletions src/FormElement/FieldsetElement.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,13 @@

use function ipl\Stdlib\get_php_type;

/**
* @template TElements of array<string, FormElement<mixed>>
* @extends BaseFormElement<mixed>
*/
class FieldsetElement extends BaseFormElement
{
/** @use FormElements<TElements> */
use FormElements {
FormElements::getValue as private getElementValue;
}
Expand Down Expand Up @@ -104,6 +109,7 @@ public function setWrapper(Wrappable $wrapper)
return parent::setWrapper($wrapper);
}

/** @param value-of<TElements> $element */
protected function onElementRegistered(FormElement $element)
{
$element->getAttributes()->registerAttributeCallback('name', function () use ($element) {
Expand Down
9 changes: 4 additions & 5 deletions src/FormElement/FileElement.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
*
* Once the file element is added to the form and the form attribute `enctype` is not set,
* it is automatically set to `multipart/form-data`.
*
* @extends InputElement<UploadedFileInterface|array<UploadedFileInterface>>
*/
class FileElement extends InputElement
{
Expand All @@ -30,9 +32,6 @@ class FileElement extends InputElement

protected $type = 'file';

/** @var UploadedFileInterface|UploadedFileInterface[] */
protected $value;

/** @var UploadedFileInterface[] Files that are stored on disk */
protected $files = [];

Expand Down Expand Up @@ -120,7 +119,7 @@ public function setValue($value)
{
if (! empty($value)) {
$fileToTest = $value;
if ($this->isMultiple()) {
if ($this->isMultiple() && is_array($value)) {
$fileToTest = $value[0];
}

Expand All @@ -135,7 +134,7 @@ public function setValue($value)
$value = null;
} else {
$files = $value;
if (! $this->isMultiple()) {
if (! $this->isMultiple() || ! is_array($files)) {
$files = [$files];
}

Expand Down
Loading
Loading