*/ private array $config = []; /** @var array> */ private array $fields; private Closure $renderer; private Closure $hydrator; /** * @param array> $fields */ public function __construct(array $fields, ?callable $renderer = null, ?callable $hydrator = null) { $this->fields = self::validateFields($fields); $this->renderer = Closure::fromCallable($renderer ?? static function (array $definition) : void { draw_edit_form($definition); }); $this->hydrator = Closure::fromCallable($hydrator ?? static function (array $fields, array $values) : array { return inject_form_variables($fields, $values); }); } /** * @param array $values */ public function withValues(array $values) : self { $form = clone $this; $form->fields = self::validateFields(($this->hydrator)($this->fields, $values)); return $form; } public function withoutFormTag() : self { return $this->withConfig('no_form_tag', true); } public function postTo(string $action) : self { if ($action === '') { throw new InvalidArgumentException('A form action cannot be empty.'); } return $this->withConfig('post_to', $action); } public function named(string $name) : self { if ($name === '') { throw new InvalidArgumentException('A form name cannot be empty.'); } return $this->withConfig('form_name', $name); } public function multipart() : self { return $this->withConfig('enctype', 'multipart/form-data'); } /** * @return array{config: array, fields: array>} */ public function definition() : array { return [ 'config' => $this->config, 'fields' => $this->fields, ]; } public function render() : void { ($this->renderer)($this->definition()); } private function withConfig(string $name, mixed $value) : self { $form = clone $this; $form->config[$name] = $value; return $form; } /** * @param array $fields * * @return array> */ private static function validateFields(array $fields) : array { foreach ($fields as $name => $field) { if (!is_string($name) || $name === '') { throw new InvalidArgumentException('Cacti form fields must have non-empty string names.'); } if (!is_array($field) || !isset($field['method']) || !is_string($field['method']) || $field['method'] === '') { throw new InvalidArgumentException(sprintf("Cacti form field '%s' must define a method.", $name)); } } return $fields; } }