PageRenderTime 52ms CodeModel.GetById 23ms RepoModel.GetById 0ms app.codeStats 1ms

/core/xpdo/validation/xpdovalidator.class.php

https://github.com/francisreboucas/revolution
PHP | 323 lines | 210 code | 18 blank | 95 comment | 46 complexity | 73cc936a93dba1f027516ce9108fc160 MD5 | raw file
  1. <?php
  2. /*
  3. * Copyright 2010-2011 by MODX, LLC.
  4. *
  5. * This file is part of xPDO.
  6. *
  7. * xPDO is free software; you can redistribute it and/or modify it under the
  8. * terms of the GNU General Public License as published by the Free Software
  9. * Foundation; either version 2 of the License, or (at your option) any later
  10. * version.
  11. *
  12. * xPDO is distributed in the hope that it will be useful, but WITHOUT ANY
  13. * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  14. * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License along with
  17. * xPDO; if not, write to the Free Software Foundation, Inc., 59 Temple Place,
  18. * Suite 330, Boston, MA 02111-1307 USA
  19. */
  20. /**
  21. * The base xPDO validation classes.
  22. *
  23. * This file contains the base validation classes used by xPDO.
  24. *
  25. * @package xpdo
  26. * @subpackage validation
  27. */
  28. /**
  29. * The base validation service class.
  30. *
  31. * Extend this class to customize the validation process.
  32. *
  33. * @package xpdo
  34. * @subpackage validation
  35. */
  36. class xPDOValidator {
  37. public $object = null;
  38. public $results = array();
  39. public $messages = array();
  40. public function __construct(& $object) {
  41. $this->object = & $object;
  42. $this->object->_loadValidation(true);
  43. }
  44. /**
  45. * Executes validation against the object attached to this validator.
  46. *
  47. * @param array $parameters A collection of parameters.
  48. * @return boolean Either true or false indicating valid or invalid.
  49. */
  50. public function validate(array $parameters = array()) {
  51. $validated= false;
  52. $this->reset();
  53. $stopOnFail= isset($parameters['stopOnFail']) && $parameters['stopOnFail']
  54. ? true
  55. : false;
  56. $stopOnRuleFail= isset($parameters['stopOnRuleFail']) && $parameters['stopOnRuleFail']
  57. ? true
  58. : false;
  59. if (!empty($this->object->_validationRules)) {
  60. foreach ($this->object->_validationRules as $column => $rules) {
  61. $this->results[$column]= $this->object->isValidated($column);
  62. if (!$this->results[$column]) {
  63. $columnResults= array();
  64. foreach ($rules as $ruleName => $rule) {
  65. $result= false;
  66. if (is_array($rule['parameters'])) $rule['parameters']['column'] = $column;
  67. switch ($rule['type']) {
  68. case 'callable':
  69. $callable= $rule['rule'];
  70. if (is_callable($callable)) {
  71. $result= call_user_func_array($callable, array($this->object->_fields[$column],$rule['parameters']));
  72. if (!$result) $this->addMessage($column, $ruleName, isset($rule['parameters']['message']) ? $rule['parameters']['message'] : $ruleName . ' failed');
  73. } else {
  74. $this->object->xpdo->log(xPDO::LOG_LEVEL_ERROR, "Validation function {$callable} is not a valid callable function.");
  75. }
  76. break;
  77. case 'preg_match':
  78. $result= (boolean) preg_match($rule['rule'], $this->object->_fields[$column]);
  79. if (!$result) $this->addMessage($column, $ruleName, isset($rule['parameters']['message']) ? $rule['parameters']['message'] : $ruleName . ' failed');
  80. if ($this->object->xpdo->getDebug() === true)
  81. $this->object->xpdo->log(xPDO::LOG_LEVEL_DEBUG, "preg_match validation against {$rule['rule']} resulted in " . print_r($result, 1));
  82. break;
  83. case 'xPDOValidationRule':
  84. if ($ruleClass= $this->object->xpdo->loadClass($rule['rule'], '', false, true)) {
  85. if ($ruleObject= new $ruleClass($this, $column, $ruleName)) {
  86. $callable= array($ruleObject, 'isValid');
  87. if (is_callable($callable)) {
  88. $callableParams= array($this->object->_fields[$column], $rule['parameters']);
  89. $result= call_user_func_array($callable, $callableParams);
  90. } else {
  91. $this->object->xpdo->log(xPDO::LOG_LEVEL_ERROR, "Validation rule class {$rule['rule']} does not have an isValid() method.");
  92. }
  93. }
  94. } else {
  95. $this->object->xpdo->log(xPDO::LOG_LEVEL_ERROR, "Could not load validation rule class: {$rule['rule']}");
  96. }
  97. break;
  98. default:
  99. $this->object->xpdo->log(xPDO::LOG_LEVEL_ERROR, "Unsupported validation rule: " . print_r($rule, true));
  100. break;
  101. }
  102. $columnResults[$ruleName]= $result;
  103. if (!$result && $stopOnRuleFail) {
  104. break;
  105. }
  106. }
  107. $this->results[$column]= !in_array(false, $columnResults, true) ? true : false;
  108. if (!$this->results[$column] && $stopOnFail) {
  109. break;
  110. }
  111. }
  112. if ($this->results[$column]) {
  113. $this->object->_validated[$column]= $column;
  114. }
  115. }
  116. if (empty($this->results) || !in_array(false, $this->results, true)) {
  117. $validated = true;
  118. if ($this->object->xpdo->getDebug() === true)
  119. $this->object->xpdo->log(xPDO::LOG_LEVEL_WARN, "Validation succeeded: " . print_r($this->results, true));
  120. } elseif ($this->object->xpdo->getDebug() === true) {
  121. $this->object->xpdo->log(xPDO::LOG_LEVEL_WARN, "Validation failed: " . print_r($this->results, true));
  122. }
  123. } else {
  124. if ($this->object->xpdo->getDebug() === true) $this->object->xpdo->log(xPDO::LOG_LEVEL_DEBUG, "Validation called but no rules were found.");
  125. $validated = true;
  126. }
  127. return $validated;
  128. }
  129. /**
  130. * Add a validation message to the stack.
  131. *
  132. * @param string $field The name of the field the message relates to.
  133. * @param string $name The name of the rule the message relates to.
  134. * @param mixed $message An optional message; the name of the rule is used
  135. * if no message is specified.
  136. */
  137. public function addMessage($field, $name, $message= null) {
  138. if (empty($message)) $message= $name;
  139. array_push($this->messages, array(
  140. 'field' => $field,
  141. 'name' => $name,
  142. 'message' => $message,
  143. ));
  144. }
  145. /**
  146. * Indicates validation messages were generated by validate().
  147. *
  148. * @return boolean True if messages were generated.
  149. */
  150. public function hasMessages() {
  151. return (count($this->messages) > 0);
  152. }
  153. /**
  154. * Get the validation messages generated by validate().
  155. *
  156. * @return array An array of validation messages.
  157. */
  158. public function getMessages() {
  159. return $this->messages;
  160. }
  161. /**
  162. * Get the validation results generated by validate().
  163. *
  164. * @return array An array of boolean validation results.
  165. */
  166. public function getResults() {
  167. return $this->results;
  168. }
  169. /**
  170. * Reset the validation results and messages.
  171. */
  172. public function reset() {
  173. $this->results= array();
  174. $this->messages= array();
  175. }
  176. }
  177. /**
  178. * The base validation rule class.
  179. *
  180. * @package xpdo
  181. * @subpackage validation
  182. */
  183. class xPDOValidationRule {
  184. public $validator = null;
  185. public $field = '';
  186. public $name = '';
  187. public $message = '';
  188. /**
  189. * Construct a new xPDOValidationRule instance.
  190. *
  191. * @param xPDOValidator &$validator A reference to the xPDOValidator executing this rule.
  192. * @param mixed $field The field being validated.
  193. * @param mixed $name The identifying name of the validation rule.
  194. * @param string $message An optional message for rule failure.
  195. * @return xPDOValidationRule The rule instance.
  196. */
  197. public function __construct(& $validator, $field, $name, $message= '') {
  198. $this->validator = & $validator;
  199. $this->field = $field;
  200. $this->name = $name;
  201. $this->message = (!empty($message) && $message !== '0' ? $message : $name);
  202. }
  203. /**
  204. * The public method for executing a validation rule.
  205. *
  206. * Extend this method to provide a reusable validation rule in your xPDOValidator instance.
  207. *
  208. * @param mixed $value The value of the field being validated.
  209. * @param array $options Any options expected by the rule.
  210. * @return boolean True if the validation rule was passed, otherwise false.
  211. */
  212. public function isValid($value, array $options = array()) {
  213. if (isset($options['message'])) {
  214. $this->setMessage($options['message']);
  215. }
  216. return true;
  217. }
  218. /**
  219. * Set the failure message for the rule.
  220. *
  221. * @param string $message A message intended to convey the reason for rule failure.
  222. */
  223. public function setMessage($message= '') {
  224. if (!empty($message) && $message !== '0') {
  225. $this->message= $message;
  226. }
  227. }
  228. }
  229. class xPDOMinLengthValidationRule extends xPDOValidationRule {
  230. public function isValid($value, array $options = array()) {
  231. $result= parent :: isValid($value, $options);
  232. $minLength= isset($options['value']) ? intval($options['value']) : 0;
  233. $result= (is_string($value) && strlen($value) >= $minLength);
  234. if ($result === false) {
  235. $this->validator->addMessage($this->field, $this->name, $this->message);
  236. }
  237. return $result;
  238. }
  239. }
  240. class xPDOMaxLengthValidationRule extends xPDOValidationRule {
  241. public function isValid($value, array $options = array()) {
  242. $result= parent :: isValid($value, $options);
  243. $maxLength= isset($options['value']) ? intval($options['value']) : 0;
  244. $result= ($maxLength > 0 && is_string($value) && strlen($value) <= $maxLength);
  245. if ($result === false) {
  246. $this->validator->addMessage($this->field, $this->name, $this->message);
  247. }
  248. }
  249. }
  250. class xPDOMinValueValidationRule extends xPDOValidationRule {
  251. public function isValid($value, array $options = array()) {
  252. $result= parent :: isValid($value, $options);
  253. $minValue= isset($options['value']) ? intval($options['value']) : 0;
  254. $result= ($value >= $minValue);
  255. if ($result === false) {
  256. $this->validator->addMessage($this->field, $this->name, $this->message);
  257. }
  258. }
  259. }
  260. class xPDOMaxValueValidationRule extends xPDOValidationRule {
  261. public function isValid($value, array $options = array()) {
  262. $result= parent :: isValid($value, $options);
  263. $maxValue= isset($options['value']) ? intval($options['value']) : 0;
  264. $result= ($value <= $maxValue);
  265. if ($result === false) {
  266. $this->validator->addMessage($this->field, $this->name, $this->message);
  267. }
  268. }
  269. }
  270. class xPDOObjectExistsValidationRule extends xPDOValidationRule {
  271. public function isValid($value, array $options = array()) {
  272. if (!isset($options['pk']) || !isset($options['className'])) return false;
  273. $result= parent :: isValid($value, $options);
  274. $xpdo =& $this->validator->object->xpdo;
  275. $obj = $xpdo->getObject($options['className'],$options['pk']);
  276. $result = ($obj !== null);
  277. if ($result === false) {
  278. $this->validator->addMessage($this->field, $this->name, $this->message);
  279. }
  280. return $result;
  281. }
  282. }
  283. class xPDOForeignKeyConstraint extends xPDOValidationRule {
  284. public function isValid($value, array $options = array()) {
  285. if (!isset($options['alias'])) return false;
  286. parent :: isValid($value, $options);
  287. $result= false;
  288. $obj=& $this->validator->object;
  289. $xpdo=& $obj->xpdo;
  290. $fkdef= $obj->getFKDefinition($options['alias']);
  291. if (isset ($obj->_relatedObjects[$options['alias']])) {
  292. if (!is_object($obj->_relatedObjects[$options['alias']])) {
  293. $result= false;
  294. }
  295. }
  296. $criteria= array ($fkdef['foreign'] => $obj->get($fkdef['local']));
  297. if ($object= $xpdo->getObject($fkdef['class'], $criteria)) {
  298. $result= ($object !== null);
  299. }
  300. if ($result === false) {
  301. $this->validator->addMessage($this->field, $this->name, $this->message);
  302. }
  303. return $result;
  304. }
  305. }