validator.php 13 KB

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