/src/Nucleus/Library/Validator/Validator.php

https://bitbucket.org/jonbiard/nucleus · PHP · 249 lines · 105 code · 21 blank · 123 comment · 17 complexity · 38757934217c0b5413c9d147f1f6391f MD5 · raw file

  1. <?php
  2. /**
  3. * Class Validator
  4. *
  5. * @package Nucleus\Library
  6. * @subpackage Validator
  7. *
  8. * @author Jonathan Biard <info@jonathanbiard.com>
  9. */
  10. namespace Nucleus\Library\Validator;
  11. use Nucleus\Library\Validator\Rule\AbstractRule;
  12. use Nucleus\Library\Validator\Rule\BlankRule;
  13. /**
  14. * Class Validator
  15. *
  16. * Usage example:
  17. * <code>
  18. * // Preparation of error messages for each rule
  19. * $errEmailRequired = $locales['emailRequired'];
  20. * $errPasswordRequired = $locales['passwordRequired'];
  21. * $errRememberInvalid = $locales['rememberInvalid'];
  22. * $errTimezoneInvalid = $locales['timezoneInvalid'];
  23. *
  24. * // Associative array of inputs to filter
  25. * $inputs = [
  26. * 'email' => 'email@domain.com',
  27. * 'password' => 'abcd1234',
  28. * 'remember' => '1',
  29. * 'timezone' => 'America/New_York',
  30. * ];
  31. *
  32. * // Get the Nucleus Validator
  33. * $f = new \Nucleus\Library\Validator\InputValidator($inputs, false);
  34. *
  35. * // Get some rules ready-to-use
  36. * $stringRule = new \Nucleus\Library\Validator\Rule\StringRule();
  37. * $blankRule = new \Nucleus\Library\Validator\Rule\BlankRule();
  38. * $intRule = new \Nucleus\Library\Validator\Rule\IntRule();
  39. * $inValuesRule = new \Nucleus\Library\Validator\Rule\InValuesRule();
  40. * $timezoneRule = new \Nucleus\Library\Validator\Rule\TimezoneRule();
  41. *
  42. * // Add rules
  43. * $f->addRule('email', $stringRule, $errEmailRequired, $f::FIX);
  44. * $f->addRule('email', $blankRule, $errEmailRequired, $f::IS_NOT);
  45. *
  46. * $f->addRule('password', $stringRule, $errPasswordRequired, $f::FIX);
  47. * $f->addRule('password', $blankRule, $errPasswordRequired, $f::IS_NOT);
  48. *
  49. * $f->addRule('remember', $intRule, $errRememberInvalid, $f::FIX);
  50. * $f->addRule('remember', $inValuesRule, $errRememberInvalid, $f::IS, [0, 1]);
  51. *
  52. * $f->addRule('timezone', $stringRule, $errTimezoneInvalid, $f::FIX);
  53. * $f->addRule('timezone', $blankRule, $errTimezoneInvalid, $f::IS_NOT);
  54. * $f->addRule('timezone', $timezoneRule, $errTimezoneInvalid, $f::IS);
  55. *
  56. * // Validate the inputs with the rules that have been set
  57. * if (!$f->validate()) {
  58. * // Report the errors that occurred and exit
  59. * die(print_r($f->getErrors(), true));
  60. * }
  61. *
  62. * // Fetch the safe data
  63. * $safeInputs = $f->getValidated();
  64. *
  65. * // ... do your work safely
  66. * </code>
  67. *
  68. * @package Nucleus\Library
  69. * @subpackage Validator
  70. */
  71. class Validator
  72. {
  73. const IS = 0b1;
  74. const IS_NOT = 0b10;
  75. const FIX = 0b100;
  76. const IS_BLANK_OR = 0b1000;
  77. const FIX_BLANK_OR = 0b11000; // Contains the IS_BLANK_OR flag
  78. /**
  79. * Holds the original unsafe array of variables
  80. * @var array
  81. */
  82. private $data = [];
  83. /**
  84. * Holds the error messages for the rules that failed
  85. * @var string[]
  86. */
  87. private $failedErrors = [];
  88. /**
  89. * Whether to stop at the first error or not
  90. * @var bool
  91. */
  92. private $haltOnError = [];
  93. /**
  94. * Holds the ValidatorData to use for validation
  95. * @var array
  96. */
  97. private $validatorData = [];
  98. /**
  99. * Constructor
  100. *
  101. * @param array $data The array to validate
  102. * @param bool $haltOnError Whether to stop at the first error or not
  103. */
  104. public function __construct($data, $haltOnError = false)
  105. {
  106. $this->data = $data;
  107. $this->haltOnError = $haltOnError;
  108. }
  109. /**
  110. * Adds a rule for this input
  111. *
  112. * Required options:
  113. * is, isNot, fix
  114. *
  115. * Optional options:
  116. * isBlankOr, fixBlankOr
  117. *
  118. * Explanation:
  119. * - One required type is necessary for all rules
  120. * - To simply allow blanks but let them through as-is: isBlankOr
  121. * - To allow blanks but force them to null: fixBlankOr
  122. *
  123. * @param string $key The key to validate on the original array
  124. * @param AbstractRule $rule The ValidatorRule to use
  125. * @param string $errorMessage The error message if $rule returns false
  126. * @param int $options The options (constants) as flags
  127. */
  128. public function addRule($key, AbstractRule $rule, $errorMessage, $options)
  129. {
  130. if (!isset($this->validatorData[$key])) {
  131. $this->validatorData[$key] = [];
  132. }
  133. $validatorData = new ValidatorData();
  134. $validatorData->setRule($rule);
  135. $validatorData->setErrorMessage($errorMessage);
  136. $validatorData->setOptions($options);
  137. $validatorData->setArgs(array_slice(func_get_args(), 4));
  138. $this->validatorData[$key][] = $validatorData;
  139. }
  140. /**
  141. * Gets the error messages for the rules that failed
  142. *
  143. * @return string[] The error messages array for the rules that failed
  144. */
  145. public function getErrors()
  146. {
  147. return $this->failedErrors;
  148. }
  149. /**
  150. * Gets the validated array
  151. *
  152. * @return array The array validated as per the rules
  153. */
  154. public function getValidated()
  155. {
  156. return $this->data;
  157. }
  158. /**
  159. * Validates all rules attached to this instance
  160. *
  161. * @return bool Success status as true or false
  162. */
  163. public function validate()
  164. {
  165. $blankRule = new BlankRule();
  166. $is = $this::IS;
  167. $isNot = $this::IS_NOT;
  168. $fix = $this::FIX;
  169. $isBlankOr = $this::IS_BLANK_OR;
  170. $fixBlankOr = $this::FIX_BLANK_OR;
  171. /** @var ValidatorData[] $validatorData */
  172. foreach ($this->validatorData as $key => $validatorData) {
  173. foreach ($validatorData as $data) {
  174. $success = false;
  175. $this->data[$key] = isset($this->data[$key]) ? $this->data[$key] : null;
  176. $value = $this->data[$key];
  177. $rule = $data->getRule();
  178. $options = $data->getOptions();
  179. $errorMessage = $data->getErrorMessage();
  180. $args = $data->getArgs();
  181. if ($options & $isBlankOr) {
  182. if ($blankRule->validate($this->data[$key])) {
  183. $success = true;
  184. } else {
  185. if ($options & $is) {
  186. if ($rule->is($value, $args)) {
  187. $success = true;
  188. }
  189. } elseif ($options & $isNot) {
  190. if ($rule->isNot($value, $args)) {
  191. $success = true;
  192. }
  193. } elseif ($options & $fix) {
  194. $success = true;
  195. }
  196. }
  197. } else {
  198. if ($options & $is) {
  199. if ($rule->is($value, $args)) {
  200. $success = true;
  201. }
  202. } elseif ($options & $isNot) {
  203. if ($rule->isNot($value, $args)) {
  204. $success = true;
  205. }
  206. } elseif ($options & $fix) {
  207. $success = true;
  208. }
  209. }
  210. if ($success) {
  211. if ($options & $fixBlankOr && $blankRule->validate($this->data[$key])) {
  212. $this->data[$key] = null;
  213. } elseif ($options & $fix) {
  214. $this->data[$key] = $rule->fix($value, $args);
  215. }
  216. } else {
  217. $this->failedErrors[$key] = $errorMessage;
  218. if ($this->haltOnError) {
  219. break 2;
  220. } else {
  221. break;
  222. }
  223. }
  224. }
  225. }
  226. return empty($this->failedErrors) ? true : false;
  227. }
  228. }