feat: coerce primitive boolean from strings for GET rules (#3)

* feat: coerce bool primitive from strings

* fix(doc): GitHub md highliting
This commit is contained in:
Victor Westerlund 2023-11-20 15:05:52 +01:00 committed by GitHub
parent d14d4bca6a
commit 7fc27ff89c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 68 additions and 23 deletions

View file

@ -114,7 +114,7 @@ Value|Coerced to
Any other value will cause the `type()` rule to fail. Any other value will cause the `type()` rule to fail.
> **Note** > [!IMPORTANT]
> This coercion is only applies for `Ruleset->GET()`. `Ruleset->POST()` will enforce real `true` and `type` values since it's JSON > This coercion is only applies for `Ruleset->GET()`. `Ruleset->POST()` will enforce real `true` and `type` values since it's JSON
#### Null coercion from string for search parameters #### Null coercion from string for search parameters
@ -122,7 +122,7 @@ Search parameters are read as strings, a null value is therefor coerced from an
Any value that isn't an empty string will cause the `type()` rule to fail. Any value that isn't an empty string will cause the `type()` rule to fail.
> **Note** > [!IMPORTANT]
> This coercion is only applies for `Ruleset->GET()`. `Ruleset->POST()` will enforce the real `null` value since it's JSON > This coercion is only applies for `Ruleset->GET()`. `Ruleset->POST()` will enforce the real `null` value since it's JSON
## `min()` ## `min()`

View file

@ -4,6 +4,8 @@
use \victorwesterlund\xEnum; use \victorwesterlund\xEnum;
use \ReflectRules\Scope;
// Supported types for is_type() // Supported types for is_type()
enum Type { enum Type {
use xEnum; use xEnum;
@ -80,17 +82,60 @@
Methods are not called until all rules have been defined. Methods are not called until all rules have been defined.
*/ */
public function eval_required(array $scope): bool { private function eval_type_boolean(mixed $value, Scope $scope): bool {
return array_key_exists($this->property, $scope); // Coerce $value to bool primitive from string for GET parameters
if ($scope === Scope::GET) {
switch ($value) {
case "true":
case "1":
case "on":
case "yes":
$value = true;
break;
case "false":
case "0":
case "off":
case "no":
$value = false;
break;
default:
$value = null;
} }
public function eval_type(mixed $value): bool { // Mutate value on superglobal from string to primitive
$GLOBALS[$scope->value][$this->property] = $value;
}
return is_bool($value);
}
/*
## Public eval methods
These are the entry-point eval methods that in turn can call other
helper methods for fine-graned validation.
*/
public function eval_required(Scope $scope): bool {
$scope_data = &$GLOBALS[$scope->value];
if (array_key_exists($this->property, $scope_data)) {
return true;
}
// Property does not exist in scope, create nulled superglobal for it
$scope_data[$this->property] = null;
return false;
}
public function eval_type(mixed $value, Scope $scope): bool {
return match($this->type) { return match($this->type) {
Type::NUMBER => is_numeric($value), Type::NUMBER => is_numeric($value),
Type::STRING => is_string($value), Type::STRING => is_string($value),
Type::BOOLEAN => is_bool($value), Type::BOOLEAN => $this->eval_type_boolean($value, $scope),
Type::ARRAY => is_array($value), Type::ARRAY,
Type::OBJECT => $this->eval_object($value), Type::OBJECT => is_array($value),
Type::NULL => is_null($value), Type::NULL => is_null($value),
default => true default => true
}; };
@ -115,9 +160,4 @@
default => true default => true
}; };
} }
// TODO: Recursive Rules eval of multidimensional object
public function eval_object(mixed $object): bool {
return is_array($object);
}
} }

View file

@ -11,6 +11,12 @@
require_once "Rules.php"; require_once "Rules.php";
// Available superglobal scopes
enum Scope: string {
case GET = "_GET";
case POST = "_POST";
}
class Ruleset { class Ruleset {
// This plugin will return exit with a Reflect\Response if errors are found // This plugin will return exit with a Reflect\Response if errors are found
private bool $exit_on_errors; private bool $exit_on_errors;
@ -52,14 +58,12 @@
} }
// Evaluate Rules against a given value // Evaluate Rules against a given value
private function eval_rules(Rules $rules, array &$scope): void { private function eval_rules(Rules $rules, Scope $scope): void {
// Get the name of the current property being evaluated
$name = $rules->get_property_name(); $name = $rules->get_property_name();
// Check if property name exists in scope // Check if property name exists in scope
if (!$rules->eval_required($scope)) { if (!$rules->eval_required($scope)) {
// Set property name value on superglobal to null if not defined
$scope[$name] = null;
// Don't perform further processing if the property is optional and not provided // Don't perform further processing if the property is optional and not provided
if (!$rules->required) { if (!$rules->required) {
return; return;
@ -68,14 +72,15 @@
$this->add_error($name, "Value can not be empty"); $this->add_error($name, "Value can not be empty");
} }
$value = $scope[$name]; // Get value from scope for the current property
$value = $GLOBALS[$scope->value][$name];
/* /*
Eval each rule that has been set. Eval each rule that has been set.
The error messages will be returned The error messages will be returned
*/ */
if ($rules->type && !$rules->eval_type($value)) { if ($rules->type && !$rules->eval_type($value, $scope)) {
$this->add_error($name, "Value must be of type '{$rules->type->name}'"); $this->add_error($name, "Value must be of type '{$rules->type->name}'");
} }
@ -89,7 +94,7 @@
} }
// Evaluate all Rules in this Ruleset against values in scope and return true if no errors were found // Evaluate all Rules in this Ruleset against values in scope and return true if no errors were found
private function eval_all_rules(array $rules, array &$scope): bool { private function eval_all_rules(array $rules, Scope $scope): bool {
foreach ($rules as $rule) { foreach ($rules as $rule) {
$this->eval_rules($rule, $scope); $this->eval_rules($rule, $scope);
} }
@ -103,7 +108,7 @@
public function GET(array $rules): true|array|Response { public function GET(array $rules): true|array|Response {
$this->eval_property_name_diff($rules, array_keys($_GET)); $this->eval_property_name_diff($rules, array_keys($_GET));
$is_valid = $this->eval_all_rules($rules, $_GET); $is_valid = $this->eval_all_rules($rules, Scope::GET);
// Return errors as a Reflect\Response // Return errors as a Reflect\Response
if (!$is_valid && $this->exit_on_errors) { if (!$is_valid && $this->exit_on_errors) {
@ -117,7 +122,7 @@
public function POST(array $rules): true|array|Response { public function POST(array $rules): true|array|Response {
$this->eval_property_name_diff($rules, array_keys($_POST)); $this->eval_property_name_diff($rules, array_keys($_POST));
$is_valid = $this->eval_all_rules($rules, $_POST); $is_valid = $this->eval_all_rules($rules, Scope::POST);
// Return errors as a Reflect\Response // Return errors as a Reflect\Response
if (!$is_valid && $this->exit_on_errors) { if (!$is_valid && $this->exit_on_errors) {