diff --git a/README.md b/README.md index 45e5336..200108d 100644 --- a/README.md +++ b/README.md @@ -141,11 +141,11 @@ Type|Description --|-- `Type::NUMERIC`|Value must be a number or a numeric string `Type::STRING`|Value must be a string -`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::BOOLEAN`|Value must be a boolean ([**considered bool for GET rules**](#boolean-coercion-from-string-for-search-parameters)) +`Type::ARRAY`|Value must be a JSON array or ([**CSV for GET rules**](#csv-for-search-parameters)) `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::NULL`|Value must be null ([**considered null for GET rules**](#null-coercion-from-string-for-search-parameters)) Will set a `Error::INVALID_PROPERTY_TYPE` error on the current scope and property if failed, except Type::ENUM that will set a `Error::INVALID_PROPERTY_VALUE` with an array of the valid vaules. @@ -182,6 +182,19 @@ Any other value will cause the `type()` rule to fail. > [!IMPORTANT] > This coercion is only applies for `Ruleset->GET()`. `Ruleset->POST()` will enforce real `true` and `type` values since it's JSON +#### CSV array for search parameters +A CSV string is expected when `Type::ARRAY` is set for a GET rule. + +*Example:* +``` +https://example.com?typeArray=key1,key2,key3 +``` + +Any other value will cause the `type()` rule to fail. + +> [!IMPORTANT] +> This coercion is only applies for `Ruleset->GET()`. `Ruleset->POST()` will enforce a JSON array + #### Null coercion from string for search parameters Search parameters are read as strings, a null value is therefor coerced from an empty string `""`. diff --git a/src/Rules.php b/src/Rules.php index 3958ec6..72447ab 100644 --- a/src/Rules.php +++ b/src/Rules.php @@ -16,8 +16,15 @@ } class Rules { + private const CSV_DELIMITER = ","; + private string $property; + /* + # Rule properties + These properties store rules for an instance of a property + */ + public bool $required = false; // Matched Type against $types array @@ -133,6 +140,40 @@ return !is_bool($value) && in_array($value, $this->enum); } + private function eval_object(mixed $value, Scope $scope): bool { + // Arrays in POST parameters should already be decoded + if ($scope === Scope::POST) { + return is_array($value); + } + + // Decode stringified JSON + $json = json_decode($value); + + // Failed to decode JSON + if ($json === null) { + return false; + } + + // Mutate property on superglobal with decoded JSON + $GLOBALS[Scope::GET->value][$this->property] = $json; + + return true; + } + + private function eval_array(string $value, Scope $scope): bool { + // Arrays in POST parameters should already be decoded + if ($scope === Scope::POST) { + return is_array($value); + } + + // Mutate property on superglobal with decoded CSV if not already an array + if (!is_array($_GET[$this->property])) { + $GLOBALS[Scope::GET->value][$this->property] = explode(self::CSV_DELIMITER, $_GET[$this->property]); + } + + return true; + } + /* ## Public eval methods These are the entry-point eval methods that in turn can call other @@ -162,8 +203,8 @@ Type::NUMBER => $match = is_numeric($value), Type::STRING => $match = is_string($value), Type::BOOLEAN => $match = $this->eval_type_boolean($value, $scope), - Type::ARRAY, - Type::OBJECT => $match = is_array($value), + Type::ARRAY => $match = $this->eval_array($value, $scope), + Type::OBJECT => $match = $this->eval_object($value, $scope), Type::ENUM => $match = $this->eval_type_enum($value), Type::NULL => $match = is_null($value) }; @@ -185,7 +226,7 @@ Type::NUMBER => $this->eval_type($value, $scope) && $value >= $this->min, Type::STRING => $this->eval_type($value, $scope) && strlen($value) >= $this->min, Type::ARRAY, - Type::OBJECT => $this->eval_type($value, $scope) && count($value) >= $this->min, + Type::OBJECT => $this->eval_type($value, $scope) && count($GLOBALS[$scope->value][$this->property]) >= $this->min, default => true }; } @@ -195,7 +236,7 @@ Type::NUMBER => $this->eval_type($value, $scope) && $value <= $this->max, Type::STRING => $this->eval_type($value, $scope) && strlen($value) <= $this->max, Type::ARRAY, - Type::OBJECT => $this->eval_type($value, $scope) && count($value) <= $this->max, + Type::OBJECT => $this->eval_type($value, $scope) && count($GLOBALS[$scope->value][$this->property]) <= $this->max, default => true }; }