Skip to content

Commit

Permalink
Guess first parameter for event by type hint (#219)
Browse files Browse the repository at this point in the history
Co-authored-by: Adam Žurek <[email protected]>
Co-authored-by: Miloslav Hůla <[email protected]>
Co-authored-by: David Grudl <[email protected]>
  • Loading branch information
4 people committed Jan 19, 2021
1 parent 4423d28 commit 2fce9b9
Show file tree
Hide file tree
Showing 4 changed files with 85 additions and 23 deletions.
15 changes: 10 additions & 5 deletions src/Forms/Container.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ class Container extends Nette\ComponentModel\Container implements \ArrayAccess

private const ARRAY = 'array';

/** @var callable[]&(callable(Container, mixed): void)[]; Occurs when the form is validated */
/** @var callable[]&((callable(Container, array|object|null): void)|(callable(array|object|null): void))[]; Occurs when the form was validated */
public $onValidate = [];

/** @var ControlGroup|null */
Expand Down Expand Up @@ -191,10 +191,15 @@ public function validate(array $controls = null): void
$isValid = !$this->getErrors();
foreach ($this->onValidate as $handler) {
$params = Nette\Utils\Callback::toReflection($handler)->getParameters();
$values = isset($params[1]) && $isValid
? $this->getValues($params[1]->getType() instanceof \ReflectionNamedType ? $params[1]->getType()->getName() : null)
: null;
$handler($this, $values);
$types = array_map([Nette\Utils\Reflection::class, 'getParameterType'], $params);
$handler(
!isset($types[0]) || $this instanceof $types[0]
? $this
: ($isValid ? $this->getValues($types[0]) : null),
isset($params[1]) && $isValid
? $this->getValues($types[1])
: null
);
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/Forms/Controls/SubmitButton.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
*/
class SubmitButton extends Button implements Nette\Forms\SubmitterControl
{
/** @var callable[]&(callable(SubmitButton): void)[]; Occurs when the button is clicked and form is successfully validated */
/** @var callable[]&((callable(Nette\Forms\Form|SubmitButton, array|object): void)|(callable(array|object): void))[]; Occurs when the button is clicked and form is successfully validated */
public $onClick = [];

/** @var callable[]&(callable(SubmitButton): void)[]; Occurs when the button is clicked and form is not validated */
Expand Down
19 changes: 14 additions & 5 deletions src/Forms/Form.php
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ class Form extends Container implements Nette\HtmlStringable
/** @internal protection token ID */
public const PROTECTOR_ID = '_token_';

/** @var callable[]&(callable(Form, mixed): void)[]; Occurs when the form is submitted and successfully validated */
/** @var callable[]&((callable(Form, array|object): void)|(callable(array|object): void))[]; Occurs when the form is submitted and successfully validated */
public $onSuccess = [];

/** @var callable[]&(callable(Form): void)[]; Occurs when the form is submitted and is not valid */
Expand Down Expand Up @@ -443,10 +443,19 @@ private function invokeHandlers(iterable $handlers, $button = null): void
{
foreach ($handlers as $handler) {
$params = Nette\Utils\Callback::toReflection($handler)->getParameters();
$values = isset($params[1])
? $this->getValues($params[1]->getType() instanceof \ReflectionNamedType ? $params[1]->getType()->getName() : null)
: null;
$handler($button ?: $this, $values);
$types = array_map([Nette\Utils\Reflection::class, 'getParameterType'], $params);
if (!isset($types[0])) {
$arg0 = $button ?: $this;
} elseif ($this instanceof $types[0]) {
$arg0 = $this;
} elseif ($button instanceof $types[0]) {
$arg0 = $button;
} else {
$arg0 = $this->getValues($types[0]);
}
$arg1 = isset($params[1]) ? $this->getValues($types[1]) : null;
$handler($arg0, $arg1);

if (!$this->isValid()) {
return;
}
Expand Down
72 changes: 60 additions & 12 deletions tests/Forms/Forms.callbackParameters.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -26,26 +26,26 @@ $types = [];

// Test the closure second parameter to be an ArrayHash instance by default
$f1 = function ($form, $values) use (&$types) {
$types[] = $form instanceof Form;
$types[] = $values instanceof ArrayHash;
$types['f1'][] = $form instanceof Form;
$types['f1'][] = $values instanceof ArrayHash;
};

// Test the closure second parameter to be an array by type-hint
$f2 = function ($form, array $values) use (&$types) {
$types[] = $form instanceof Form;
$types[] = is_array($values);
$types['f2'][] = $form instanceof Form;
$types['f2'][] = is_array($values);
};

// Test the closure second parameter to be an ArrayHash instance by default
$b1 = function ($form, $values) use (&$types) {
$types[] = $form instanceof SubmitButton;
$types[] = $values instanceof ArrayHash;
$types['b1'][] = $form instanceof SubmitButton;
$types['b1'][] = $values instanceof ArrayHash;
};

// Test the closure second parameter to be an array by type-hint
$b2 = function ($form, array $values) use (&$types) {
$types[] = $form instanceof SubmitButton;
$types[] = is_array($values);
$types['b2'][] = $form instanceof SubmitButton;
$types['b2'][] = is_array($values);
};

// Test the second parameter in ArrayHash form to be immutable
Expand All @@ -57,10 +57,58 @@ $f5 = function ($form, $values) use (&$arrayHashIsImmutable) {
$arrayHashIsImmutable[] = $values->text === 'a';
};

$form->onSuccess = [$f1, $f2, $f1, $f4, $f5];
$form->onValidate = [$f1, $f2, $f1, $f4, $f5];
$form['btn']->onClick = [$b1, $b2, $b1, $f4, $f5];
// Test the closure single parameter without type-hint
$f6 = function ($form) use (&$types) {
$types['f6'][] = $form instanceof Form;
};

// Test the closure single array parameter
$f7 = function (array $values) use (&$types) {
$types['f7'][] = is_array($values);
};

// Test the closure single Form-typed parameter
$f8 = function (Form $values) use (&$types) {
$types['f8'][] = $values instanceof Form;
};

// Test the closure single not-Form-typed parameter
$f9 = function (ArrayHash $values) use (&$types) {
$types['f9'][] = $values instanceof ArrayHash;
};

// Test the closure single parameter without type-hint
$b3 = function ($form) use (&$types) {
$types['b3'][] = $form instanceof SubmitButton;
};

// Test the closure single array parameter
$b4 = function (array $values) use (&$types) {
$types['b4'][] = is_array($values);
};

// Test the closure single parameter SubmitButton-typed
$b5 = function (SubmitButton $form) use (&$types) {
$types['b5'][] = $form instanceof SubmitButton;
};


$form->onSuccess = [$f1, $f2, $f1, $f4, $f5, $f6, $f7, $f8, $f9];
$form->onValidate = [$f1, $f2, $f1, $f4, $f5, $f6, $f7, $f8, $f9];
$form['btn']->onClick = [$b1, $b2, $b1, $f4, $f5, $b3, $b4, $b5];
$form->fireEvents();

Assert::same([true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true], $types);
Assert::same([
'f1' => [true, true, true, true, true, true, true, true],
'f2' => [true, true, true, true],
'f6' => [true, true],
'f7' => [true, true],
'f8' => [true, true],
'f9' => [true, true],
'b1' => [true, true, true, true],
'b2' => [true, true],
'b3' => [true],
'b4' => [true],
'b5' => [true],
], $types);
Assert::same([true, true, true], $arrayHashIsImmutable);

0 comments on commit 2fce9b9

Please sign in to comment.