Assert.php 37KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088
  1. <?php
  2. /*
  3. * This file is part of the webmozart/assert package.
  4. *
  5. * (c) Bernhard Schussek <bschussek@gmail.com>
  6. *
  7. * For the full copyright and license information, please view the LICENSE
  8. * file that was distributed with this source code.
  9. */
  10. namespace Webmozart\Assert;
  11. use ArrayAccess;
  12. use BadMethodCallException;
  13. use Closure;
  14. use Countable;
  15. use Exception;
  16. use InvalidArgumentException;
  17. use Throwable;
  18. use Traversable;
  19. /**
  20. * Efficient assertions to validate the input/output of your methods.
  21. *
  22. * @method static void nullOrString($value, $message = '')
  23. * @method static void nullOrStringNotEmpty($value, $message = '')
  24. * @method static void nullOrInteger($value, $message = '')
  25. * @method static void nullOrIntegerish($value, $message = '')
  26. * @method static void nullOrFloat($value, $message = '')
  27. * @method static void nullOrNumeric($value, $message = '')
  28. * @method static void nullOrBoolean($value, $message = '')
  29. * @method static void nullOrScalar($value, $message = '')
  30. * @method static void nullOrObject($value, $message = '')
  31. * @method static void nullOrResource($value, $type = null, $message = '')
  32. * @method static void nullOrIsCallable($value, $message = '')
  33. * @method static void nullOrIsArray($value, $message = '')
  34. * @method static void nullOrIsTraversable($value, $message = '')
  35. * @method static void nullOrIsArrayAccessible($value, $message = '')
  36. * @method static void nullOrIsCountable($value, $message = '')
  37. * @method static void nullOrIsInstanceOf($value, $class, $message = '')
  38. * @method static void nullOrNotInstanceOf($value, $class, $message = '')
  39. * @method static void nullOrIsInstanceOfAny($value, $classes, $message = '')
  40. * @method static void nullOrIsEmpty($value, $message = '')
  41. * @method static void nullOrNotEmpty($value, $message = '')
  42. * @method static void nullOrTrue($value, $message = '')
  43. * @method static void nullOrFalse($value, $message = '')
  44. * @method static void nullOrEq($value, $value2, $message = '')
  45. * @method static void nullOrNotEq($value,$value2, $message = '')
  46. * @method static void nullOrSame($value, $value2, $message = '')
  47. * @method static void nullOrNotSame($value, $value2, $message = '')
  48. * @method static void nullOrGreaterThan($value, $value2, $message = '')
  49. * @method static void nullOrGreaterThanEq($value, $value2, $message = '')
  50. * @method static void nullOrLessThan($value, $value2, $message = '')
  51. * @method static void nullOrLessThanEq($value, $value2, $message = '')
  52. * @method static void nullOrRange($value, $min, $max, $message = '')
  53. * @method static void nullOrOneOf($value, $values, $message = '')
  54. * @method static void nullOrContains($value, $subString, $message = '')
  55. * @method static void nullOrNotContains($value, $subString, $message = '')
  56. * @method static void nullOrNotWhitespaceOnly($value, $message = '')
  57. * @method static void nullOrStartsWith($value, $prefix, $message = '')
  58. * @method static void nullOrStartsWithLetter($value, $message = '')
  59. * @method static void nullOrEndsWith($value, $suffix, $message = '')
  60. * @method static void nullOrRegex($value, $pattern, $message = '')
  61. * @method static void nullOrAlpha($value, $message = '')
  62. * @method static void nullOrDigits($value, $message = '')
  63. * @method static void nullOrAlnum($value, $message = '')
  64. * @method static void nullOrLower($value, $message = '')
  65. * @method static void nullOrUpper($value, $message = '')
  66. * @method static void nullOrLength($value, $length, $message = '')
  67. * @method static void nullOrMinLength($value, $min, $message = '')
  68. * @method static void nullOrMaxLength($value, $max, $message = '')
  69. * @method static void nullOrLengthBetween($value, $min, $max, $message = '')
  70. * @method static void nullOrFileExists($value, $message = '')
  71. * @method static void nullOrFile($value, $message = '')
  72. * @method static void nullOrDirectory($value, $message = '')
  73. * @method static void nullOrReadable($value, $message = '')
  74. * @method static void nullOrWritable($value, $message = '')
  75. * @method static void nullOrClassExists($value, $message = '')
  76. * @method static void nullOrSubclassOf($value, $class, $message = '')
  77. * @method static void nullOrImplementsInterface($value, $interface, $message = '')
  78. * @method static void nullOrPropertyExists($value, $property, $message = '')
  79. * @method static void nullOrPropertyNotExists($value, $property, $message = '')
  80. * @method static void nullOrMethodExists($value, $method, $message = '')
  81. * @method static void nullOrMethodNotExists($value, $method, $message = '')
  82. * @method static void nullOrKeyExists($value, $key, $message = '')
  83. * @method static void nullOrKeyNotExists($value, $key, $message = '')
  84. * @method static void nullOrCount($value, $key, $message = '')
  85. * @method static void nullOrMinCount($value, $min, $message = '')
  86. * @method static void nullOrMaxCount($value, $max, $message = '')
  87. * @method static void nullCountBetween($value, $min, $max, $message = '')
  88. * @method static void nullOrUuid($values, $message = '')
  89. * @method static void allString($values, $message = '')
  90. * @method static void allStringNotEmpty($values, $message = '')
  91. * @method static void allInteger($values, $message = '')
  92. * @method static void allIntegerish($values, $message = '')
  93. * @method static void allFloat($values, $message = '')
  94. * @method static void allNumeric($values, $message = '')
  95. * @method static void allBoolean($values, $message = '')
  96. * @method static void allScalar($values, $message = '')
  97. * @method static void allObject($values, $message = '')
  98. * @method static void allResource($values, $type = null, $message = '')
  99. * @method static void allIsCallable($values, $message = '')
  100. * @method static void allIsArray($values, $message = '')
  101. * @method static void allIsTraversable($values, $message = '')
  102. * @method static void allIsArrayAccessible($values, $message = '')
  103. * @method static void allIsCountable($values, $message = '')
  104. * @method static void allIsInstanceOf($values, $class, $message = '')
  105. * @method static void allNotInstanceOf($values, $class, $message = '')
  106. * @method static void allIsInstanceOfAny($values, $classes, $message = '')
  107. * @method static void allNull($values, $message = '')
  108. * @method static void allNotNull($values, $message = '')
  109. * @method static void allIsEmpty($values, $message = '')
  110. * @method static void allNotEmpty($values, $message = '')
  111. * @method static void allTrue($values, $message = '')
  112. * @method static void allFalse($values, $message = '')
  113. * @method static void allEq($values, $value2, $message = '')
  114. * @method static void allNotEq($values,$value2, $message = '')
  115. * @method static void allSame($values, $value2, $message = '')
  116. * @method static void allNotSame($values, $value2, $message = '')
  117. * @method static void allGreaterThan($values, $value2, $message = '')
  118. * @method static void allGreaterThanEq($values, $value2, $message = '')
  119. * @method static void allLessThan($values, $value2, $message = '')
  120. * @method static void allLessThanEq($values, $value2, $message = '')
  121. * @method static void allRange($values, $min, $max, $message = '')
  122. * @method static void allOneOf($values, $values, $message = '')
  123. * @method static void allContains($values, $subString, $message = '')
  124. * @method static void allNotContains($values, $subString, $message = '')
  125. * @method static void allNotWhitespaceOnly($values, $message = '')
  126. * @method static void allStartsWith($values, $prefix, $message = '')
  127. * @method static void allStartsWithLetter($values, $message = '')
  128. * @method static void allEndsWith($values, $suffix, $message = '')
  129. * @method static void allRegex($values, $pattern, $message = '')
  130. * @method static void allAlpha($values, $message = '')
  131. * @method static void allDigits($values, $message = '')
  132. * @method static void allAlnum($values, $message = '')
  133. * @method static void allLower($values, $message = '')
  134. * @method static void allUpper($values, $message = '')
  135. * @method static void allLength($values, $length, $message = '')
  136. * @method static void allMinLength($values, $min, $message = '')
  137. * @method static void allMaxLength($values, $max, $message = '')
  138. * @method static void allLengthBetween($values, $min, $max, $message = '')
  139. * @method static void allFileExists($values, $message = '')
  140. * @method static void allFile($values, $message = '')
  141. * @method static void allDirectory($values, $message = '')
  142. * @method static void allReadable($values, $message = '')
  143. * @method static void allWritable($values, $message = '')
  144. * @method static void allClassExists($values, $message = '')
  145. * @method static void allSubclassOf($values, $class, $message = '')
  146. * @method static void allImplementsInterface($values, $interface, $message = '')
  147. * @method static void allPropertyExists($values, $property, $message = '')
  148. * @method static void allPropertyNotExists($values, $property, $message = '')
  149. * @method static void allMethodExists($values, $method, $message = '')
  150. * @method static void allMethodNotExists($values, $method, $message = '')
  151. * @method static void allKeyExists($values, $key, $message = '')
  152. * @method static void allKeyNotExists($values, $key, $message = '')
  153. * @method static void allCount($values, $key, $message = '')
  154. * @method static void allMinCount($values, $min, $message = '')
  155. * @method static void allMaxCount($values, $max, $message = '')
  156. * @method static void allCountBetween($values, $min, $max, $message = '')
  157. * @method static void allUuid($values, $message = '')
  158. *
  159. * @since 1.0
  160. *
  161. * @author Bernhard Schussek <bschussek@gmail.com>
  162. */
  163. class Assert
  164. {
  165. public static function string($value, $message = '')
  166. {
  167. if (!is_string($value)) {
  168. static::reportInvalidArgument(sprintf(
  169. $message ?: 'Expected a string. Got: %s',
  170. static::typeToString($value)
  171. ));
  172. }
  173. }
  174. public static function stringNotEmpty($value, $message = '')
  175. {
  176. static::string($value, $message);
  177. static::notEq($value, '', $message);
  178. }
  179. public static function integer($value, $message = '')
  180. {
  181. if (!is_int($value)) {
  182. static::reportInvalidArgument(sprintf(
  183. $message ?: 'Expected an integer. Got: %s',
  184. static::typeToString($value)
  185. ));
  186. }
  187. }
  188. public static function integerish($value, $message = '')
  189. {
  190. if (!is_numeric($value) || $value != (int) $value) {
  191. static::reportInvalidArgument(sprintf(
  192. $message ?: 'Expected an integerish value. Got: %s',
  193. static::typeToString($value)
  194. ));
  195. }
  196. }
  197. public static function float($value, $message = '')
  198. {
  199. if (!is_float($value)) {
  200. static::reportInvalidArgument(sprintf(
  201. $message ?: 'Expected a float. Got: %s',
  202. static::typeToString($value)
  203. ));
  204. }
  205. }
  206. public static function numeric($value, $message = '')
  207. {
  208. if (!is_numeric($value)) {
  209. static::reportInvalidArgument(sprintf(
  210. $message ?: 'Expected a numeric. Got: %s',
  211. static::typeToString($value)
  212. ));
  213. }
  214. }
  215. public static function natural($value, $message = '')
  216. {
  217. if (!is_int($value) || $value < 0) {
  218. static::reportInvalidArgument(sprintf(
  219. $message ?: 'Expected a non-negative integer. Got %s',
  220. static::valueToString($value)
  221. ));
  222. }
  223. }
  224. public static function boolean($value, $message = '')
  225. {
  226. if (!is_bool($value)) {
  227. static::reportInvalidArgument(sprintf(
  228. $message ?: 'Expected a boolean. Got: %s',
  229. static::typeToString($value)
  230. ));
  231. }
  232. }
  233. public static function scalar($value, $message = '')
  234. {
  235. if (!is_scalar($value)) {
  236. static::reportInvalidArgument(sprintf(
  237. $message ?: 'Expected a scalar. Got: %s',
  238. static::typeToString($value)
  239. ));
  240. }
  241. }
  242. public static function object($value, $message = '')
  243. {
  244. if (!is_object($value)) {
  245. static::reportInvalidArgument(sprintf(
  246. $message ?: 'Expected an object. Got: %s',
  247. static::typeToString($value)
  248. ));
  249. }
  250. }
  251. public static function resource($value, $type = null, $message = '')
  252. {
  253. if (!is_resource($value)) {
  254. static::reportInvalidArgument(sprintf(
  255. $message ?: 'Expected a resource. Got: %s',
  256. static::typeToString($value)
  257. ));
  258. }
  259. if ($type && $type !== get_resource_type($value)) {
  260. static::reportInvalidArgument(sprintf(
  261. $message ?: 'Expected a resource of type %2$s. Got: %s',
  262. static::typeToString($value),
  263. $type
  264. ));
  265. }
  266. }
  267. public static function isCallable($value, $message = '')
  268. {
  269. if (!is_callable($value)) {
  270. static::reportInvalidArgument(sprintf(
  271. $message ?: 'Expected a callable. Got: %s',
  272. static::typeToString($value)
  273. ));
  274. }
  275. }
  276. public static function isArray($value, $message = '')
  277. {
  278. if (!is_array($value)) {
  279. static::reportInvalidArgument(sprintf(
  280. $message ?: 'Expected an array. Got: %s',
  281. static::typeToString($value)
  282. ));
  283. }
  284. }
  285. public static function isTraversable($value, $message = '')
  286. {
  287. @trigger_error(
  288. sprintf(
  289. 'The "%s" assertion is deprecated. You should stop using it, as it will soon be removed in 2.0 version. Use "isIterable" or "isInstanceOf" instead.',
  290. __METHOD__
  291. ),
  292. E_USER_DEPRECATED
  293. );
  294. if (!is_array($value) && !($value instanceof Traversable)) {
  295. static::reportInvalidArgument(sprintf(
  296. $message ?: 'Expected a traversable. Got: %s',
  297. static::typeToString($value)
  298. ));
  299. }
  300. }
  301. public static function isArrayAccessible($value, $message = '')
  302. {
  303. if (!is_array($value) && !($value instanceof ArrayAccess)) {
  304. static::reportInvalidArgument(sprintf(
  305. $message ?: 'Expected an array accessible. Got: %s',
  306. static::typeToString($value)
  307. ));
  308. }
  309. }
  310. public static function isCountable($value, $message = '')
  311. {
  312. if (!is_array($value) && !($value instanceof Countable)) {
  313. static::reportInvalidArgument(sprintf(
  314. $message ?: 'Expected a countable. Got: %s',
  315. static::typeToString($value)
  316. ));
  317. }
  318. }
  319. public static function isIterable($value, $message = '')
  320. {
  321. if (!is_array($value) && !($value instanceof Traversable)) {
  322. static::reportInvalidArgument(sprintf(
  323. $message ?: 'Expected an iterable. Got: %s',
  324. static::typeToString($value)
  325. ));
  326. }
  327. }
  328. public static function isInstanceOf($value, $class, $message = '')
  329. {
  330. if (!($value instanceof $class)) {
  331. static::reportInvalidArgument(sprintf(
  332. $message ?: 'Expected an instance of %2$s. Got: %s',
  333. static::typeToString($value),
  334. $class
  335. ));
  336. }
  337. }
  338. public static function notInstanceOf($value, $class, $message = '')
  339. {
  340. if ($value instanceof $class) {
  341. static::reportInvalidArgument(sprintf(
  342. $message ?: 'Expected an instance other than %2$s. Got: %s',
  343. static::typeToString($value),
  344. $class
  345. ));
  346. }
  347. }
  348. public static function isInstanceOfAny($value, array $classes, $message = '')
  349. {
  350. foreach ($classes as $class) {
  351. if ($value instanceof $class) {
  352. return;
  353. }
  354. }
  355. static::reportInvalidArgument(sprintf(
  356. $message ?: 'Expected an instance of any of %2$s. Got: %s',
  357. static::typeToString($value),
  358. implode(', ', array_map(array('static', 'valueToString'), $classes))
  359. ));
  360. }
  361. public static function isEmpty($value, $message = '')
  362. {
  363. if (!empty($value)) {
  364. static::reportInvalidArgument(sprintf(
  365. $message ?: 'Expected an empty value. Got: %s',
  366. static::valueToString($value)
  367. ));
  368. }
  369. }
  370. public static function notEmpty($value, $message = '')
  371. {
  372. if (empty($value)) {
  373. static::reportInvalidArgument(sprintf(
  374. $message ?: 'Expected a non-empty value. Got: %s',
  375. static::valueToString($value)
  376. ));
  377. }
  378. }
  379. public static function null($value, $message = '')
  380. {
  381. if (null !== $value) {
  382. static::reportInvalidArgument(sprintf(
  383. $message ?: 'Expected null. Got: %s',
  384. static::valueToString($value)
  385. ));
  386. }
  387. }
  388. public static function notNull($value, $message = '')
  389. {
  390. if (null === $value) {
  391. static::reportInvalidArgument(
  392. $message ?: 'Expected a value other than null.'
  393. );
  394. }
  395. }
  396. public static function true($value, $message = '')
  397. {
  398. if (true !== $value) {
  399. static::reportInvalidArgument(sprintf(
  400. $message ?: 'Expected a value to be true. Got: %s',
  401. static::valueToString($value)
  402. ));
  403. }
  404. }
  405. public static function false($value, $message = '')
  406. {
  407. if (false !== $value) {
  408. static::reportInvalidArgument(sprintf(
  409. $message ?: 'Expected a value to be false. Got: %s',
  410. static::valueToString($value)
  411. ));
  412. }
  413. }
  414. public static function eq($value, $value2, $message = '')
  415. {
  416. if ($value2 != $value) {
  417. static::reportInvalidArgument(sprintf(
  418. $message ?: 'Expected a value equal to %2$s. Got: %s',
  419. static::valueToString($value),
  420. static::valueToString($value2)
  421. ));
  422. }
  423. }
  424. public static function notEq($value, $value2, $message = '')
  425. {
  426. if ($value2 == $value) {
  427. static::reportInvalidArgument(sprintf(
  428. $message ?: 'Expected a different value than %s.',
  429. static::valueToString($value2)
  430. ));
  431. }
  432. }
  433. public static function same($value, $value2, $message = '')
  434. {
  435. if ($value2 !== $value) {
  436. static::reportInvalidArgument(sprintf(
  437. $message ?: 'Expected a value identical to %2$s. Got: %s',
  438. static::valueToString($value),
  439. static::valueToString($value2)
  440. ));
  441. }
  442. }
  443. public static function notSame($value, $value2, $message = '')
  444. {
  445. if ($value2 === $value) {
  446. static::reportInvalidArgument(sprintf(
  447. $message ?: 'Expected a value not identical to %s.',
  448. static::valueToString($value2)
  449. ));
  450. }
  451. }
  452. public static function greaterThan($value, $limit, $message = '')
  453. {
  454. if ($value <= $limit) {
  455. static::reportInvalidArgument(sprintf(
  456. $message ?: 'Expected a value greater than %2$s. Got: %s',
  457. static::valueToString($value),
  458. static::valueToString($limit)
  459. ));
  460. }
  461. }
  462. public static function greaterThanEq($value, $limit, $message = '')
  463. {
  464. if ($value < $limit) {
  465. static::reportInvalidArgument(sprintf(
  466. $message ?: 'Expected a value greater than or equal to %2$s. Got: %s',
  467. static::valueToString($value),
  468. static::valueToString($limit)
  469. ));
  470. }
  471. }
  472. public static function lessThan($value, $limit, $message = '')
  473. {
  474. if ($value >= $limit) {
  475. static::reportInvalidArgument(sprintf(
  476. $message ?: 'Expected a value less than %2$s. Got: %s',
  477. static::valueToString($value),
  478. static::valueToString($limit)
  479. ));
  480. }
  481. }
  482. public static function lessThanEq($value, $limit, $message = '')
  483. {
  484. if ($value > $limit) {
  485. static::reportInvalidArgument(sprintf(
  486. $message ?: 'Expected a value less than or equal to %2$s. Got: %s',
  487. static::valueToString($value),
  488. static::valueToString($limit)
  489. ));
  490. }
  491. }
  492. public static function range($value, $min, $max, $message = '')
  493. {
  494. if ($value < $min || $value > $max) {
  495. static::reportInvalidArgument(sprintf(
  496. $message ?: 'Expected a value between %2$s and %3$s. Got: %s',
  497. static::valueToString($value),
  498. static::valueToString($min),
  499. static::valueToString($max)
  500. ));
  501. }
  502. }
  503. public static function oneOf($value, array $values, $message = '')
  504. {
  505. if (!in_array($value, $values, true)) {
  506. static::reportInvalidArgument(sprintf(
  507. $message ?: 'Expected one of: %2$s. Got: %s',
  508. static::valueToString($value),
  509. implode(', ', array_map(array('static', 'valueToString'), $values))
  510. ));
  511. }
  512. }
  513. public static function contains($value, $subString, $message = '')
  514. {
  515. if (false === strpos($value, $subString)) {
  516. static::reportInvalidArgument(sprintf(
  517. $message ?: 'Expected a value to contain %2$s. Got: %s',
  518. static::valueToString($value),
  519. static::valueToString($subString)
  520. ));
  521. }
  522. }
  523. public static function notContains($value, $subString, $message = '')
  524. {
  525. if (false !== strpos($value, $subString)) {
  526. static::reportInvalidArgument(sprintf(
  527. $message ?: '%2$s was not expected to be contained in a value. Got: %s',
  528. static::valueToString($value),
  529. static::valueToString($subString)
  530. ));
  531. }
  532. }
  533. public static function notWhitespaceOnly($value, $message = '')
  534. {
  535. if (preg_match('/^\s*$/', $value)) {
  536. static::reportInvalidArgument(sprintf(
  537. $message ?: 'Expected a non-whitespace string. Got: %s',
  538. static::valueToString($value)
  539. ));
  540. }
  541. }
  542. public static function startsWith($value, $prefix, $message = '')
  543. {
  544. if (0 !== strpos($value, $prefix)) {
  545. static::reportInvalidArgument(sprintf(
  546. $message ?: 'Expected a value to start with %2$s. Got: %s',
  547. static::valueToString($value),
  548. static::valueToString($prefix)
  549. ));
  550. }
  551. }
  552. public static function startsWithLetter($value, $message = '')
  553. {
  554. $valid = isset($value[0]);
  555. if ($valid) {
  556. $locale = setlocale(LC_CTYPE, 0);
  557. setlocale(LC_CTYPE, 'C');
  558. $valid = ctype_alpha($value[0]);
  559. setlocale(LC_CTYPE, $locale);
  560. }
  561. if (!$valid) {
  562. static::reportInvalidArgument(sprintf(
  563. $message ?: 'Expected a value to start with a letter. Got: %s',
  564. static::valueToString($value)
  565. ));
  566. }
  567. }
  568. public static function endsWith($value, $suffix, $message = '')
  569. {
  570. if ($suffix !== substr($value, -static::strlen($suffix))) {
  571. static::reportInvalidArgument(sprintf(
  572. $message ?: 'Expected a value to end with %2$s. Got: %s',
  573. static::valueToString($value),
  574. static::valueToString($suffix)
  575. ));
  576. }
  577. }
  578. public static function regex($value, $pattern, $message = '')
  579. {
  580. if (!preg_match($pattern, $value)) {
  581. static::reportInvalidArgument(sprintf(
  582. $message ?: 'The value %s does not match the expected pattern.',
  583. static::valueToString($value)
  584. ));
  585. }
  586. }
  587. public static function alpha($value, $message = '')
  588. {
  589. $locale = setlocale(LC_CTYPE, 0);
  590. setlocale(LC_CTYPE, 'C');
  591. $valid = !ctype_alpha($value);
  592. setlocale(LC_CTYPE, $locale);
  593. if ($valid) {
  594. static::reportInvalidArgument(sprintf(
  595. $message ?: 'Expected a value to contain only letters. Got: %s',
  596. static::valueToString($value)
  597. ));
  598. }
  599. }
  600. public static function digits($value, $message = '')
  601. {
  602. $locale = setlocale(LC_CTYPE, 0);
  603. setlocale(LC_CTYPE, 'C');
  604. $valid = !ctype_digit($value);
  605. setlocale(LC_CTYPE, $locale);
  606. if ($valid) {
  607. static::reportInvalidArgument(sprintf(
  608. $message ?: 'Expected a value to contain digits only. Got: %s',
  609. static::valueToString($value)
  610. ));
  611. }
  612. }
  613. public static function alnum($value, $message = '')
  614. {
  615. $locale = setlocale(LC_CTYPE, 0);
  616. setlocale(LC_CTYPE, 'C');
  617. $valid = !ctype_alnum($value);
  618. setlocale(LC_CTYPE, $locale);
  619. if ($valid) {
  620. static::reportInvalidArgument(sprintf(
  621. $message ?: 'Expected a value to contain letters and digits only. Got: %s',
  622. static::valueToString($value)
  623. ));
  624. }
  625. }
  626. public static function lower($value, $message = '')
  627. {
  628. $locale = setlocale(LC_CTYPE, 0);
  629. setlocale(LC_CTYPE, 'C');
  630. $valid = !ctype_lower($value);
  631. setlocale(LC_CTYPE, $locale);
  632. if ($valid) {
  633. static::reportInvalidArgument(sprintf(
  634. $message ?: 'Expected a value to contain lowercase characters only. Got: %s',
  635. static::valueToString($value)
  636. ));
  637. }
  638. }
  639. public static function upper($value, $message = '')
  640. {
  641. $locale = setlocale(LC_CTYPE, 0);
  642. setlocale(LC_CTYPE, 'C');
  643. $valid = !ctype_upper($value);
  644. setlocale(LC_CTYPE, $locale);
  645. if ($valid) {
  646. static::reportInvalidArgument(sprintf(
  647. $message ?: 'Expected a value to contain uppercase characters only. Got: %s',
  648. static::valueToString($value)
  649. ));
  650. }
  651. }
  652. public static function length($value, $length, $message = '')
  653. {
  654. if ($length !== static::strlen($value)) {
  655. static::reportInvalidArgument(sprintf(
  656. $message ?: 'Expected a value to contain %2$s characters. Got: %s',
  657. static::valueToString($value),
  658. $length
  659. ));
  660. }
  661. }
  662. public static function minLength($value, $min, $message = '')
  663. {
  664. if (static::strlen($value) < $min) {
  665. static::reportInvalidArgument(sprintf(
  666. $message ?: 'Expected a value to contain at least %2$s characters. Got: %s',
  667. static::valueToString($value),
  668. $min
  669. ));
  670. }
  671. }
  672. public static function maxLength($value, $max, $message = '')
  673. {
  674. if (static::strlen($value) > $max) {
  675. static::reportInvalidArgument(sprintf(
  676. $message ?: 'Expected a value to contain at most %2$s characters. Got: %s',
  677. static::valueToString($value),
  678. $max
  679. ));
  680. }
  681. }
  682. public static function lengthBetween($value, $min, $max, $message = '')
  683. {
  684. $length = static::strlen($value);
  685. if ($length < $min || $length > $max) {
  686. static::reportInvalidArgument(sprintf(
  687. $message ?: 'Expected a value to contain between %2$s and %3$s characters. Got: %s',
  688. static::valueToString($value),
  689. $min,
  690. $max
  691. ));
  692. }
  693. }
  694. public static function fileExists($value, $message = '')
  695. {
  696. static::string($value);
  697. if (!file_exists($value)) {
  698. static::reportInvalidArgument(sprintf(
  699. $message ?: 'The file %s does not exist.',
  700. static::valueToString($value)
  701. ));
  702. }
  703. }
  704. public static function file($value, $message = '')
  705. {
  706. static::fileExists($value, $message);
  707. if (!is_file($value)) {
  708. static::reportInvalidArgument(sprintf(
  709. $message ?: 'The path %s is not a file.',
  710. static::valueToString($value)
  711. ));
  712. }
  713. }
  714. public static function directory($value, $message = '')
  715. {
  716. static::fileExists($value, $message);
  717. if (!is_dir($value)) {
  718. static::reportInvalidArgument(sprintf(
  719. $message ?: 'The path %s is no directory.',
  720. static::valueToString($value)
  721. ));
  722. }
  723. }
  724. public static function readable($value, $message = '')
  725. {
  726. if (!is_readable($value)) {
  727. static::reportInvalidArgument(sprintf(
  728. $message ?: 'The path %s is not readable.',
  729. static::valueToString($value)
  730. ));
  731. }
  732. }
  733. public static function writable($value, $message = '')
  734. {
  735. if (!is_writable($value)) {
  736. static::reportInvalidArgument(sprintf(
  737. $message ?: 'The path %s is not writable.',
  738. static::valueToString($value)
  739. ));
  740. }
  741. }
  742. public static function classExists($value, $message = '')
  743. {
  744. if (!class_exists($value)) {
  745. static::reportInvalidArgument(sprintf(
  746. $message ?: 'Expected an existing class name. Got: %s',
  747. static::valueToString($value)
  748. ));
  749. }
  750. }
  751. public static function subclassOf($value, $class, $message = '')
  752. {
  753. if (!is_subclass_of($value, $class)) {
  754. static::reportInvalidArgument(sprintf(
  755. $message ?: 'Expected a sub-class of %2$s. Got: %s',
  756. static::valueToString($value),
  757. static::valueToString($class)
  758. ));
  759. }
  760. }
  761. public static function implementsInterface($value, $interface, $message = '')
  762. {
  763. if (!in_array($interface, class_implements($value))) {
  764. static::reportInvalidArgument(sprintf(
  765. $message ?: 'Expected an implementation of %2$s. Got: %s',
  766. static::valueToString($value),
  767. static::valueToString($interface)
  768. ));
  769. }
  770. }
  771. public static function propertyExists($classOrObject, $property, $message = '')
  772. {
  773. if (!property_exists($classOrObject, $property)) {
  774. static::reportInvalidArgument(sprintf(
  775. $message ?: 'Expected the property %s to exist.',
  776. static::valueToString($property)
  777. ));
  778. }
  779. }
  780. public static function propertyNotExists($classOrObject, $property, $message = '')
  781. {
  782. if (property_exists($classOrObject, $property)) {
  783. static::reportInvalidArgument(sprintf(
  784. $message ?: 'Expected the property %s to not exist.',
  785. static::valueToString($property)
  786. ));
  787. }
  788. }
  789. public static function methodExists($classOrObject, $method, $message = '')
  790. {
  791. if (!method_exists($classOrObject, $method)) {
  792. static::reportInvalidArgument(sprintf(
  793. $message ?: 'Expected the method %s to exist.',
  794. static::valueToString($method)
  795. ));
  796. }
  797. }
  798. public static function methodNotExists($classOrObject, $method, $message = '')
  799. {
  800. if (method_exists($classOrObject, $method)) {
  801. static::reportInvalidArgument(sprintf(
  802. $message ?: 'Expected the method %s to not exist.',
  803. static::valueToString($method)
  804. ));
  805. }
  806. }
  807. public static function keyExists($array, $key, $message = '')
  808. {
  809. if (!array_key_exists($key, $array)) {
  810. static::reportInvalidArgument(sprintf(
  811. $message ?: 'Expected the key %s to exist.',
  812. static::valueToString($key)
  813. ));
  814. }
  815. }
  816. public static function keyNotExists($array, $key, $message = '')
  817. {
  818. if (array_key_exists($key, $array)) {
  819. static::reportInvalidArgument(sprintf(
  820. $message ?: 'Expected the key %s to not exist.',
  821. static::valueToString($key)
  822. ));
  823. }
  824. }
  825. public static function count($array, $number, $message = '')
  826. {
  827. static::eq(
  828. count($array),
  829. $number,
  830. $message ?: sprintf('Expected an array to contain %d elements. Got: %d.', $number, count($array))
  831. );
  832. }
  833. public static function minCount($array, $min, $message = '')
  834. {
  835. if (count($array) < $min) {
  836. static::reportInvalidArgument(sprintf(
  837. $message ?: 'Expected an array to contain at least %2$d elements. Got: %d',
  838. count($array),
  839. $min
  840. ));
  841. }
  842. }
  843. public static function maxCount($array, $max, $message = '')
  844. {
  845. if (count($array) > $max) {
  846. static::reportInvalidArgument(sprintf(
  847. $message ?: 'Expected an array to contain at most %2$d elements. Got: %d',
  848. count($array),
  849. $max
  850. ));
  851. }
  852. }
  853. public static function countBetween($array, $min, $max, $message = '')
  854. {
  855. $count = count($array);
  856. if ($count < $min || $count > $max) {
  857. static::reportInvalidArgument(sprintf(
  858. $message ?: 'Expected an array to contain between %2$d and %3$d elements. Got: %d',
  859. $count,
  860. $min,
  861. $max
  862. ));
  863. }
  864. }
  865. public static function uuid($value, $message = '')
  866. {
  867. $value = str_replace(array('urn:', 'uuid:', '{', '}'), '', $value);
  868. // The nil UUID is special form of UUID that is specified to have all
  869. // 128 bits set to zero.
  870. if ('00000000-0000-0000-0000-000000000000' === $value) {
  871. return;
  872. }
  873. if (!preg_match('/^[0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{12}$/', $value)) {
  874. static::reportInvalidArgument(sprintf(
  875. $message ?: 'Value %s is not a valid UUID.',
  876. static::valueToString($value)
  877. ));
  878. }
  879. }
  880. public static function throws(Closure $expression, $class = 'Exception', $message = '')
  881. {
  882. static::string($class);
  883. $actual = 'none';
  884. try {
  885. $expression();
  886. } catch (Exception $e) {
  887. $actual = get_class($e);
  888. if ($e instanceof $class) {
  889. return;
  890. }
  891. } catch (Throwable $e) {
  892. $actual = get_class($e);
  893. if ($e instanceof $class) {
  894. return;
  895. }
  896. }
  897. static::reportInvalidArgument($message ?: sprintf(
  898. 'Expected to throw "%s", got "%s"',
  899. $class,
  900. $actual
  901. ));
  902. }
  903. public static function __callStatic($name, $arguments)
  904. {
  905. if ('nullOr' === substr($name, 0, 6)) {
  906. if (null !== $arguments[0]) {
  907. $method = lcfirst(substr($name, 6));
  908. call_user_func_array(array('static', $method), $arguments);
  909. }
  910. return;
  911. }
  912. if ('all' === substr($name, 0, 3)) {
  913. static::isIterable($arguments[0]);
  914. $method = lcfirst(substr($name, 3));
  915. $args = $arguments;
  916. foreach ($arguments[0] as $entry) {
  917. $args[0] = $entry;
  918. call_user_func_array(array('static', $method), $args);
  919. }
  920. return;
  921. }
  922. throw new BadMethodCallException('No such method: '.$name);
  923. }
  924. protected static function valueToString($value)
  925. {
  926. if (null === $value) {
  927. return 'null';
  928. }
  929. if (true === $value) {
  930. return 'true';
  931. }
  932. if (false === $value) {
  933. return 'false';
  934. }
  935. if (is_array($value)) {
  936. return 'array';
  937. }
  938. if (is_object($value)) {
  939. return get_class($value);
  940. }
  941. if (is_resource($value)) {
  942. return 'resource';
  943. }
  944. if (is_string($value)) {
  945. return '"'.$value.'"';
  946. }
  947. return (string) $value;
  948. }
  949. protected static function typeToString($value)
  950. {
  951. return is_object($value) ? get_class($value) : gettype($value);
  952. }
  953. protected static function strlen($value)
  954. {
  955. if (!function_exists('mb_detect_encoding')) {
  956. return strlen($value);
  957. }
  958. if (false === $encoding = mb_detect_encoding($value)) {
  959. return strlen($value);
  960. }
  961. return mb_strwidth($value, $encoding);
  962. }
  963. protected static function reportInvalidArgument($message)
  964. {
  965. throw new InvalidArgumentException($message);
  966. }
  967. private function __construct()
  968. {
  969. }
  970. }