Skip to main content
Code Review

Return to Question

replaced http://codereview.stackexchange.com/ with https://codereview.stackexchange.com/
Source Link

This question is a sub-post to this question question

This post is a follow up for this this and this this.

This question is a sub-post to this question

This post is a follow up for this and this.

This question is a sub-post to this question

This post is a follow up for this and this.

Source Link

My included in every script file - follow-up -3 (the Validator class)

This question is a sub-post to this question

This post is a follow up for this and this.

"In this particular question I need the reviewers help to judge my Validator class"


Validator.class.php

a class designed to get used by the controllers. it has one purpose "handle client inputs"

<?php
namespace aap;
// flags
define('F_REQUIRED', 1);
define('F_MUSTBESET', 2);
define('F_ESCAPE', 4); // if data is valid it can work only with STRING and EMAIL
define('F_SANITIZE', 8); // if data is valid it can work only with STRING and EMAIL
/*
 * define('F_SETIFNOT', 16); // this flag is uselss since passing the $data var
 * by reference will make php define it as null anyway
 * see my question at :http://stackoverflow.com/questions/41483976
 */
// filter types
define('INT', 1);
define('STRING', 2);
define('FLOAT', 3);
define('DATE', 4);
define('EMAIL', 5);
// define('LONGSTRING', 5);
// error types
define('ER_NOT_SET', 10);
define('ER_EMPTY', 11);
define('ER_DATA', 12);
class Validator
{
 protected $options = [];
 protected $errors = [];
 protected $sysModel;
 public function __construct(SystemModel $sysModel, array $options = [])
 {
 $this->sysModel = $sysModel;
 $this->resetOptions();
 $this->setOptions($options);
 }
 public function resetOptions()
 {
 // defaults
 $this->options[INT]['min'] = - 1 * PHP_INT_MAX - 1;
 $this->options[INT]['max'] = PHP_INT_MAX;
 $this->options[STRING]['length'] = 80;
 $this->options[STRING]['regex'] = "/[^a-zA-Z0-9_ أ-ي]/u";
 $this->options[STRING]['ischarsinvalid'] = true;
 $this->options[FLOAT]['min'] = 0.0;
 $this->options[FLOAT]['max'] = INF;
 $this->options[DATE]['min'] = "1970年01月01日"; // mysql timestamp range
 $this->options[DATE]['max'] = "2038年01月18日"; // mysql timestamp range
 $this->options[DATE]['format'] = "Y-m-d"; // 2 formats only are supported Y-m-d H:i:s
 }
 /*
 * function validate is designed to report errors in the object $error array
 * @return: the variable $data if it is valid or return false if it is not valid
 * @note: passing by reference will make the
 */
 public function validate(&$data, $varName, $filterType, $flags = null)
 {
 if (! isset($data) && ($flags & F_MUSTBESET)) {
 $this->errors[$varName]['type'] = ER_NOT_SET;
 return false;
 }
 if (empty($data) && ($flags & F_REQUIRED)) {
 $this->errors[$varName]['type'] = ER_EMPTY;
 return false;
 }
 switch ($filterType) {
 case INT:
 if (isset($data) && ($data < $this->options[INT]['min'] || $data > $this->options[INT]['max'])) {
 $this->errors[$varName]['type'] = ER_DATA;
 $data = $this->checkEscapeAndSanitize($data, $flags);
 return false;
 }
 return $data;
 break;
 case STRING:
 // @formatter:off
 if (! empty($data) && (strlen($data) > $this->options[STRING]['length'] || (preg_match($this->options[STRING]['regex'], $data) && $this->options[STRING]['ischarsinvalid']) || (! preg_match($this->options[STRING]['regex'], $data) && ! $this->options[STRING]['ischarsinvalid']))) {
 // @formatter:on
 $this->errors[$varName]['type'] = ER_DATA;
 $data = $this->checkEscapeAndSanitize($data, $flags);
 return false;
 }
 $data = $this->checkEscapeAndSanitize($data, $flags);
 return $data;
 break;
 case FLOAT:
 if (! empty($data) && ($data < $this->options[FLOAT]['min'] || $data > $this->options[FLOAT]['max'])) {
 $this->errors[$varName]['type'] = ER_DATA;
 $data = $this->checkEscapeAndSanitize($data, $flags);
 return false;
 }
 return $data;
 break;
 case DATE:
 if (! empty($data) && ! Validator::isDate($data, $this->options[DATE]['min'], $this->options[DATE]['max'], $this->options[DATE]['format'])) {
 $this->errors[$varName]['type'] = ER_DATA;
 $data = $this->checkEscapeAndSanitize($data, $flags);
 return false;
 }
 return $data;
 break;
 case EMAIL:
 if (! empty($data) && filter_var($data, FILTER_VALIDATE_EMAIL) === false) {
 $this->errors[$varName]['type'] = ER_DATA;
 $data = $this->checkEscapeAndSanitize($data, $flags);
 return false;
 }
 $data = $this->checkEscapeAndSanitize($data, $flags);
 return $data;
 break;
 default:
 throw new \InvalidArgumentException("'{$filterType}'filterType value is an unknown filter type!");
 break;
 }
 }
 protected function checkEscapeAndSanitize($data, $flags)
 {
 if ($flags & F_ESCAPE) {
 $data = $this->sysModel->escape($data);
 }
 if ($flags & F_SANITIZE) {
 $data = htmlspecialchars($data);
 }
 return $data;
 }
 public function flushErrors()
 {
 $this->errors = [];
 }
 public function getErrors()
 {
 return $this->errors;
 }
 public function setOptions(array $options)
 {
 foreach ($options as $type => $value) {
 switch ($type) {
 case INT:
 foreach ($value as $option => $optionValue) {
 switch ($option) {
 case "min":
 if (! filter_var($options[INT]['min'], FILTER_VALIDATE_INT)) {
 throw new \InvalidArgumentException("'{$options[INT]['min']}'[int][min] value is not an integer!");
 } else {
 $this->options[INT]['min'] = $options[INT]['min'];
 }
 break;
 case "max":
 if (! filter_var($options[INT]['max'], FILTER_VALIDATE_INT)) {
 throw new \InvalidArgumentException("'{$options[INT]['max']}'[int][max] value is not an integer!");
 } else {
 $this->options[INT]['max'] = $options[INT]['max'];
 }
 break;
 default:
 throw new \InvalidArgumentException("'$option' is not a valid option name for int data types!");
 break;
 }
 }
 break;
 case STRING:
 foreach ($value as $option => $optionValue) {
 switch ($option) {
 case "length":
 if (! filter_var($options[STRING]['length'], FILTER_VALIDATE_INT, array(
 "options" => array(
 "min_range" => 0
 )
 ))) {
 throw new \InvalidArgumentException("'{$options[STRING]['length']}'[string][length] value is not a positive integer!");
 } else {
 $this->options[STRING]['length'] = $options[STRING]['length'];
 }
 break;
 case "regex":
 $this->options[STRING]['regex'] = $options[STRING]['regex'];
 break;
 case "ischarsinvalid":
 if (! is_bool($options[STRING]['ischarsinvalid'])) {
 throw new \InvalidArgumentException("'{$options[STRING]['ischarsinvalid']}'[string][ischarsinvalid] value is not a bool!");
 } else {
 $this->options[STRING]['ischarsinvalid'] = $options[STRING]['ischarsinvalid'];
 }
 break;
 default:
 throw new \InvalidArgumentException("'$option' is not a valid option name for string data types!");
 break;
 }
 }
 break;
 case FLOAT:
 foreach ($value as $option => $optionValue) {
 switch ($option) {
 case "min":
 if (! is_numeric($options[FLOAT]['min'])) {
 throw new \InvalidArgumentException("'{$options[FLOAT]['min']}'[float][min] value is not numeric");
 } else {
 $this->options[FLOAT]['min'] = $options[FLOAT]['min'];
 }
 break;
 case "max":
 if (! is_numeric($options[FLOAT]['max'])) {
 throw new \InvalidArgumentException("'{$options[FLOAT]['max']}'[float][max] value is not numeric!");
 } else {
 $this->options[FLOAT]['max'] = $options[FLOAT]['max'];
 }
 break;
 default:
 throw new \InvalidArgumentException("'$option' is not a valid option name for float data types!");
 break;
 }
 }
 break;
 case DATE:
 foreach ($value as $option => $optionValue) {
 switch ($option) {
 case "min":
 if (! Validator::isDate($options[DATE]['min'])) {
 throw new \InvalidArgumentException("'{$options[DATE]['min']}'[date][min] value is not a date");
 } else {
 $this->options[DATE]['min'] = $options[DATE]['min'];
 }
 break;
 case "max":
 if (! Validator::isDate($options[DATE]['max'])) {
 throw new \InvalidArgumentException("'{$options[DATE]['max']}'[date][max] value is not a date!");
 } else {
 $this->options[DATE]['max'] = $options[DATE]['max'];
 }
 break;
 case "format":
 if ($options[DATE]['format'] != "Y-m-d H:i:s" && $options[DATE]['format'] != "Y-m-d") {
 throw new \InvalidArgumentException("'{$options[DATE]['format']}'[date][format] value is not a valid date format");
 } else {
 $this->options[DATE]['format'] = $options[DATE]['format'];
 }
 break;
 default:
 throw new \InvalidArgumentException("'$option' is not a valid option name for date data types!");
 break;
 }
 }
 break;
 default:
 throw new \InvalidArgumentException("'$type' is not a valid type name!");
 break;
 }
 ;
 }
 }
 public static function compareDatetime($date1, $date2, $unit)
 {
 /*
 * compare 2 dates and return the difference between date1 and date2 \
 * (positive value or nigative)
 * @return date1- date2 or false on failure
 */
 //
 if ($unit != "h" && $unit != "i" && $unit != "s" && $unit != "d") {
 return false;
 }
 ;
 $date1 = strtotime($date1);
 $date2 = strtotime($date2);
 if ($date1 === false || $date2 === false) {
 return false;
 }
 ;
 $diff = $date1 - $date2;
 switch ($unit) {
 case "d":
 return (int) round($diff / (3600 * 24));
 case "h":
 return (int) round($diff / 3600);
 case "i":
 return (int) round($diff / 60);
 case "s":
 return (int) $diff;
 }
 }
 public static function isDate($date, $from = "0001年01月01日", $to = "9999年12月31日", $format = "Y-m-d")
 {
 // 2 formats only are supported Y-m-d H:i:s Y-m-d
 if ($format != "Y-m-d" && $format != "Y-m-d H:i:s") {
 return false;
 }
 if ($format == "Y-m-d") {
 if (! $from) {
 $from = "0001年01月01日";
 }
 ;
 if (! $to) {
 $to = "9999年12月31日";
 }
 ;
 } else {
 if (! $from) {
 $from = "0001年01月01日 00:00:00";
 }
 ;
 if (! $to) {
 $to = "9999年12月31日 00:00:00";
 }
 ;
 }
 if (($date = \DateTime::createFromFormat($format, $date)) === false) {
 return false;
 }
 if (\DateTime::getLastErrors()['warning_count'] > 0) {
 return false;
 }
 if (($from = \DateTime::createFromFormat($format, $from)) === false) {
 return false;
 }
 if (\DateTime::getLastErrors()['warning_count'] > 0) {
 return false;
 }
 if (($to = \DateTime::createFromFormat($format, $to)) === false) {
 return false;
 }
 if (\DateTime::getLastErrors()['warning_count'] > 0) {
 return false;
 }
 if ($date->format("U") < $from->format("U")) {
 return false;
 }
 if ($date->format("U") > $to->format("U")) {
 return false;
 }
 return true;
 }
}

