/WDB/Validation/ColumnValidator.php

https://bitbucket.org/rejem/wheeldb · PHP · 168 lines · 119 code · 10 blank · 39 comment · 27 complexity · 8cab131ec62913ada426fc5cd95d7514 MD5 · raw file

  1. <?php
  2. namespace WDB\Validation;
  3. use WDB,
  4. WDB\Exception;
  5. /**
  6. * @property-read ColumnValidationError[] $errors
  7. * @author Richard Ejem <richard(at)ejem.cz>
  8. * @package WDB
  9. */
  10. final class ColumnValidator implements iValidator
  11. {
  12. /**@var array*/
  13. private $errors;
  14. //WDB\Event\iEventListener implementation
  15. /**
  16. * Validate passed record.
  17. *
  18. * @param WDB\Wrapper\Record $record
  19. * @return bool
  20. * @throws WDB\Exception\BadArgument
  21. */
  22. public function raise(WDB\Wrapper\iRecord $record = NULL)
  23. {
  24. if ($record === NULL) throw new Exception\BadArgument("Validator event raise() method needs"
  25. . " WDB\Wrapper\iRecord as a raise argument");
  26. $this->errors = array();
  27. foreach ($record->getTable()->getColumns() as $column)
  28. {
  29. $this->validateColumn($column, $record);
  30. }
  31. $record->addValidationErrors($this->errors);
  32. return $this->success();
  33. }
  34. //WDB\Event\iEventListener end
  35. /**
  36. * Returns true if last validation was successful.
  37. *
  38. * @return bool
  39. */
  40. public function success()
  41. {
  42. return count($this->errors) == 0;
  43. }
  44. //TODO create structure for errors
  45. /**
  46. * Returns list of validation errors occured
  47. *
  48. * @return array
  49. */
  50. public function getErrors()
  51. {
  52. return $this->errors;
  53. }
  54. //iValidator implementation
  55. public function fetchValidatorData(WDB\Wrapper\iRecord $record, &$data)
  56. {
  57. $columns = $record->getTable()->getColumns();
  58. foreach ($data['columns'] as $key=>&$c)
  59. {
  60. if (isset($columns[$key]) && $columns[$key]->getRules() !== NULL)
  61. {
  62. $c['ColumnValidator'] = $columns[$key]->getRules()->toArray();
  63. }
  64. }
  65. }
  66. //iValidator end
  67. /**
  68. * If condition is false, adds a message to this->errors based on currently validated column and rule.
  69. *
  70. * @param bool $condition
  71. */
  72. private function assert(WDB\Wrapper\iColumn $column, $rule, WDB\Wrapper\Field $field, $condition)
  73. {
  74. if (!$condition)
  75. {
  76. $rules = $column->getRules();
  77. $err = $rules[$rule]['message'];
  78. $this->errors[] = array('message'=>$err, 'column'=>$column);
  79. $field->addValidationError($err);
  80. }
  81. }
  82. /**
  83. * Validates current column by class private variables (column, field).
  84. *
  85. * @throws WDB\Exception\BadArgument
  86. */
  87. private function validateColumn(WDB\Wrapper\iColumn $column, WDB\Wrapper\iRecord $record)
  88. {
  89. $value = $record[$column->getName()];
  90. $columns = $record->getTable()->getColumns();
  91. $c = $column;
  92. $f = $record->getField($column->getName());
  93. //column is empty and not required - skip some validation
  94. if ($value === NULL && empty($column->rules[ColumnRules::REQUIRED])) return;
  95. $isEmpty = (($value === NULL || $value === '') && empty($column->rules[ColumnRules::REQUIRED]));
  96. foreach ($column->rules as $id=>$rule)
  97. {
  98. $r = $id;
  99. $special = FALSE;
  100. if (is_array($value) || is_object($value))
  101. {
  102. $special = TRUE;
  103. if (!in_array($id, array(ColumnRules::REQUIRED, ColumnRules::EQUALTO, ColumnRules::CALLBACK))) continue;
  104. }
  105. switch ($id)
  106. {
  107. case ColumnRules::REQUIRED:
  108. $this->assert($c,$r,$f,($value !== '' && $value !== NULL && $value !== FALSE && $value !== array()));
  109. break;
  110. case ColumnRules::INTEGER:
  111. $this->assert($c,$r,$f,preg_match('~[+-]?[0-9]+~', trim($value)));
  112. break;
  113. case ColumnRules::FLOAT:
  114. $this->assert($c,$r,$f,is_numeric(trim($value)));
  115. break;
  116. case ColumnRules::MINLENGTH:
  117. $this->assert($c,$r,$f,strlen($value) >= $rule['argument']);
  118. break;
  119. case ColumnRules::MAXLENGTH:
  120. $this->assert($c,$r,$f,strlen($value) <= $rule['argument']);
  121. break;
  122. case ColumnRules::MINVALUE:
  123. $this->assert($c,$r,$f,is_numeric($value) && bccomp($value, $rule['argument']) >= 0);
  124. break;
  125. case ColumnRules::MAXVALUE:
  126. $this->assert($c,$r,$f,is_numeric($value) && bccomp($value, $rule['argument']) <= 0);
  127. break;
  128. case ColumnRules::PATTERN:
  129. $this->assert($c,$r,$f,$isEmpty || preg_match($rule['argument'], $value));
  130. break;
  131. case ColumnRules::EQUALTO:
  132. if (!isset($record[$rule['argument']]))
  133. {
  134. throw new Exception\BadArgument("field {$rule['argument']} tested for equality with {$column->getName()} was not found");
  135. }
  136. $this->assert($c,$r,$f,$value == $record[$rule['argument']]);
  137. break;
  138. case ColumnRules::CALLBACK:
  139. if (!$rule['argument'] instanceof WDB\Utils\Caller && !is_callable($rule['argument']))
  140. {
  141. throw new Exception\BadArgument("Validation rule with custom callback need the callback to be an instance of \WDB\Utils\Caller");
  142. }
  143. if ($rule['argument'] instanceof WDB\Utils\Caller)
  144. {
  145. $rule['argument']->arguments['value'] = $value;
  146. $this->assert($c,$r,$f,$isEmpty || $rule['argument']($value));
  147. }
  148. else
  149. {
  150. $this->assert($c,$r,$f,$isEmpty || call_user_func($rule['argument'], $value));
  151. }
  152. break;
  153. default:
  154. throw new Exception\BadArgument("unknown validation rule #$id");
  155. }
  156. }
  157. }
  158. }