Assert.php 52 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702
  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 nullOrNatural($value, $message = '')
  29. * @method static void nullOrBoolean($value, $message = '')
  30. * @method static void nullOrScalar($value, $message = '')
  31. * @method static void nullOrObject($value, $message = '')
  32. * @method static void nullOrResource($value, $type = null, $message = '')
  33. * @method static void nullOrIsCallable($value, $message = '')
  34. * @method static void nullOrIsArray($value, $message = '')
  35. * @method static void nullOrIsTraversable($value, $message = '')
  36. * @method static void nullOrIsArrayAccessible($value, $message = '')
  37. * @method static void nullOrIsCountable($value, $message = '')
  38. * @method static void nullOrIsIterable($value, $message = '')
  39. * @method static void nullOrIsInstanceOf($value, $class, $message = '')
  40. * @method static void nullOrNotInstanceOf($value, $class, $message = '')
  41. * @method static void nullOrIsInstanceOfAny($value, $classes, $message = '')
  42. * @method static void nullOrIsEmpty($value, $message = '')
  43. * @method static void nullOrNotEmpty($value, $message = '')
  44. * @method static void nullOrTrue($value, $message = '')
  45. * @method static void nullOrFalse($value, $message = '')
  46. * @method static void nullOrIp($value, $message = '')
  47. * @method static void nullOrIpv4($value, $message = '')
  48. * @method static void nullOrIpv6($value, $message = '')
  49. * @method static void nullOrEmail($value, $message = '')
  50. * @method static void nullOrUniqueValues($values, $message = '')
  51. * @method static void nullOrEq($value, $expect, $message = '')
  52. * @method static void nullOrNotEq($value, $expect, $message = '')
  53. * @method static void nullOrSame($value, $expect, $message = '')
  54. * @method static void nullOrNotSame($value, $expect, $message = '')
  55. * @method static void nullOrGreaterThan($value, $limit, $message = '')
  56. * @method static void nullOrGreaterThanEq($value, $limit, $message = '')
  57. * @method static void nullOrLessThan($value, $limit, $message = '')
  58. * @method static void nullOrLessThanEq($value, $limit, $message = '')
  59. * @method static void nullOrRange($value, $min, $max, $message = '')
  60. * @method static void nullOrOneOf($value, $values, $message = '')
  61. * @method static void nullOrContains($value, $subString, $message = '')
  62. * @method static void nullOrNotContains($value, $subString, $message = '')
  63. * @method static void nullOrNotWhitespaceOnly($value, $message = '')
  64. * @method static void nullOrStartsWith($value, $prefix, $message = '')
  65. * @method static void nullOrStartsWithLetter($value, $message = '')
  66. * @method static void nullOrEndsWith($value, $suffix, $message = '')
  67. * @method static void nullOrRegex($value, $pattern, $message = '')
  68. * @method static void nullOrNotRegex($value, $pattern, $message = '')
  69. * @method static void nullOrUnicodeLetters($value, $message = '')
  70. * @method static void nullOrAlpha($value, $message = '')
  71. * @method static void nullOrDigits($value, $message = '')
  72. * @method static void nullOrAlnum($value, $message = '')
  73. * @method static void nullOrLower($value, $message = '')
  74. * @method static void nullOrUpper($value, $message = '')
  75. * @method static void nullOrLength($value, $length, $message = '')
  76. * @method static void nullOrMinLength($value, $min, $message = '')
  77. * @method static void nullOrMaxLength($value, $max, $message = '')
  78. * @method static void nullOrLengthBetween($value, $min, $max, $message = '')
  79. * @method static void nullOrFileExists($value, $message = '')
  80. * @method static void nullOrFile($value, $message = '')
  81. * @method static void nullOrDirectory($value, $message = '')
  82. * @method static void nullOrReadable($value, $message = '')
  83. * @method static void nullOrWritable($value, $message = '')
  84. * @method static void nullOrClassExists($value, $message = '')
  85. * @method static void nullOrSubclassOf($value, $class, $message = '')
  86. * @method static void nullOrInterfaceExists($value, $message = '')
  87. * @method static void nullOrImplementsInterface($value, $interface, $message = '')
  88. * @method static void nullOrPropertyExists($value, $property, $message = '')
  89. * @method static void nullOrPropertyNotExists($value, $property, $message = '')
  90. * @method static void nullOrMethodExists($value, $method, $message = '')
  91. * @method static void nullOrMethodNotExists($value, $method, $message = '')
  92. * @method static void nullOrKeyExists($value, $key, $message = '')
  93. * @method static void nullOrKeyNotExists($value, $key, $message = '')
  94. * @method static void nullOrCount($value, $key, $message = '')
  95. * @method static void nullOrMinCount($value, $min, $message = '')
  96. * @method static void nullOrMaxCount($value, $max, $message = '')
  97. * @method static void nullOrIsList($value, $message = '')
  98. * @method static void nullOrIsMap($value, $message = '')
  99. * @method static void nullOrCountBetween($value, $min, $max, $message = '')
  100. * @method static void nullOrUuid($values, $message = '')
  101. * @method static void nullOrThrows($expression, $class = 'Exception', $message = '')
  102. * @method static void allString($values, $message = '')
  103. * @method static void allStringNotEmpty($values, $message = '')
  104. * @method static void allInteger($values, $message = '')
  105. * @method static void allIntegerish($values, $message = '')
  106. * @method static void allFloat($values, $message = '')
  107. * @method static void allNumeric($values, $message = '')
  108. * @method static void allNatural($values, $message = '')
  109. * @method static void allBoolean($values, $message = '')
  110. * @method static void allScalar($values, $message = '')
  111. * @method static void allObject($values, $message = '')
  112. * @method static void allResource($values, $type = null, $message = '')
  113. * @method static void allIsCallable($values, $message = '')
  114. * @method static void allIsArray($values, $message = '')
  115. * @method static void allIsTraversable($values, $message = '')
  116. * @method static void allIsArrayAccessible($values, $message = '')
  117. * @method static void allIsCountable($values, $message = '')
  118. * @method static void allIsIterable($values, $message = '')
  119. * @method static void allIsInstanceOf($values, $class, $message = '')
  120. * @method static void allNotInstanceOf($values, $class, $message = '')
  121. * @method static void allIsInstanceOfAny($values, $classes, $message = '')
  122. * @method static void allNull($values, $message = '')
  123. * @method static void allNotNull($values, $message = '')
  124. * @method static void allIsEmpty($values, $message = '')
  125. * @method static void allNotEmpty($values, $message = '')
  126. * @method static void allTrue($values, $message = '')
  127. * @method static void allFalse($values, $message = '')
  128. * @method static void allIp($values, $message = '')
  129. * @method static void allIpv4($values, $message = '')
  130. * @method static void allIpv6($values, $message = '')
  131. * @method static void allEmail($values, $message = '')
  132. * @method static void allUniqueValues($values, $message = '')
  133. * @method static void allEq($values, $expect, $message = '')
  134. * @method static void allNotEq($values, $expect, $message = '')
  135. * @method static void allSame($values, $expect, $message = '')
  136. * @method static void allNotSame($values, $expect, $message = '')
  137. * @method static void allGreaterThan($values, $limit, $message = '')
  138. * @method static void allGreaterThanEq($values, $limit, $message = '')
  139. * @method static void allLessThan($values, $limit, $message = '')
  140. * @method static void allLessThanEq($values, $limit, $message = '')
  141. * @method static void allRange($values, $min, $max, $message = '')
  142. * @method static void allOneOf($values, $values, $message = '')
  143. * @method static void allContains($values, $subString, $message = '')
  144. * @method static void allNotContains($values, $subString, $message = '')
  145. * @method static void allNotWhitespaceOnly($values, $message = '')
  146. * @method static void allStartsWith($values, $prefix, $message = '')
  147. * @method static void allStartsWithLetter($values, $message = '')
  148. * @method static void allEndsWith($values, $suffix, $message = '')
  149. * @method static void allRegex($values, $pattern, $message = '')
  150. * @method static void allNotRegex($values, $pattern, $message = '')
  151. * @method static void allUnicodeLetters($values, $message = '')
  152. * @method static void allAlpha($values, $message = '')
  153. * @method static void allDigits($values, $message = '')
  154. * @method static void allAlnum($values, $message = '')
  155. * @method static void allLower($values, $message = '')
  156. * @method static void allUpper($values, $message = '')
  157. * @method static void allLength($values, $length, $message = '')
  158. * @method static void allMinLength($values, $min, $message = '')
  159. * @method static void allMaxLength($values, $max, $message = '')
  160. * @method static void allLengthBetween($values, $min, $max, $message = '')
  161. * @method static void allFileExists($values, $message = '')
  162. * @method static void allFile($values, $message = '')
  163. * @method static void allDirectory($values, $message = '')
  164. * @method static void allReadable($values, $message = '')
  165. * @method static void allWritable($values, $message = '')
  166. * @method static void allClassExists($values, $message = '')
  167. * @method static void allSubclassOf($values, $class, $message = '')
  168. * @method static void allInterfaceExists($values, $message = '')
  169. * @method static void allImplementsInterface($values, $interface, $message = '')
  170. * @method static void allPropertyExists($values, $property, $message = '')
  171. * @method static void allPropertyNotExists($values, $property, $message = '')
  172. * @method static void allMethodExists($values, $method, $message = '')
  173. * @method static void allMethodNotExists($values, $method, $message = '')
  174. * @method static void allKeyExists($values, $key, $message = '')
  175. * @method static void allKeyNotExists($values, $key, $message = '')
  176. * @method static void allCount($values, $key, $message = '')
  177. * @method static void allMinCount($values, $min, $message = '')
  178. * @method static void allMaxCount($values, $max, $message = '')
  179. * @method static void allCountBetween($values, $min, $max, $message = '')
  180. * @method static void allIsList($values, $message = '')
  181. * @method static void allIsMap($values, $message = '')
  182. * @method static void allUuid($values, $message = '')
  183. * @method static void allThrows($expressions, $class = 'Exception', $message = '')
  184. *
  185. * @since 1.0
  186. *
  187. * @author Bernhard Schussek <bschussek@gmail.com>
  188. */
  189. class Assert
  190. {
  191. /**
  192. * @psalm-assert string $value
  193. *
  194. * @param mixed $value
  195. * @param string $message
  196. */
  197. public static function string($value, $message = '')
  198. {
  199. if (!\is_string($value)) {
  200. static::reportInvalidArgument(\sprintf(
  201. $message ?: 'Expected a string. Got: %s',
  202. static::typeToString($value)
  203. ));
  204. }
  205. }
  206. /**
  207. * @psalm-assert string $value
  208. *
  209. * @param mixed $value
  210. * @param string $message
  211. */
  212. public static function stringNotEmpty($value, $message = '')
  213. {
  214. static::string($value, $message);
  215. static::notEq($value, '', $message);
  216. }
  217. /**
  218. * @psalm-assert int $value
  219. *
  220. * @param mixed $value
  221. * @param string $message
  222. */
  223. public static function integer($value, $message = '')
  224. {
  225. if (!\is_int($value)) {
  226. static::reportInvalidArgument(\sprintf(
  227. $message ?: 'Expected an integer. Got: %s',
  228. static::typeToString($value)
  229. ));
  230. }
  231. }
  232. /**
  233. * @psalm-assert numeric $value
  234. *
  235. * @param mixed $value
  236. * @param string $message
  237. */
  238. public static function integerish($value, $message = '')
  239. {
  240. if (!\is_numeric($value) || $value != (int) $value) {
  241. static::reportInvalidArgument(\sprintf(
  242. $message ?: 'Expected an integerish value. Got: %s',
  243. static::typeToString($value)
  244. ));
  245. }
  246. }
  247. /**
  248. * @psalm-assert float $value
  249. *
  250. * @param mixed $value
  251. * @param string $message
  252. */
  253. public static function float($value, $message = '')
  254. {
  255. if (!\is_float($value)) {
  256. static::reportInvalidArgument(\sprintf(
  257. $message ?: 'Expected a float. Got: %s',
  258. static::typeToString($value)
  259. ));
  260. }
  261. }
  262. /**
  263. * @psalm-assert numeric $value
  264. *
  265. * @param mixed $value
  266. * @param string $message
  267. */
  268. public static function numeric($value, $message = '')
  269. {
  270. if (!\is_numeric($value)) {
  271. static::reportInvalidArgument(\sprintf(
  272. $message ?: 'Expected a numeric. Got: %s',
  273. static::typeToString($value)
  274. ));
  275. }
  276. }
  277. /**
  278. * @psalm-assert int $value
  279. *
  280. * @param mixed $value
  281. * @param string $message
  282. */
  283. public static function natural($value, $message = '')
  284. {
  285. if (!\is_int($value) || $value < 0) {
  286. static::reportInvalidArgument(\sprintf(
  287. $message ?: 'Expected a non-negative integer. Got %s',
  288. static::valueToString($value)
  289. ));
  290. }
  291. }
  292. /**
  293. * @psalm-assert bool $value
  294. *
  295. * @param mixed $value
  296. * @param string $message
  297. */
  298. public static function boolean($value, $message = '')
  299. {
  300. if (!\is_bool($value)) {
  301. static::reportInvalidArgument(\sprintf(
  302. $message ?: 'Expected a boolean. Got: %s',
  303. static::typeToString($value)
  304. ));
  305. }
  306. }
  307. /**
  308. * @psalm-assert scalar $value
  309. *
  310. * @param mixed $value
  311. * @param string $message
  312. */
  313. public static function scalar($value, $message = '')
  314. {
  315. if (!\is_scalar($value)) {
  316. static::reportInvalidArgument(\sprintf(
  317. $message ?: 'Expected a scalar. Got: %s',
  318. static::typeToString($value)
  319. ));
  320. }
  321. }
  322. /**
  323. * @psalm-assert object $value
  324. *
  325. * @param mixed $value
  326. * @param string $message
  327. */
  328. public static function object($value, $message = '')
  329. {
  330. if (!\is_object($value)) {
  331. static::reportInvalidArgument(\sprintf(
  332. $message ?: 'Expected an object. Got: %s',
  333. static::typeToString($value)
  334. ));
  335. }
  336. }
  337. /**
  338. * @psalm-assert resource $value
  339. *
  340. * @param mixed $value
  341. * @param string|null $type type of resource this should be. @see https://www.php.net/manual/en/function.get-resource-type.php
  342. * @param string $message
  343. */
  344. public static function resource($value, $type = null, $message = '')
  345. {
  346. if (!\is_resource($value)) {
  347. static::reportInvalidArgument(\sprintf(
  348. $message ?: 'Expected a resource. Got: %s',
  349. static::typeToString($value)
  350. ));
  351. }
  352. if ($type && $type !== \get_resource_type($value)) {
  353. static::reportInvalidArgument(\sprintf(
  354. $message ?: 'Expected a resource of type %2$s. Got: %s',
  355. static::typeToString($value),
  356. $type
  357. ));
  358. }
  359. }
  360. /**
  361. * @psalm-assert callable $value
  362. *
  363. * @param mixed $value
  364. * @param string $message
  365. */
  366. public static function isCallable($value, $message = '')
  367. {
  368. if (!\is_callable($value)) {
  369. static::reportInvalidArgument(\sprintf(
  370. $message ?: 'Expected a callable. Got: %s',
  371. static::typeToString($value)
  372. ));
  373. }
  374. }
  375. /**
  376. * @psalm-assert array $value
  377. *
  378. * @param mixed $value
  379. * @param string $message
  380. */
  381. public static function isArray($value, $message = '')
  382. {
  383. if (!\is_array($value)) {
  384. static::reportInvalidArgument(\sprintf(
  385. $message ?: 'Expected an array. Got: %s',
  386. static::typeToString($value)
  387. ));
  388. }
  389. }
  390. /**
  391. * @psalm-assert iterable $value
  392. *
  393. * @deprecated use "isIterable" or "isInstanceOf" instead
  394. *
  395. * @param mixed $value
  396. * @param string $message
  397. */
  398. public static function isTraversable($value, $message = '')
  399. {
  400. @\trigger_error(
  401. \sprintf(
  402. '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.',
  403. __METHOD__
  404. ),
  405. \E_USER_DEPRECATED
  406. );
  407. if (!\is_array($value) && !($value instanceof Traversable)) {
  408. static::reportInvalidArgument(\sprintf(
  409. $message ?: 'Expected a traversable. Got: %s',
  410. static::typeToString($value)
  411. ));
  412. }
  413. }
  414. /**
  415. * @param mixed $value
  416. * @param string $message
  417. */
  418. public static function isArrayAccessible($value, $message = '')
  419. {
  420. if (!\is_array($value) && !($value instanceof ArrayAccess)) {
  421. static::reportInvalidArgument(\sprintf(
  422. $message ?: 'Expected an array accessible. Got: %s',
  423. static::typeToString($value)
  424. ));
  425. }
  426. }
  427. /**
  428. * @psalm-assert countable $value
  429. *
  430. * @param mixed $value
  431. * @param string $message
  432. */
  433. public static function isCountable($value, $message = '')
  434. {
  435. if (!\is_array($value) && !($value instanceof Countable)) {
  436. static::reportInvalidArgument(\sprintf(
  437. $message ?: 'Expected a countable. Got: %s',
  438. static::typeToString($value)
  439. ));
  440. }
  441. }
  442. /**
  443. * @psalm-assert iterable $value
  444. *
  445. * @param mixed $value
  446. * @param string $message
  447. */
  448. public static function isIterable($value, $message = '')
  449. {
  450. if (!\is_array($value) && !($value instanceof Traversable)) {
  451. static::reportInvalidArgument(\sprintf(
  452. $message ?: 'Expected an iterable. Got: %s',
  453. static::typeToString($value)
  454. ));
  455. }
  456. }
  457. /**
  458. * @psalm-template ExpectedType of object
  459. * @psalm-param class-string<ExpectedType> $class
  460. * @psalm-assert ExpectedType $value
  461. *
  462. * @param mixed $value
  463. * @param string|object $class
  464. * @param string $message
  465. */
  466. public static function isInstanceOf($value, $class, $message = '')
  467. {
  468. if (!($value instanceof $class)) {
  469. static::reportInvalidArgument(\sprintf(
  470. $message ?: 'Expected an instance of %2$s. Got: %s',
  471. static::typeToString($value),
  472. $class
  473. ));
  474. }
  475. }
  476. /**
  477. * @psalm-template ExpectedType of object
  478. * @psalm-param class-string<ExpectedType> $class
  479. * @psalm-assert !ExpectedType $value
  480. *
  481. * @param mixed $value
  482. * @param string|object $class
  483. * @param string $message
  484. */
  485. public static function notInstanceOf($value, $class, $message = '')
  486. {
  487. if ($value instanceof $class) {
  488. static::reportInvalidArgument(\sprintf(
  489. $message ?: 'Expected an instance other than %2$s. Got: %s',
  490. static::typeToString($value),
  491. $class
  492. ));
  493. }
  494. }
  495. /**
  496. * @param mixed $value
  497. * @param array<object|string> $classes
  498. * @param string $message
  499. */
  500. public static function isInstanceOfAny($value, array $classes, $message = '')
  501. {
  502. foreach ($classes as $class) {
  503. if ($value instanceof $class) {
  504. return;
  505. }
  506. }
  507. static::reportInvalidArgument(\sprintf(
  508. $message ?: 'Expected an instance of any of %2$s. Got: %s',
  509. static::typeToString($value),
  510. \implode(', ', \array_map(array('static', 'valueToString'), $classes))
  511. ));
  512. }
  513. /**
  514. * @psalm-assert empty $value
  515. *
  516. * @param mixed $value
  517. * @param string $message
  518. */
  519. public static function isEmpty($value, $message = '')
  520. {
  521. if (!empty($value)) {
  522. static::reportInvalidArgument(\sprintf(
  523. $message ?: 'Expected an empty value. Got: %s',
  524. static::valueToString($value)
  525. ));
  526. }
  527. }
  528. /**
  529. * @psalm-assert !empty $value
  530. *
  531. * @param mixed $value
  532. * @param string $message
  533. */
  534. public static function notEmpty($value, $message = '')
  535. {
  536. if (empty($value)) {
  537. static::reportInvalidArgument(\sprintf(
  538. $message ?: 'Expected a non-empty value. Got: %s',
  539. static::valueToString($value)
  540. ));
  541. }
  542. }
  543. /**
  544. * @psalm-assert null $value
  545. *
  546. * @param mixed $value
  547. * @param string $message
  548. */
  549. public static function null($value, $message = '')
  550. {
  551. if (null !== $value) {
  552. static::reportInvalidArgument(\sprintf(
  553. $message ?: 'Expected null. Got: %s',
  554. static::valueToString($value)
  555. ));
  556. }
  557. }
  558. /**
  559. * @psalm-assert !null $value
  560. *
  561. * @param mixed $value
  562. * @param string $message
  563. */
  564. public static function notNull($value, $message = '')
  565. {
  566. if (null === $value) {
  567. static::reportInvalidArgument(
  568. $message ?: 'Expected a value other than null.'
  569. );
  570. }
  571. }
  572. /**
  573. * @psalm-assert true $value
  574. *
  575. * @param mixed $value
  576. * @param string $message
  577. */
  578. public static function true($value, $message = '')
  579. {
  580. if (true !== $value) {
  581. static::reportInvalidArgument(\sprintf(
  582. $message ?: 'Expected a value to be true. Got: %s',
  583. static::valueToString($value)
  584. ));
  585. }
  586. }
  587. /**
  588. * @psalm-assert false $value
  589. *
  590. * @param mixed $value
  591. * @param string $message
  592. */
  593. public static function false($value, $message = '')
  594. {
  595. if (false !== $value) {
  596. static::reportInvalidArgument(\sprintf(
  597. $message ?: 'Expected a value to be false. Got: %s',
  598. static::valueToString($value)
  599. ));
  600. }
  601. }
  602. /**
  603. * @param mixed $value
  604. * @param string $message
  605. */
  606. public static function ip($value, $message = '')
  607. {
  608. if (false === \filter_var($value, \FILTER_VALIDATE_IP)) {
  609. static::reportInvalidArgument(\sprintf(
  610. $message ?: 'Expected a value to be an IP. Got: %s',
  611. static::valueToString($value)
  612. ));
  613. }
  614. }
  615. /**
  616. * @param mixed $value
  617. * @param string $message
  618. */
  619. public static function ipv4($value, $message = '')
  620. {
  621. if (false === \filter_var($value, \FILTER_VALIDATE_IP, \FILTER_FLAG_IPV4)) {
  622. static::reportInvalidArgument(\sprintf(
  623. $message ?: 'Expected a value to be an IPv4. Got: %s',
  624. static::valueToString($value)
  625. ));
  626. }
  627. }
  628. /**
  629. * @param mixed $value
  630. * @param string $message
  631. */
  632. public static function ipv6($value, $message = '')
  633. {
  634. if (false === \filter_var($value, \FILTER_VALIDATE_IP, \FILTER_FLAG_IPV6)) {
  635. static::reportInvalidArgument(\sprintf(
  636. $message ?: 'Expected a value to be an IPv6. Got %s',
  637. static::valueToString($value)
  638. ));
  639. }
  640. }
  641. /**
  642. * @param mixed $value
  643. * @param string $message
  644. */
  645. public static function email($value, $message = '')
  646. {
  647. if (false === \filter_var($value, FILTER_VALIDATE_EMAIL)) {
  648. static::reportInvalidArgument(\sprintf(
  649. $message ?: 'Expected a value to be a valid e-mail address. Got %s',
  650. static::valueToString($value)
  651. ));
  652. }
  653. }
  654. /**
  655. * Does non strict comparisons on the items, so ['3', 3] will not pass the assertion.
  656. *
  657. * @param array $values
  658. * @param string $message
  659. */
  660. public static function uniqueValues(array $values, $message = '')
  661. {
  662. $allValues = \count($values);
  663. $uniqueValues = \count(\array_unique($values));
  664. if ($allValues !== $uniqueValues) {
  665. $difference = $allValues - $uniqueValues;
  666. static::reportInvalidArgument(\sprintf(
  667. $message ?: 'Expected an array of unique values, but %s of them %s duplicated',
  668. $difference,
  669. (1 === $difference ? 'is' : 'are')
  670. ));
  671. }
  672. }
  673. /**
  674. * @param mixed $value
  675. * @param mixed $expect
  676. * @param string $message
  677. */
  678. public static function eq($value, $expect, $message = '')
  679. {
  680. if ($expect != $value) {
  681. static::reportInvalidArgument(\sprintf(
  682. $message ?: 'Expected a value equal to %2$s. Got: %s',
  683. static::valueToString($value),
  684. static::valueToString($expect)
  685. ));
  686. }
  687. }
  688. /**
  689. * @param mixed $value
  690. * @param mixed $expect
  691. * @param string $message
  692. */
  693. public static function notEq($value, $expect, $message = '')
  694. {
  695. if ($expect == $value) {
  696. static::reportInvalidArgument(\sprintf(
  697. $message ?: 'Expected a different value than %s.',
  698. static::valueToString($expect)
  699. ));
  700. }
  701. }
  702. /**
  703. * @psalm-template ExpectedType
  704. * @psalm-param ExpectedType $expect
  705. * @psalm-assert =ExpectedType $value
  706. *
  707. * @param mixed $value
  708. * @param mixed $expect
  709. * @param string $message
  710. */
  711. public static function same($value, $expect, $message = '')
  712. {
  713. if ($expect !== $value) {
  714. static::reportInvalidArgument(\sprintf(
  715. $message ?: 'Expected a value identical to %2$s. Got: %s',
  716. static::valueToString($value),
  717. static::valueToString($expect)
  718. ));
  719. }
  720. }
  721. /**
  722. * @param mixed $value
  723. * @param mixed $expect
  724. * @param string $message
  725. */
  726. public static function notSame($value, $expect, $message = '')
  727. {
  728. if ($expect === $value) {
  729. static::reportInvalidArgument(\sprintf(
  730. $message ?: 'Expected a value not identical to %s.',
  731. static::valueToString($expect)
  732. ));
  733. }
  734. }
  735. /**
  736. * @param mixed $value
  737. * @param mixed $limit
  738. * @param string $message
  739. */
  740. public static function greaterThan($value, $limit, $message = '')
  741. {
  742. if ($value <= $limit) {
  743. static::reportInvalidArgument(\sprintf(
  744. $message ?: 'Expected a value greater than %2$s. Got: %s',
  745. static::valueToString($value),
  746. static::valueToString($limit)
  747. ));
  748. }
  749. }
  750. /**
  751. * @param mixed $value
  752. * @param mixed $limit
  753. * @param string $message
  754. */
  755. public static function greaterThanEq($value, $limit, $message = '')
  756. {
  757. if ($value < $limit) {
  758. static::reportInvalidArgument(\sprintf(
  759. $message ?: 'Expected a value greater than or equal to %2$s. Got: %s',
  760. static::valueToString($value),
  761. static::valueToString($limit)
  762. ));
  763. }
  764. }
  765. /**
  766. * @param mixed $value
  767. * @param mixed $limit
  768. * @param string $message
  769. */
  770. public static function lessThan($value, $limit, $message = '')
  771. {
  772. if ($value >= $limit) {
  773. static::reportInvalidArgument(\sprintf(
  774. $message ?: 'Expected a value less than %2$s. Got: %s',
  775. static::valueToString($value),
  776. static::valueToString($limit)
  777. ));
  778. }
  779. }
  780. /**
  781. * @param mixed $value
  782. * @param mixed $limit
  783. * @param string $message
  784. */
  785. public static function lessThanEq($value, $limit, $message = '')
  786. {
  787. if ($value > $limit) {
  788. static::reportInvalidArgument(\sprintf(
  789. $message ?: 'Expected a value less than or equal to %2$s. Got: %s',
  790. static::valueToString($value),
  791. static::valueToString($limit)
  792. ));
  793. }
  794. }
  795. /**
  796. * Inclusive range, so Assert::(3, 3, 5) passes.
  797. *
  798. * @param mixed $value
  799. * @param mixed min
  800. * @param mixed max
  801. * @param string $message
  802. */
  803. public static function range($value, $min, $max, $message = '')
  804. {
  805. if ($value < $min || $value > $max) {
  806. static::reportInvalidArgument(\sprintf(
  807. $message ?: 'Expected a value between %2$s and %3$s. Got: %s',
  808. static::valueToString($value),
  809. static::valueToString($min),
  810. static::valueToString($max)
  811. ));
  812. }
  813. }
  814. /**
  815. * Does strict comparison, so Assert::oneOf(3, ['3']) does not pass the assertion.
  816. *
  817. * @psalm-template ExpectedType
  818. * @psalm-param array<ExpectedType> $values
  819. * @psalm-assert ExpectedType $value
  820. *
  821. * @param mixed $value
  822. * @param array $values
  823. * @param string $message
  824. */
  825. public static function oneOf($value, array $values, $message = '')
  826. {
  827. if (!\in_array($value, $values, true)) {
  828. static::reportInvalidArgument(\sprintf(
  829. $message ?: 'Expected one of: %2$s. Got: %s',
  830. static::valueToString($value),
  831. \implode(', ', \array_map(array('static', 'valueToString'), $values))
  832. ));
  833. }
  834. }
  835. /**
  836. * @param mixed $value
  837. * @param string $subString
  838. * @param string $message
  839. */
  840. public static function contains($value, $subString, $message = '')
  841. {
  842. if (false === \strpos($value, $subString)) {
  843. static::reportInvalidArgument(\sprintf(
  844. $message ?: 'Expected a value to contain %2$s. Got: %s',
  845. static::valueToString($value),
  846. static::valueToString($subString)
  847. ));
  848. }
  849. }
  850. /**
  851. * @param mixed $value
  852. * @param string $subString
  853. * @param string $message
  854. */
  855. public static function notContains($value, $subString, $message = '')
  856. {
  857. if (false !== \strpos($value, $subString)) {
  858. static::reportInvalidArgument(\sprintf(
  859. $message ?: '%2$s was not expected to be contained in a value. Got: %s',
  860. static::valueToString($value),
  861. static::valueToString($subString)
  862. ));
  863. }
  864. }
  865. /**
  866. * @param mixed $value
  867. * @param string $message
  868. */
  869. public static function notWhitespaceOnly($value, $message = '')
  870. {
  871. if (\preg_match('/^\s*$/', $value)) {
  872. static::reportInvalidArgument(\sprintf(
  873. $message ?: 'Expected a non-whitespace string. Got: %s',
  874. static::valueToString($value)
  875. ));
  876. }
  877. }
  878. /**
  879. * @param mixed $value
  880. * @param string $prefix
  881. * @param string $message
  882. */
  883. public static function startsWith($value, $prefix, $message = '')
  884. {
  885. if (0 !== \strpos($value, $prefix)) {
  886. static::reportInvalidArgument(\sprintf(
  887. $message ?: 'Expected a value to start with %2$s. Got: %s',
  888. static::valueToString($value),
  889. static::valueToString($prefix)
  890. ));
  891. }
  892. }
  893. /**
  894. * @param mixed $value
  895. * @param string $message
  896. */
  897. public static function startsWithLetter($value, $message = '')
  898. {
  899. $valid = isset($value[0]);
  900. if ($valid) {
  901. $locale = \setlocale(LC_CTYPE, 0);
  902. \setlocale(LC_CTYPE, 'C');
  903. $valid = \ctype_alpha($value[0]);
  904. \setlocale(LC_CTYPE, $locale);
  905. }
  906. if (!$valid) {
  907. static::reportInvalidArgument(\sprintf(
  908. $message ?: 'Expected a value to start with a letter. Got: %s',
  909. static::valueToString($value)
  910. ));
  911. }
  912. }
  913. /**
  914. * @param mixed $value
  915. * @param string $suffix
  916. * @param string $message
  917. */
  918. public static function endsWith($value, $suffix, $message = '')
  919. {
  920. if ($suffix !== \substr($value, -\strlen($suffix))) {
  921. static::reportInvalidArgument(\sprintf(
  922. $message ?: 'Expected a value to end with %2$s. Got: %s',
  923. static::valueToString($value),
  924. static::valueToString($suffix)
  925. ));
  926. }
  927. }
  928. /**
  929. * @param mixed $value
  930. * @param mixed $pattern
  931. * @param string $message
  932. */
  933. public static function regex($value, $pattern, $message = '')
  934. {
  935. if (!\preg_match($pattern, $value)) {
  936. static::reportInvalidArgument(\sprintf(
  937. $message ?: 'The value %s does not match the expected pattern.',
  938. static::valueToString($value)
  939. ));
  940. }
  941. }
  942. /**
  943. * @param mixed $value
  944. * @param mixed $pattern
  945. * @param string $message
  946. */
  947. public static function notRegex($value, $pattern, $message = '')
  948. {
  949. if (\preg_match($pattern, $value, $matches, PREG_OFFSET_CAPTURE)) {
  950. static::reportInvalidArgument(\sprintf(
  951. $message ?: 'The value %s matches the pattern %s (at offset %d).',
  952. static::valueToString($value),
  953. static::valueToString($pattern),
  954. $matches[0][1]
  955. ));
  956. }
  957. }
  958. /**
  959. * @psalm-assert !numeric $value
  960. *
  961. * @param mixed $value
  962. * @param string $message
  963. */
  964. public static function unicodeLetters($value, $message = '')
  965. {
  966. static::string($value);
  967. if (!\preg_match('/^\p{L}+$/u', $value)) {
  968. static::reportInvalidArgument(\sprintf(
  969. $message ?: 'Expected a value to contain only Unicode letters. Got: %s',
  970. static::valueToString($value)
  971. ));
  972. }
  973. }
  974. /**
  975. * @param mixed $value
  976. * @param string $message
  977. */
  978. public static function alpha($value, $message = '')
  979. {
  980. $locale = \setlocale(LC_CTYPE, 0);
  981. \setlocale(LC_CTYPE, 'C');
  982. $valid = !\ctype_alpha($value);
  983. \setlocale(LC_CTYPE, $locale);
  984. if ($valid) {
  985. static::reportInvalidArgument(\sprintf(
  986. $message ?: 'Expected a value to contain only letters. Got: %s',
  987. static::valueToString($value)
  988. ));
  989. }
  990. }
  991. /**
  992. * @param mixed $value
  993. * @param string $message
  994. */
  995. public static function digits($value, $message = '')
  996. {
  997. $locale = \setlocale(LC_CTYPE, 0);
  998. \setlocale(LC_CTYPE, 'C');
  999. $valid = !\ctype_digit($value);
  1000. \setlocale(LC_CTYPE, $locale);
  1001. if ($valid) {
  1002. static::reportInvalidArgument(\sprintf(
  1003. $message ?: 'Expected a value to contain digits only. Got: %s',
  1004. static::valueToString($value)
  1005. ));
  1006. }
  1007. }
  1008. /**
  1009. * @param mixed $value
  1010. * @param string $message
  1011. */
  1012. public static function alnum($value, $message = '')
  1013. {
  1014. $locale = \setlocale(LC_CTYPE, 0);
  1015. \setlocale(LC_CTYPE, 'C');
  1016. $valid = !\ctype_alnum($value);
  1017. \setlocale(LC_CTYPE, $locale);
  1018. if ($valid) {
  1019. static::reportInvalidArgument(\sprintf(
  1020. $message ?: 'Expected a value to contain letters and digits only. Got: %s',
  1021. static::valueToString($value)
  1022. ));
  1023. }
  1024. }
  1025. /**
  1026. * @param mixed $value
  1027. * @param string $message
  1028. */
  1029. public static function lower($value, $message = '')
  1030. {
  1031. $locale = \setlocale(LC_CTYPE, 0);
  1032. \setlocale(LC_CTYPE, 'C');
  1033. $valid = !\ctype_lower($value);
  1034. \setlocale(LC_CTYPE, $locale);
  1035. if ($valid) {
  1036. static::reportInvalidArgument(\sprintf(
  1037. $message ?: 'Expected a value to contain lowercase characters only. Got: %s',
  1038. static::valueToString($value)
  1039. ));
  1040. }
  1041. }
  1042. /**
  1043. * @param mixed $value
  1044. * @param string $message
  1045. */
  1046. public static function upper($value, $message = '')
  1047. {
  1048. $locale = \setlocale(LC_CTYPE, 0);
  1049. \setlocale(LC_CTYPE, 'C');
  1050. $valid = !\ctype_upper($value);
  1051. \setlocale(LC_CTYPE, $locale);
  1052. if ($valid) {
  1053. static::reportInvalidArgument(\sprintf(
  1054. $message ?: 'Expected a value to contain uppercase characters only. Got: %s',
  1055. static::valueToString($value)
  1056. ));
  1057. }
  1058. }
  1059. /**
  1060. * @param mixed $value
  1061. * @param mixed $length
  1062. * @param string $message
  1063. */
  1064. public static function length($value, $length, $message = '')
  1065. {
  1066. if ($length !== static::strlen($value)) {
  1067. static::reportInvalidArgument(\sprintf(
  1068. $message ?: 'Expected a value to contain %2$s characters. Got: %s',
  1069. static::valueToString($value),
  1070. $length
  1071. ));
  1072. }
  1073. }
  1074. /**
  1075. * Inclusive min.
  1076. *
  1077. * @param mixed $value
  1078. * @param mixed $min
  1079. * @param string $message
  1080. */
  1081. public static function minLength($value, $min, $message = '')
  1082. {
  1083. if (static::strlen($value) < $min) {
  1084. static::reportInvalidArgument(\sprintf(
  1085. $message ?: 'Expected a value to contain at least %2$s characters. Got: %s',
  1086. static::valueToString($value),
  1087. $min
  1088. ));
  1089. }
  1090. }
  1091. /**
  1092. * Inclusive max.
  1093. *
  1094. * @param mixed $value
  1095. * @param mixed $max
  1096. * @param string $message
  1097. */
  1098. public static function maxLength($value, $max, $message = '')
  1099. {
  1100. if (static::strlen($value) > $max) {
  1101. static::reportInvalidArgument(\sprintf(
  1102. $message ?: 'Expected a value to contain at most %2$s characters. Got: %s',
  1103. static::valueToString($value),
  1104. $max
  1105. ));
  1106. }
  1107. }
  1108. /**
  1109. * Inclusive , so Assert::lengthBetween('asd', 3, 5); passes the assertion.
  1110. *
  1111. * @param mixed $value
  1112. * @param mixed $min
  1113. * @param mixed $max
  1114. * @param string $message
  1115. */
  1116. public static function lengthBetween($value, $min, $max, $message = '')
  1117. {
  1118. $length = static::strlen($value);
  1119. if ($length < $min || $length > $max) {
  1120. static::reportInvalidArgument(\sprintf(
  1121. $message ?: 'Expected a value to contain between %2$s and %3$s characters. Got: %s',
  1122. static::valueToString($value),
  1123. $min,
  1124. $max
  1125. ));
  1126. }
  1127. }
  1128. /**
  1129. * Will also pass if $value is a directory, use Assert::file() instead if you need to be sure it is a file.
  1130. *
  1131. * @param mixed $value
  1132. * @param string $message
  1133. */
  1134. public static function fileExists($value, $message = '')
  1135. {
  1136. static::string($value);
  1137. if (!\file_exists($value)) {
  1138. static::reportInvalidArgument(\sprintf(
  1139. $message ?: 'The file %s does not exist.',
  1140. static::valueToString($value)
  1141. ));
  1142. }
  1143. }
  1144. /**
  1145. * @param mixed $value
  1146. * @param string $message
  1147. */
  1148. public static function file($value, $message = '')
  1149. {
  1150. static::fileExists($value, $message);
  1151. if (!\is_file($value)) {
  1152. static::reportInvalidArgument(\sprintf(
  1153. $message ?: 'The path %s is not a file.',
  1154. static::valueToString($value)
  1155. ));
  1156. }
  1157. }
  1158. /**
  1159. * @param mixed $value
  1160. * @param string $message
  1161. */
  1162. public static function directory($value, $message = '')
  1163. {
  1164. static::fileExists($value, $message);
  1165. if (!\is_dir($value)) {
  1166. static::reportInvalidArgument(\sprintf(
  1167. $message ?: 'The path %s is no directory.',
  1168. static::valueToString($value)
  1169. ));
  1170. }
  1171. }
  1172. /**
  1173. * @param mixed $value
  1174. * @param string $message
  1175. */
  1176. public static function readable($value, $message = '')
  1177. {
  1178. if (!\is_readable($value)) {
  1179. static::reportInvalidArgument(\sprintf(
  1180. $message ?: 'The path %s is not readable.',
  1181. static::valueToString($value)
  1182. ));
  1183. }
  1184. }
  1185. /**
  1186. * @param mixed $value
  1187. * @param string $message
  1188. */
  1189. public static function writable($value, $message = '')
  1190. {
  1191. if (!\is_writable($value)) {
  1192. static::reportInvalidArgument(\sprintf(
  1193. $message ?: 'The path %s is not writable.',
  1194. static::valueToString($value)
  1195. ));
  1196. }
  1197. }
  1198. /**
  1199. * @psalm-assert class-string $value
  1200. *
  1201. * @param mixed $value
  1202. * @param string $message
  1203. */
  1204. public static function classExists($value, $message = '')
  1205. {
  1206. if (!\class_exists($value)) {
  1207. static::reportInvalidArgument(\sprintf(
  1208. $message ?: 'Expected an existing class name. Got: %s',
  1209. static::valueToString($value)
  1210. ));
  1211. }
  1212. }
  1213. /**
  1214. * @param mixed $value
  1215. * @param string|object $class
  1216. * @param string $message
  1217. */
  1218. public static function subclassOf($value, $class, $message = '')
  1219. {
  1220. if (!\is_subclass_of($value, $class)) {
  1221. static::reportInvalidArgument(\sprintf(
  1222. $message ?: 'Expected a sub-class of %2$s. Got: %s',
  1223. static::valueToString($value),
  1224. static::valueToString($class)
  1225. ));
  1226. }
  1227. }
  1228. /**
  1229. * @psalm-assert class-string $value
  1230. *
  1231. * @param mixed $value
  1232. * @param string $message
  1233. */
  1234. public static function interfaceExists($value, $message = '')
  1235. {
  1236. if (!\interface_exists($value)) {
  1237. static::reportInvalidArgument(\sprintf(
  1238. $message ?: 'Expected an existing interface name. got %s',
  1239. static::valueToString($value)
  1240. ));
  1241. }
  1242. }
  1243. /**
  1244. * @param mixed $value
  1245. * @param mixed $interface
  1246. * @param string $message
  1247. */
  1248. public static function implementsInterface($value, $interface, $message = '')
  1249. {
  1250. if (!\in_array($interface, \class_implements($value))) {
  1251. static::reportInvalidArgument(\sprintf(
  1252. $message ?: 'Expected an implementation of %2$s. Got: %s',
  1253. static::valueToString($value),
  1254. static::valueToString($interface)
  1255. ));
  1256. }
  1257. }
  1258. /**
  1259. * @param string|object $classOrObject
  1260. * @param mixed $property
  1261. * @param string $message
  1262. */
  1263. public static function propertyExists($classOrObject, $property, $message = '')
  1264. {
  1265. if (!\property_exists($classOrObject, $property)) {
  1266. static::reportInvalidArgument(\sprintf(
  1267. $message ?: 'Expected the property %s to exist.',
  1268. static::valueToString($property)
  1269. ));
  1270. }
  1271. }
  1272. /**
  1273. * @param string|object $classOrObject
  1274. * @param mixed $property
  1275. * @param string $message
  1276. */
  1277. public static function propertyNotExists($classOrObject, $property, $message = '')
  1278. {
  1279. if (\property_exists($classOrObject, $property)) {
  1280. static::reportInvalidArgument(\sprintf(
  1281. $message ?: 'Expected the property %s to not exist.',
  1282. static::valueToString($property)
  1283. ));
  1284. }
  1285. }
  1286. /**
  1287. * @param string|object $classOrObject
  1288. * @param mixed $method
  1289. * @param string $message
  1290. */
  1291. public static function methodExists($classOrObject, $method, $message = '')
  1292. {
  1293. if (!\method_exists($classOrObject, $method)) {
  1294. static::reportInvalidArgument(\sprintf(
  1295. $message ?: 'Expected the method %s to exist.',
  1296. static::valueToString($method)
  1297. ));
  1298. }
  1299. }
  1300. /**
  1301. * @param string|object $classOrObject
  1302. * @param mixed $method
  1303. * @param string $message
  1304. */
  1305. public static function methodNotExists($classOrObject, $method, $message = '')
  1306. {
  1307. if (\method_exists($classOrObject, $method)) {
  1308. static::reportInvalidArgument(\sprintf(
  1309. $message ?: 'Expected the method %s to not exist.',
  1310. static::valueToString($method)
  1311. ));
  1312. }
  1313. }
  1314. /**
  1315. * @param array $array
  1316. * @param string|int $key
  1317. * @param string $message
  1318. */
  1319. public static function keyExists($array, $key, $message = '')
  1320. {
  1321. if (!(isset($array[$key]) || \array_key_exists($key, $array))) {
  1322. static::reportInvalidArgument(\sprintf(
  1323. $message ?: 'Expected the key %s to exist.',
  1324. static::valueToString($key)
  1325. ));
  1326. }
  1327. }
  1328. /**
  1329. * @param array $array
  1330. * @param string|int $key
  1331. * @param string $message
  1332. */
  1333. public static function keyNotExists($array, $key, $message = '')
  1334. {
  1335. if (isset($array[$key]) || \array_key_exists($key, $array)) {
  1336. static::reportInvalidArgument(\sprintf(
  1337. $message ?: 'Expected the key %s to not exist.',
  1338. static::valueToString($key)
  1339. ));
  1340. }
  1341. }
  1342. /**
  1343. * Does not check if $array is countable, this can generate a warning on php versions after 7.2.
  1344. *
  1345. * @param mixed $array
  1346. * @param mixed $number
  1347. * @param string $message
  1348. */
  1349. public static function count($array, $number, $message = '')
  1350. {
  1351. static::eq(
  1352. \count($array),
  1353. $number,
  1354. $message ?: \sprintf('Expected an array to contain %d elements. Got: %d.', $number, \count($array))
  1355. );
  1356. }
  1357. /**
  1358. * Does not check if $array is countable, this can generate a warning on php versions after 7.2.
  1359. *
  1360. * @param mixed $array
  1361. * @param mixed $min
  1362. * @param string $message
  1363. */
  1364. public static function minCount($array, $min, $message = '')
  1365. {
  1366. if (\count($array) < $min) {
  1367. static::reportInvalidArgument(\sprintf(
  1368. $message ?: 'Expected an array to contain at least %2$d elements. Got: %d',
  1369. \count($array),
  1370. $min
  1371. ));
  1372. }
  1373. }
  1374. /**
  1375. * Does not check if $array is countable, this can generate a warning on php versions after 7.2.
  1376. *
  1377. * @param mixed $array
  1378. * @param mixed $max
  1379. * @param string $message
  1380. */
  1381. public static function maxCount($array, $max, $message = '')
  1382. {
  1383. if (\count($array) > $max) {
  1384. static::reportInvalidArgument(\sprintf(
  1385. $message ?: 'Expected an array to contain at most %2$d elements. Got: %d',
  1386. \count($array),
  1387. $max
  1388. ));
  1389. }
  1390. }
  1391. /**
  1392. * Does not check if $array is countable, this can generate a warning on php versions after 7.2.
  1393. *
  1394. * @param mixed $array
  1395. * @param mixed $min
  1396. * @param mixed $max
  1397. * @param string $message
  1398. */
  1399. public static function countBetween($array, $min, $max, $message = '')
  1400. {
  1401. $count = \count($array);
  1402. if ($count < $min || $count > $max) {
  1403. static::reportInvalidArgument(\sprintf(
  1404. $message ?: 'Expected an array to contain between %2$d and %3$d elements. Got: %d',
  1405. $count,
  1406. $min,
  1407. $max
  1408. ));
  1409. }
  1410. }
  1411. /**
  1412. * @param mixed $array
  1413. * @param string $message
  1414. */
  1415. public static function isList($array, $message = '')
  1416. {
  1417. if (!\is_array($array) || !$array || \array_keys($array) !== \range(0, \count($array) - 1)) {
  1418. static::reportInvalidArgument(
  1419. $message ?: 'Expected list - non-associative array.'
  1420. );
  1421. }
  1422. }
  1423. /**
  1424. * @param mixed $array
  1425. * @param string $message
  1426. */
  1427. public static function isMap($array, $message = '')
  1428. {
  1429. if (
  1430. !\is_array($array) ||
  1431. !$array ||
  1432. \array_keys($array) !== \array_filter(\array_keys($array), function ($key) {
  1433. return \is_string($key);
  1434. })
  1435. ) {
  1436. static::reportInvalidArgument(
  1437. $message ?: 'Expected map - associative array with string keys.'
  1438. );
  1439. }
  1440. }
  1441. /**
  1442. * @param mixed $value
  1443. * @param string $message
  1444. */
  1445. public static function uuid($value, $message = '')
  1446. {
  1447. $value = \str_replace(array('urn:', 'uuid:', '{', '}'), '', $value);
  1448. // The nil UUID is special form of UUID that is specified to have all
  1449. // 128 bits set to zero.
  1450. if ('00000000-0000-0000-0000-000000000000' === $value) {
  1451. return;
  1452. }
  1453. 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)) {
  1454. static::reportInvalidArgument(\sprintf(
  1455. $message ?: 'Value %s is not a valid UUID.',
  1456. static::valueToString($value)
  1457. ));
  1458. }
  1459. }
  1460. /**
  1461. * @param Closure $expression
  1462. * @param string|object $class
  1463. * @param string $message
  1464. */
  1465. public static function throws(Closure $expression, $class = 'Exception', $message = '')
  1466. {
  1467. static::string($class);
  1468. $actual = 'none';
  1469. try {
  1470. $expression();
  1471. } catch (Exception $e) {
  1472. $actual = \get_class($e);
  1473. if ($e instanceof $class) {
  1474. return;
  1475. }
  1476. } catch (Throwable $e) {
  1477. $actual = \get_class($e);
  1478. if ($e instanceof $class) {
  1479. return;
  1480. }
  1481. }
  1482. static::reportInvalidArgument($message ?: \sprintf(
  1483. 'Expected to throw "%s", got "%s"',
  1484. $class,
  1485. $actual
  1486. ));
  1487. }
  1488. public static function __callStatic($name, $arguments)
  1489. {
  1490. if ('nullOr' === \substr($name, 0, 6)) {
  1491. if (null !== $arguments[0]) {
  1492. $method = \lcfirst(\substr($name, 6));
  1493. \call_user_func_array(array('static', $method), $arguments);
  1494. }
  1495. return;
  1496. }
  1497. if ('all' === \substr($name, 0, 3)) {
  1498. static::isIterable($arguments[0]);
  1499. $method = \lcfirst(\substr($name, 3));
  1500. $args = $arguments;
  1501. foreach ($arguments[0] as $entry) {
  1502. $args[0] = $entry;
  1503. \call_user_func_array(array('static', $method), $args);
  1504. }
  1505. return;
  1506. }
  1507. throw new BadMethodCallException('No such method: '.$name);
  1508. }
  1509. /**
  1510. * @param mixed $value
  1511. *
  1512. * @return string
  1513. */
  1514. protected static function valueToString($value)
  1515. {
  1516. if (null === $value) {
  1517. return 'null';
  1518. }
  1519. if (true === $value) {
  1520. return 'true';
  1521. }
  1522. if (false === $value) {
  1523. return 'false';
  1524. }
  1525. if (\is_array($value)) {
  1526. return 'array';
  1527. }
  1528. if (\is_object($value)) {
  1529. if (\method_exists($value, '__toString')) {
  1530. return \get_class($value).': '.self::valueToString($value->__toString());
  1531. }
  1532. return \get_class($value);
  1533. }
  1534. if (\is_resource($value)) {
  1535. return 'resource';
  1536. }
  1537. if (\is_string($value)) {
  1538. return '"'.$value.'"';
  1539. }
  1540. return (string) $value;
  1541. }
  1542. /**
  1543. * @param mixed $value
  1544. *
  1545. * @return string
  1546. */
  1547. protected static function typeToString($value)
  1548. {
  1549. return \is_object($value) ? \get_class($value) : \gettype($value);
  1550. }
  1551. protected static function strlen($value)
  1552. {
  1553. if (!\function_exists('mb_detect_encoding')) {
  1554. return \strlen($value);
  1555. }
  1556. if (false === $encoding = \mb_detect_encoding($value)) {
  1557. return \strlen($value);
  1558. }
  1559. return \mb_strlen($value, $encoding);
  1560. }
  1561. /**
  1562. * @param string $message
  1563. */
  1564. protected static function reportInvalidArgument($message)
  1565. {
  1566. throw new InvalidArgumentException($message);
  1567. }
  1568. private function __construct()
  1569. {
  1570. }
  1571. }