an example of how I plan to use the class

 $validator = new Validator($sysModel);
 //validating inputs
 $options[STRING]['length'] = 20;
 $validator->setOptions($options);
 $validator->validate($_POST['name'], "name", STRING, F_MUSTBESET );
 $options[STRING]['regex'] = "/[^0-9 +]/u";
 $options[STRING]['length'] = 15;
 $validator->setOptions($options);
 $validator->validate($_POST['phone'], "phone", STRING, F_MUSTBESET);
 $validator->resetOptions();
 $options[STRING]['regex'] = "/.*/u";
 $options[STRING]['ischarsinvalid'] = false;
 $options[STRING]['length'] = 500;
 $validator->setOptions($options);
 $validator->validate($_POST['message'], "message", STRING, F_REQUIRED | F_MUSTBESET | F_ESCAPE | F_SANITIZE);
 $validator->validate($_POST['email'], "email", EMAIL, F_MUSTBESET | F_ESCAPE | F_SANITIZE);
 $validator->resetOptions();
 $validator->validate($_POST['title'], "title", STRING, F_MUSTBESET | F_ESCAPE | F_SANITIZE);
 $validator->validate($_POST['subject'], "subject", STRING, F_REQUIRED | F_MUSTBESET | F_ESCAPE | F_SANITIZE);
 $errorMessage = "";
 foreach ($validator->getErrors() as $error => $value) {
 switch ($error) {
 case "name":
 $contact->set("nameInput-class", "incorrect-field");
 $errorMessage .= "<li>name field is not correct!</li>\n";
 // switch ($value['type']) {case ER_NOT_SET:
 break;
 case "phone":
 $contact->set("phoneInput-class", "incorrect-field");
 $errorMessage .= "<li>phone field is not correct!</li>\n";
 break;
 case "email":
 $contact->set("emailInput-class", "incorrect-field");
 $errorMessage .= "<li>email field is not correct!</li>\n";
 break;
 case "subject":
 $contact->set("subjectInput-class", "incorrect-field");
 $errorMessage .= "<li>invalid input!</li>\n";
 break;
 case "title":
 $contact->set("titleInput-class", "incorrect-field");
 $errorMessage .= "<li>title field is not correct!</li>\n";
 break;
 case "message":
 $contact->set("messageInput-class", "incorrect-field");
 $errorMessage .= "<li>message field is not correct!</li>\n";
 break;
 }
 }
lang-php

AltStyle によって変換されたページ (->オリジナル) /