PageRenderTime 45ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 0ms

/Nette/Utils/Validators.php

https://github.com/stekycz/nette
PHP | 297 lines | 162 code | 52 blank | 83 comment | 31 complexity | 1f97758658ac9a296797743baedafde5 MD5 | raw file
Possible License(s): BSD-3-Clause
  1. <?php
  2. /**
  3. * This file is part of the Nette Framework (http://nette.org)
  4. *
  5. * Copyright (c) 2004 David Grudl (http://davidgrudl.com)
  6. *
  7. * For the full copyright and license information, please view
  8. * the file license.txt that was distributed with this source code.
  9. */
  10. namespace Nette\Utils;
  11. use Nette;
  12. /**
  13. * Validation utilites.
  14. *
  15. * @author David Grudl
  16. */
  17. class Validators extends Nette\Object
  18. {
  19. protected static $validators = array(
  20. 'bool' => 'is_bool',
  21. 'boolean' => 'is_bool',
  22. 'int' => 'is_int',
  23. 'integer' => 'is_int',
  24. 'float' => 'is_float',
  25. 'number' => NULL, // is_int || is_float,
  26. 'numeric' => array(__CLASS__, 'isNumeric'),
  27. 'numericint' => array(__CLASS__, 'isNumericInt'),
  28. 'string' => 'is_string',
  29. 'unicode' => array(__CLASS__, 'isUnicode'),
  30. 'array' => 'is_array',
  31. 'list' => array('Nette\Utils\Arrays', 'isList'),
  32. 'object' => 'is_object',
  33. 'resource' => 'is_resource',
  34. 'scalar' => 'is_scalar',
  35. 'callable' => array(__CLASS__, 'isCallable'),
  36. 'null' => 'is_null',
  37. 'email' => array(__CLASS__, 'isEmail'),
  38. 'url' => array(__CLASS__, 'isUrl'),
  39. 'none' => array(__CLASS__, 'isNone'),
  40. 'type' => array(__CLASS__, 'isType'),
  41. 'pattern' => NULL,
  42. 'alnum' => 'ctype_alnum',
  43. 'alpha' => 'ctype_alpha',
  44. 'digit' => 'ctype_digit',
  45. 'lower' => 'ctype_lower',
  46. 'upper' => 'ctype_upper',
  47. 'space' => 'ctype_space',
  48. 'xdigit' => 'ctype_xdigit',
  49. );
  50. protected static $counters = array(
  51. 'string' => 'strlen',
  52. 'unicode' => array('Nette\Utils\Strings', 'length'),
  53. 'array' => 'count',
  54. 'list' => 'count',
  55. 'alnum' => 'strlen',
  56. 'alpha' => 'strlen',
  57. 'digit' => 'strlen',
  58. 'lower' => 'strlen',
  59. 'space' => 'strlen',
  60. 'upper' => 'strlen',
  61. 'xdigit' => 'strlen',
  62. );
  63. /**
  64. * Throws exception if a variable is of unexpected type.
  65. * @param mixed
  66. * @param string expected types separated by pipe
  67. * @param string label
  68. * @return void
  69. */
  70. public static function assert($value, $expected, $label = 'variable')
  71. {
  72. if (!static::is($value, $expected)) {
  73. $expected = str_replace(array('|', ':'), array(' or ', ' in range '), $expected);
  74. if (is_array($value)) {
  75. $type = 'array(' . count($value) . ')';
  76. } elseif (is_object($value)) {
  77. $type = 'object ' . get_class($value);
  78. } elseif (is_string($value) && strlen($value) < 40) {
  79. $type = "string '$value'";
  80. } else {
  81. $type = gettype($value);
  82. }
  83. throw new AssertionException("The $label expects to be $expected, $type given.");
  84. }
  85. }
  86. /**
  87. * Throws exception if an array field is missing or of unexpected type.
  88. * @param array
  89. * @param string item
  90. * @param string expected types separated by pipe
  91. * @return void
  92. */
  93. public static function assertField($arr, $field, $expected = NULL, $label = "item '%' in array")
  94. {
  95. self::assert($arr, 'array', 'first argument');
  96. if (!array_key_exists($field, $arr)) {
  97. throw new AssertionException('Missing ' . str_replace('%', $field, $label) . '.');
  98. } elseif ($expected) {
  99. static::assert($arr[$field], $expected, str_replace('%', $field, $label));
  100. }
  101. }
  102. /**
  103. * Finds whether a variable is of expected type.
  104. * @param mixed
  105. * @param string expected types separated by pipe with optional ranges
  106. * @return bool
  107. */
  108. public static function is($value, $expected)
  109. {
  110. foreach (explode('|', $expected) as $item) {
  111. list($type) = $item = explode(':', $item, 2);
  112. if (isset(static::$validators[$type])) {
  113. if (!call_user_func(static::$validators[$type], $value)) {
  114. continue;
  115. }
  116. } elseif ($type === 'number') {
  117. if (!is_int($value) && !is_float($value)) {
  118. continue;
  119. }
  120. } elseif ($type === 'pattern') {
  121. if (preg_match('|^' . (isset($item[1]) ? $item[1] : '') . '\z|', $value)) {
  122. return TRUE;
  123. }
  124. continue;
  125. } elseif (!$value instanceof $type) {
  126. continue;
  127. }
  128. if (isset($item[1])) {
  129. if (isset(static::$counters[$type])) {
  130. $value = call_user_func(static::$counters[$type], $value);
  131. }
  132. $range = explode('..', $item[1]);
  133. if (!isset($range[1])) {
  134. $range[1] = $range[0];
  135. }
  136. if (($range[0] !== '' && $value < $range[0]) || ($range[1] !== '' && $value > $range[1])) {
  137. continue;
  138. }
  139. }
  140. return TRUE;
  141. }
  142. return FALSE;
  143. }
  144. /**
  145. * Finds whether a value is an integer.
  146. * @return bool
  147. */
  148. public static function isNumericInt($value)
  149. {
  150. return is_int($value) || is_string($value) && preg_match('#^-?[0-9]+\z#', $value);
  151. }
  152. /**
  153. * Finds whether a string is a floating point number in decimal base.
  154. * @return bool
  155. */
  156. public static function isNumeric($value)
  157. {
  158. return is_float($value) || is_int($value) || is_string($value) && preg_match('#^-?[0-9]*[.]?[0-9]+\z#', $value);
  159. }
  160. /**
  161. * Finds whether a value is a syntactically correct callback.
  162. * @return bool
  163. */
  164. public static function isCallable($value)
  165. {
  166. return $value && is_callable($value, TRUE);
  167. }
  168. /**
  169. * Finds whether a value is an UTF-8 encoded string.
  170. * @param string
  171. * @return bool
  172. */
  173. public static function isUnicode($value)
  174. {
  175. return is_string($value) && preg_match('##u', $value);
  176. }
  177. /**
  178. * Finds whether a value is "falsy".
  179. * @return bool
  180. */
  181. public static function isNone($value)
  182. {
  183. return $value == NULL; // intentionally ==
  184. }
  185. /**
  186. * Finds whether a variable is a zero-based integer indexed array.
  187. * @param array
  188. * @return bool
  189. */
  190. public static function isList($value)
  191. {
  192. return Arrays::isList($value);
  193. }
  194. /**
  195. * Is a value in specified range?
  196. * @param mixed
  197. * @param array min and max value pair
  198. * @return bool
  199. */
  200. public static function isInRange($value, $range)
  201. {
  202. return (!isset($range[0]) || $value >= $range[0]) && (!isset($range[1]) || $value <= $range[1]);
  203. }
  204. /**
  205. * Finds whether a string is a valid email address.
  206. * @param string
  207. * @return bool
  208. */
  209. public static function isEmail($value)
  210. {
  211. $atom = "[-a-z0-9!#$%&'*+/=?^_`{|}~]"; // RFC 5322 unquoted characters in local-part
  212. $localPart = "(?:\"(?:[ !\\x23-\\x5B\\x5D-\\x7E]*|\\\\[ -~])+\"|$atom+(?:\\.$atom+)*)"; // quoted or unquoted
  213. $alpha = "a-z\x80-\xFF"; // superset of IDN
  214. $domain = "[0-9$alpha](?:[-0-9$alpha]{0,61}[0-9$alpha])?"; // RFC 1034 one domain component
  215. $topDomain = "[$alpha][-0-9$alpha]{0,17}[$alpha]";
  216. return (bool) preg_match("(^$localPart@(?:$domain\\.)+$topDomain\\z)i", $value);
  217. }
  218. /**
  219. * Finds whether a string is a valid URL.
  220. * @param string
  221. * @return bool
  222. */
  223. public static function isUrl($value)
  224. {
  225. $alpha = "a-z\x80-\xFF";
  226. $domain = "[0-9$alpha](?:[-0-9$alpha]{0,61}[0-9$alpha])?";
  227. $topDomain = "[$alpha][-0-9$alpha]{0,17}[$alpha]";
  228. return (bool) preg_match("(^https?://(?:(?:$domain\\.)*$topDomain|\\d{1,3}\.\\d{1,3}\.\\d{1,3}\.\\d{1,3})(:\\d{1,5})?(/\\S*)?\\z)i", $value);
  229. }
  230. /**
  231. * Checks whether the input is a class, interface or trait
  232. * @param string
  233. * @return bool
  234. */
  235. public static function isType($type)
  236. {
  237. return class_exists($type) || interface_exists($type) || (PHP_VERSION_ID >= 50400 && trait_exists($type));
  238. }
  239. }
  240. /**
  241. * The exception that indicates assertion error.
  242. */
  243. class AssertionException extends \Exception
  244. {
  245. }