diff --git a/app/Controllers/HomeController.php b/app/Controllers/HomeController.php index 5567987..f85bbb7 100644 --- a/app/Controllers/HomeController.php +++ b/app/Controllers/HomeController.php @@ -1,9 +1,9 @@ APP_NAME, 'description' => APP_DESCRIPTION ]); - } } diff --git a/app/Helper/SampleHelper.php b/app/Helper/SampleHelper.php index 75db725..017fe6c 100644 --- a/app/Helper/SampleHelper.php +++ b/app/Helper/SampleHelper.php @@ -4,7 +4,6 @@ class SampleHelper { - /** * @param null $var - string to be uppercase * @return string @@ -13,5 +12,4 @@ public static function uppercase($var = null) { return strtoupper($var); } - } \ No newline at end of file diff --git a/app/Helper/Twig/FunctionExtension.php b/app/Helper/Twig/FunctionExtension.php index 4e69078..69d8829 100644 --- a/app/Helper/Twig/FunctionExtension.php +++ b/app/Helper/Twig/FunctionExtension.php @@ -5,9 +5,9 @@ use Twig\Extension\AbstractExtension; use Twig\TwigFunction; use function Simple\alias; + class FunctionExtension extends AbstractExtension { - /** * Extension for twig functions * Read Documentation at: https://twig.symfony.com/doc/2.x/advanced.html @@ -21,11 +21,9 @@ public function getFunctions() * new TwigFunction($function_name_to_be_called_in_template, [$callable, method_name]) */ new TwigFunction('phpinfo', [$this,'phpinfo']), - new TwigFunction('alias', [$this,'alias']), - + new TwigFunction('alias', [$this,'alias']), ]; } - /** * Methods for Twig Functions @@ -43,4 +41,4 @@ public function alias($var, $param=null) return alias($var, $param); } -} +} \ No newline at end of file diff --git a/app/Helper/Twig/GlobalExtension.php b/app/Helper/Twig/GlobalExtension.php index 2a44d0f..a5832e7 100644 --- a/app/Helper/Twig/GlobalExtension.php +++ b/app/Helper/Twig/GlobalExtension.php @@ -7,12 +7,10 @@ class GlobalExtension extends AbstractExtension implements GlobalsInterface { - public function getGlobals() { return [ 'text' => 'sample text', ]; } - } \ No newline at end of file diff --git a/app/Helper/Validation/BaseValidator.php b/app/Helper/Validation/BaseValidator.php index d65d5b9..91b4d0a 100644 --- a/app/Helper/Validation/BaseValidator.php +++ b/app/Helper/Validation/BaseValidator.php @@ -1,8 +1,11 @@ Street Name). + * + * @var array + */ + public static $field_chars_to_spaces = ['_', '-']; // ** ------------------------- Validation Data ------------------------------- ** // @@ -66,46 +105,85 @@ public static function get_instance(){ very,was,way,we,well,were,what,where,which,while,who,with,would,you,your,a, b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,$,1,2,3,4,5,6,7,8,9,0,_"; - // field characters below will be replaced with a space. - protected $fieldCharsToRemove = array('_', '-'); + private static $alpha_regex = 'a-zÀÁÂÃÄÅÇÈÉÊËÌÍÎÏÒÓÔÕÖßÙÚÛÜÝŸÑàáâãäåçèéêëìíîïðòóôõöùúûüýÿñ'; + + public static $trues = ['1', 1, 'true', true, 'yes', 'on']; + public static $falses = ['0', 0, 'false', false, 'no', 'off']; + /** + * Language for error messages. + * + * @var string + */ protected $lang; + /** + * Custom field-rule messages. + * + * @var array + */ + protected $fields_error_messages = []; + + /** + * Set of validation rules for execution. + * + * @var array + */ + protected $validation_rules = []; + + /** + * Set of filters rules for execution. + * + * @var array + */ + protected $filter_rules = []; + + /** + * Errors. + * + * @var array + */ + protected $errors = []; // ** ------------------------- Validation Helpers ---------------------------- ** // - public function __construct($lang = 'en') + /** + * BaseValidator constructor. + * + * @param string $lang + * @throws Exception when language is not supported + */ + public function __construct(string $lang = 'en') { - if ($lang) { - $lang_file = __DIR__.DIRECTORY_SEPARATOR.'lang'.DIRECTORY_SEPARATOR.$lang.'.php'; - - if (file_exists($lang_file)) { - $this->lang = $lang; - } else { - throw new \Exception('Language with key "'.$lang.'" does not exist'); - } + $lang_file_location = __DIR__.DIRECTORY_SEPARATOR.'lang'.DIRECTORY_SEPARATOR.$lang.'.php'; + + if (!EnvHelpers::file_exists($lang_file_location)) { + throw new Exception(sprintf("'%s' language is not supported.", $lang)); } + + $this->lang = $lang; } /** * Shorthand method for inline validation. * - * @param array $data The data to be validated - * @param array $validators The GUMP validators - * @throws + * @param array $data The data to be validated + * @param array $validators The BaseValidator validators + * @param array $fields_error_messages * @return mixed True(boolean) or the array of error messages + * @throws Exception If validation rule does not exist */ - public static function is_valid(array $data, array $validators) + public static function is_valid(array $data, array $validators, array $fields_error_messages = []) { - $gump = self::get_instance(); + $baseValidator = self::get_instance(); + $baseValidator->validation_rules($validators); + $baseValidator->set_fields_error_messages($fields_error_messages); - $gump->validation_rules($validators); - - if ($gump->run($data) === false) { - return $gump->get_readable_errors(false); - } else { - return true; + if ($baseValidator->run($data) === false) { + return $baseValidator->get_readable_errors(); } + + return true; } /** @@ -113,20 +191,20 @@ public static function is_valid(array $data, array $validators) * * @param array $data * @param array $filters - * * @return mixed + * @throws Exception If filter does not exist */ public static function filter_input(array $data, array $filters) { - $gump = self::get_instance(); - - return $gump->filter($data, $filters); + $baseValidator = self::get_instance(); + return $baseValidator->filter($data, $filters); } /** * Magic method to generate the validation error messages. * * @return string + * @throws Exception */ public function __toString() { @@ -136,10 +214,7 @@ public function __toString() /** * Perform XSS clean to prevent cross site scripting. * - * @static - * * @param array $data - * * @return array */ public static function xss_clean(array $data) @@ -151,85 +226,80 @@ public static function xss_clean(array $data) return $data; } + /** + * An empty value for us is: null, empty string or empty array + * + * @param $value + * @return bool + */ + public static function is_empty($value) + { + return (is_null($value) || $value === '' || (is_array($value) && count($value) === 0)); + } + /** * Adds a custom validation rule using a callback function. * - * @param string $rule + * @param string $rule * @param callable $callback - * @param string $error_message - * - * @return bool + * @param string $error_message * - * @throws Exception + * @return void + * @throws Exception when validator with the same name already exists */ - public static function add_validator($rule, $callback, $error_message = null) + public static function add_validator(string $rule, callable $callback, string $error_message) { - $method = 'validate_'.$rule; - - if (method_exists(__CLASS__, $method) || isset(self::$validation_methods[$rule])) { - throw new \Exception("Validator rule '$rule' already exists."); + if (method_exists(__CLASS__, self::validator_to_method($rule)) || isset(self::$validation_methods[$rule])) { + throw new Exception(sprintf("'%s' validator is already defined.", $rule)); } self::$validation_methods[$rule] = $callback; - if ($error_message) { - self::$validation_methods_errors[$rule] = $error_message; - } - - return true; + self::$validation_methods_errors[$rule] = $error_message; } /** * Adds a custom filter using a callback function. * - * @param string $rule + * @param string $rule * @param callable $callback * - * @return bool - * - * @throws Exception + * @return void + * @throws Exception when filter with the same name already exists */ - public static function add_filter($rule, $callback) + public static function add_filter(string $rule, callable $callback) { - $method = 'filter_'.$rule; - - if (method_exists(__CLASS__, $method) || isset(self::$filter_methods[$rule])) { - throw new \Exception("Filter rule '$rule' already exists."); + if (method_exists(__CLASS__, self::filter_to_method($rule)) || isset(self::$filter_methods[$rule])) { + throw new Exception(sprintf("'%s' filter is already defined.", $rule)); } self::$filter_methods[$rule] = $callback; - - return true; } /** * Helper method to extract an element from an array safely * - * @param mixed $key - * @param array $array - * @param mixed $default + * @param mixed $key + * @param array $array + * @param mixed $default + * * @return mixed */ public static function field($key, array $array, $default = null) { - if(!is_array($array)) { - return null; - } + if (isset($array[$key])) { + return $array[$key]; + } - if(isset($array[$key])) { - return $array[$key]; - } else { return $default; - } } /** * Getter/Setter for the validation rules. * * @param array $rules - * * @return array */ - public function validation_rules(array $rules = array()) + public function validation_rules(array $rules = []) { if (empty($rules)) { return $this->validation_rules; @@ -238,14 +308,24 @@ public function validation_rules(array $rules = array()) $this->validation_rules = $rules; } + /** + * Set field-rule specific error messages. + * + * @param array $fields_error_messages + * @return array + */ + public function set_fields_error_messages(array $fields_error_messages) + { + return $this->fields_error_messages = $fields_error_messages; + } + /** * Getter/Setter for the filter rules. * * @param array $rules - * * @return array */ - public function filter_rules(array $rules = array()) + public function filter_rules(array $rules = []) { if (empty($rules)) { return $this->filter_rules; @@ -257,23 +337,17 @@ public function filter_rules(array $rules = array()) /** * Run the filtering and validation after each other. * - * @param array $data - * @param bool $check_fields - * @param string $rules_delimiter - * @param string $parameters_delimiters - * - * @return array + * @param array $data + * @param bool $check_fields * + * @return array|bool * @throws Exception */ - public function run(array $data, $check_fields = false, $rules_delimiter='|', $parameters_delimiters=',') + public function run(array $data, $check_fields = false) { - $data = $this->filter($data, $this->filter_rules(), $rules_delimiter, $parameters_delimiters); + $data = $this->filter($data, $this->filter_rules()); - $validated = $this->validate( - $data, $this->validation_rules(), - $rules_delimiter, $parameters_delimiters - ); + $validated = $this->validate($data, $this->validation_rules()); if ($check_fields === true) { $this->check_fields($data); @@ -298,12 +372,7 @@ private function check_fields(array $data) $fields = array_keys($mismatch); foreach ($fields as $field) { - $this->errors[] = array( - 'field' => $field, - 'value' => $data[$field], - 'rule' => 'mismatch', - 'param' => null, - ); + $this->errors[] = $this->generate_error_array($field, $data[$field], 'mismatch'); } } @@ -311,51 +380,45 @@ private function check_fields(array $data) * Sanitize the input data. * * @param array $input - * @param null $fields - * @param bool $utf8_encode + * @param array $fields + * @param bool $utf8_encode * * @return array */ - public function sanitize(array $input, array $fields = array(), $utf8_encode = true) + public function sanitize(array $input, array $fields = [], bool $utf8_encode = true) { - $magic_quotes = (bool) get_magic_quotes_gpc(); - if (empty($fields)) { $fields = array_keys($input); } - $return = array(); + $return = []; foreach ($fields as $field) { if (!isset($input[$field])) { continue; - } else { - $value = $input[$field]; - if (is_array($value)) { - $value = $this->sanitize($value); - } - if (is_string($value)) { - if ($magic_quotes === true) { - $value = stripslashes($value); - } + } - if (strpos($value, "\r") !== false) { - $value = trim($value); - } + $value = $input[$field]; + if (is_array($value)) { + $value = $this->sanitize($value, [], $utf8_encode); + } + if (is_string($value)) { + if (strpos($value, "\r") !== false) { + $value = trim($value); + } - if (function_exists('iconv') && function_exists('mb_detect_encoding') && $utf8_encode) { - $current_encoding = mb_detect_encoding($value); + if (function_exists('iconv') && function_exists('mb_detect_encoding') && $utf8_encode) { + $current_encoding = mb_detect_encoding($value); - if ($current_encoding != 'UTF-8' && $current_encoding != 'UTF-16') { - $value = iconv($current_encoding, 'UTF-8', $value); - } + if ($current_encoding !== 'UTF-8' && $current_encoding !== 'UTF-16') { + $value = iconv($current_encoding, 'UTF-8', $value); } - - $value = filter_var($value, FILTER_SANITIZE_STRING); } - $return[$field] = $value; + $value = filter_var($value, FILTER_SANITIZE_STRING); } + + $return[$field] = $value; } return $return; @@ -373,97 +436,34 @@ public function errors() /** * Perform data validation against the provided ruleset. - * If any rule's parameter contains either '|' or ',', the corresponding default separator can be changed * - * @param mixed $input - * @param array $ruleset - * @param string $rules_delimiter - * @param string $parameters_delimiter - * - * @return mixed + * @param array $input Input data. + * @param array $ruleset Validation rules. * + * @return bool|array Returns bool true when no errors. Returns array when errors. * @throws Exception */ - public function validate(array $input, array $ruleset, $rules_delimiter='|', $parameters_delimiter=',') + public function validate(array $input, array $ruleset) { - $this->errors = array(); - - foreach ($ruleset as $field => $rules) { + $this->errors = []; - $rules = explode($rules_delimiter, $rules); + foreach ($ruleset as $field => $rawRules) { + $input[$field] = ArrayHelpers::data_get($input, $field); - $look_for = array('required_file', 'required'); + $rules = $this->parse_rules($rawRules); + $is_required = $this->field_has_required_rules($rules); - if (count(array_intersect($look_for, $rules)) > 0 || (isset($input[$field]))) { + if (!$is_required && self::is_empty($input[$field])) { + continue; + } - if (isset($input[$field])) { - if (is_array($input[$field]) && in_array('required_file', $ruleset)) { - $input_array = $input[$field]; - } else { - $input_array = array($input[$field]); - } - } else { - $input_array = array(''); - } + foreach ($rules as $rule) { + $parsed_rule = $this->parse_rule($rule); + $result = $this->foreach_call_validator($parsed_rule['rule'], $field, $input, $parsed_rule['param']); - foreach ($input_array as $value) { - - $input[$field] = $value; - - foreach ($rules as $rule) { - $method = null; - $param = null; - - // Check if we have rule parameters - if (strstr($rule, $parameters_delimiter) !== false) { - $rule = explode($parameters_delimiter, $rule); - $method = 'validate_'.$rule[0]; - $param = $rule[1]; - $rule = $rule[0]; - - // If there is a reference to a field - if (preg_match('/(?:(?:^|;)_([a-z_]+))/', $param, $matches)) { - - // If provided parameter is a field - if (isset($input[$matches[1]])) { - $param = str_replace('_'.$matches[1], $input[$matches[1]], $param); - } - } - } else { - $method = 'validate_'.$rule; - } - - //self::$validation_methods[$rule] = $callback; - - if (is_callable(array($this, $method))) { - $result = $this->$method( - $field, $input, $param - ); - - if (is_array($result)) { - if (array_search($result['field'], array_column($this->errors, 'field')) === false) { - $this->errors[] = $result; - } - } - - } elseif(isset(self::$validation_methods[$rule])) { - $result = call_user_func(self::$validation_methods[$rule], $field, $input, $param); - - if($result === false) { - if (array_search($result['field'], array_column($this->errors, 'field')) === false) { - $this->errors[] = array( - 'field' => $field, - 'value' => $input[$field], - 'rule' => $rule, - 'param' => $param, - ); - } - } - - } else { - throw new \Exception("Validator method '$method' does not exist."); - } - } + if (is_array($result)) { + $this->errors[] = $result; + break; // exit on first error } } } @@ -472,225 +472,446 @@ public function validate(array $input, array $ruleset, $rules_delimiter='|', $pa } /** - * Set a readable name for a specified field names. + * Parses filters and validators rules group. * - * @param string $field - * @param string $readable_name + * @param string|array $rules + * @return array */ - public static function set_field_name($field, $readable_name) + private function parse_rules($rules) { - self::$fields[$field] = $readable_name; + // v2 + if (is_array($rules)) { + $rules_names = []; + foreach ($rules as $key => $value) { + $rules_names[] = is_numeric($key) ? $value : $key; + } + + return array_map(function($value, $key) use($rules) { + if ($value === $key) { + return [ $key ]; + } + + return [$key, $value]; + }, $rules, $rules_names); + } + + return explode(self::$rules_delimiter, $rules); } /** - * Set readable name for specified fields in an array. - * - * Usage: - * - * GUMP::set_field_names(array( - * "name" => "My Lovely Name", - * "username" => "My Beloved Username", - * )); + * Parses filters and validators individual rules. * - * @param array $array + * @param string|array $rule + * @return array */ - public static function set_field_names(array $array) + private function parse_rule($rule) { - foreach ($array as $field => $readable_name) { - self::set_field_name($field, $readable_name); + // v2 + if (is_array($rule)) { + return [ + 'rule' => $rule[0], + 'param' => $this->parse_rule_params($rule[1] ?? []) + ]; + } + + $result = [ + 'rule' => $rule, + 'param' => [] + ]; + + if (strpos($rule, self::$rules_parameters_delimiter) !== false) { + list($rule, $param) = explode(self::$rules_parameters_delimiter, $rule); + + $result['rule'] = $rule; + $result['param'] = $this->parse_rule_params($param); } + + return $result; } /** - * Set a custom error message for a validation rule. + * Parse rule parameters. * - * @param string $rule - * @param string $message + * @param string|array $param + * @return array|string|null */ - public static function set_error_message($rule, $message) + private function parse_rule_params($param) { - $gump = self::get_instance(); - self::$validation_methods_errors[$rule] = $message; + if (is_array($param)) { + return $param; + } + + if (strpos($param, self::$rules_parameters_arrays_delimiter) !== false) { + return explode(self::$rules_parameters_arrays_delimiter, $param); + } + + return [ $param ]; } /** - * Set custom error messages for validation rules in an array. - * - * Usage: + * Checks if array of rules contains a required type of validator. * - * GUMP::set_error_messages(array( - * "validate_required" => "{field} is required", - * "validate_valid_email" => "{field} must be a valid email", - * )); - * - * @param array $array + * @param array $rules + * @return bool */ - public static function set_error_messages(array $array) + private function field_has_required_rules(array $rules) { - foreach ($array as $rule => $message) { - self::set_error_message($rule, $message); + $require_type_of_rules = ['required', 'required_file']; + + // v2 + if (is_array($rules) && is_array($rules[0])) { + $found = array_filter($rules, function($item) use($require_type_of_rules) { + return in_array($item[0], $require_type_of_rules); + }); + return count($found) > 0; } + + $found = array_values(array_intersect($require_type_of_rules, $rules)); + return count($found) > 0; } /** - * Get error messages. + * Helper to convert validator rule name to validator rule method name. * - * @return array + * @param string $rule + * @return string */ - protected function get_messages() + private static function validator_to_method(string $rule) { - $lang_file = __DIR__.DIRECTORY_SEPARATOR.'lang'.DIRECTORY_SEPARATOR.$this->lang.'.php'; - $messages = require $lang_file; - - if ($validation_methods_errors = self::$validation_methods_errors) { - $messages = array_merge($messages, $validation_methods_errors); - } - return $messages; + return sprintf('validate_%s', $rule); } /** - * Process the validation errors and return human readable error messages. - * - * @param bool $convert_to_string = false - * @param string $field_class - * @param string $error_class + * Helper to convert filter rule name to filter rule method name. * - * @return array + * @param string $rule * @return string */ - public function get_readable_errors($convert_to_string = false, $field_class = 'gump-field', $error_class = 'gump-error-message') + private static function filter_to_method(string $rule) { - if (empty($this->errors)) { - return ($convert_to_string) ? null : array(); - } - - $resp = array(); - - // Error messages - $messages = $this->get_messages(); - - foreach ($this->errors as $e) { - $field = ucwords(str_replace($this->fieldCharsToRemove, chr(32), $e['field'])); - $param = $e['param']; - - // Let's fetch explicitly if the field names exist - if (array_key_exists($e['field'], self::$fields)) { - $field = self::$fields[$e['field']]; + return sprintf('filter_%s', $rule); + } - // If param is a field (i.e. equalsfield validator) - if (array_key_exists($param, self::$fields)) { - $param = self::$fields[$e['param']]; - } - } + /** + * Calls call_validator. + * + * @param string $rule + * @param string $field + * @param mixed $input + * @param array $rule_params + * @return array|bool + * @throws Exception + */ + private function foreach_call_validator(string $rule, string $field, array $input, array $rule_params = []) + { + $values = !is_array($input[$field]) ? [ $input[$field] ] : $input[$field]; - // Messages - if (isset($messages[$e['rule']])) { - if (is_array($param)) { - $param = implode(', ', $param); - } - $message = str_replace('{param}', $param, str_replace('{field}', ''.$field.'', $messages[$e['rule']])); - $resp[] = $message; - } else { - throw new \Exception ('Rule "'.$e['rule'].'" does not have an error message'); + foreach ($values as $value) { + $result = $this->call_validator($rule, $field, $input, $rule_params, $value); + if (is_array($result)) { + return $result; } } - if (!$convert_to_string) { - return $resp; - } else { - $buffer = ''; - foreach ($resp as $s) { - $buffer .= "$s"; - } - return $buffer; - } + return true; } /** - * Process the validation errors and return an array of errors with field names as keys. - * - * @param $convert_to_string + * Calls a validator. * - * @return array | null (if empty) + * @param string $rule + * @param string $field + * @param mixed $input + * @param array $rule_params + * @return array|bool + * @throws Exception */ - public function get_errors_array($convert_to_string = null) + private function call_validator(string $rule, string $field, array $input, array $rule_params = [], $value = null) { - if (empty($this->errors)) { - return ($convert_to_string) ? null : array(); - } - - $resp = array(); - - // Error messages - $messages = $this->get_messages(); + $method = self::validator_to_method($rule); - foreach ($this->errors as $e) - { - $field = ucwords(str_replace(array('_', '-'), chr(32), $e['field'])); - $param = $e['param']; + // use native validations + if (is_callable([$this, $method])) { + $result = $this->$method($field, $input, $rule_params, $value); - // Let's fetch explicitly if the field names exist - if (array_key_exists($e['field'], self::$fields)) { - $field = self::$fields[$e['field']]; + // is_array check for backward compatibility + return (is_array($result) || $result === false) + ? $this->generate_error_array($field, $input[$field], $rule, $rule_params) + : true; + } - // If param is a field (i.e. equalsfield validator) - if (array_key_exists($param, self::$fields)) { - $param = self::$fields[$e['param']]; - } - } + // use custom validations + if (isset(self::$validation_methods[$rule])) { + $result = call_user_func(self::$validation_methods[$rule], $field, $input, $rule_params, $value); - // Messages - if (isset($messages[$e['rule']])) { - // Show first validation error and don't allow to be overwritten - if (!isset($resp[$e['field']])) { - if (is_array($param)) { - $param = implode(', ', $param); - } - $message = str_replace('{param}', $param, str_replace('{field}', $field, $messages[$e['rule']])); - $resp[$e['field']] = $message; - } - } else { - throw new \Exception ('Rule "'.$e['rule'].'" does not have an error message'); - } + return ($result === false) + ? $this->generate_error_array($field, $input[$field], $rule, $rule_params) + : true; } - return $resp; + throw new Exception(sprintf("'%s' validator does not exist.", $rule)); } /** - * Filter the input data according to the specified filter set. - * If any filter's parameter contains either '|' or ',', the corresponding default separator can be changed - * - * @param mixed $input - * @param array $filterset - * @param string $filters_delimeter - * @param string $parameters_delimiter - * - * @throws Exception + * Calls a filter. * + * @param string $rule + * @param mixed $value + * @param array $rule_params * @return mixed - * * @throws Exception */ - public function filter(array $input, array $filterset, $filters_delimeter='|', $parameters_delimiter=',') + private function call_filter(string $rule, $value, array $rule_params = []) { - foreach ($filterset as $field => $filters) { - if (!array_key_exists($field, $input)) { - continue; - } + $method = self::filter_to_method($rule); - $filters = explode($filters_delimeter, $filters); + // use native filters + if (is_callable(array($this, $method))) { + return $this->$method($value, $rule_params); + } - foreach ($filters as $filter) { - $params = null; + // use custom filters + if (isset(self::$filter_methods[$rule])) { + return call_user_func(self::$filter_methods[$rule], $value, $rule_params); + } - if (strstr($filter, $parameters_delimiter) !== false) { - $filter = explode($parameters_delimiter, $filter); + // use php functions as filters + if (function_exists($rule)) { + return call_user_func($rule, $value, ...$rule_params); + } - $params = array_slice($filter, 1, count($filter) - 1); + throw new Exception(sprintf("'%s' filter does not exist.", $rule)); + } - $filter = $filter[0]; - } + /** + * Generates error array. + * + * @param string $field + * @param mixed $value + * @param string $rule + * @param array $rule_params + * @return array + */ + private function generate_error_array(string $field, $value, string $rule, array $rule_params = []) + { + return [ + 'field' => $field, + 'value' => $value, + 'rule' => $rule, + 'params' => $rule_params + ]; + } + + /** + * Set a readable name for a specified field names. + * + * @param string $field + * @param string $readable_name + */ + public static function set_field_name(string $field, string $readable_name) + { + self::$fields[$field] = $readable_name; + } + + /** + * Set readable name for specified fields in an array. + * + * @param array $array + */ + public static function set_field_names(array $array) + { + foreach ($array as $field => $readable_name) { + self::set_field_name($field, $readable_name); + } + } + + /** + * Set a custom error message for a validation rule. + * + * @param string $rule + * @param string $message + */ + public static function set_error_message(string $rule, string $message) + { + self::$validation_methods_errors[$rule] = $message; + } + + /** + * Set custom error messages for validation rules in an array. + * + * @param array $array + */ + public static function set_error_messages(array $array) + { + foreach ($array as $rule => $message) { + self::set_error_message($rule, $message); + } + } + + /** + * Get all error messages. + * + * @return array + */ + protected function get_messages() + { + $lang_file = __DIR__.DIRECTORY_SEPARATOR.'lang'.DIRECTORY_SEPARATOR.$this->lang.'.php'; + $messages = include $lang_file; + + return array_merge($messages, self::$validation_methods_errors); + } + + /** + * Get error message. + * + * @param array $messages + * @param string $field + * @param string $rule + * @return mixed|null + * @throws Exception + */ + private function get_error_message(array $messages, string $field, string $rule) + { + $custom_error_message = $this->get_custom_error_message($field, $rule); + if ($custom_error_message !== null) { + return $custom_error_message; + } + + if (isset($messages[$rule])) { + return $messages[$rule]; + } + + throw new Exception(sprintf("'%s' validator does not have an error message.", $rule)); + } + + /** + * Get custom error message for field and rule. + * + * @param string $field + * @param string $rule + * @return string|null + */ + private function get_custom_error_message(string $field, string $rule) + { + $rule_name = str_replace('validate_', '', $rule); + return $this->fields_error_messages[$field][$rule_name] ?? null; + } + + /** + * Process error message string. + * + * @param $field + * @param array $params + * @param string $message + * @param callable|null $transformer + * @return string + */ + private function process_error_message($field, array $params, string $message, callable $transformer = null) + { + // if field name is explicitly set, use it + if (array_key_exists($field, self::$fields)) { + $field = self::$fields[$field]; + } else { + $field = ucwords(str_replace(self::$field_chars_to_spaces, chr(32), $field)); + } + + // if param is a field (i.e. equalsfield validator) + if (isset($params[0]) && array_key_exists($params[0], self::$fields)) { + $params[0] = self::$fields[$params[0]]; + } + + $replace = [ + '{field}' => $field, + '{param}' => implode(', ', $params) + ]; + + foreach ($params as $key => $value) { + $replace[sprintf('{param[%s]}', $key)] = $value; + } + + // for get_readable_errors() + if ($transformer) { + $replace = $transformer($replace); + } + + return strtr($message, $replace); + } + + /** + * Process the validation errors and return human readable error messages. + * + * @param bool $convert_to_string = false + * @param string $field_class + * @param string $error_class + * @return array|string + * @throws Exception if validator doesn't have an error message to set + */ + public function get_readable_errors(bool $convert_to_string = false, string $field_class = 'gump-field', string $error_class = 'gump-error-message') + { + if (empty($this->errors)) { + return $convert_to_string ? '' : []; + } + + $messages = $this->get_messages(); + $result = []; + + $transformer = static function($replace) use($field_class) { + $replace['{field}'] = sprintf('%s', $field_class, $replace['{field}']); + return $replace; + }; + + foreach ($this->errors as $error) { + $message = $this->get_error_message($messages, $error['field'], $error['rule']); + $result[] = $this->process_error_message($error['field'], $error['params'], $message, $transformer); + } + + if ($convert_to_string) { + return array_reduce($result, static function($prev, $next) use($error_class) { + return sprintf('%s%s', $prev, $error_class, $next); + }); + } + + return $result; + } + + /** + * Process the validation errors and return an array of errors with field names as keys. + * + * @return array + * @throws Exception + */ + public function get_errors_array() + { + $messages = $this->get_messages(); + $result = []; + + foreach ($this->errors as $error) { + $message = $this->get_error_message($messages, $error['field'], $error['rule']); + $result[$error['field']] = $this->process_error_message($error['field'], $error['params'], $message); + } + + return $result; + } + + /** + * Filter the input data according to the specified filter set. + * + * @param mixed $input + * @param array $filterset + * @return mixed + * @throws Exception + */ + public function filter(array $input, array $filterset) + { + foreach ($filterset as $field => $filters) { + if (!array_key_exists($field, $input)) { + continue; + } + + $filters = $this->parse_rules($filters); + + foreach ($filters as $filter) { + $parsed_rule = $this->parse_rule($filter); if (is_array($input[$field])) { $input_array = &$input[$field]; @@ -699,16 +920,7 @@ public function filter(array $input, array $filterset, $filters_delimeter='|', $ } foreach ($input_array as &$value) { - if (is_callable(array($this, 'filter_'.$filter))) { - $method = 'filter_'.$filter; - $value = $this->$method($value, $params); - } elseif (function_exists($filter)) { - $value = $filter($value); - } elseif (isset(self::$filter_methods[$filter])) { - $value = call_user_func(self::$filter_methods[$filter], $value, $params); - } else { - throw new \Exception("Filter method '$filter' does not exist."); - } + $value = $this->call_filter($parsed_rule['rule'], $value, $parsed_rule['param']); } } } @@ -721,14 +933,12 @@ public function filter(array $input, array $filterset, $filters_delimeter='|', $ /** * Replace noise words in a string (http://tax.cchgroup.com/help/Avoiding_noise_words_in_your_search.htm). * - * Usage: '' => 'noise_words' - * * @param string $value * @param array $params * * @return string */ - protected function filter_noise_words($value, $params = null) + protected function filter_noise_words($value, array $params = []) { $value = preg_replace('/\s\s+/u', chr(32), $value); @@ -752,102 +962,110 @@ protected function filter_noise_words($value, $params = null) /** * Remove all known punctuation from a string. * - * Usage: '' => 'rmpunctuataion' - * * @param string $value * @param array $params * * @return string */ - protected function filter_rmpunctuation($value, $params = null) + protected function filter_rmpunctuation($value, array $params = []) { return preg_replace("/(?![.=$'€%-])\p{P}/u", '', $value); } /** - * Sanitize the string by removing any script tags. - * - * Usage: '' => 'sanitize_string' + * Sanitize the string by urlencoding characters. * * @param string $value * @param array $params * * @return string */ - protected function filter_sanitize_string($value, $params = null) + protected function filter_urlencode($value, array $params = []) { - return filter_var($value, FILTER_SANITIZE_STRING); + return filter_var($value, FILTER_SANITIZE_ENCODED); } /** - * Sanitize the string by urlencoding characters. - * - * Usage: '' => 'urlencode' + * Sanitize the string by converting HTML characters to their HTML entities. * * @param string $value * @param array $params * * @return string */ - protected function filter_urlencode($value, $params = null) + protected function filter_htmlencode($value, array $params = []) { - return filter_var($value, FILTER_SANITIZE_ENCODED); + return filter_var($value, FILTER_SANITIZE_SPECIAL_CHARS); } /** - * Sanitize the string by converting HTML characters to their HTML entities. - * - * Usage: '' => 'htmlencode' + * Sanitize the string by removing illegal characters from emails. * * @param string $value * @param array $params * * @return string */ - protected function filter_htmlencode($value, $params = null) + protected function filter_sanitize_email($value, array $params = []) { - return filter_var($value, FILTER_SANITIZE_SPECIAL_CHARS); + return filter_var($value, FILTER_SANITIZE_EMAIL); } /** - * Sanitize the string by removing illegal characters from emails. + * Sanitize the string by removing illegal characters from numbers. * - * Usage: '' => 'sanitize_email' + * @param string $value + * @param array $params + * + * @return string + */ + protected function filter_sanitize_numbers($value, array $params = []) + { + return filter_var($value, FILTER_SANITIZE_NUMBER_INT); + } + + /** + * Sanitize the string by removing illegal characters from float numbers. * * @param string $value * @param array $params * * @return string */ - protected function filter_sanitize_email($value, $params = null) + protected function filter_sanitize_floats($value, array $params = []) { - return filter_var($value, FILTER_SANITIZE_EMAIL); + return filter_var($value, FILTER_SANITIZE_NUMBER_FLOAT, FILTER_FLAG_ALLOW_FRACTION); } + /** - * Sanitize the string by removing illegal characters from numbers. + * Sanitize the string by removing any script tags. * * @param string $value * @param array $params * * @return string */ - protected function filter_sanitize_numbers($value, $params = null) + protected function filter_sanitize_string($value, array $params = []) { - return filter_var($value, FILTER_SANITIZE_NUMBER_INT); + return filter_var($value, FILTER_SANITIZE_STRING); } /** - * Sanitize the string by removing illegal characters from float numbers. + * Converts ['1', 1, 'true', true, 'yes', 'on'] to true, anything else is false ('on' is useful for form checkboxes). * * @param string $value * @param array $params * * @return string */ - protected function filter_sanitize_floats($value, $params = null) + protected function filter_boolean($value, array $params = []) { - return filter_var($value, FILTER_SANITIZE_NUMBER_FLOAT, FILTER_FLAG_ALLOW_FRACTION); + if (in_array($value, self::$trues, true)) { + return true; + } + + return false; } /** @@ -858,7 +1076,7 @@ protected function filter_sanitize_floats($value, $params = null) * * @return string */ - protected function filter_basic_tags($value, $params = null) + protected function filter_basic_tags($value, array $params = []) { return strip_tags($value, self::$basic_tags); } @@ -871,45 +1089,22 @@ protected function filter_basic_tags($value, $params = null) * * @return string */ - protected function filter_whole_number($value, $params = null) + protected function filter_whole_number($value, array $params = []) { return intval($value); } /** - * Convert MS Word special characters to web safe characters. - * [“, ”, ‘, ’, –, …] => [", ", ', ', -, ...] + * Convert MS Word special characters to web safe characters. ([“ ”] => ", [‘ ’] => ', [–] => -, […] => ...) * * @param string $value * @param array $params * * @return string */ - protected function filter_ms_word_characters($value, $params = null) + protected function filter_ms_word_characters($value, array $params = []) { - $word_open_double = '“'; - $word_close_double = '”'; - $web_safe_double = '"'; - - $value = str_replace(array($word_open_double, $word_close_double), $web_safe_double, $value); - - $word_open_single = '‘'; - $word_close_single = '’'; - $web_safe_single = "'"; - - $value = str_replace(array($word_open_single, $word_close_single), $web_safe_single, $value); - - $word_em = '–'; - $web_safe_em = '-'; - - $value = str_replace($word_em, $web_safe_em, $value); - - $word_ellipsis = '…'; - $web_ellipsis = '...'; - - $value = str_replace($word_ellipsis, $web_ellipsis, $value); - - return $value; + return str_replace(['“', '”', '‘', '’', '–', '…'], ['"', '"', "'", "'", '-', '...'], $value); } /** @@ -920,9 +1115,9 @@ protected function filter_ms_word_characters($value, $params = null) * * @return string */ - protected function filter_lower_case($value, $params = null) + protected function filter_lower_case($value, array $params = []) { - return strtolower($value); + return mb_strtolower($value); } /** @@ -933,747 +1128,447 @@ protected function filter_lower_case($value, $params = null) * * @return string */ - protected function filter_upper_case($value, $params = null) + protected function filter_upper_case($value, array $params = []) { - return strtoupper($value); + return mb_strtoupper($value); } /** - * Converts value to url-web-slugs. - * - * Credit: - * https://stackoverflow.com/questions/40641973/php-to-convert-string-to-slug - * http://cubiq.org/the-perfect-php-clean-url-generator + * Converts value to url-web-slugs. + * + * @see https://stackoverflow.com/questions/40641973/php-to-convert-string-to-slug + * @see http://cubiq.org/the-perfect-php-clean-url-generator * * @param string $value * @param array $params * * @return string */ - protected function filter_slug($value, $params = null) + protected function filter_slug($value, array $params = []) { $delimiter = '-'; - $slug = strtolower(trim(preg_replace('/[\s-]+/', $delimiter, preg_replace('/[^A-Za-z0-9-]+/', $delimiter, preg_replace('/[&]/', 'and', preg_replace('/[\']/', '', iconv('UTF-8', 'ASCII//TRANSLIT', $str))))), $delimiter)); - return $slug; + return mb_strtolower(trim(preg_replace('/[\s-]+/', $delimiter, preg_replace('/[^A-Za-z0-9-]+/', $delimiter, preg_replace('/[&]/', 'and', preg_replace('/[\']/', '', iconv('UTF-8', 'ASCII//TRANSLIT', $value))))), $delimiter)); } // ** ------------------------- Validators ------------------------------------ ** // - /** - * Verify that a value is contained within the pre-defined value set. - * - * Usage: '' => 'contains,value value value' + * Ensures the specified key value exists and is not empty (not null, not empty string, not empty array). * * @param string $field - * @param array $input - * @param null $param + * @param array $input + * @param array $params + * @param mixed $value * - * @return mixed + * @return bool */ - protected function validate_contains($field, $input, $param = null) + protected function validate_required($field, array $input, array $params = [], $value) { - if (!isset($input[$field])) { - return; - } - - $param = trim(strtolower($param)); - - $value = trim(strtolower($input[$field])); - - if (preg_match_all('#\'(.+?)\'#', $param, $matches, PREG_PATTERN_ORDER)) { - $param = $matches[1]; - } else { - $param = explode(chr(32), $param); - } - - if (in_array($value, $param)) { // valid, return nothing - return; - } - - return array( - 'field' => $field, - 'value' => $value, - 'rule' => __FUNCTION__, - 'param' => $param, - ); + return isset($value) && !self::is_empty($value); } /** * Verify that a value is contained within the pre-defined value set. - * OUTPUT: will NOT show the list of values. * - * Usage: '' => 'contains_list,value;value;value' + * @example_parameter one;two;use array format if one of the values contains semicolons * * @param string $field * @param array $input + * @param array $params * - * @return mixed + * @return bool */ - protected function validate_contains_list($field, $input, $param = null) + protected function validate_contains($field, array $input, array $params) { - if (!isset($input[$field]) || empty($input[$field])) { - return; - } - - $param = trim(strtolower($param)); + $value = mb_strtolower(trim($input[$field])); - $value = trim(strtolower($input[$field])); + $params = array_map(static function($value) { + return mb_strtolower(trim($value)); + }, $params); - $param = explode(';', $param); - - // consider: in_array(strtolower($value), array_map('strtolower', $param) - - if (in_array($value, $param)) { // valid, return nothing - return; - } else { - return array( - 'field' => $field, - 'value' => $value, - 'rule' => __FUNCTION__, - 'param' => $param, - ); - } + return in_array($value, $params, true); } /** - * Verify that a value is NOT contained within the pre-defined value set. - * OUTPUT: will NOT show the list of values. + * Verify that a value is contained within the pre-defined value set. Error message will NOT show the list of possible values. * - * Usage: '' => 'doesnt_contain_list,value;value;value' + * @example_parameter value1;value2 * * @param string $field - * @param array $input + * @param array $input + * @param array $params + * @param mixed $value * - * @return mixed + * @return bool */ - protected function validate_doesnt_contain_list($field, $input, $param = null) + protected function validate_contains_list($field, $input, array $params) { - if (!isset($input[$field]) || empty($input[$field])) { - return; - } - - $param = trim(strtolower($param)); - - $value = trim(strtolower($input[$field])); - - $param = explode(';', $param); - - if (!in_array($value, $param)) { // valid, return nothing - return; - } else { - return array( - 'field' => $field, - 'value' => $value, - 'rule' => __FUNCTION__, - 'param' => $param, - ); - } + return $this->validate_contains($field, $input, $params); } /** - * Check if the specified key is present and not empty. + * Verify that a value is contained within the pre-defined value set. Error message will NOT show the list of possible values. * - * Usage: '' => 'required' + * @example_parameter value1;value2 * * @param string $field - * @param array $input - * @param null $param + * @param array $input + * @param array $params + * @param mixed $value * - * @return mixed + * @return bool */ - protected function validate_required($field, $input, $param = null) + protected function validate_doesnt_contain_list($field, $input, array $params) { - if (isset($input[$field]) && ($input[$field] === false || $input[$field] === 0 || $input[$field] === 0.0 || $input[$field] === '0' || !empty($input[$field]))) { - return; - } - - return array( - 'field' => $field, - 'value' => null, - 'rule' => __FUNCTION__, - 'param' => $param, - ); + return !$this->validate_contains($field, $input, $params); } /** - * Determine if the provided email is valid. + * Determine if the provided value is a valid boolean. Returns true for: yes/no, on/off, 1/0, true/false. In strict mode (optional) only true/false will be valid which you can combine with boolean filter. * - * Usage: '' => 'valid_email' + * @example_parameter strict * * @param string $field - * @param array $input - * @param null $param + * @param array $input + * @param array $params + * @param mixed $value * - * @return mixed + * @return bool */ - protected function validate_valid_email($field, $input, $param = null) + protected function validate_boolean($field, array $input, array $params = [], $value) { - if (!isset($input[$field]) || empty($input[$field])) { - return; + if (isset($params[0]) && $params[0] === 'strict') { + return in_array($input[$field], [true, false], true); } - if (!filter_var($input[$field], FILTER_VALIDATE_EMAIL)) { - return array( - 'field' => $field, - 'value' => $input[$field], - 'rule' => __FUNCTION__, - 'param' => $param, - ); + $booleans = []; + foreach (self::$trues as $true) { + $booleans[] = $true; } + foreach (self::$falses as $false) { + $booleans[] = $false; + } + + return in_array($input[$field], $booleans, true); + } + + /** + * Determine if the provided email has valid format. + * + * @param string $field + * @param array $input + * @param array $params + * @param mixed $value individual value (in case of array) + * + * @return bool + */ + protected function validate_valid_email($field, array $input, array $params = [], $value) + { + return filter_var($value, FILTER_VALIDATE_EMAIL) !== false; } /** * Determine if the provided value length is less or equal to a specific value. * - * Usage: '' => 'max_len,240' + * @example_parameter 240 * * @param string $field - * @param array $input - * @param null $param + * @param array $input + * @param array $params + * @param mixed $value * - * @return mixed + * @return bool */ - protected function validate_max_len($field, $input, $param = null) + protected function validate_max_len($field, array $input, array $params = [], $value) { - if (!isset($input[$field])) { - return; - } - - if (function_exists('mb_strlen')) { - if (mb_strlen($input[$field]) <= (int) $param) { - return; - } - } else { - if (strlen($input[$field]) <= (int) $param) { - return; - } - } - - return array( - 'field' => $field, - 'value' => $input[$field], - 'rule' => __FUNCTION__, - 'param' => $param, - ); + return mb_strlen($value) <= (int)$params[0]; } /** * Determine if the provided value length is more or equal to a specific value. * - * Usage: '' => 'min_len,4' + * @example_parameter 4 * * @param string $field - * @param array $input - * @param null $param + * @param array $input + * @param array $params + * @param mixed $value * - * @return mixed + * @return bool */ - protected function validate_min_len($field, $input, $param = null) + protected function validate_min_len($field, array $input, array $params = [], $value) { - if (!isset($input[$field]) || empty($input[$field])) { - return; - } - - if (function_exists('mb_strlen')) { - if (mb_strlen($input[$field]) >= (int) $param) { - return; - } - } else { - if (strlen($input[$field]) >= (int) $param) { - return; - } - } - - return array( - 'field' => $field, - 'value' => $input[$field], - 'rule' => __FUNCTION__, - 'param' => $param, - ); + return mb_strlen($value) >= (int)$params[0]; } /** * Determine if the provided value length matches a specific value. * - * Usage: '' => 'exact_len,5' + * @example_parameter 5 * * @param string $field * @param array $input - * @param null $param + * @param array $params * - * @return mixed + * @return bool */ - protected function validate_exact_len($field, $input, $param = null) + protected function validate_exact_len($field, array $input, array $params = [], $value) { - if (!isset($input[$field]) || empty($input[$field])) { - return; - } - - if (function_exists('mb_strlen')) { - if (mb_strlen($input[$field]) == (int) $param) { - return; - } - } else { - if (strlen($input[$field]) == (int) $param) { - return; - } - } - - return array( - 'field' => $field, - 'value' => $input[$field], - 'rule' => __FUNCTION__, - 'param' => $param, - ); + return mb_strlen($value) == (int)$params[0]; } /** - * Determine if the provided value contains only alpha characters. + * Determine if the provided value length is between min and max values. * - * Usage: '' => 'alpha' + * @example_parameter 3;11 * * @param string $field - * @param array $input - * @param null $param + * @param array $input + * @param array $params + * @param mixed $value * - * @return mixed + * @return bool */ - protected function validate_alpha($field, $input, $param = null) + protected function validate_between_len($field, $input, array $params, $value) { - if (!isset($input[$field]) || empty($input[$field])) { - return; - } + return $this->validate_min_len($field, $input, [$params[0]], $value) + && $this->validate_max_len($field, $input, [$params[1]], $value); + } - if (!preg_match('/^([a-zÀÁÂÃÄÅÇÈÉÊËÌÍÎÏÒÓÔÕÖßÙÚÛÜÝàáâãäåçèéêëìíîïðòóôõöùúûüýÿ])+$/i', $input[$field]) !== false) { - return array( - 'field' => $field, - 'value' => $input[$field], - 'rule' => __FUNCTION__, - 'param' => $param, - ); - } + /** + * Determine if the provided value contains only alpha characters. + * + * @param string $field + * @param array $input + * @param array $params + * @return bool + */ + protected function validate_alpha($field, array $input, array $params = [], $value = null) + { + return preg_match('/^(['.self::$alpha_regex.'])+$/i', $value) > 0; } /** * Determine if the provided value contains only alpha-numeric characters. * - * Usage: '' => 'alpha_numeric' - * * @param string $field * @param array $input - * @param null $param + * @param array $params * - * @return mixed + * @return bool */ - protected function validate_alpha_numeric($field, $input, $param = null) + protected function validate_alpha_numeric($field, array $input, array $params = [], $value) { - if (!isset($input[$field]) || empty($input[$field])) { - return; - } - - if (!preg_match('/^([a-z0-9ÀÁÂÃÄÅÇÈÉÊËÌÍÎÏÒÓÔÕÖßÙÚÛÜÝàáâãäåçèéêëìíîïðòóôõöùúûüýÿ])+$/i', $input[$field]) !== false) { - return array( - 'field' => $field, - 'value' => $input[$field], - 'rule' => __FUNCTION__, - 'param' => $param, - ); - } + return preg_match('/^(['.self::$alpha_regex.'0-9])+$/i', $value) > 0; } /** * Determine if the provided value contains only alpha characters with dashed and underscores. * - * Usage: '' => 'alpha_dash' - * * @param string $field * @param array $input - * @param null $param + * @param array $params * - * @return mixed + * @return bool */ - protected function validate_alpha_dash($field, $input, $param = null) + protected function validate_alpha_dash($field, array $input, array $params = [], $value) { - if (!isset($input[$field]) || empty($input[$field])) { - return; - } - - if (!preg_match('/^([a-zÀÁÂÃÄÅÇÈÉÊËÌÍÎÏÒÓÔÕÖßÙÚÛÜÝàáâãäåçèéêëìíîïðòóôõöùúûüýÿ_-])+$/i', $input[$field]) !== false) { - return array( - 'field' => $field, - 'value' => $input[$field], - 'rule' => __FUNCTION__, - 'param' => $param, - ); - } + return preg_match('/^(['.self::$alpha_regex.'_-])+$/i', $value) > 0; } /** - * Determine if the provided value contains only alpha numeric characters with spaces. - * - * Usage: '' => 'alpha_numeric_space' + * Determine if the provided value contains only alpha numeric characters with dashed and underscores. * * @param string $field * @param array $input - * @param null $param + * @param array $params * - * @return mixed + * @return bool */ - protected function validate_alpha_numeric_space($field, $input, $param = null) + protected function validate_alpha_numeric_dash($field, array $input, array $params = [], $value) { - if (!isset($input[$field]) || empty($input[$field])) { - return; - } - - if (!preg_match("/^([a-z0-9ÀÁÂÃÄÅÇÈÉÊËÌÍÎÏÒÓÔÕÖßÙÚÛÜÝàáâãäåçèéêëìíîïðòóôõöùúûüýÿ\s])+$/i", $input[$field]) !== false) { - return array( - 'field' => $field, - 'value' => $input[$field], - 'rule' => __FUNCTION__, - 'param' => $param, - ); - } + return preg_match('/^(['.self::$alpha_regex.'0-9_-])+$/i', $value) > 0; } /** * Determine if the provided value contains only alpha numeric characters with spaces. * - * Usage: '' => 'alpha_space' - * * @param string $field * @param array $input - * @param null $param + * @param array $params * - * @return mixed + * @return bool */ - protected function validate_alpha_space($field, $input, $param = null) + protected function validate_alpha_numeric_space($field, array $input, array $params = [], $value) { - if (!isset($input[$field]) || empty($input[$field])) { - return; - } - - if (!preg_match("/^([0-9a-zÀÁÂÃÄÅÇÈÉÊËÌÍÎÏÒÓÔÕÖßÙÚÛÜÝàáâãäåçèéêëìíîïðòóôõöùúûüýÿ\s])+$/i", $input[$field]) !== false) { - return array( - 'field' => $field, - 'value' => $input[$field], - 'rule' => __FUNCTION__, - 'param' => $param, - ); - } + return preg_match('/^(['.self::$alpha_regex.'\s0-9])+$/i', $value) > 0; } /** - * Determine if the provided value is a valid number or numeric string. - * - * Usage: '' => 'numeric' + * Determine if the provided value contains only alpha characters with spaces. * * @param string $field * @param array $input - * @param null $param + * @param array $params * - * @return mixed + * @return bool */ - protected function validate_numeric($field, $input, $param = null) + protected function validate_alpha_space($field, array $input, array $params = [], $value) { - if (!isset($input[$field]) || empty($input[$field])) { - return; - } - - if (!is_numeric($input[$field])) { - return array( - 'field' => $field, - 'value' => $input[$field], - 'rule' => __FUNCTION__, - 'param' => $param, - ); - } + return preg_match('/^(['.self::$alpha_regex.'\s])+$/i', $value) > 0; } /** - * Determine if the provided value is a valid integer. - * - * Usage: '' => 'integer' + * Determine if the provided value is a valid number or numeric string. * * @param string $field * @param array $input - * @param null $param + * @param array $params * - * @return mixed + * @return bool */ - protected function validate_integer($field, $input, $param = null) + protected function validate_numeric($field, array $input, array $params = [], $value) { - if (!isset($input[$field]) || empty($input[$field])) { - return; - } - - if (filter_var($input[$field], FILTER_VALIDATE_INT) === false) { - return array( - 'field' => $field, - 'value' => $input[$field], - 'rule' => __FUNCTION__, - 'param' => $param, - ); - } + return is_numeric($value); } /** - * Determine if the provided value is a PHP accepted boolean. - * - * Usage: '' => 'boolean' + * Determine if the provided value is a valid integer. * * @param string $field * @param array $input - * @param null $param + * @param array $params * - * @return mixed + * @return bool */ - protected function validate_boolean($field, $input, $param = null) + protected function validate_integer($field, array $input, array $params = [], $value) { - if (!isset($input[$field]) || empty($input[$field]) && $input[$field] !== 0) { - return; - } - - $booleans = array('1','true',true,1,'0','false',false,0,'yes','no','on','off'); - if (in_array($input[$field], $booleans, true )) { - return; - } - - return array( - 'field' => $field, - 'value' => $input[$field], - 'rule' => __FUNCTION__, - 'param' => $param, - ); + return !(filter_var($value, FILTER_VALIDATE_INT) === false || is_bool($value) || is_null($value)); } /** * Determine if the provided value is a valid float. * - * Usage: '' => 'float' - * * @param string $field * @param array $input - * @param null $param + * @param array $params * - * @return mixed + * @return bool */ - protected function validate_float($field, $input, $param = null) + protected function validate_float($field, array $input, array $params = [], $value) { - if (!isset($input[$field]) || empty($input[$field])) { - return; - } - - if (filter_var($input[$field], FILTER_VALIDATE_FLOAT) === false) { - return array( - 'field' => $field, - 'value' => $input[$field], - 'rule' => __FUNCTION__, - 'param' => $param, - ); - } + return filter_var($value, FILTER_VALIDATE_FLOAT) !== false; } /** * Determine if the provided value is a valid URL. * - * Usage: '' => 'valid_url' - * * @param string $field * @param array $input - * @param null $param + * @param array $params * - * @return mixed + * @return bool */ - protected function validate_valid_url($field, $input, $param = null) + protected function validate_valid_url($field, array $input, array $params = [], $value) { - if (!isset($input[$field]) || empty($input[$field])) { - return; - } - - if (!filter_var($input[$field], FILTER_VALIDATE_URL)) { - return array( - 'field' => $field, - 'value' => $input[$field], - 'rule' => __FUNCTION__, - 'param' => $param, - ); - } + return filter_var($value, FILTER_VALIDATE_URL) !== false; } /** * Determine if a URL exists & is accessible. * - * Usage: '' => 'url_exists' - * * @param string $field * @param array $input - * @param null $param + * @param array $params * - * @return mixed + * @return bool */ - protected function validate_url_exists($field, $input, $param = null) + protected function validate_url_exists($field, array $input, array $params = [], $value) { - if (!isset($input[$field]) || empty($input[$field])) { - return; - } - - $url = parse_url(strtolower($input[$field])); + $url = parse_url(mb_strtolower($value)); if (isset($url['host'])) { $url = $url['host']; } - if (function_exists('checkdnsrr') && function_exists('idn_to_ascii')) { - if (checkdnsrr(idn_to_ascii($url), 'A') === false) { - return array( - 'field' => $field, - 'value' => $input[$field], - 'rule' => __FUNCTION__, - 'param' => $param, - ); - } - } else { - if (gethostbyname($url) == $url) { - return array( - 'field' => $field, - 'value' => $input[$field], - 'rule' => __FUNCTION__, - 'param' => $param, - ); - } - } + return EnvHelpers::checkdnsrr(idn_to_ascii($url, IDNA_DEFAULT, INTL_IDNA_VARIANT_UTS46), 'A') !== false; } /** * Determine if the provided value is a valid IP address. * - * Usage: '' => 'valid_ip' - * * @param string $field - * @param array $input + * @param array $input + * @param array $params + * @param mixed $value * - * @return mixed + * @return bool */ - protected function validate_valid_ip($field, $input, $param = null) + protected function validate_valid_ip($field, array $input, array $params = [], $value) { - if (!isset($input[$field]) || empty($input[$field])) { - return; - } - - if (!filter_var($input[$field], FILTER_VALIDATE_IP) !== false) { - return array( - 'field' => $field, - 'value' => $input[$field], - 'rule' => __FUNCTION__, - 'param' => $param, - ); - } + return filter_var($value, FILTER_VALIDATE_IP) !== false; } /** * Determine if the provided value is a valid IPv4 address. * - * Usage: '' => 'valid_ipv4' + * @see What about private networks? What about loop-back address? 127.0.0.1 http://en.wikipedia.org/wiki/Private_network * * @param string $field - * @param array $input - * - * @return mixed + * @param array $input + * @param array $params + * @param mixed $value * - * @see http://pastebin.com/UvUPPYK0 - */ - - /* - * What about private networks? http://en.wikipedia.org/wiki/Private_network - * What about loop-back address? 127.0.0.1 + * @return bool */ - protected function validate_valid_ipv4($field, $input, $param = null) + protected function validate_valid_ipv4($field, array $input, array $params = [], $value) { - if (!isset($input[$field]) || empty($input[$field])) { - return; - } - - if (!filter_var($input[$field], FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) { - // removed !== FALSE - - return array( - 'field' => $field, - 'value' => $input[$field], - 'rule' => __FUNCTION__, - 'param' => $param, - ); - } + return filter_var($value, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) !== false; } /** * Determine if the provided value is a valid IPv6 address. * - * Usage: '' => 'valid_ipv6' - * * @param string $field - * @param array $input + * @param array $input + * @param array $params + * @param mixed $value * - * @return mixed + * @return bool */ - protected function validate_valid_ipv6($field, $input, $param = null) + protected function validate_valid_ipv6($field, array $input, array $params = [], $value) { - if (!isset($input[$field]) || empty($input[$field])) { - return; - } - - if (!filter_var($input[$field], FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) { - return array( - 'field' => $field, - 'value' => $input[$field], - 'rule' => __FUNCTION__, - 'param' => $param, - ); - } + return filter_var($value, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) !== false; } /** * Determine if the input is a valid credit card number. * - * See: http://stackoverflow.com/questions/174730/what-is-the-best-way-to-validate-a-credit-card-in-php - * Usage: '' => 'valid_cc' + * @see http://stackoverflow.com/questions/174730/what-is-the-best-way-to-validate-a-credit-card-in-php * * @param string $field - * @param array $input + * @param array $input + * @param array $params + * @param mixed $value * - * @return mixed + * @return bool */ - protected function validate_valid_cc($field, $input, $param = null) + protected function validate_valid_cc($field, array $input, array $params = [], $value) { - if (!isset($input[$field]) || empty($input[$field])) { - return; - } - - $number = preg_replace('/\D/', '', $input[$field]); - - if (function_exists('mb_strlen')) { - $number_length = mb_strlen($number); - } else { - $number_length = strlen($number); - } + $number = preg_replace('/\D/', '', $value); + $number_length = mb_strlen($number); /** - * Bail out if $number_length is 0. + * Bail out if $number_length is 0. * This can be the case if a user has entered only alphabets - * + * * @since 1.5 */ - if( $number_length == 0 ) { - return array( - 'field' => $field, - 'value' => $input[$field], - 'rule' => __FUNCTION__, - 'param' => $param, - ); + if ($number_length == 0 ) { + return false; } - $parity = $number_length % 2; $total = 0; @@ -1692,755 +1587,391 @@ protected function validate_valid_cc($field, $input, $param = null) $total += $digit; } - if ($total % 10 == 0) { - return; // Valid - } - - return array( - 'field' => $field, - 'value' => $input[$field], - 'rule' => __FUNCTION__, - 'param' => $param, - ); + return $total % 10 == 0; } /** - * Determine if the input is a valid human name [Credits to http://github.com/ben-s]. + * Determine if the input is a valid human name. * - * See: https://github.com/Wixel/GUMP/issues/5 - * Usage: '' => 'valid_name' + * @see https://github.com/Wixel/GUMP/issues/5 * * @param string $field - * @param array $input + * @param array $input + * @param array $params + * @param mixed $value * - * @return mixed + * @return bool */ - protected function validate_valid_name($field, $input, $param = null) + protected function validate_valid_name($field, array $input, array $params = [], $value) { - if (!isset($input[$field]) || empty($input[$field])) { - return; - } - - if (!preg_match("/^([a-z \p{L} '-])+$/i", $input[$field]) !== false) { - return array( - 'field' => $field, - 'value' => $input[$field], - 'rule' => __FUNCTION__, - 'param' => $param, - ); - } + return preg_match("/^([a-z \p{L} '-])+$/i", $value) > 0; } /** * Determine if the provided input is likely to be a street address using weak detection. * - * Usage: '' => 'street_address' - * * @param string $field - * @param array $input + * @param array $input + * @param array $params + * @param mixed $value * - * @return mixed + * @return bool */ - protected function validate_street_address($field, $input, $param = null) + protected function validate_street_address($field, array $input, array $params = [], $value) { - if (!isset($input[$field]) || empty($input[$field])) { - return; - } - // Theory: 1 number, 1 or more spaces, 1 or more words - $hasLetter = preg_match('/[a-zA-Z]/', $input[$field]); - $hasDigit = preg_match('/\d/', $input[$field]); - $hasSpace = preg_match('/\s/', $input[$field]); - - $passes = $hasLetter && $hasDigit && $hasSpace; - - if (!$passes) { - return array( - 'field' => $field, - 'value' => $input[$field], - 'rule' => __FUNCTION__, - 'param' => $param, - ); - } + $has_letter = preg_match('/[a-zA-Z]/', $value); + $has_digit = preg_match('/\d/', $value); + $has_space = preg_match('/\s/', $value); + + return $has_letter && $has_digit && $has_space; } /** * Determine if the provided value is a valid IBAN. * - * Usage: '' => 'iban' - * * @param string $field - * @param array $input + * @param array $input + * @param array $params + * @param mixed $value * - * @return mixed + * @return bool */ - protected function validate_iban($field, $input, $param = null) + protected function validate_iban($field, array $input, array $params = [], $value) { - if (!isset($input[$field]) || empty($input[$field])) { - return; - } - - static $character = array( + $character = [ 'A' => 10, 'C' => 12, 'D' => 13, 'E' => 14, 'F' => 15, 'G' => 16, 'H' => 17, 'I' => 18, 'J' => 19, 'K' => 20, 'L' => 21, 'M' => 22, 'N' => 23, 'O' => 24, 'P' => 25, 'Q' => 26, 'R' => 27, 'S' => 28, 'T' => 29, 'U' => 30, 'V' => 31, 'W' => 32, 'X' => 33, 'Y' => 34, 'Z' => 35, 'B' => 11 - ); - - if (!preg_match("/\A[A-Z]{2}\d{2} ?[A-Z\d]{4}( ?\d{4}){1,} ?\d{1,4}\z/", $input[$field])) { - return array( - 'field' => $field, - 'value' => $input[$field], - 'rule' => __FUNCTION__, - 'param' => $param, - ); + ]; + + if (!preg_match("/\A[A-Z]{2}\d{2} ?[A-Z\d]{4}( ?\d{4}){1,} ?\d{1,4}\z/", $value)) { + return false; } - $iban = str_replace(' ', '', $input[$field]); + $iban = str_replace(' ', '', $value); $iban = substr($iban, 4).substr($iban, 0, 4); $iban = strtr($iban, $character); - if (bcmod($iban, 97) != 1) { - return array( - 'field' => $field, - 'value' => $input[$field], - 'rule' => __FUNCTION__, - 'param' => $param, - ); - } + return bcmod($iban, 97) == 1; } /** - * Determine if the provided input is a valid date (ISO 8601) - * or specify a custom format. + * Determine if the provided input is a valid date (ISO 8601) or specify a custom format (optional). * - * Usage: '' => 'date' + * @example_parameter d/m/Y * * @param string $field - * @param string $input date ('Y-m-d') or datetime ('Y-m-d H:i:s') - * @param string $param Custom date format + * @param array $input + * @param array $params + * @param mixed $value * - * @return mixed + * @return bool */ - protected function validate_date($field, $input, $param = null) + protected function validate_date($field, array $input, array $params = [], $value) { - if (!isset($input[$field]) || empty($input[$field])) { - return; - } - // Default - if (!$param) - { - $cdate1 = date('Y-m-d', strtotime($input[$field])); - $cdate2 = date('Y-m-d H:i:s', strtotime($input[$field])); - - if ($cdate1 != $input[$field] && $cdate2 != $input[$field]) - { - return array( - 'field' => $field, - 'value' => $input[$field], - 'rule' => __FUNCTION__, - 'param' => $param, - ); - } - } else { - $date = \DateTime::createFromFormat($param, $input[$field]); - - if ($date === false || $input[$field] != date($param, $date->getTimestamp())) - { - return array( - 'field' => $field, - 'value' => $input[$field], - 'rule' => __FUNCTION__, - 'param' => $param, - ); - } + if (count($params) === 0) { + $cdate1 = date('Y-m-d', strtotime($value)); + $cdate2 = date('Y-m-d H:i:s', strtotime($value)); + + return !($cdate1 != $value && $cdate2 != $value); } + + $date = \DateTime::createFromFormat($params[0], $value); + + return !($date === false || $value != date($params[0], $date->getTimestamp())); } /** - * Determine if the provided input meets age requirement (ISO 8601). + * Determine if the provided input meets age requirement (ISO 8601). Input should be a date (Y-m-d). * - * Usage: '' => 'min_age,13' + * @example_parameter 18 * * @param string $field - * @param string $input date ('Y-m-d') or datetime ('Y-m-d H:i:s') - * @param string $param int + * @param array $input + * @param array $params + * @param mixed $value * - * @return mixed + * @return bool + * @throws Exception */ - protected function validate_min_age($field, $input, $param = null) + protected function validate_min_age($field, array $input, array $params, $value) { - if (!isset($input[$field]) || empty($input[$field])) { - return; - } + $inputDatetime = new DateTime(EnvHelpers::date('Y-m-d', strtotime($value))); + $todayDatetime = new DateTime(EnvHelpers::date('Y-m-d')); - $cdate1 = new DateTime(date('Y-m-d', strtotime($input[$field]))); - $today = new DateTime(date('d-m-Y')); + $interval = $todayDatetime->diff($inputDatetime); + $yearsPassed = $interval->y; - $interval = $cdate1->diff($today); - $age = $interval->y; - - if ($age <= $param) { - return array( - 'field' => $field, - 'value' => $input[$field], - 'rule' => __FUNCTION__, - 'param' => $param, - ); - } + return $yearsPassed >= $params[0]; } /** * Determine if the provided numeric value is lower or equal to a specific value. * - * Usage: '' => 'max_numeric,50' + * @example_parameter 50 * * @param string $field * @param array $input - * @param null $param - * - * @return mixed + * @param array $params + * @return bool */ - protected function validate_max_numeric($field, $input, $param = null) + protected function validate_max_numeric($field, array $input, array $params = [], $value) { - if (!isset($input[$field]) || empty($input[$field])) { - return; - } - - if (is_numeric($input[$field]) && is_numeric($param) && ($input[$field] <= $param)) { - return; - } - - return array( - 'field' => $field, - 'value' => $input[$field], - 'rule' => __FUNCTION__, - 'param' => $param, - ); + return is_numeric($value) && is_numeric($params[0]) && ($value <= $params[0]); } /** * Determine if the provided numeric value is higher or equal to a specific value. * - * Usage: '' => 'min_numeric,1' + * @example_parameter 1 * * @param string $field * @param array $input - * @param null $param - * @return mixed + * @param array $params + * @return bool */ - protected function validate_min_numeric($field, $input, $param = null) + protected function validate_min_numeric($field, array $input, array $params = [], $value) { - if (!isset($input[$field]) || $input[$field] === '') { - return; - } - - if (is_numeric($input[$field]) && is_numeric($param) && ($input[$field] >= $param)) { - return; - } - - return array( - 'field' => $field, - 'value' => $input[$field], - 'rule' => __FUNCTION__, - 'param' => $param, - ); + return is_numeric($value) && is_numeric($params[0]) && ($value >= $params[0]); } /** * Determine if the provided value starts with param. * - * Usage: '' => 'starts,Z' + * @example_parameter Z * * @param string $field - * @param array $input - * - * @return mixed + * @param array $input + * @param array $params + * @param mixed $value + * @return bool */ - protected function validate_starts($field, $input, $param = null) + protected function validate_starts($field, array $input, array $params, $value) { - if (!isset($input[$field]) || empty($input[$field])) { - return; - } - - if (strpos($input[$field], $param) !== 0) { - return array( - 'field' => $field, - 'value' => $input[$field], - 'rule' => __FUNCTION__, - 'param' => $param, - ); - } + return strpos($value, $params[0]) === 0; } - /** - * Checks if a file was uploaded. - * - * Usage: '' => 'required_file' - * - * @param string $field - * @param array $input - * - * @return mixed - */ - protected function validate_required_file($field, $input, $param = null) - { - if (!isset($input[$field])) { - return; - } - - if (is_array($input[$field]) && $input[$field]['error'] !== 4) { - return; - } - - return array( - 'field' => $field, - 'value' => $input[$field], - 'rule' => __FUNCTION__, - 'param' => $param, - ); - } - - /** - * Check the uploaded file for extension for now - * checks only the ext should add mime type check. - * - * Usage: '' => 'extension,png;jpg;gif + /** + * Determine if the file was successfully uploaded. * * @param string $field - * @param array $input + * @param array $input + * @param array $params + * @param mixed $value * - * @return mixed + * @return bool */ - protected function validate_extension($field, $input, $param = null) + protected function validate_required_file($field, array $input, array $params = [], $value) { - if (!isset($input[$field])) { - return; - } - - if (is_array($input[$field]) && $input[$field]['error'] !== 4) { - $param = trim(strtolower($param)); - $allowed_extensions = explode(';', $param); - - $path_info = pathinfo($input[$field]['name']); - $extension = isset($path_info['extension']) ? $path_info['extension'] : false; - - if ($extension && in_array(strtolower($extension), $allowed_extensions)) { - return; - } - - return array( - 'field' => $field, - 'value' => $input[$field], - 'rule' => __FUNCTION__, - 'param' => $param, - ); - } + return isset($input[$field]) && is_array($input[$field]) && $input[$field]['error'] === 0; } /** - * Determine if the provided field value equals current field value. - * + * Check the uploaded file for extension. Doesn't check mime-type yet. * - * Usage: '' => 'equalsfield,Z' + * @example_parameter png;jpg;gif * * @param string $field - * @param string $input - * @param string $param field to compare with + * @param array $input + * @param array $params + * @param mixed $value * - * @return mixed + * @return bool */ - protected function validate_equalsfield($field, $input, $param = null) + protected function validate_extension($field, $input, array $params, $value) { - if (!isset($input[$field]) || empty($input[$field])) { - return; - } + if (is_array($input[$field]) && $input[$field]['error'] === 0) { + $params = array_map(function($v) { + return trim(mb_strtolower($v)); + }, $params); + + $path_info = pathinfo($input[$field]['name']); + $extension = $path_info['extension'] ?? null; - if ($input[$field] == $input[$param]) { - return; + return $extension && in_array(mb_strtolower($extension), $params, true); } - return array( - 'field' => $field, - 'value' => $input[$field], - 'rule' => __FUNCTION__, - 'param' => $param, - ); + return false; } /** - * Determine if the provided field value is a valid GUID (v4) + * Determine if the provided field value equals current field value. * - * Usage: '' => 'guidv4' + * @example_parameter other_field_name * * @param string $field - * @param string $input - * @param string $param field to compare with - * @return mixed + * @param array $input + * @param array $params + * @param mixed $value + * + * @return bool */ - protected function validate_guidv4($field, $input, $param = null) + protected function validate_equalsfield($field, array $input, array $params, $value) { - if (!isset($input[$field]) || empty($input[$field])) { - return; - } - - if (preg_match("/\{?[A-Z0-9]{8}-[A-Z0-9]{4}-[A-Z0-9]{4}-[A-Z0-9]{4}-[A-Z0-9]{12}\}?$/", $input[$field])) { - return; - } - - return array( - 'field' => $field, - 'value' => $input[$field], - 'rule' => __FUNCTION__, - 'param' => $param, - ); + return $input[$field] == $input[$params[0]]; } /** - * Trims whitespace only when the value is a scalar. + * Determine if the provided field value is a valid GUID (v4) * + * @param string $field + * @param array $input + * @param array $params * @param mixed $value * - * @return mixed + * @return bool */ - private function trimScalar($value) + protected function validate_guidv4($field, array $input, array $params = [], $value) { - if (is_scalar($value)) { - $value = trim($value); - } - - return $value; + return preg_match("/\{?[a-zA-Z0-9]{8}-[a-zA-Z0-9]{4}-[a-zA-Z0-9]{4}-[a-zA-Z0-9]{4}-[a-zA-Z0-9]{12}\}?$/", $value) > 0; } /** * Determine if the provided value is a valid phone number. * - * Usage: '' => 'phone_number' + * @example_value 5555425555 + * @example_value 555-555-5555 + * @example_value 1(519) 555-4444 + * @example_value 1-555-555-5555 + * @example_value 1-(555)-555-5555 * * @param string $field - * @param array $input - * - * @return mixed - * - * Examples: + * @param array $input + * @param array $params + * @param mixed $value * - * 555-555-5555: valid - * 555.555.5555: valid - * 5555425555: valid - * 555 555 5555: valid - * 1(519) 555-4444: valid - * 1 (519) 555-4422: valid - * 1-555-555-5555: valid - * 1-(555)-555-5555: valid + * @return bool */ - protected function validate_phone_number($field, $input, $param = null) + protected function validate_phone_number($field, array $input, array $params = [], $value) { - if (!isset($input[$field]) || empty($input[$field])) { - return; - } + $regex = '/^(\d[\s-]?)?[\(\[\s-]{0,2}?\d{3}[\)\]\s-]{0,2}?\d{3}[\s-]?\d{4}$/i'; - $regex = '/^(\d[\s-\.]?)?[\(\[\s-\.]{0,2}?\d{3}[\)\]\s-\.]{0,2}?\d{3}[\s-\.]?\d{4}$/i'; - if (!preg_match($regex, $input[$field])) { - return array( - 'field' => $field, - 'value' => $input[$field], - 'rule' => __FUNCTION__, - 'param' => $param, - ); - } + return preg_match($regex, $value) > 0; } /** * Custom regex validator. * - * Usage: '' => 'regex,/your-regex-expression/' + * @example_parameter /test-[0-9]{3}/ + * @example_value test-123 * * @param string $field - * @param array $input + * @param array $input + * @param array $params + * @param mixed $value * - * @return mixed + * @return bool */ - protected function validate_regex($field, $input, $param = null) + protected function validate_regex($field, array $input, array $params = [], $value) { - if (!isset($input[$field]) || empty($input[$field])) { - return; - } - - $regex = $param; - if (!preg_match($regex, $input[$field])) { - return array( - 'field' => $field, - 'value' => $input[$field], - 'rule' => __FUNCTION__, - 'param' => $param, - ); - } + return preg_match($params[0], $value) > 0; } /** - * JSON validator. + * Determine if the provided value is a valid JSON string. * - * Usage: '' => 'valid_json_string' + * @example_value {"test": true} * * @param string $field - * @param array $input + * @param array $input + * @param array $params + * @param mixed $value * - * @return mixed + * @return bool */ - protected function validate_valid_json_string($field, $input, $param = null) + protected function validate_valid_json_string($field, array $input, array $params = [], $value) { - if (!isset($input[$field]) || empty($input[$field])) { - return; + if (!is_string($input[$field]) || !is_object(json_decode($value))) { + return false; } - if (!is_string($input[$field]) || !is_object(json_decode($input[$field]))) { - return array( - 'field' => $field, - 'value' => $input[$field], - 'rule' => __FUNCTION__, - 'param' => $param, - ); - } + return true; } /** * Check if an input is an array and if the size is more or equal to a specific value. * - * Usage: '' => 'valid_array_size_greater,1' + * @example_parameter 1 * * @param string $field - * @param array $input + * @param array $input + * @param array $params + * @param mixed $value * - * @return mixed + * @return bool */ - protected function validate_valid_array_size_greater($field, $input, $param = null) + protected function validate_valid_array_size_greater($field, array $input, array $params, $value) { - if (!isset($input[$field]) || empty($input[$field])) { - return; + if (!is_array($input[$field]) || count($input[$field]) < $params[0]) { + return false; } - if (!is_array($input[$field]) || sizeof($input[$field]) < (int)$param) { - return array( - 'field' => $field, - 'value' => $input[$field], - 'rule' => __FUNCTION__, - 'param' => $param, - ); - } + return true; } /** * Check if an input is an array and if the size is less or equal to a specific value. * - * Usage: '' => 'valid_array_size_lesser,1' + * @example_parameter 1 * * @param string $field * @param array $input + * @param array $params + * @param mixed $value * - * @return mixed + * @return bool */ - protected function validate_valid_array_size_lesser($field, $input, $param = null) + protected function validate_valid_array_size_lesser($field, array $input, array $params = [], $value) { - if (!isset($input[$field]) || empty($input[$field])) { - return; + if (!is_array($input[$field]) || count($input[$field]) > $params[0]) { + return false; } - if (!is_array($input[$field]) || sizeof($input[$field]) > (int)$param) { - return array( - 'field' => $field, - 'value' => $input[$field], - 'rule' => __FUNCTION__, - 'param' => $param, - ); - } + return true; } /** * Check if an input is an array and if the size is equal to a specific value. * - * Usage: '' => 'valid_array_size_equal,1' + * @example_parameter 1 * * @param string $field * @param array $input + * @param array $params + * @param mixed $value * - * @return mixed + * @return bool */ - protected function validate_valid_array_size_equal($field, $input, $param = null) + protected function validate_valid_array_size_equal($field, array $input, array $params = [], $value) { - if (!isset($input[$field]) || empty($input[$field])) { - return; - } - - if (!is_array($input[$field]) || sizeof($input[$field]) == (int)$param) { - return array( - 'field' => $field, - 'value' => $input[$field], - 'rule' => __FUNCTION__, - 'param' => $param, - ); - } + return !(!is_array($input[$field]) || count($input[$field]) != $params[0]); } - - /** - * Determine if the input is a valid person name in Persian/Dari or Arabic mainly in Afghanistan and Iran. - * - * Usage: '' => 'valid_persian_name' - * - * @param string $field - * @param array $input - * - * @return mixed - */ - protected function validate_valid_persian_name($field, $input, $param = null) - { - if (!isset($input[$field]) || empty($input[$field])) { - return; - } - - if (!preg_match("/^([ا آ أ إ ب پ ت ث ج چ ح خ د ذ ر ز ژ س ش ص ض ط ظ ع غ ف ق ک ك گ ل م ن و ؤ ه ة ی ي ئ ء ّ َ ِ ُ ً ٍ ٌ ْ\x{200B}-\x{200D}])+$/u", $input[$field]) !== false) { - return array( - 'field' => $field, - 'value' => $input[$field], - 'rule' => __FUNCTION__, - 'param' => $param, - ); - } - } - - /** - * Determine if the input is a valid person name in English, Persian/Dari/Pashtu or Arabic mainly in Afghanistan and Iran. - * - * Usage: '' => 'valid_eng_per_pas_name' + * Determine if the provided value is a valid Twitter account. * * @param string $field - * @param array $input + * @param array $input + * @param array $params + * @param mixed $value * - * @return mixed + * @return bool + * @throws Exception if Twitter API has changed, in such case report on GitHub please. */ - protected function validate_valid_eng_per_pas_name($field, $input, $param = null) + protected function validate_valid_twitter($field, array $input, array $params = [], $value) { - if (!isset($input[$field]) || empty($input[$field])) { - return; - } + $json = EnvHelpers::file_get_contents("http://twitter.com/users/username_available?username=".$input[$field]); - if (!preg_match("/^([A-Za-zÀÁÂÃÄÅÇÈÉÊËÌÍÎÏÒÓÔÕÖßÙÚÛÜÝàáâãäåçèéêëìíîïñðòóôõöùúûüýÿ'\- ا آ أ إ ب پ ت ټ ث څ ج چ ح ځ خ د ډ ذ ر ړ ز ږ ژ س ش ښ ص ض ط ظ ع غ ف ق ک ګ ك گ ل م ن ڼ و ؤ ه ة ی ي ې ۍ ئ ؋ ء ّ َ ِ ُ ً ٍ ٌ ْ \x{200B}-\x{200D} \s])+$/u", $input[$field]) !== false) { - return array( - 'field' => $field, - 'value' => $input[$field], - 'rule' => __FUNCTION__, - 'param' => $param, - ); - } - } - - /** - * Determine if the input is valid digits in Persian/Dari, Pashtu or Arabic format. - * - * Usage: '' => 'valid_persian_digit' - * - * @param string $field - * @param array $input - * - * @return mixed - */ - protected function validate_valid_persian_digit($field, $input, $param = null) - { - if (!isset($input[$field]) || empty($input[$field])) { - return; - } + $result = json_decode($json); - if (!preg_match("/^([۰۱۲۳۴۵۶۷۸۹٠١٢٣٤٥٦٧٨٩])+$/u", $input[$field]) !== false) { - return array( - 'field' => $field, - 'value' => $input[$field], - 'rule' => __FUNCTION__, - 'param' => $param, - ); - } - } - - - /** - * Determine if the input is a valid text in Persian/Dari or Arabic mainly in Afghanistan and Iran. - * - * Usage: '' => 'valid_persian_text' - * - * @param string $field - * @param array $input - * - * @return mixed - */ - protected function validate_valid_persian_text($field, $input, $param = null) - { - if (!isset($input[$field]) || empty($input[$field])) { - return; - } - - if (!preg_match("/^([ا آ أ إ ب پ ت ث ج چ ح خ د ذ ر ز ژ س ش ص ض ط ظ ع غ ف ق ک ك گ ل م ن و ؤ ه ة ی ي ئ ء ّ َ ِ ُ ً ٍ ٌ \. \/ \\ = \- \| \{ \} \[ \] ؛ : « » ؟ > < \+ \( \) \* ، × ٪ ٫ ٬ ! ۰۱۲۳۴۵۶۷۸۹٠١٢٣٤٥٦٧٨٩\x{200B}-\x{200D} \x{FEFF} \x{22} \x{27} \x{60} \x{B4} \x{2018} \x{2019} \x{201C} \x{201D} \s])+$/u", $input[$field]) !== false) { - return array( - 'field' => $field, - 'value' => $input[$field], - 'rule' => __FUNCTION__, - 'param' => $param, - ); - } - } - - /** - * Determine if the input is a valid text in Pashtu mainly in Afghanistan. - * - * Usage: '' => 'valid_pashtu_text' - * - * @param string $field - * @param array $input - * - * @return mixed - */ - protected function validate_valid_pashtu_text($field, $input, $param = null) - { - if (!isset($input[$field]) || empty($input[$field])) { - return; + if (!isset($result->reason)) { + throw new Exception('Twitter JSON response changed. Please report this on GitHub.'); } - if (!preg_match("/^([ا آ أ ب پ ت ټ ث څ ج چ ح ځ خ د ډ ذ ر ړ ز ږ ژ س ش ښ ص ض ط ظ ع غ ف ق ک ګ ل م ن ڼ و ؤ ه ة ی ې ۍ ي ئ ء ْ ٌ ٍ ً ُ ِ َ ّ ؋ \. \/ \\ = \- \| \{ \} \[ \] ؛ : « » ؟ > < \+ \( \) \* ، × ٪ ٫ ٬ ! ۰۱۲۳۴۵۶۷۸۹٠١٢٣٤٥٦٧٨٩ \x{200B}-\x{200D} \x{FEFF} \x{22} \x{27} \x{60} \x{B4} \x{2018} \x{2019} \x{201C} \x{201D} \s])+$/u", $input[$field]) !== false) { - return array( - 'field' => $field, - 'value' => $input[$field], - 'rule' => __FUNCTION__, - 'param' => $param, - ); - } - } - - /** - * Determine if the provided value is a valid twitter handle. - * - * @access protected - * @param string $field - * @param array $input - * @return mixed - */ - protected function validate_valid_twitter($field, $input, $param = NULL) - { - if(!isset($input[$field]) || empty($input[$field])) - { - return; - } - $json_twitter = file_get_contents("http://twitter.com/users/username_available?username=".$input[$field]); - - $twitter_response = json_decode($json_twitter); - if($twitter_response->reason != "taken"){ - return array( - 'field' => $field, - 'value' => $input[$field], - 'rule' => __FUNCTION__, - 'param' => $param - ); - } + return $result->reason === "taken"; } - -} \ No newline at end of file +} diff --git a/app/Helper/Validation/ValidationHelpers/ArrayHelpers.php b/app/Helper/Validation/ValidationHelpers/ArrayHelpers.php new file mode 100644 index 0000000..3957dff --- /dev/null +++ b/app/Helper/Validation/ValidationHelpers/ArrayHelpers.php @@ -0,0 +1,89 @@ +{$segment})) { + $target = $target->{$segment}; + } else { + return $default; + } + } + + return $target; + } + + /** + * Determine whether the given value is array accessible. + * + * @param mixed $value + * @return bool + */ + public static function accessible($value) + { + return is_array($value) || $value instanceof ArrayAccess; + } + + /** + * Determine if the given key exists in the provided array. + * + * @param \ArrayAccess|array $array + * @param string|int $key + * @return bool + */ + public static function exists($array, $key) + { + if ($array instanceof ArrayAccess) { + return $array->offsetExists($key); + } + + return array_key_exists($key, $array); + } + + /** + * Collapse an array of arrays into a single array. + * + * @param array $array + * @return array + */ + public static function collapse($array) + { + $results = []; + + foreach ($array as $values) { + if (! is_array($values)) { + continue; + } + + $results[] = $values; + } + + return array_merge([], ...$results); + } +} \ No newline at end of file diff --git a/app/Helper/Validation/ValidationHelpers/EnvHelpers.php b/app/Helper/Validation/ValidationHelpers/EnvHelpers.php new file mode 100644 index 0000000..a149ac3 --- /dev/null +++ b/app/Helper/Validation/ValidationHelpers/EnvHelpers.php @@ -0,0 +1,62 @@ + "{field} is already Taken.", )); diff --git a/app/Routes.php b/app/Routes.php index 62a28db..963e318 100644 --- a/app/Routes.php +++ b/app/Routes.php @@ -5,4 +5,4 @@ */ use Simple\Routing\Router; -Router::get('',['controller' => 'home', 'action' => 'index']); +Router::get('','home@index'); \ No newline at end of file diff --git a/composer.json b/composer.json index 463df17..9a5fb2c 100644 --- a/composer.json +++ b/composer.json @@ -13,12 +13,18 @@ } ], "require": { - "simplyphp/framework": "1.2.*", + "simplyphp/framework": "v1.2.*", "php": ">=7.1" }, "autoload": { "psr-4": { "App\\": "app/" } - } + }, + + "config": { + "sort-packages": true + }, + "minimum-stability": "dev", + "prefer-stable": true } diff --git a/composer.lock b/composer.lock new file mode 100644 index 0000000..20a7e79 --- /dev/null +++ b/composer.lock @@ -0,0 +1,393 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", + "This file is @generated automatically" + ], + "content-hash": "2a59633baa4ff6a4326c2c710ab6afbc", + "packages": [ + { + "name": "simplyphp/framework", + "version": "v2.0.0-beta", + "source": { + "type": "git", + "url": "https://github.com/yourjhay/framework.git", + "reference": "5efc5b97d50991f3c75f822dae5cd4204825fe3a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/yourjhay/framework/zipball/5efc5b97d50991f3c75f822dae5cd4204825fe3a", + "reference": "5efc5b97d50991f3c75f822dae5cd4204825fe3a", + "shasum": "" + }, + "require": { + "php": ">=7.1", + "symfony/var-dumper": "^4.3", + "twig/twig": "^2.0" + }, + "require-dev": { + "phpunit/phpunit": "^7.5|^8.0" + }, + "suggest": { + "filp/whoops": "Install this to have a pretty page for errors.", + "intervention/image": "To handle Image processing in this micro framework", + "phpmailer/phpmailer": "For handling emails and provide extensible features" + }, + "type": "library", + "autoload": { + "files": [ + "src/Simple/functions.php", + "src/Simple/QueryBuilder/functions.php", + "src/Simple/ViewRenderer.php" + ], + "psr-4": { + "Simple\\": "src/Simple" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Rey Jhon A. Baquirin", + "email": "reyjhonbaquirin@yahoo.com", + "role": "Magic Programmer" + } + ], + "description": "The Simply PHP is lightweight web application framework.", + "homepage": "https://gitlab.com/jhayann/simple", + "keywords": [ + "micro framework", + "php framework", + "simplyphp" + ], + "time": "2020-05-18T11:01:56+00:00" + }, + { + "name": "symfony/polyfill-ctype", + "version": "v1.17.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-ctype.git", + "reference": "e94c8b1bbe2bc77507a1056cdb06451c75b427f9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/e94c8b1bbe2bc77507a1056cdb06451c75b427f9", + "reference": "e94c8b1bbe2bc77507a1056cdb06451c75b427f9", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "suggest": { + "ext-ctype": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.17-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Ctype\\": "" + }, + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Gert de Pagter", + "email": "BackEndTea@gmail.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for ctype functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "ctype", + "polyfill", + "portable" + ], + "time": "2020-05-12T16:14:59+00:00" + }, + { + "name": "symfony/polyfill-mbstring", + "version": "v1.17.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "fa79b11539418b02fc5e1897267673ba2c19419c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/fa79b11539418b02fc5e1897267673ba2c19419c", + "reference": "fa79b11539418b02fc5e1897267673ba2c19419c", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "suggest": { + "ext-mbstring": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.17-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Mbstring\\": "" + }, + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for the Mbstring extension", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "mbstring", + "polyfill", + "portable", + "shim" + ], + "time": "2020-05-12T16:47:27+00:00" + }, + { + "name": "symfony/polyfill-php72", + "version": "v1.17.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php72.git", + "reference": "f048e612a3905f34931127360bdd2def19a5e582" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/f048e612a3905f34931127360bdd2def19a5e582", + "reference": "f048e612a3905f34931127360bdd2def19a5e582", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.17-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Php72\\": "" + }, + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 7.2+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "time": "2020-05-12T16:47:27+00:00" + }, + { + "name": "symfony/var-dumper", + "version": "v4.4.8", + "source": { + "type": "git", + "url": "https://github.com/symfony/var-dumper.git", + "reference": "c587e04ce5d1aa62d534a038f574d9a709e814cf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/c587e04ce5d1aa62d534a038f574d9a709e814cf", + "reference": "c587e04ce5d1aa62d534a038f574d9a709e814cf", + "shasum": "" + }, + "require": { + "php": "^7.1.3", + "symfony/polyfill-mbstring": "~1.0", + "symfony/polyfill-php72": "~1.5" + }, + "conflict": { + "phpunit/phpunit": "<4.8.35|<5.4.3,>=5.0", + "symfony/console": "<3.4" + }, + "require-dev": { + "ext-iconv": "*", + "symfony/console": "^3.4|^4.0|^5.0", + "symfony/process": "^4.4|^5.0", + "twig/twig": "^1.34|^2.4|^3.0" + }, + "suggest": { + "ext-iconv": "To convert non-UTF-8 strings to UTF-8 (or symfony/polyfill-iconv in case ext-iconv cannot be used).", + "ext-intl": "To show region name in time zone dump", + "symfony/console": "To use the ServerDumpCommand and/or the bin/var-dump-server script" + }, + "bin": [ + "Resources/bin/var-dump-server" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.4-dev" + } + }, + "autoload": { + "files": [ + "Resources/functions/dump.php" + ], + "psr-4": { + "Symfony\\Component\\VarDumper\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony mechanism for exploring and dumping PHP variables", + "homepage": "https://symfony.com", + "keywords": [ + "debug", + "dump" + ], + "time": "2020-04-12T16:14:02+00:00" + }, + { + "name": "twig/twig", + "version": "v2.12.5", + "source": { + "type": "git", + "url": "https://github.com/twigphp/Twig.git", + "reference": "18772e0190734944277ee97a02a9a6c6555fcd94" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/twigphp/Twig/zipball/18772e0190734944277ee97a02a9a6c6555fcd94", + "reference": "18772e0190734944277ee97a02a9a6c6555fcd94", + "shasum": "" + }, + "require": { + "php": "^7.0", + "symfony/polyfill-ctype": "^1.8", + "symfony/polyfill-mbstring": "^1.3" + }, + "require-dev": { + "psr/container": "^1.0", + "symfony/phpunit-bridge": "^4.4|^5.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.12-dev" + } + }, + "autoload": { + "psr-0": { + "Twig_": "lib/" + }, + "psr-4": { + "Twig\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com", + "homepage": "http://fabien.potencier.org", + "role": "Lead Developer" + }, + { + "name": "Twig Team", + "role": "Contributors" + }, + { + "name": "Armin Ronacher", + "email": "armin.ronacher@active-4.com", + "role": "Project Founder" + } + ], + "description": "Twig, the flexible, fast, and secure template language for PHP", + "homepage": "https://twig.symfony.com", + "keywords": [ + "templating" + ], + "time": "2020-02-11T15:31:23+00:00" + } + ], + "packages-dev": [], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": { + "simplyphp/framework": 10 + }, + "prefer-stable": false, + "prefer-lowest": false, + "platform": { + "php": ">=7.1" + }, + "platform-dev": [] +} diff --git a/simply concept.png b/simply concept.png new file mode 100644 index 0000000..b54a082 Binary files /dev/null and b/simply concept.png differ