Assert.php 61 KB

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