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

Guess parameters for event by type hint #219

Merged
merged 3 commits into from
Jan 19, 2021
Merged
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
22 changes: 20 additions & 2 deletions src/Forms/Container.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ class Container extends Nette\ComponentModel\Container implements \ArrayAccess

private const ARRAY = 'array';

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

/** @var ControlGroup|null */
protected $currentGroup;
Expand Down Expand Up @@ -186,6 +186,24 @@ public function validate(array $controls = null): void
$control->validate();
}
}
if ($this->onValidate !== null) {
if (!is_iterable($this->onValidate)) {
throw new Nette\UnexpectedValueException('Property Form::$onValidate must be iterable, ' . gettype($this->onValidate) . ' given.');
}
foreach ($this->onValidate as $handler) {
$args = [];
foreach (Nette\Utils\Callback::toReflection($handler)->getParameters() as $pos => $parameter) {
if ($parameter->hasType()) {
$type = (string) $parameter->getType();
$args[] = $this instanceof $type ? $this : $this->getValues($type);

} else {
$args[] = $pos === 0 ? $this : $this->getValues();
}
}
$handler(...$args);
}
}
$this->validated = true;

$isValid = !$this->getErrors();
Expand Down
4 changes: 2 additions & 2 deletions src/Forms/Controls/SubmitButton.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,10 @@
class SubmitButton extends Button implements Nette\Forms\SubmitterControl
{
/** @var callable[]&(callable(SubmitButton): void)[]; Occurs when the button is clicked and form is successfully validated */
public $onClick = [];
public $onClick;

/** @var callable[]&(callable(SubmitButton): void)[]; Occurs when the button is clicked and form is not validated */
public $onInvalidClick = [];
public $onInvalidClick;

/** @var array|null */
private $validationScope;
Expand Down
33 changes: 23 additions & 10 deletions src/Forms/Form.php
Original file line number Diff line number Diff line change
Expand Up @@ -82,17 +82,17 @@ 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 */
public $onSuccess = [];
/** @var callable[]&((callable(Form, array): void)|(callable(Form, ArrayHash): 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 */
public $onError = [];
public $onError;

/** @var callable[]&(callable(Form): void)[]; Occurs when the form is submitted */
public $onSubmit = [];
public $onSubmit;

/** @var callable[]&(callable(Form): void)[]; Occurs before the form is rendered */
public $onRender = [];
public $onRender;

/** @internal @var Nette\Http\IRequest used only by standalone form */
public $httpRequest;
Expand Down Expand Up @@ -442,11 +442,24 @@ public function fireEvents(): void
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);
$args = [];
foreach (Nette\Utils\Callback::toReflection($handler)->getParameters() as $pos => $parameter) {
if ($parameter->hasType()) {
$type = (string) $parameter->getType();
if ($this instanceof $type) {
$args[] = $this;
} elseif ($button instanceof $type) {
$args[] = $button;
} else {
$args[] = $this->getValues($type);
}

} else {
$args[] = $pos === 0 ? ($button ?: $this) : $this->getValues();
}
}
$handler(...$args);

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);