PageRenderTime 25ms CodeModel.GetById 23ms RepoModel.GetById 0ms app.codeStats 0ms

/core/phpactiverecord/Validations.php

https://github.com/rosianesrocha/nanico
PHP | 833 lines | 454 code | 107 blank | 272 comment | 103 complexity | 01a6da497b3b4fd12201189bc5e3a363 MD5 | raw file
  1. <?php
  2. /**
  3. * These two classes have been <i>heavily borrowed</i> from Ruby on Rails' ActiveRecord so much that
  4. * this piece can be considered a straight port. The reason for this is that the vaildation process is
  5. * tricky due to order of operations/events. The former combined with PHP's odd typecasting means
  6. * that it was easier to formulate this piece base on the rails code.
  7. *
  8. * @package ActiveRecord
  9. */
  10. namespace ActiveRecord;
  11. use ActiveRecord\Model;
  12. use IteratorAggregate;
  13. use ArrayIterator;
  14. /**
  15. * Manages validations for a {@link Model}.
  16. *
  17. * This class isn't meant to be directly used. Instead you define
  18. * validators thru static variables in your {@link Model}. Example:
  19. *
  20. * <code>
  21. * class Person extends ActiveRecord\Model {
  22. * static $validates_length_of = array(
  23. * array('name', 'within' => array(30,100),
  24. * array('state', 'is' => 2)
  25. * );
  26. * }
  27. *
  28. * $person = new Person();
  29. * $person->name = 'Tito';
  30. * $person->state = 'this is not two characters';
  31. *
  32. * if (!$person->is_valid())
  33. * print_r($person->errors);
  34. * </code>
  35. *
  36. * @package ActiveRecord
  37. * @see Errors
  38. * @link http://www.phpactiverecord.org/guides/validations
  39. */
  40. class Validations
  41. {
  42. private $model;
  43. private $options = array();
  44. private $validators = array();
  45. private $record;
  46. private static $VALIDATION_FUNCTIONS = array(
  47. 'validates_presence_of',
  48. 'validates_size_of',
  49. 'validates_length_of',
  50. 'validates_inclusion_of',
  51. 'validates_exclusion_of',
  52. 'validates_format_of',
  53. 'validates_numericality_of',
  54. 'validates_uniqueness_of'
  55. );
  56. private static $DEFAULT_VALIDATION_OPTIONS = array(
  57. 'on' => 'save',
  58. 'allow_null' => false,
  59. 'allow_blank' => false,
  60. 'message' => null,
  61. );
  62. private static $ALL_RANGE_OPTIONS = array(
  63. 'is' => null,
  64. 'within' => null,
  65. 'in' => null,
  66. 'minimum' => null,
  67. 'maximum' => null,
  68. );
  69. private static $ALL_NUMERICALITY_CHECKS = array(
  70. 'greater_than' => null,
  71. 'greater_than_or_equal_to' => null,
  72. 'equal_to' => null,
  73. 'less_than' => null,
  74. 'less_than_or_equal_to' => null,
  75. 'odd' => null,
  76. 'even' => null
  77. );
  78. /**
  79. * Constructs a {@link Validations} object.
  80. *
  81. * @param Model $model The model to validate
  82. * @return Validations
  83. */
  84. public function __construct(Model $model)
  85. {
  86. $this->model = $model;
  87. $this->record = new Errors($this->model);
  88. $this->validators = array_intersect(array_keys(Reflections::instance()->get(get_class($this->model))->getStaticProperties()), self::$VALIDATION_FUNCTIONS);
  89. }
  90. /**
  91. * Returns validator data.
  92. *
  93. * @return array
  94. */
  95. public function rules()
  96. {
  97. $data = array();
  98. $reflection = Reflections::instance()->get(get_class($this->model));
  99. foreach ($this->validators as $validate)
  100. {
  101. $attrs = $reflection->getStaticPropertyValue($validate);
  102. foreach ($attrs as $attr)
  103. {
  104. $field = $attr[0];
  105. if (!isset($data[$field]) || !is_array($data[$field]))
  106. $data[$field] = array();
  107. $attr['validator'] = $validate;
  108. unset($attr[0]);
  109. array_push($data[$field],$attr);
  110. }
  111. }
  112. return $data;
  113. }
  114. /**
  115. * Runs the validators.
  116. *
  117. * @return Errors the validation errors if any
  118. */
  119. public function validate()
  120. {
  121. $reflection = Reflections::instance()->get(get_class($this->model));
  122. foreach ($this->validators as $validate)
  123. $this->$validate($reflection->getStaticPropertyValue($validate));
  124. $this->record->clear_model();
  125. return $this->record;
  126. }
  127. /**
  128. * Validates a field is not null and not blank.
  129. *
  130. * <code>
  131. * class Person extends ActiveRecord\Model {
  132. * static $validates_presence_of = array(
  133. * array('first_name'),
  134. * array('last_name')
  135. * );
  136. * }
  137. * </code>
  138. *
  139. * Available options:
  140. *
  141. * <ul>
  142. * <li><b>message:</b> custom error message</li>
  143. * </ul>
  144. *
  145. * @param array $attrs Validation definition
  146. */
  147. public function validates_presence_of($attrs)
  148. {
  149. $configuration = array_merge(self::$DEFAULT_VALIDATION_OPTIONS, array('message' => Errors::$DEFAULT_ERROR_MESSAGES['blank'], 'on' => 'save'));
  150. foreach ($attrs as $attr)
  151. {
  152. $options = array_merge($configuration, $attr);
  153. $this->record->add_on_blank($options[0], $options['message']);
  154. }
  155. }
  156. /**
  157. * Validates that a value is included the specified array.
  158. *
  159. * <code>
  160. * class Car extends ActiveRecord\Model {
  161. * static $validates_inclusion_of = array(
  162. * array('fuel_type', 'in' => array('hyrdogen', 'petroleum', 'electric')),
  163. * );
  164. * }
  165. * </code>
  166. *
  167. * Available options:
  168. *
  169. * <ul>
  170. * <li><b>in/within:</b> attribute should/shouldn't be a value within an array</li>
  171. * <li><b>message:</b> custome error message</li>
  172. * </ul>
  173. *
  174. * @param array $attrs Validation definition
  175. */
  176. public function validates_inclusion_of($attrs)
  177. {
  178. $this->validates_inclusion_or_exclusion_of('inclusion', $attrs);
  179. }
  180. /**
  181. * This is the opposite of {@link validates_include_of}.
  182. *
  183. * @param array $attrs Validation definition
  184. * @see validates_inclusion_of
  185. */
  186. public function validates_exclusion_of($attrs)
  187. {
  188. $this->validates_inclusion_or_exclusion_of('exclusion', $attrs);
  189. }
  190. /**
  191. * Validates that a value is in or out of a specified list of values.
  192. *
  193. * @see validates_inclusion_of
  194. * @see validates_exclusion_of
  195. * @param string $type Either inclusion or exclusion
  196. * @param $attrs Validation definition
  197. */
  198. public function validates_inclusion_or_exclusion_of($type, $attrs)
  199. {
  200. $configuration = array_merge(self::$DEFAULT_VALIDATION_OPTIONS, array('message' => Errors::$DEFAULT_ERROR_MESSAGES[$type], 'on' => 'save'));
  201. foreach ($attrs as $attr)
  202. {
  203. $options = array_merge($configuration, $attr);
  204. $attribute = $options[0];
  205. $var = $this->model->$attribute;
  206. if (isset($options['in']))
  207. $enum = $options['in'];
  208. elseif (isset($options['within']))
  209. $enum = $options['within'];
  210. if (!is_array($enum))
  211. array($enum);
  212. $message = str_replace('%s', $var, $options['message']);
  213. if ($this->is_null_with_option($var, $options) || $this->is_blank_with_option($var, $options))
  214. continue;
  215. if (('inclusion' == $type && !in_array($var, $enum)) || ('exclusion' == $type && in_array($var, $enum)))
  216. $this->record->add($attribute, $message);
  217. }
  218. }
  219. /**
  220. * Validates that a value is numeric.
  221. *
  222. * <code>
  223. * class Person extends ActiveRecord\Model {
  224. * static $validates_numericality_of = array(
  225. * array('salary', 'greater_than' => 19.99, 'less_than' => 99.99)
  226. * );
  227. * }
  228. * </code>
  229. *
  230. * Available options:
  231. *
  232. * <ul>
  233. * <li><b>only_integer:</b> value must be an integer (e.g. not a float)</li>
  234. * <li><b>even:</b> must be even</li>
  235. * <li><b>odd:</b> must be odd"</li>
  236. * <li><b>greater_than:</b> must be greater than specified number</li>
  237. * <li><b>greater_than_or_equal_to:</b> must be greater than or equal to specified number</li>
  238. * <li><b>equal_to:</b> ...</li>
  239. * <li><b>less_than:</b> ...</li>
  240. * <li><b>less_than_or_equal_to:</b> ...</li>
  241. * </ul>
  242. *
  243. * @param array $attrs Validation definition
  244. */
  245. public function validates_numericality_of($attrs)
  246. {
  247. $configuration = array_merge(self::$DEFAULT_VALIDATION_OPTIONS, array('only_integer' => false));
  248. // Notice that for fixnum and float columns empty strings are converted to nil.
  249. // Validates whether the value of the specified attribute is numeric by trying to convert it to a float with Kernel.Float
  250. // (if only_integer is false) or applying it to the regular expression /\A[+\-]?\d+\Z/ (if only_integer is set to true).
  251. foreach ($attrs as $attr)
  252. {
  253. $options = array_merge($configuration, $attr);
  254. $attribute = $options[0];
  255. $var = $this->model->$attribute;
  256. $numericalityOptions = array_intersect_key(self::$ALL_NUMERICALITY_CHECKS, $options);
  257. if ($this->is_null_with_option($var, $options))
  258. continue;
  259. if (true === $options['only_integer'] && !is_integer($var))
  260. {
  261. if (!preg_match('/\A[+-]?\d+\Z/', (string)($var)))
  262. {
  263. if (isset($options['message']))
  264. $message = $options['message'];
  265. else
  266. $message = Errors::$DEFAULT_ERROR_MESSAGES['not_a_number'];
  267. $this->record->add($attribute, $message);
  268. continue;
  269. }
  270. }
  271. else
  272. {
  273. if (!is_numeric($var))
  274. {
  275. $this->record->add($attribute, Errors::$DEFAULT_ERROR_MESSAGES['not_a_number']);
  276. continue;
  277. }
  278. $var = (float)$var;
  279. }
  280. foreach ($numericalityOptions as $option => $check)
  281. {
  282. $option_value = $options[$option];
  283. if ('odd' != $option && 'even' != $option)
  284. {
  285. $option_value = (float)$options[$option];
  286. if (!is_numeric($option_value))
  287. throw new ValidationsArgumentError("$option must be a number");
  288. if (isset($options['message']))
  289. $message = $options['message'];
  290. else
  291. $message = Errors::$DEFAULT_ERROR_MESSAGES[$option];
  292. $message = str_replace('%d', $option_value, $message);
  293. if ('greater_than' == $option && !($var > $option_value))
  294. $this->record->add($attribute, $message);
  295. elseif ('greater_than_or_equal_to' == $option && !($var >= $option_value))
  296. $this->record->add($attribute, $message);
  297. elseif ('equal_to' == $option && !($var == $option_value))
  298. $this->record->add($attribute, $message);
  299. elseif ('less_than' == $option && !($var < $option_value))
  300. $this->record->add($attribute, $message);
  301. elseif ('less_than_or_equal_to' == $option && !($var <= $option_value))
  302. $this->record->add($attribute, $message);
  303. }
  304. else
  305. {
  306. if (isset($options['message']))
  307. $message = $options['message'];
  308. else
  309. $message = Errors::$DEFAULT_ERROR_MESSAGES[$option];
  310. if ( ('odd' == $option && !( Utils::is_odd($var))) || ('even' == $option && ( Utils::is_odd($var))))
  311. $this->record->add($attribute, $message);
  312. }
  313. }
  314. }
  315. }
  316. /**
  317. * Alias of {@link validates_length_of}
  318. *
  319. * @param array $attrs Validation definition
  320. */
  321. public function validates_size_of($attrs)
  322. {
  323. $this->validates_length_of($attrs);
  324. }
  325. /**
  326. * Validates that a value is matches a regex.
  327. *
  328. * <code>
  329. * class Person extends ActiveRecord\Model {
  330. * static $validates_format_of = array(
  331. * array('email', 'with' => '/^.*?@.*$/')
  332. * );
  333. * }
  334. * </code>
  335. *
  336. * Available options:
  337. *
  338. * <ul>
  339. * <li><b>with:</b> a regular expression</li>
  340. * <li><b>message:</b> custom error message</li>
  341. * </ul>
  342. *
  343. * @param array $attrs Validation definition
  344. */
  345. public function validates_format_of($attrs)
  346. {
  347. $configuration = array_merge(self::$DEFAULT_VALIDATION_OPTIONS, array('message' => Errors::$DEFAULT_ERROR_MESSAGES['invalid'], 'on' => 'save', 'with' => null));
  348. foreach ($attrs as $attr)
  349. {
  350. $options = array_merge($configuration, $attr);
  351. $attribute = $options[0];
  352. $var = $this->model->$attribute;
  353. if (is_null($options['with']) || !is_string($options['with']) || !is_string($options['with']))
  354. throw new ValidationsArgumentError('A regular expression must be supplied as the [with] option of the configuration array.');
  355. else
  356. $expression = $options['with'];
  357. if ($this->is_null_with_option($var, $options) || $this->is_blank_with_option($var, $options))
  358. continue;
  359. if (!@preg_match($expression, $var))
  360. $this->record->add($attribute, $options['message']);
  361. }
  362. }
  363. /**
  364. * Validates the length of a value.
  365. *
  366. * <code>
  367. * class Person extends ActiveRecord\Model {
  368. * static $validates_length_of = array(
  369. * array('name', 'within' => array(1,50))
  370. * );
  371. * }
  372. * </code>
  373. *
  374. * Available options:
  375. *
  376. * <ul>
  377. * <li><b>is:</b> attribute should be exactly n characters long</li>
  378. * <li><b>in/within:</b> attribute should be within an range array(min,max)</li>
  379. * <li><b>maximum/minimum:</b> attribute should not be above/below respectively</li>
  380. * </ul>
  381. *
  382. * @param array $attrs Validation definition
  383. */
  384. public function validates_length_of($attrs)
  385. {
  386. $configuration = array_merge(self::$DEFAULT_VALIDATION_OPTIONS, array(
  387. 'too_long' => Errors::$DEFAULT_ERROR_MESSAGES['too_long'],
  388. 'too_short' => Errors::$DEFAULT_ERROR_MESSAGES['too_short'],
  389. 'wrong_length' => Errors::$DEFAULT_ERROR_MESSAGES['wrong_length']
  390. ));
  391. foreach ($attrs as $attr)
  392. {
  393. $options = array_merge($configuration, $attr);
  394. $range_options = array_intersect(array_keys(self::$ALL_RANGE_OPTIONS), array_keys($attr));
  395. sort($range_options);
  396. switch (sizeof($range_options))
  397. {
  398. case 0:
  399. throw new ValidationsArgumentError('Range unspecified. Specify the [within], [maximum], or [is] option.');
  400. case 1:
  401. break;
  402. default:
  403. throw new ValidationsArgumentError('Too many range options specified. Choose only one.');
  404. }
  405. $attribute = $options[0];
  406. $var = $this->model->$attribute;
  407. $range_option = $range_options[0];
  408. if ($this->is_null_with_option($var, $options) || $this->is_blank_with_option($var, $options))
  409. continue;
  410. if ('within' == $range_option || 'in' == $range_option)
  411. {
  412. $range = $options[$range_options[0]];
  413. if (!(Utils::is_a('range', $range)))
  414. throw new ValidationsArgumentError("$range_option must be an array composing a range of numbers with key [0] being less than key [1]");
  415. if (is_float($range[0]) || is_float($range[1]))
  416. throw new ValidationsArgumentError("Range values cannot use floats for length.");
  417. if ((int)$range[0] <= 0 || (int)$range[1] <= 0)
  418. throw new ValidationsArgumentError("Range values cannot use signed integers.");
  419. $too_short = isset($options['message']) ? $options['message'] : $options['too_short'];
  420. $too_long = isset($options['message']) ? $options['message'] : $options['too_long'];
  421. $too_short = str_replace('%d', $range[0], $too_short);
  422. $too_long = str_replace('%d', $range[1], $too_long);
  423. if (strlen($this->model->$attribute) < (int)$range[0])
  424. $this->record->add($attribute, $too_short);
  425. elseif (strlen($this->model->$attribute) > (int)$range[1])
  426. $this->record->add($attribute, $too_long);
  427. }
  428. elseif ('is' == $range_option || 'minimum' == $range_option || 'maximum' == $range_option)
  429. {
  430. $option = $options[$range_option];
  431. if ((int)$option <= 0)
  432. throw new ValidationsArgumentError("$range_option value cannot use a signed integer.");
  433. if (is_float($option))
  434. throw new ValidationsArgumentError("$range_option value cannot use a float for length.");
  435. if (!is_null($this->model->$attribute))
  436. {
  437. $messageOptions = array('is' => 'wrong_length', 'minimum' => 'too_short', 'maximum' => 'too_long');
  438. if (isset($options[$messageOptions[$range_option]]))
  439. $message = $options[$messageOptions[$range_option]];
  440. else
  441. $message = $options['message'];
  442. $message = str_replace('%d', $option, $message);
  443. $attribute_value = $this->model->$attribute;
  444. $len = strlen($attribute_value);
  445. $value = (int)$attr[$range_option];
  446. if ('maximum' == $range_option && $len > $value)
  447. $this->record->add($attribute, $message);
  448. if ('minimum' == $range_option && $len < $value)
  449. $this->record->add($attribute, $message);
  450. if ('is' == $range_option && $len !== $value)
  451. $this->record->add($attribute, $message);
  452. }
  453. }
  454. }
  455. }
  456. /**
  457. * Validates the uniqueness of a value.
  458. *
  459. * <code>
  460. * class Person extends ActiveRecord\Model {
  461. * static $validates_uniqueness_of = array(
  462. * array('name'),
  463. * array(array('blah','bleh'), 'message' => 'blech')
  464. * );
  465. * }
  466. * </code>
  467. *
  468. * @param array $attrs Validation definition
  469. */
  470. public function validates_uniqueness_of($attrs)
  471. {
  472. $configuration = array_merge(self::$DEFAULT_VALIDATION_OPTIONS, array(
  473. 'message' => Errors::$DEFAULT_ERROR_MESSAGES['unique']
  474. ));
  475. foreach ($attrs as $attr)
  476. {
  477. $options = array_merge($configuration, $attr);
  478. $pk = $this->model->get_primary_key();
  479. $pk_value = $this->model->$pk[0];
  480. if (is_array($options[0]))
  481. {
  482. $add_record = join("_and_", $options[0]);
  483. $fields = $options[0];
  484. }
  485. else
  486. {
  487. $add_record = $options[0];
  488. $fields = array($options[0]);
  489. }
  490. $sql = "";
  491. $conditions = array("");
  492. if ($pk_value === null)
  493. $sql = "{$pk[0]} is not null";
  494. else
  495. {
  496. $sql = "{$pk[0]}!=?";
  497. array_push($conditions,$pk_value);
  498. }
  499. foreach ($fields as $field)
  500. {
  501. $field = $this->model->get_real_attribute_name($field);
  502. $sql .= " and {$field}=?";
  503. array_push($conditions,$this->model->$field);
  504. }
  505. $conditions[0] = $sql;
  506. if ($this->model->exists(array('conditions' => $conditions)))
  507. $this->record->add($add_record, $options['message']);
  508. }
  509. }
  510. private function is_null_with_option($var, &$options)
  511. {
  512. return (is_null($var) && (isset($options['allow_null']) && $options['allow_null']));
  513. }
  514. private function is_blank_with_option($var, &$options)
  515. {
  516. return (Utils::is_blank($var) && (isset($options['allow_blank']) && $options['allow_blank']));
  517. }
  518. }
  519. /**
  520. * Class that holds {@link Validations} errors.
  521. *
  522. * @package ActiveRecord
  523. */
  524. class Errors implements IteratorAggregate
  525. {
  526. private $model;
  527. private $errors;
  528. public static $DEFAULT_ERROR_MESSAGES = array(
  529. 'inclusion' => "is not included in the list",
  530. 'exclusion' => "is reserved",
  531. 'invalid' => "is invalid",
  532. 'confirmation' => "doesn't match confirmation",
  533. 'accepted' => "must be accepted",
  534. 'empty' => "can't be empty",
  535. 'blank' => "can't be blank",
  536. 'too_long' => "is too long (maximum is %d characters)",
  537. 'too_short' => "is too short (minimum is %d characters)",
  538. 'wrong_length' => "is the wrong length (should be %d characters)",
  539. 'taken' => "has already been taken",
  540. 'not_a_number' => "is not a number",
  541. 'greater_than' => "must be greater than %d",
  542. 'equal_to' => "must be equal to %d",
  543. 'less_than' => "must be less than %d",
  544. 'odd' => "must be odd",
  545. 'even' => "must be even",
  546. 'unique' => "must be unique",
  547. 'less_than_or_equal_to' => "must be less than or equal to %d",
  548. 'greater_than_or_equal_to' => "must be greater than or equal to %d"
  549. );
  550. /**
  551. * Constructs an {@link Errors} object.
  552. *
  553. * @param Model $model The model the error is for
  554. * @return Errors
  555. */
  556. public function __construct(Model $model)
  557. {
  558. $this->model = $model;
  559. }
  560. /**
  561. * Nulls $model so we don't get pesky circular references. $model is only needed during the
  562. * validation process and so can be safely cleared once that is done.
  563. */
  564. public function clear_model()
  565. {
  566. $this->model = null;
  567. }
  568. /**
  569. * Add an error message.
  570. *
  571. * @param string $attribute Name of an attribute on the model
  572. * @param string $msg The error message
  573. */
  574. public function add($attribute, $msg)
  575. {
  576. if (is_null($msg))
  577. $msg = self :: $DEFAULT_ERROR_MESSAGES['invalid'];
  578. if (!isset($this->errors[$attribute]))
  579. $this->errors[$attribute] = array($msg);
  580. else
  581. $this->errors[$attribute][] = $msg;
  582. }
  583. /**
  584. * Adds an error message only if the attribute value is {@link http://www.php.net/empty empty}.
  585. *
  586. * @param string $attribute Name of an attribute on the model
  587. * @param string $msg The error message
  588. */
  589. public function add_on_empty($attribute, $msg)
  590. {
  591. if (empty($msg))
  592. $msg = self::$DEFAULT_ERROR_MESSAGES['empty'];
  593. if (empty($this->model->$attribute))
  594. $this->add($attribute, $msg);
  595. }
  596. /**
  597. * Retrieve error message for an attribute.
  598. *
  599. * @param string $attribute Name of an attribute on the model
  600. * @return string
  601. */
  602. public function __get($attribute)
  603. {
  604. if (!isset($this->errors[$attribute]))
  605. return null;
  606. return $this->errors[$attribute];
  607. }
  608. /**
  609. * Adds the error message only if the attribute value was null or an empty string.
  610. *
  611. * @param string $attribute Name of an attribute on the model
  612. * @param string $msg The error message
  613. */
  614. public function add_on_blank($attribute, $msg)
  615. {
  616. if (!$msg)
  617. $msg = self::$DEFAULT_ERROR_MESSAGES['blank'];
  618. if (($value = $this->model->$attribute) === '' || $value === null)
  619. $this->add($attribute, $msg);
  620. }
  621. /**
  622. * Returns true if the specified attribute had any error messages.
  623. *
  624. * @param string $attribute Name of an attribute on the model
  625. * @return boolean
  626. */
  627. public function is_invalid($attribute)
  628. {
  629. return isset($this->errors[$attribute]);
  630. }
  631. /**
  632. * Returns the error message for the specified attribute or null if none.
  633. *
  634. * @param string $attribute Name of an attribute on the model
  635. * @return string
  636. */
  637. public function on($attribute)
  638. {
  639. if (!isset($this->errors[$attribute]))
  640. return null;
  641. $errors = $this->errors[$attribute];
  642. if (null === $errors)
  643. return null;
  644. else
  645. return count($errors) == 1 ? $errors[0] : $errors;
  646. }
  647. /**
  648. * Returns all the error messages as an array.
  649. *
  650. * <code>
  651. * $model->errors->full_messages();
  652. *
  653. * # array(
  654. * # "Name can't be blank",
  655. * # "State is the wrong length (should be 2 chars)"
  656. * # )
  657. * </code>
  658. *
  659. * @param array $options Options for messages
  660. * @return array
  661. */
  662. public function full_messages()
  663. {
  664. $full_messages = array();
  665. if ($this->errors)
  666. {
  667. foreach ($this->errors as $attribute => $messages)
  668. {
  669. foreach ($messages as $msg)
  670. {
  671. if (is_null($msg))
  672. continue;
  673. $full_messages[] = Utils::human_attribute($attribute) . ' ' . $msg;
  674. }
  675. }
  676. }
  677. return $full_messages;
  678. }
  679. /**
  680. * Returns true if there are no error messages.
  681. * @return boolean
  682. */
  683. public function is_empty()
  684. {
  685. return empty($this->errors);
  686. }
  687. /**
  688. * Clears out all error messages.
  689. */
  690. public function clear()
  691. {
  692. $this->errors = array();
  693. }
  694. /**
  695. * Returns the number of error messages there are.
  696. * @return int
  697. */
  698. public function size()
  699. {
  700. if ($this->is_empty())
  701. return 0;
  702. $count = 0;
  703. foreach ($this->errors as $attribute => $error)
  704. $count += count($error);
  705. return $count;
  706. }
  707. /**
  708. * Returns an iterator to the error messages.
  709. *
  710. * This will allow you to iterate over the {@link Errors} object using foreach.
  711. *
  712. * <code>
  713. * foreach ($model->errors as $msg)
  714. * echo "$msg\n";
  715. * </code>
  716. *
  717. * @return ArrayIterator
  718. */
  719. public function getIterator()
  720. {
  721. return new ArrayIterator($this->full_messages());
  722. }
  723. };
  724. ?>