validator.php 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554
  1. <?php namespace Laravel\Validation;
  2. use Closure;
  3. use Laravel\IoC;
  4. use Laravel\Str;
  5. use Laravel\Lang;
  6. use Laravel\Database\Manager as Database;
  7. class Validator {
  8. /**
  9. * The validation rules.
  10. *
  11. * @var array
  12. */
  13. protected $rules = array();
  14. /**
  15. * The validation messages.
  16. *
  17. * @var array
  18. */
  19. protected $messages = array();
  20. /**
  21. * The language that should be used when retrieving error messages.
  22. *
  23. * @var string
  24. */
  25. protected $language;
  26. /**
  27. * The size related validation rules.
  28. *
  29. * @var array
  30. */
  31. protected $size_rules = array('size', 'between', 'min', 'max');
  32. /**
  33. * The numeric related validation rules.
  34. *
  35. * @var array
  36. */
  37. protected $numeric_rules = array('numeric', 'integer');
  38. /**
  39. * The database connection that should be used by the validator.
  40. *
  41. * @var Database\Connection
  42. */
  43. public $connection;
  44. /**
  45. * The array being validated.
  46. *
  47. * @var array
  48. */
  49. public $attributes;
  50. /**
  51. * The post-validation error messages.
  52. *
  53. * @var Messages
  54. */
  55. public $errors;
  56. /**
  57. * Create a new validator instance.
  58. *
  59. * @param array $attributes
  60. * @param array $rules
  61. * @param array $messages
  62. * @return void
  63. */
  64. public function __construct($attributes, $rules, $messages = array())
  65. {
  66. foreach ($rules as $key => &$rule)
  67. {
  68. $rule = (is_string($rule)) ? explode('|', $rule) : $rule;
  69. }
  70. $this->rules = $rules;
  71. $this->attributes = $attributes;
  72. $this->messages = array_merge($this->messages, $messages);
  73. }
  74. /**
  75. * Create a new validator instance.
  76. *
  77. * @param array $attributes
  78. * @param array $rules
  79. * @param array $messages
  80. * @return Validator
  81. */
  82. public static function make($attributes, $rules, $messages = array())
  83. {
  84. return new static($attributes, $rules, $messages);
  85. }
  86. /**
  87. * Validate the target array using the specified validation rules.
  88. *
  89. * @return bool
  90. */
  91. public function invalid()
  92. {
  93. return ! $this->valid();
  94. }
  95. /**
  96. * Validate the target array using the specified validation rules.
  97. *
  98. * @return bool
  99. */
  100. public function valid()
  101. {
  102. $this->errors = new Messages;
  103. foreach ($this->rules as $attribute => $rules)
  104. {
  105. foreach ($rules as $rule)
  106. {
  107. $this->check($attribute, $rule);
  108. }
  109. }
  110. return count($this->errors->messages) == 0;
  111. }
  112. /**
  113. * Evaluate an attribute against a validation rule.
  114. *
  115. * @param string $attribute
  116. * @param string $rule
  117. * @return void
  118. */
  119. protected function check($attribute, $rule)
  120. {
  121. list($rule, $parameters) = $this->parse($rule);
  122. if ( ! method_exists($this, $validator = 'validate_'.$rule))
  123. {
  124. throw new \Exception("Validation rule [$rule] doesn't exist.");
  125. }
  126. // No validation will be run for attributes that do not exist unless the rule being validated
  127. // is "required" or "accepted". No other rules have implicit "required" checks.
  128. if ( ! $this->validate_required($attribute) and ! in_array($rule, array('required', 'accepted'))) return;
  129. if ( ! $this->$validator($attribute, $parameters, $this))
  130. {
  131. $message = $this->format_message($this->get_message($attribute, $rule), $attribute, $rule, $parameters);
  132. $this->errors->add($attribute, $message, $attribute, $rule, $parameters);
  133. }
  134. }
  135. /**
  136. * Validate that a required attribute exists in the attributes array.
  137. *
  138. * @param string $attribute
  139. * @return bool
  140. */
  141. protected function validate_required($attribute)
  142. {
  143. if ( ! array_key_exists($attribute, $this->attributes)) return false;
  144. if (is_string($this->attributes[$attribute]) and trim($this->attributes[$attribute]) === '') return false;
  145. return true;
  146. }
  147. /**
  148. * Validate that an attribute has a matching confirmation attribute.
  149. *
  150. * @param string $attribute
  151. * @return bool
  152. */
  153. protected function validate_confirmed($attribute)
  154. {
  155. $value = $this->attributes[$attribute];
  156. $confirmation = $this->attributes[$attribute.'_confirmation'];
  157. return array_key_exists($attribute.'_confirmation', $this->attributes) and $value == $confirmation;
  158. }
  159. /**
  160. * Validate that an attribute was "accepted".
  161. *
  162. * This validation rule implies the attribute is "required".
  163. *
  164. * @param string $attribute
  165. * @return bool
  166. */
  167. protected function validate_accepted($attribute)
  168. {
  169. $value = $this->attributes[$attribute];
  170. return $this->validate_required($attribute) and ($value == 'yes' or $value == '1');
  171. }
  172. /**
  173. * Validate that an attribute is numeric.
  174. *
  175. * @param string $attribute
  176. * @return bool
  177. */
  178. protected function validate_numeric($attribute)
  179. {
  180. return is_numeric($this->attributes[$attribute]);
  181. }
  182. /**
  183. * Validate that an attribute is an integer.
  184. *
  185. * @param string $attribute
  186. * @return bool
  187. */
  188. protected function validate_integer($attribute)
  189. {
  190. return filter_var($this->attributes[$attribute], FILTER_VALIDATE_INT) !== false;
  191. }
  192. /**
  193. * Validate the size of an attribute.
  194. *
  195. * @param string $attribute
  196. * @param array $parameters
  197. * @return bool
  198. */
  199. protected function validate_size($attribute, $parameters)
  200. {
  201. return $this->get_size($attribute) == $parameters[0];
  202. }
  203. /**
  204. * Validate the size of an attribute is between a set of values.
  205. *
  206. * @param string $attribute
  207. * @param array $parameters
  208. * @return bool
  209. */
  210. protected function validate_between($attribute, $parameters)
  211. {
  212. return $this->get_size($attribute) >= $parameters[0] and $this->get_size($attribute) <= $parameters[1];
  213. }
  214. /**
  215. * Validate the size of an attribute is greater than a minimum value.
  216. *
  217. * @param string $attribute
  218. * @param array $parameters
  219. * @return bool
  220. */
  221. protected function validate_min($attribute, $parameters)
  222. {
  223. return $this->get_size($attribute) >= $parameters[0];
  224. }
  225. /**
  226. * Validate the size of an attribute is less than a maximum value.
  227. *
  228. * @param string $attribute
  229. * @param array $parameters
  230. * @return bool
  231. */
  232. protected function validate_max($attribute, $parameters)
  233. {
  234. return $this->get_size($attribute) <= $parameters[0];
  235. }
  236. /**
  237. * Get the size of an attribute.
  238. *
  239. * @param string $attribute
  240. * @return mixed
  241. */
  242. protected function get_size($attribute)
  243. {
  244. if (is_numeric($this->attributes[$attribute]) and $this->has_rule($attribute, $this->numeric_rules))
  245. {
  246. return $this->attributes[$attribute];
  247. }
  248. $value = $this->attributes[$attribute];
  249. return (array_key_exists($attribute, $_FILES)) ? $value['size'] / 1024 : Str::length(trim($value));
  250. }
  251. /**
  252. * Validate an attribute is contained within a list of values.
  253. *
  254. * @param string $attribute
  255. * @param array $parameters
  256. * @return bool
  257. */
  258. protected function validate_in($attribute, $parameters)
  259. {
  260. return in_array($this->attributes[$attribute], $parameters);
  261. }
  262. /**
  263. * Validate an attribute is not contained within a list of values.
  264. *
  265. * @param string $attribute
  266. * @param array $parameters
  267. * @return bool
  268. */
  269. protected function validate_not_in($attribute, $parameters)
  270. {
  271. return ! in_array($this->attributes[$attribute], $parameters);
  272. }
  273. /**
  274. * Validate the uniqueness of an attribute value on a given database table.
  275. *
  276. * If a database column is not specified, the attribute name will be used.
  277. *
  278. * @param string $attribute
  279. * @param array $parameters
  280. * @return bool
  281. */
  282. protected function validate_unique($attribute, $parameters)
  283. {
  284. if ( ! isset($parameters[1])) $parameters[1] = $attribute;
  285. if (is_null($this->connection)) $this->connection = Database::connection();
  286. return $this->connection->table($parameters[0])->where($parameters[1], '=', $this->attributes[$attribute])->count() == 0;
  287. }
  288. /**
  289. * Validate than an attribute is a valid e-mail address.
  290. *
  291. * @param string $attribute
  292. * @return bool
  293. */
  294. protected function validate_email($attribute)
  295. {
  296. return filter_var($this->attributes[$attribute], FILTER_VALIDATE_EMAIL) !== false;
  297. }
  298. /**
  299. * Validate than an attribute is a valid URL.
  300. *
  301. * @param string $attribute
  302. * @return bool
  303. */
  304. protected function validate_url($attribute)
  305. {
  306. return filter_var($this->attributes[$attribute], FILTER_VALIDATE_URL) !== false;
  307. }
  308. /**
  309. * Validate that an attribute is an active URL.
  310. *
  311. * @param string $attribute
  312. * @return bool
  313. */
  314. protected function validate_active_url($attribute)
  315. {
  316. $url = str_replace(array('http://', 'https://', 'ftp://'), '', Str::lower($this->attributes[$attribute]));
  317. return checkdnsrr($url);
  318. }
  319. /**
  320. * Validate the MIME type of a file is an image MIME type.
  321. *
  322. * @param string $attribute
  323. * @return bool
  324. */
  325. protected function validate_image($attribute)
  326. {
  327. return $this->validate_mimes($attribute, array('jpg', 'png', 'gif', 'bmp'));
  328. }
  329. /**
  330. * Validate than an attribute contains only alphabetic characters.
  331. *
  332. * @param string $attribute
  333. * @return bool
  334. */
  335. protected function validate_alpha($attribute)
  336. {
  337. return preg_match('/^([a-z])+$/i', $this->attributes[$attribute]);
  338. }
  339. /**
  340. * Validate than an attribute contains only alpha-numeric characters.
  341. *
  342. * @param string $attribute
  343. * @return bool
  344. */
  345. protected function validate_alpha_num($attribute)
  346. {
  347. return preg_match('/^([a-z0-9])+$/i', $this->attributes[$attribute]);
  348. }
  349. /**
  350. * Validate than an attribute contains only alpha-numeric characters, dashes, and underscores.
  351. *
  352. * @param string $attribute
  353. * @return bool
  354. */
  355. protected function validate_alpha_dash($attribute)
  356. {
  357. return preg_match('/^([-a-z0-9_-])+$/i', $this->attributes[$attribute]);
  358. }
  359. /**
  360. * Validate the MIME type of a file upload attribute is in a set of MIME types.
  361. *
  362. * @param string $attribute
  363. * @param array $parameters
  364. * @return bool
  365. */
  366. protected function validate_mimes($attribute, $parameters)
  367. {
  368. foreach ($parameters as $extension)
  369. {
  370. if (File::is($extension, $this->attributes[$attribute]['tmp_name'])) return true;
  371. }
  372. return false;
  373. }
  374. /**
  375. * Get the proper error message for an attribute and rule.
  376. *
  377. * Developer specified attribute specific rules take first priority.
  378. * Developer specified error rules take second priority.
  379. *
  380. * If the message has not been specified by the developer, the default will be used
  381. * from the validation language file.
  382. *
  383. * @param string $attribute
  384. * @param string $rule
  385. * @return string
  386. */
  387. protected function get_message($attribute, $rule)
  388. {
  389. if (array_key_exists($attribute.'_'.$rule, $this->messages))
  390. {
  391. return $this->messages[$attribute.'_'.$rule];
  392. }
  393. elseif (array_key_exists($rule, $this->messages))
  394. {
  395. return $this->messages[$rule];
  396. }
  397. else
  398. {
  399. $message = Lang::line('validation.'.$rule)->get($this->language);
  400. // For "size" rules that are validating strings or files, we need to adjust
  401. // the default error message for the appropriate units.
  402. if (in_array($rule, $this->size_rules) and ! $this->has_rule($attribute, $this->numeric_rules))
  403. {
  404. return (array_key_exists($attribute, IoC::container()->resolve('laravel.input')->files()))
  405. ? rtrim($message, '.').' '.Lang::line('validation.kilobytes')->get($this->language).'.'
  406. : rtrim($message, '.').' '.Lang::line('validation.characters')->get($this->language).'.';
  407. }
  408. return $message;
  409. }
  410. }
  411. /**
  412. * Replace all error message place-holders with actual values.
  413. *
  414. * @param string $message
  415. * @param string $attribute
  416. * @param string $rule
  417. * @param array $parameters
  418. * @return string
  419. */
  420. protected function format_message($message, $attribute, $rule, $parameters)
  421. {
  422. $display = Lang::line('attributes.'.$attribute)->get($this->language, str_replace('_', ' ', $attribute));
  423. $message = str_replace(':attribute', $display, $message);
  424. if (in_array($rule, $this->size_rules))
  425. {
  426. $max = ($rule == 'between') ? $parameters[1] : $parameters[0];
  427. $message = str_replace(array(':size', ':min', ':max'), array($parameters[0], $parameters[0], $max), $message);
  428. }
  429. elseif (in_array($rule, array('in', 'not_in', 'mimes')))
  430. {
  431. $message = str_replace(':values', implode(', ', $parameters), $message);
  432. }
  433. return $message;
  434. }
  435. /**
  436. * Determine if an attribute has a rule assigned to it.
  437. *
  438. * @param string $attribute
  439. * @param array $rules
  440. * @return bool
  441. */
  442. protected function has_rule($attribute, $rules)
  443. {
  444. foreach ($this->rules[$attribute] as $rule)
  445. {
  446. list($rule, $parameters) = $this->parse($rule);
  447. if (in_array($rule, $rules)) return true;
  448. }
  449. return false;
  450. }
  451. /**
  452. * Extract the rule name and parameters from a rule.
  453. *
  454. * @param string $rule
  455. * @return array
  456. */
  457. protected function parse($rule)
  458. {
  459. $parameters = (($colon = strpos($rule, ':')) !== false) ? explode(',', substr($rule, $colon + 1)) : array();
  460. return array(is_numeric($colon) ? substr($rule, 0, $colon) : $rule, $parameters);
  461. }
  462. /**
  463. * Set the language that should be used when retrieving error messages.
  464. *
  465. * @param string $language
  466. * @return Validator
  467. */
  468. public function lang($language)
  469. {
  470. $this->language = $language;
  471. return $this;
  472. }
  473. /**
  474. * Set the database connection that should be used by the validator.
  475. *
  476. * @param Database\Connection $connection
  477. * @return Validator
  478. */
  479. public function connection(\Laravel\Database\Connection $connection)
  480. {
  481. $this->connection = $connection;
  482. return $this;
  483. }
  484. }