Created
July 9, 2026 03:25
-
-
Save masakielastic/4830c6fa583d28ac5ce03673ad65d0f8 to your computer and use it in GitHub Desktop.
filter_var_array の第2引数の配列を生成する関数
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <?php | |
| declare(strict_types=1); | |
| $input = [ | |
| 'email' => 'taro@example.com', | |
| 'age' => '17', | |
| 'message' => '', | |
| ]; | |
| $app_spec = [ | |
| 'email' => [ | |
| 'validate' => 'email', | |
| ], | |
| 'age' => [ | |
| 'validate' => 'int', | |
| 'options' => [ | |
| 'min_range' => 18, | |
| 'max_range' => 120, | |
| ], | |
| ], | |
| 'message' => [ | |
| 'callback' => 'message', | |
| ], | |
| ]; | |
| $callback_registry = [ | |
| 'message' => 'validate_message', | |
| ]; | |
| $filter_descriptors = build_filter_var_array_descriptors( | |
| spec: $app_spec, | |
| callback_registry: $callback_registry, | |
| ); | |
| $result = filter_var_array($input, $filter_descriptors); | |
| var_dump($filter_descriptors); | |
| var_dump($result); | |
| function build_filter_var_array_descriptors( | |
| array $spec, | |
| array $callback_registry = [], | |
| ): array { | |
| $descriptors = []; | |
| foreach ($spec as $field => $field_spec) { | |
| if (!is_string($field)) { | |
| throw new InvalidArgumentException('Field name must be a string.'); | |
| } | |
| if (!is_array($field_spec)) { | |
| throw new InvalidArgumentException("Field spec must be an array: {$field}"); | |
| } | |
| $descriptors[$field] = filter_var_array_descriptor_from_spec( | |
| field: $field, | |
| field_spec: $field_spec, | |
| callback_registry: $callback_registry, | |
| ); | |
| } | |
| return $descriptors; | |
| } | |
| function filter_var_array_descriptor_from_spec( | |
| string $field, | |
| array $field_spec, | |
| array $callback_registry, | |
| ): array { | |
| $kind = filter_descriptor_kind($field, $field_spec); | |
| return match ($kind) { | |
| 'validate' => filter_descriptor( | |
| filter: validation_filter_id(rule_name($field, $field_spec['validate'], 'validate')), | |
| options: filter_options($field_spec), | |
| flags: filter_flags($field_spec), | |
| ), | |
| 'sanitize' => filter_descriptor( | |
| filter: sanitize_filter_id(rule_name($field, $field_spec['sanitize'], 'sanitize')), | |
| options: filter_options($field_spec), | |
| flags: filter_flags($field_spec), | |
| ), | |
| 'callback' => callback_filter_descriptor( | |
| field: $field, | |
| field_spec: $field_spec, | |
| callback: callback_for_rule( | |
| field: $field, | |
| rule_name: rule_name($field, $field_spec['callback'], 'callback'), | |
| callback_registry: $callback_registry, | |
| ), | |
| ), | |
| }; | |
| } | |
| function filter_descriptor_kind(string $field, array $field_spec): string | |
| { | |
| $keys = array_values(array_intersect( | |
| ['validate', 'sanitize', 'callback'], | |
| array_keys($field_spec), | |
| )); | |
| if (count($keys) !== 1) { | |
| throw new InvalidArgumentException( | |
| "Specify exactly one of validate, sanitize, callback: {$field}" | |
| ); | |
| } | |
| return $keys[0]; | |
| } | |
| function filter_descriptor( | |
| int $filter, | |
| array $options = [], | |
| int $flags = 0, | |
| ): array { | |
| $descriptor = [ | |
| 'filter' => $filter, | |
| ]; | |
| if ($options !== []) { | |
| $descriptor['options'] = $options; | |
| } | |
| if ($flags !== 0) { | |
| $descriptor['flags'] = $flags; | |
| } | |
| return $descriptor; | |
| } | |
| function callback_filter_descriptor( | |
| string $field, | |
| array $field_spec, | |
| callable $callback, | |
| ): array { | |
| if (array_key_exists('options', $field_spec) || array_key_exists('flags', $field_spec)) { | |
| throw new InvalidArgumentException( | |
| "callback rule does not accept options or flags: {$field}" | |
| ); | |
| } | |
| return [ | |
| 'filter' => FILTER_CALLBACK, | |
| 'options' => $callback, | |
| ]; | |
| } | |
| function rule_name(string $field, mixed $value, string $key): string | |
| { | |
| if (!is_string($value) || $value === '') { | |
| throw new InvalidArgumentException( | |
| "{$key} rule name must be a non-empty string: {$field}" | |
| ); | |
| } | |
| return $value; | |
| } | |
| function filter_options(array $field_spec): array | |
| { | |
| $options = $field_spec['options'] ?? []; | |
| if (!is_array($options)) { | |
| throw new InvalidArgumentException('options must be an array.'); | |
| } | |
| return $options; | |
| } | |
| function filter_flags(array $field_spec): int | |
| { | |
| $flags = $field_spec['flags'] ?? 0; | |
| if (!is_int($flags)) { | |
| throw new InvalidArgumentException('flags must be an int.'); | |
| } | |
| return $flags; | |
| } | |
| function callback_for_rule( | |
| string $field, | |
| string $rule_name, | |
| array $callback_registry, | |
| ): callable { | |
| if (!array_key_exists($rule_name, $callback_registry)) { | |
| throw new InvalidArgumentException( | |
| "Unknown callback rule: {$rule_name} for {$field}" | |
| ); | |
| } | |
| $callback = $callback_registry[$rule_name]; | |
| if (!is_callable($callback)) { | |
| throw new InvalidArgumentException( | |
| "Callback rule is not callable: {$rule_name}" | |
| ); | |
| } | |
| return $callback; | |
| } | |
| function validation_filter_id(string $name): int | |
| { | |
| return match ($name) { | |
| 'email' => FILTER_VALIDATE_EMAIL, | |
| 'int' => FILTER_VALIDATE_INT, | |
| 'float' => FILTER_VALIDATE_FLOAT, | |
| 'boolean' => FILTER_VALIDATE_BOOL, | |
| 'url' => FILTER_VALIDATE_URL, | |
| 'ip' => FILTER_VALIDATE_IP, | |
| 'domain' => FILTER_VALIDATE_DOMAIN, | |
| 'mac' => FILTER_VALIDATE_MAC, | |
| 'regexp' => FILTER_VALIDATE_REGEXP, | |
| default => throw new InvalidArgumentException("Unknown validation rule: {$name}"), | |
| }; | |
| } | |
| function sanitize_filter_id(string $name): int | |
| { | |
| return match ($name) { | |
| 'email' => FILTER_SANITIZE_EMAIL, | |
| 'url' => FILTER_SANITIZE_URL, | |
| 'number_int' => FILTER_SANITIZE_NUMBER_INT, | |
| 'number_float' => FILTER_SANITIZE_NUMBER_FLOAT, | |
| 'special_chars' => FILTER_SANITIZE_SPECIAL_CHARS, | |
| 'full_special_chars' => FILTER_SANITIZE_FULL_SPECIAL_CHARS, | |
| 'add_slashes' => FILTER_SANITIZE_ADD_SLASHES, | |
| 'encoded' => FILTER_SANITIZE_ENCODED, | |
| 'unsafe_raw' => FILTER_UNSAFE_RAW, | |
| default => throw new InvalidArgumentException("Unknown sanitize rule: {$name}"), | |
| }; | |
| } | |
| function validate_message(string $value): string|false | |
| { | |
| if ($value === '') { | |
| return false; | |
| } | |
| if (mb_strlen($value, 'UTF-8') > 1000) { | |
| return false; | |
| } | |
| return $value; | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment