diff --git a/README.md b/README.md index c822625..63eae75 100644 --- a/README.md +++ b/README.md @@ -102,8 +102,20 @@ Type|Description `Type::BOOLEAN`|Value must be a boolean or ([**considered bool for GET rules**](#boolean-coercion-from-string-for-search-parameters)) `Type::ARRAY`|Value must be a JSON array `Type::OBJECT`|Value must be a JSON object +`Type::ENUM`|Value must be exactly one of pre-defined values ([**more information**](#typeenum)) `Type::NULL`|Value must be null or ([**considered null for GET rules**](#null-coercion-from-string-for-search-parameters)) +#### `Type::ENUM` + +Provided value for property must be an exact match of any value provided as an `array` to the second argument of `type(Type::ENUM, )` +```php +Rules->type(Type::ENUM, [ + "FOO", + "BAR" +]); +``` +Any value that isn't `"FOO"` or `"BAR"` will be rejected. + #### Boolean coercion from string for search parameters Search parameters are read as strings, a boolean is therefor coerced from the following rules. diff --git a/src/Rules.php b/src/Rules.php index a08ca59..c498ccb 100644 --- a/src/Rules.php +++ b/src/Rules.php @@ -10,12 +10,13 @@ enum Type { use xEnum; + case NULL; + case ENUM; + case ARRAY; case NUMBER; case STRING; - case BOOLEAN; - case ARRAY; case OBJECT; - case NULL; + case BOOLEAN; } class Rules { @@ -28,6 +29,8 @@ // Typed array of type ReflectRules\Type public ?array $types = null; + public ?array $enum = null; + private bool $default_enabled = false; public mixed $default; @@ -72,8 +75,16 @@ return $this; } - // Set property Types - public function type(Type $type): self { + // Add Type constraint with optional argument + public function type(Type $type, mixed $arg = null): self { + if ($type === Type::ENUM) { + if (!is_array($arg)) { + throw new \Exception("Expected type 'array' of ENUM values as second argument"); + } + + $this->enum = $arg; + } + $this->types[] = $type; return $this; } @@ -121,6 +132,11 @@ return is_bool($value); } + private function eval_type_enum(mixed $value): bool { + // Return true if value isn't boolean and exists in enum array + return !is_bool($value) && in_array($value, $this->enum); + } + /* ## Public eval methods These are the entry-point eval methods that in turn can call other @@ -152,6 +168,7 @@ Type::BOOLEAN => $match = $this->eval_type_boolean($value, $scope), Type::ARRAY, Type::OBJECT => $match = is_array($value), + Type::ENUM => $match = $this->eval_type_enum($value), Type::NULL => $match = is_null($value) }; diff --git a/src/Ruleset.php b/src/Ruleset.php index d9b9193..57f1a1e 100644 --- a/src/Ruleset.php +++ b/src/Ruleset.php @@ -84,6 +84,12 @@ // List names of each allowed type $types = implode(" or ", array_map(fn($type): string => $type->name, $rules->types)); + // List allowed enum values + if ($rules->enum) { + $values = implode(" or ", array_map(fn($value): string => "'{$value}'", $rules->enum)); + $this->add_error($name, "Value must be exactly: {$values}"); + } + $this->add_error($name, "Value must be of type {$types}"); }