diff --git a/src/Forms/Container.php b/src/Forms/Container.php index 28d8ad465..496f36bfc 100644 --- a/src/Forms/Container.php +++ b/src/Forms/Container.php @@ -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 */ @@ -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 + ); } } diff --git a/src/Forms/Controls/SubmitButton.php b/src/Forms/Controls/SubmitButton.php index 8d2346d81..02f40adf5 100644 --- a/src/Forms/Controls/SubmitButton.php +++ b/src/Forms/Controls/SubmitButton.php @@ -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 */ diff --git a/src/Forms/Form.php b/src/Forms/Form.php index 3f9c26400..5209a3e39 100644 --- a/src/Forms/Form.php +++ b/src/Forms/Form.php @@ -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 */ @@ -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; } diff --git a/tests/Forms/Forms.callbackParameters.phpt b/tests/Forms/Forms.callbackParameters.phpt index a0fea8abc..d3d780338 100644 --- a/tests/Forms/Forms.callbackParameters.phpt +++ b/tests/Forms/Forms.callbackParameters.phpt @@ -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 @@ -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);