Assertion.php 118 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797
  1. <?php
  2. /**
  3. * Assert
  4. *
  5. * LICENSE
  6. *
  7. * This source file is subject to the MIT license that is bundled
  8. * with this package in the file LICENSE.txt.
  9. * If you did not receive a copy of the license and are unable to
  10. * obtain it through the world-wide-web, please send an email
  11. * to kontakt@beberlei.de so I can send you a copy immediately.
  12. */
  13. namespace Assert;
  14. use ArrayAccess;
  15. use BadMethodCallException;
  16. use Countable;
  17. use DateTime;
  18. use ReflectionClass;
  19. use ReflectionException;
  20. use ResourceBundle;
  21. use SimpleXMLElement;
  22. use Throwable;
  23. use Traversable;
  24. /**
  25. * Assert library.
  26. *
  27. * @author Benjamin Eberlei <kontakt@beberlei.de>
  28. *
  29. * @method static bool allAlnum(mixed[] $value, string|callable $message = null, string $propertyPath = null) Assert that value is alphanumeric for all values.
  30. * @method static bool allBase64(string[] $value, string|callable $message = null, string $propertyPath = null) Assert that a constant is defined for all values.
  31. * @method static bool allBetween(mixed[] $value, mixed $lowerLimit, mixed $upperLimit, string|callable $message = null, string $propertyPath = null) Assert that a value is greater or equal than a lower limit, and less than or equal to an upper limit for all values.
  32. * @method static bool allBetweenExclusive(mixed[] $value, mixed $lowerLimit, mixed $upperLimit, string|callable $message = null, string $propertyPath = null) Assert that a value is greater than a lower limit, and less than an upper limit for all values.
  33. * @method static bool allBetweenLength(mixed[] $value, int $minLength, int $maxLength, string|callable $message = null, string $propertyPath = null, string $encoding = 'utf8') Assert that string length is between min and max lengths for all values.
  34. * @method static bool allBoolean(mixed[] $value, string|callable $message = null, string $propertyPath = null) Assert that value is php boolean for all values.
  35. * @method static bool allChoice(mixed[] $value, array $choices, string|callable $message = null, string $propertyPath = null) Assert that value is in array of choices for all values.
  36. * @method static bool allChoicesNotEmpty(array[] $values, array $choices, string|callable $message = null, string $propertyPath = null) Determines if the values array has every choice as key and that this choice has content for all values.
  37. * @method static bool allClassExists(mixed[] $value, string|callable $message = null, string $propertyPath = null) Assert that the class exists for all values.
  38. * @method static bool allContains(mixed[] $string, string $needle, string|callable $message = null, string $propertyPath = null, string $encoding = 'utf8') Assert that string contains a sequence of chars for all values.
  39. * @method static bool allCount(array[]|Countable[]|ResourceBundle[]|SimpleXMLElement[] $countable, int $count, string|callable $message = null, string $propertyPath = null) Assert that the count of countable is equal to count for all values.
  40. * @method static bool allDate(string[] $value, string $format, string|callable $message = null, string $propertyPath = null) Assert that date is valid and corresponds to the given format for all values.
  41. * @method static bool allDefined(mixed[] $constant, string|callable $message = null, string $propertyPath = null) Assert that a constant is defined for all values.
  42. * @method static bool allDigit(mixed[] $value, string|callable $message = null, string $propertyPath = null) Validates if an integer or integerish is a digit for all values.
  43. * @method static bool allDirectory(string[] $value, string|callable $message = null, string $propertyPath = null) Assert that a directory exists for all values.
  44. * @method static bool allE164(string[] $value, string|callable $message = null, string $propertyPath = null) Assert that the given string is a valid E164 Phone Number for all values.
  45. * @method static bool allEmail(mixed[] $value, string|callable $message = null, string $propertyPath = null) Assert that value is an email address (using input_filter/FILTER_VALIDATE_EMAIL) for all values.
  46. * @method static bool allEndsWith(mixed[] $string, string $needle, string|callable $message = null, string $propertyPath = null, string $encoding = 'utf8') Assert that string ends with a sequence of chars for all values.
  47. * @method static bool allEq(mixed[] $value, mixed $value2, string|callable $message = null, string $propertyPath = null) Assert that two values are equal (using ==) for all values.
  48. * @method static bool allEqArraySubset(mixed[] $value, mixed $value2, string|callable $message = null, string $propertyPath = null) Assert that the array contains the subset for all values.
  49. * @method static bool allExtensionLoaded(mixed[] $value, string|callable $message = null, string $propertyPath = null) Assert that extension is loaded for all values.
  50. * @method static bool allExtensionVersion(string[] $extension, string $operator, mixed $version, string|callable $message = null, string $propertyPath = null) Assert that extension is loaded and a specific version is installed for all values.
  51. * @method static bool allFalse(mixed[] $value, string|callable $message = null, string $propertyPath = null) Assert that the value is boolean False for all values.
  52. * @method static bool allFile(string[] $value, string|callable $message = null, string $propertyPath = null) Assert that a file exists for all values.
  53. * @method static bool allFloat(mixed[] $value, string|callable $message = null, string $propertyPath = null) Assert that value is a php float for all values.
  54. * @method static bool allGreaterOrEqualThan(mixed[] $value, mixed $limit, string|callable $message = null, string $propertyPath = null) Determines if the value is greater or equal than given limit for all values.
  55. * @method static bool allGreaterThan(mixed[] $value, mixed $limit, string|callable $message = null, string $propertyPath = null) Determines if the value is greater than given limit for all values.
  56. * @method static bool allImplementsInterface(mixed[] $class, string $interfaceName, string|callable $message = null, string $propertyPath = null) Assert that the class implements the interface for all values.
  57. * @method static bool allInArray(mixed[] $value, array $choices, string|callable $message = null, string $propertyPath = null) Assert that value is in array of choices. This is an alias of Assertion::choice() for all values.
  58. * @method static bool allInteger(mixed[] $value, string|callable $message = null, string $propertyPath = null) Assert that value is a php integer for all values.
  59. * @method static bool allIntegerish(mixed[] $value, string|callable $message = null, string $propertyPath = null) Assert that value is a php integer'ish for all values.
  60. * @method static bool allInterfaceExists(mixed[] $value, string|callable $message = null, string $propertyPath = null) Assert that the interface exists for all values.
  61. * @method static bool allIp(string[] $value, int $flag = null, string|callable $message = null, string $propertyPath = null) Assert that value is an IPv4 or IPv6 address for all values.
  62. * @method static bool allIpv4(string[] $value, int $flag = null, string|callable $message = null, string $propertyPath = null) Assert that value is an IPv4 address for all values.
  63. * @method static bool allIpv6(string[] $value, int $flag = null, string|callable $message = null, string $propertyPath = null) Assert that value is an IPv6 address for all values.
  64. * @method static bool allIsArray(mixed[] $value, string|callable $message = null, string $propertyPath = null) Assert that value is an array for all values.
  65. * @method static bool allIsArrayAccessible(mixed[] $value, string|callable $message = null, string $propertyPath = null) Assert that value is an array or an array-accessible object for all values.
  66. * @method static bool allIsCallable(mixed[] $value, string|callable $message = null, string $propertyPath = null) Determines that the provided value is callable for all values.
  67. * @method static bool allIsCountable(array[]|Countable[]|ResourceBundle[]|SimpleXMLElement[] $value, string|callable $message = null, string $propertyPath = null) Assert that value is countable for all values.
  68. * @method static bool allIsInstanceOf(mixed[] $value, string $className, string|callable $message = null, string $propertyPath = null) Assert that value is instance of given class-name for all values.
  69. * @method static bool allIsJsonString(mixed[] $value, string|callable $message = null, string $propertyPath = null) Assert that the given string is a valid json string for all values.
  70. * @method static bool allIsObject(mixed[] $value, string|callable $message = null, string $propertyPath = null) Determines that the provided value is an object for all values.
  71. * @method static bool allIsResource(mixed[] $value, string|callable $message = null, string $propertyPath = null) Assert that value is a resource for all values.
  72. * @method static bool allIsTraversable(mixed[] $value, string|callable $message = null, string $propertyPath = null) Assert that value is an array or a traversable object for all values.
  73. * @method static bool allKeyExists(mixed[] $value, string|int $key, string|callable $message = null, string $propertyPath = null) Assert that key exists in an array for all values.
  74. * @method static bool allKeyIsset(mixed[] $value, string|int $key, string|callable $message = null, string $propertyPath = null) Assert that key exists in an array/array-accessible object using isset() for all values.
  75. * @method static bool allKeyNotExists(mixed[] $value, string|int $key, string|callable $message = null, string $propertyPath = null) Assert that key does not exist in an array for all values.
  76. * @method static bool allLength(mixed[] $value, int $length, string|callable $message = null, string $propertyPath = null, string $encoding = 'utf8') Assert that string has a given length for all values.
  77. * @method static bool allLessOrEqualThan(mixed[] $value, mixed $limit, string|callable $message = null, string $propertyPath = null) Determines if the value is less or equal than given limit for all values.
  78. * @method static bool allLessThan(mixed[] $value, mixed $limit, string|callable $message = null, string $propertyPath = null) Determines if the value is less than given limit for all values.
  79. * @method static bool allMax(mixed[] $value, mixed $maxValue, string|callable $message = null, string $propertyPath = null) Assert that a number is smaller as a given limit for all values.
  80. * @method static bool allMaxCount(array[]|Countable[]|ResourceBundle[]|SimpleXMLElement[] $countable, int $count, string|callable $message = null, string $propertyPath = null) Assert that the countable have at most $count elements for all values.
  81. * @method static bool allMaxLength(mixed[] $value, int $maxLength, string|callable $message = null, string $propertyPath = null, string $encoding = 'utf8') Assert that string value is not longer than $maxLength chars for all values.
  82. * @method static bool allMethodExists(string[] $value, mixed $object, string|callable $message = null, string $propertyPath = null) Determines that the named method is defined in the provided object for all values.
  83. * @method static bool allMin(mixed[] $value, mixed $minValue, string|callable $message = null, string $propertyPath = null) Assert that a value is at least as big as a given limit for all values.
  84. * @method static bool allMinCount(array[]|Countable[]|ResourceBundle[]|SimpleXMLElement[] $countable, int $count, string|callable $message = null, string $propertyPath = null) Assert that the countable have at least $count elements for all values.
  85. * @method static bool allMinLength(mixed[] $value, int $minLength, string|callable $message = null, string $propertyPath = null, string $encoding = 'utf8') Assert that a string is at least $minLength chars long for all values.
  86. * @method static bool allNoContent(mixed[] $value, string|callable $message = null, string $propertyPath = null) Assert that value is empty for all values.
  87. * @method static bool allNotBlank(mixed[] $value, string|callable $message = null, string $propertyPath = null) Assert that value is not blank for all values.
  88. * @method static bool allNotContains(mixed[] $string, string $needle, string|callable $message = null, string $propertyPath = null, string $encoding = 'utf8') Assert that string does not contains a sequence of chars for all values.
  89. * @method static bool allNotEmpty(mixed[] $value, string|callable $message = null, string $propertyPath = null) Assert that value is not empty for all values.
  90. * @method static bool allNotEmptyKey(mixed[] $value, string|int $key, string|callable $message = null, string $propertyPath = null) Assert that key exists in an array/array-accessible object and its value is not empty for all values.
  91. * @method static bool allNotEq(mixed[] $value1, mixed $value2, string|callable $message = null, string $propertyPath = null) Assert that two values are not equal (using ==) for all values.
  92. * @method static bool allNotInArray(mixed[] $value, array $choices, string|callable $message = null, string $propertyPath = null) Assert that value is not in array of choices for all values.
  93. * @method static bool allNotIsInstanceOf(mixed[] $value, string $className, string|callable $message = null, string $propertyPath = null) Assert that value is not instance of given class-name for all values.
  94. * @method static bool allNotNull(mixed[] $value, string|callable $message = null, string $propertyPath = null) Assert that value is not null for all values.
  95. * @method static bool allNotRegex(mixed[] $value, string $pattern, string|callable $message = null, string $propertyPath = null) Assert that value does not match a regex for all values.
  96. * @method static bool allNotSame(mixed[] $value1, mixed $value2, string|callable $message = null, string $propertyPath = null) Assert that two values are not the same (using ===) for all values.
  97. * @method static bool allNull(mixed[] $value, string|callable $message = null, string $propertyPath = null) Assert that value is null for all values.
  98. * @method static bool allNumeric(mixed[] $value, string|callable $message = null, string $propertyPath = null) Assert that value is numeric for all values.
  99. * @method static bool allObjectOrClass(mixed[] $value, string|callable $message = null, string $propertyPath = null) Assert that the value is an object, or a class that exists for all values.
  100. * @method static bool allPhpVersion(string[] $operator, mixed $version, string|callable $message = null, string $propertyPath = null) Assert on PHP version for all values.
  101. * @method static bool allPropertiesExist(mixed[] $value, array $properties, string|callable $message = null, string $propertyPath = null) Assert that the value is an object or class, and that the properties all exist for all values.
  102. * @method static bool allPropertyExists(mixed[] $value, string $property, string|callable $message = null, string $propertyPath = null) Assert that the value is an object or class, and that the property exists for all values.
  103. * @method static bool allRange(mixed[] $value, mixed $minValue, mixed $maxValue, string|callable $message = null, string $propertyPath = null) Assert that value is in range of numbers for all values.
  104. * @method static bool allReadable(string[] $value, string|callable $message = null, string $propertyPath = null) Assert that the value is something readable for all values.
  105. * @method static bool allRegex(mixed[] $value, string $pattern, string|callable $message = null, string $propertyPath = null) Assert that value matches a regex for all values.
  106. * @method static bool allSame(mixed[] $value, mixed $value2, string|callable $message = null, string $propertyPath = null) Assert that two values are the same (using ===) for all values.
  107. * @method static bool allSatisfy(mixed[] $value, callable $callback, string|callable $message = null, string $propertyPath = null) Assert that the provided value is valid according to a callback for all values.
  108. * @method static bool allScalar(mixed[] $value, string|callable $message = null, string $propertyPath = null) Assert that value is a PHP scalar for all values.
  109. * @method static bool allStartsWith(mixed[] $string, string $needle, string|callable $message = null, string $propertyPath = null, string $encoding = 'utf8') Assert that string starts with a sequence of chars for all values.
  110. * @method static bool allString(mixed[] $value, string|callable $message = null, string $propertyPath = null) Assert that value is a string for all values.
  111. * @method static bool allSubclassOf(mixed[] $value, string $className, string|callable $message = null, string $propertyPath = null) Assert that value is subclass of given class-name for all values.
  112. * @method static bool allTrue(mixed[] $value, string|callable $message = null, string $propertyPath = null) Assert that the value is boolean True for all values.
  113. * @method static bool allUniqueValues(array[] $values, string|callable $message = null, string $propertyPath = null) Assert that values in array are unique (using strict equality) for all values.
  114. * @method static bool allUrl(mixed[] $value, string|callable $message = null, string $propertyPath = null) Assert that value is an URL for all values.
  115. * @method static bool allUuid(string[] $value, string|callable $message = null, string $propertyPath = null) Assert that the given string is a valid UUID for all values.
  116. * @method static bool allVersion(string[] $version1, string $operator, string $version2, string|callable $message = null, string $propertyPath = null) Assert comparison of two versions for all values.
  117. * @method static bool allWriteable(string[] $value, string|callable $message = null, string $propertyPath = null) Assert that the value is something writeable for all values.
  118. * @method static bool nullOrAlnum(mixed|null $value, string|callable $message = null, string $propertyPath = null) Assert that value is alphanumeric or that the value is null.
  119. * @method static bool nullOrBase64(string|null $value, string|callable $message = null, string $propertyPath = null) Assert that a constant is defined or that the value is null.
  120. * @method static bool nullOrBetween(mixed|null $value, mixed $lowerLimit, mixed $upperLimit, string|callable $message = null, string $propertyPath = null) Assert that a value is greater or equal than a lower limit, and less than or equal to an upper limit or that the value is null.
  121. * @method static bool nullOrBetweenExclusive(mixed|null $value, mixed $lowerLimit, mixed $upperLimit, string|callable $message = null, string $propertyPath = null) Assert that a value is greater than a lower limit, and less than an upper limit or that the value is null.
  122. * @method static bool nullOrBetweenLength(mixed|null $value, int $minLength, int $maxLength, string|callable $message = null, string $propertyPath = null, string $encoding = 'utf8') Assert that string length is between min and max lengths or that the value is null.
  123. * @method static bool nullOrBoolean(mixed|null $value, string|callable $message = null, string $propertyPath = null) Assert that value is php boolean or that the value is null.
  124. * @method static bool nullOrChoice(mixed|null $value, array $choices, string|callable $message = null, string $propertyPath = null) Assert that value is in array of choices or that the value is null.
  125. * @method static bool nullOrChoicesNotEmpty(array|null $values, array $choices, string|callable $message = null, string $propertyPath = null) Determines if the values array has every choice as key and that this choice has content or that the value is null.
  126. * @method static bool nullOrClassExists(mixed|null $value, string|callable $message = null, string $propertyPath = null) Assert that the class exists or that the value is null.
  127. * @method static bool nullOrContains(mixed|null $string, string $needle, string|callable $message = null, string $propertyPath = null, string $encoding = 'utf8') Assert that string contains a sequence of chars or that the value is null.
  128. * @method static bool nullOrCount(array|Countable|ResourceBundle|SimpleXMLElement|null $countable, int $count, string|callable $message = null, string $propertyPath = null) Assert that the count of countable is equal to count or that the value is null.
  129. * @method static bool nullOrDate(string|null $value, string $format, string|callable $message = null, string $propertyPath = null) Assert that date is valid and corresponds to the given format or that the value is null.
  130. * @method static bool nullOrDefined(mixed|null $constant, string|callable $message = null, string $propertyPath = null) Assert that a constant is defined or that the value is null.
  131. * @method static bool nullOrDigit(mixed|null $value, string|callable $message = null, string $propertyPath = null) Validates if an integer or integerish is a digit or that the value is null.
  132. * @method static bool nullOrDirectory(string|null $value, string|callable $message = null, string $propertyPath = null) Assert that a directory exists or that the value is null.
  133. * @method static bool nullOrE164(string|null $value, string|callable $message = null, string $propertyPath = null) Assert that the given string is a valid E164 Phone Number or that the value is null.
  134. * @method static bool nullOrEmail(mixed|null $value, string|callable $message = null, string $propertyPath = null) Assert that value is an email address (using input_filter/FILTER_VALIDATE_EMAIL) or that the value is null.
  135. * @method static bool nullOrEndsWith(mixed|null $string, string $needle, string|callable $message = null, string $propertyPath = null, string $encoding = 'utf8') Assert that string ends with a sequence of chars or that the value is null.
  136. * @method static bool nullOrEq(mixed|null $value, mixed $value2, string|callable $message = null, string $propertyPath = null) Assert that two values are equal (using ==) or that the value is null.
  137. * @method static bool nullOrEqArraySubset(mixed|null $value, mixed $value2, string|callable $message = null, string $propertyPath = null) Assert that the array contains the subset or that the value is null.
  138. * @method static bool nullOrExtensionLoaded(mixed|null $value, string|callable $message = null, string $propertyPath = null) Assert that extension is loaded or that the value is null.
  139. * @method static bool nullOrExtensionVersion(string|null $extension, string $operator, mixed $version, string|callable $message = null, string $propertyPath = null) Assert that extension is loaded and a specific version is installed or that the value is null.
  140. * @method static bool nullOrFalse(mixed|null $value, string|callable $message = null, string $propertyPath = null) Assert that the value is boolean False or that the value is null.
  141. * @method static bool nullOrFile(string|null $value, string|callable $message = null, string $propertyPath = null) Assert that a file exists or that the value is null.
  142. * @method static bool nullOrFloat(mixed|null $value, string|callable $message = null, string $propertyPath = null) Assert that value is a php float or that the value is null.
  143. * @method static bool nullOrGreaterOrEqualThan(mixed|null $value, mixed $limit, string|callable $message = null, string $propertyPath = null) Determines if the value is greater or equal than given limit or that the value is null.
  144. * @method static bool nullOrGreaterThan(mixed|null $value, mixed $limit, string|callable $message = null, string $propertyPath = null) Determines if the value is greater than given limit or that the value is null.
  145. * @method static bool nullOrImplementsInterface(mixed|null $class, string $interfaceName, string|callable $message = null, string $propertyPath = null) Assert that the class implements the interface or that the value is null.
  146. * @method static bool nullOrInArray(mixed|null $value, array $choices, string|callable $message = null, string $propertyPath = null) Assert that value is in array of choices. This is an alias of Assertion::choice() or that the value is null.
  147. * @method static bool nullOrInteger(mixed|null $value, string|callable $message = null, string $propertyPath = null) Assert that value is a php integer or that the value is null.
  148. * @method static bool nullOrIntegerish(mixed|null $value, string|callable $message = null, string $propertyPath = null) Assert that value is a php integer'ish or that the value is null.
  149. * @method static bool nullOrInterfaceExists(mixed|null $value, string|callable $message = null, string $propertyPath = null) Assert that the interface exists or that the value is null.
  150. * @method static bool nullOrIp(string|null $value, int $flag = null, string|callable $message = null, string $propertyPath = null) Assert that value is an IPv4 or IPv6 address or that the value is null.
  151. * @method static bool nullOrIpv4(string|null $value, int $flag = null, string|callable $message = null, string $propertyPath = null) Assert that value is an IPv4 address or that the value is null.
  152. * @method static bool nullOrIpv6(string|null $value, int $flag = null, string|callable $message = null, string $propertyPath = null) Assert that value is an IPv6 address or that the value is null.
  153. * @method static bool nullOrIsArray(mixed|null $value, string|callable $message = null, string $propertyPath = null) Assert that value is an array or that the value is null.
  154. * @method static bool nullOrIsArrayAccessible(mixed|null $value, string|callable $message = null, string $propertyPath = null) Assert that value is an array or an array-accessible object or that the value is null.
  155. * @method static bool nullOrIsCallable(mixed|null $value, string|callable $message = null, string $propertyPath = null) Determines that the provided value is callable or that the value is null.
  156. * @method static bool nullOrIsCountable(array|Countable|ResourceBundle|SimpleXMLElement|null $value, string|callable $message = null, string $propertyPath = null) Assert that value is countable or that the value is null.
  157. * @method static bool nullOrIsInstanceOf(mixed|null $value, string $className, string|callable $message = null, string $propertyPath = null) Assert that value is instance of given class-name or that the value is null.
  158. * @method static bool nullOrIsJsonString(mixed|null $value, string|callable $message = null, string $propertyPath = null) Assert that the given string is a valid json string or that the value is null.
  159. * @method static bool nullOrIsObject(mixed|null $value, string|callable $message = null, string $propertyPath = null) Determines that the provided value is an object or that the value is null.
  160. * @method static bool nullOrIsResource(mixed|null $value, string|callable $message = null, string $propertyPath = null) Assert that value is a resource or that the value is null.
  161. * @method static bool nullOrIsTraversable(mixed|null $value, string|callable $message = null, string $propertyPath = null) Assert that value is an array or a traversable object or that the value is null.
  162. * @method static bool nullOrKeyExists(mixed|null $value, string|int $key, string|callable $message = null, string $propertyPath = null) Assert that key exists in an array or that the value is null.
  163. * @method static bool nullOrKeyIsset(mixed|null $value, string|int $key, string|callable $message = null, string $propertyPath = null) Assert that key exists in an array/array-accessible object using isset() or that the value is null.
  164. * @method static bool nullOrKeyNotExists(mixed|null $value, string|int $key, string|callable $message = null, string $propertyPath = null) Assert that key does not exist in an array or that the value is null.
  165. * @method static bool nullOrLength(mixed|null $value, int $length, string|callable $message = null, string $propertyPath = null, string $encoding = 'utf8') Assert that string has a given length or that the value is null.
  166. * @method static bool nullOrLessOrEqualThan(mixed|null $value, mixed $limit, string|callable $message = null, string $propertyPath = null) Determines if the value is less or equal than given limit or that the value is null.
  167. * @method static bool nullOrLessThan(mixed|null $value, mixed $limit, string|callable $message = null, string $propertyPath = null) Determines if the value is less than given limit or that the value is null.
  168. * @method static bool nullOrMax(mixed|null $value, mixed $maxValue, string|callable $message = null, string $propertyPath = null) Assert that a number is smaller as a given limit or that the value is null.
  169. * @method static bool nullOrMaxCount(array|Countable|ResourceBundle|SimpleXMLElement|null $countable, int $count, string|callable $message = null, string $propertyPath = null) Assert that the countable have at most $count elements or that the value is null.
  170. * @method static bool nullOrMaxLength(mixed|null $value, int $maxLength, string|callable $message = null, string $propertyPath = null, string $encoding = 'utf8') Assert that string value is not longer than $maxLength chars or that the value is null.
  171. * @method static bool nullOrMethodExists(string|null $value, mixed $object, string|callable $message = null, string $propertyPath = null) Determines that the named method is defined in the provided object or that the value is null.
  172. * @method static bool nullOrMin(mixed|null $value, mixed $minValue, string|callable $message = null, string $propertyPath = null) Assert that a value is at least as big as a given limit or that the value is null.
  173. * @method static bool nullOrMinCount(array|Countable|ResourceBundle|SimpleXMLElement|null $countable, int $count, string|callable $message = null, string $propertyPath = null) Assert that the countable have at least $count elements or that the value is null.
  174. * @method static bool nullOrMinLength(mixed|null $value, int $minLength, string|callable $message = null, string $propertyPath = null, string $encoding = 'utf8') Assert that a string is at least $minLength chars long or that the value is null.
  175. * @method static bool nullOrNoContent(mixed|null $value, string|callable $message = null, string $propertyPath = null) Assert that value is empty or that the value is null.
  176. * @method static bool nullOrNotBlank(mixed|null $value, string|callable $message = null, string $propertyPath = null) Assert that value is not blank or that the value is null.
  177. * @method static bool nullOrNotContains(mixed|null $string, string $needle, string|callable $message = null, string $propertyPath = null, string $encoding = 'utf8') Assert that string does not contains a sequence of chars or that the value is null.
  178. * @method static bool nullOrNotEmpty(mixed|null $value, string|callable $message = null, string $propertyPath = null) Assert that value is not empty or that the value is null.
  179. * @method static bool nullOrNotEmptyKey(mixed|null $value, string|int $key, string|callable $message = null, string $propertyPath = null) Assert that key exists in an array/array-accessible object and its value is not empty or that the value is null.
  180. * @method static bool nullOrNotEq(mixed|null $value1, mixed $value2, string|callable $message = null, string $propertyPath = null) Assert that two values are not equal (using ==) or that the value is null.
  181. * @method static bool nullOrNotInArray(mixed|null $value, array $choices, string|callable $message = null, string $propertyPath = null) Assert that value is not in array of choices or that the value is null.
  182. * @method static bool nullOrNotIsInstanceOf(mixed|null $value, string $className, string|callable $message = null, string $propertyPath = null) Assert that value is not instance of given class-name or that the value is null.
  183. * @method static bool nullOrNotNull(mixed|null $value, string|callable $message = null, string $propertyPath = null) Assert that value is not null or that the value is null.
  184. * @method static bool nullOrNotRegex(mixed|null $value, string $pattern, string|callable $message = null, string $propertyPath = null) Assert that value does not match a regex or that the value is null.
  185. * @method static bool nullOrNotSame(mixed|null $value1, mixed $value2, string|callable $message = null, string $propertyPath = null) Assert that two values are not the same (using ===) or that the value is null.
  186. * @method static bool nullOrNull(mixed|null $value, string|callable $message = null, string $propertyPath = null) Assert that value is null or that the value is null.
  187. * @method static bool nullOrNumeric(mixed|null $value, string|callable $message = null, string $propertyPath = null) Assert that value is numeric or that the value is null.
  188. * @method static bool nullOrObjectOrClass(mixed|null $value, string|callable $message = null, string $propertyPath = null) Assert that the value is an object, or a class that exists or that the value is null.
  189. * @method static bool nullOrPhpVersion(string|null $operator, mixed $version, string|callable $message = null, string $propertyPath = null) Assert on PHP version or that the value is null.
  190. * @method static bool nullOrPropertiesExist(mixed|null $value, array $properties, string|callable $message = null, string $propertyPath = null) Assert that the value is an object or class, and that the properties all exist or that the value is null.
  191. * @method static bool nullOrPropertyExists(mixed|null $value, string $property, string|callable $message = null, string $propertyPath = null) Assert that the value is an object or class, and that the property exists or that the value is null.
  192. * @method static bool nullOrRange(mixed|null $value, mixed $minValue, mixed $maxValue, string|callable $message = null, string $propertyPath = null) Assert that value is in range of numbers or that the value is null.
  193. * @method static bool nullOrReadable(string|null $value, string|callable $message = null, string $propertyPath = null) Assert that the value is something readable or that the value is null.
  194. * @method static bool nullOrRegex(mixed|null $value, string $pattern, string|callable $message = null, string $propertyPath = null) Assert that value matches a regex or that the value is null.
  195. * @method static bool nullOrSame(mixed|null $value, mixed $value2, string|callable $message = null, string $propertyPath = null) Assert that two values are the same (using ===) or that the value is null.
  196. * @method static bool nullOrSatisfy(mixed|null $value, callable $callback, string|callable $message = null, string $propertyPath = null) Assert that the provided value is valid according to a callback or that the value is null.
  197. * @method static bool nullOrScalar(mixed|null $value, string|callable $message = null, string $propertyPath = null) Assert that value is a PHP scalar or that the value is null.
  198. * @method static bool nullOrStartsWith(mixed|null $string, string $needle, string|callable $message = null, string $propertyPath = null, string $encoding = 'utf8') Assert that string starts with a sequence of chars or that the value is null.
  199. * @method static bool nullOrString(mixed|null $value, string|callable $message = null, string $propertyPath = null) Assert that value is a string or that the value is null.
  200. * @method static bool nullOrSubclassOf(mixed|null $value, string $className, string|callable $message = null, string $propertyPath = null) Assert that value is subclass of given class-name or that the value is null.
  201. * @method static bool nullOrTrue(mixed|null $value, string|callable $message = null, string $propertyPath = null) Assert that the value is boolean True or that the value is null.
  202. * @method static bool nullOrUniqueValues(array|null $values, string|callable $message = null, string $propertyPath = null) Assert that values in array are unique (using strict equality) or that the value is null.
  203. * @method static bool nullOrUrl(mixed|null $value, string|callable $message = null, string $propertyPath = null) Assert that value is an URL or that the value is null.
  204. * @method static bool nullOrUuid(string|null $value, string|callable $message = null, string $propertyPath = null) Assert that the given string is a valid UUID or that the value is null.
  205. * @method static bool nullOrVersion(string|null $version1, string $operator, string $version2, string|callable $message = null, string $propertyPath = null) Assert comparison of two versions or that the value is null.
  206. * @method static bool nullOrWriteable(string|null $value, string|callable $message = null, string $propertyPath = null) Assert that the value is something writeable or that the value is null.
  207. */
  208. class Assertion
  209. {
  210. const INVALID_FLOAT = 9;
  211. const INVALID_INTEGER = 10;
  212. const INVALID_DIGIT = 11;
  213. const INVALID_INTEGERISH = 12;
  214. const INVALID_BOOLEAN = 13;
  215. const VALUE_EMPTY = 14;
  216. const VALUE_NULL = 15;
  217. const VALUE_NOT_NULL = 25;
  218. const INVALID_STRING = 16;
  219. const INVALID_REGEX = 17;
  220. const INVALID_MIN_LENGTH = 18;
  221. const INVALID_MAX_LENGTH = 19;
  222. const INVALID_STRING_START = 20;
  223. const INVALID_STRING_CONTAINS = 21;
  224. const INVALID_CHOICE = 22;
  225. const INVALID_NUMERIC = 23;
  226. const INVALID_ARRAY = 24;
  227. const INVALID_KEY_EXISTS = 26;
  228. const INVALID_NOT_BLANK = 27;
  229. const INVALID_INSTANCE_OF = 28;
  230. const INVALID_SUBCLASS_OF = 29;
  231. const INVALID_RANGE = 30;
  232. const INVALID_ALNUM = 31;
  233. const INVALID_TRUE = 32;
  234. const INVALID_EQ = 33;
  235. const INVALID_SAME = 34;
  236. const INVALID_MIN = 35;
  237. const INVALID_MAX = 36;
  238. const INVALID_LENGTH = 37;
  239. const INVALID_FALSE = 38;
  240. const INVALID_STRING_END = 39;
  241. const INVALID_UUID = 40;
  242. const INVALID_COUNT = 41;
  243. const INVALID_NOT_EQ = 42;
  244. const INVALID_NOT_SAME = 43;
  245. const INVALID_TRAVERSABLE = 44;
  246. const INVALID_ARRAY_ACCESSIBLE = 45;
  247. const INVALID_KEY_ISSET = 46;
  248. const INVALID_VALUE_IN_ARRAY = 47;
  249. const INVALID_E164 = 48;
  250. const INVALID_BASE64 = 49;
  251. const INVALID_NOT_REGEX = 50;
  252. const INVALID_DIRECTORY = 101;
  253. const INVALID_FILE = 102;
  254. const INVALID_READABLE = 103;
  255. const INVALID_WRITEABLE = 104;
  256. const INVALID_CLASS = 105;
  257. const INVALID_INTERFACE = 106;
  258. const INVALID_FILE_NOT_EXISTS = 107;
  259. const INVALID_EMAIL = 201;
  260. const INTERFACE_NOT_IMPLEMENTED = 202;
  261. const INVALID_URL = 203;
  262. const INVALID_NOT_INSTANCE_OF = 204;
  263. const VALUE_NOT_EMPTY = 205;
  264. const INVALID_JSON_STRING = 206;
  265. const INVALID_OBJECT = 207;
  266. const INVALID_METHOD = 208;
  267. const INVALID_SCALAR = 209;
  268. const INVALID_LESS = 210;
  269. const INVALID_LESS_OR_EQUAL = 211;
  270. const INVALID_GREATER = 212;
  271. const INVALID_GREATER_OR_EQUAL = 213;
  272. const INVALID_DATE = 214;
  273. const INVALID_CALLABLE = 215;
  274. const INVALID_KEY_NOT_EXISTS = 216;
  275. const INVALID_SATISFY = 217;
  276. const INVALID_IP = 218;
  277. const INVALID_BETWEEN = 219;
  278. const INVALID_BETWEEN_EXCLUSIVE = 220;
  279. const INVALID_EXTENSION = 222;
  280. const INVALID_CONSTANT = 221;
  281. const INVALID_VERSION = 223;
  282. const INVALID_PROPERTY = 224;
  283. const INVALID_RESOURCE = 225;
  284. const INVALID_COUNTABLE = 226;
  285. const INVALID_MIN_COUNT = 227;
  286. const INVALID_MAX_COUNT = 228;
  287. const INVALID_STRING_NOT_CONTAINS = 229;
  288. const INVALID_UNIQUE_VALUES = 230;
  289. /**
  290. * Exception to throw when an assertion failed.
  291. *
  292. * @var string
  293. */
  294. protected static $exceptionClass = InvalidArgumentException::class;
  295. /**
  296. * Assert that two values are equal (using ==).
  297. *
  298. * @param mixed $value
  299. * @param mixed $value2
  300. * @param string|callable|null $message
  301. *
  302. * @throws AssertionFailedException
  303. */
  304. public static function eq($value, $value2, $message = null, ?string $propertyPath = null): bool
  305. {
  306. if ($value != $value2) {
  307. $message = \sprintf(
  308. static::generateMessage($message ?: 'Value "%s" does not equal expected value "%s".'),
  309. static::stringify($value),
  310. static::stringify($value2)
  311. );
  312. throw static::createException($value, $message, static::INVALID_EQ, $propertyPath, ['expected' => $value2]);
  313. }
  314. return true;
  315. }
  316. /**
  317. * Assert that the array contains the subset.
  318. *
  319. * @param mixed $value
  320. * @param mixed $value2
  321. * @param string|callable|null $message
  322. *
  323. * @throws AssertionFailedException
  324. */
  325. public static function eqArraySubset($value, $value2, $message = null, ?string $propertyPath = null): bool
  326. {
  327. static::isArray($value, $message, $propertyPath);
  328. static::isArray($value2, $message, $propertyPath);
  329. $patched = \array_replace_recursive($value, $value2);
  330. static::eq($patched, $value, $message, $propertyPath);
  331. return true;
  332. }
  333. /**
  334. * Assert that two values are the same (using ===).
  335. *
  336. * @param mixed $value
  337. * @param mixed $value2
  338. * @param string|callable|null $message
  339. * @param string|null $propertyPath
  340. *
  341. * @psalm-template ExpectedType
  342. * @psalm-param ExpectedType $value2
  343. * @psalm-assert =ExpectedType $value
  344. *
  345. * @return bool
  346. *
  347. * @throws AssertionFailedException
  348. */
  349. public static function same($value, $value2, $message = null, ?string $propertyPath = null): bool
  350. {
  351. if ($value !== $value2) {
  352. $message = \sprintf(
  353. static::generateMessage($message ?: 'Value "%s" is not the same as expected value "%s".'),
  354. static::stringify($value),
  355. static::stringify($value2)
  356. );
  357. throw static::createException($value, $message, static::INVALID_SAME, $propertyPath, ['expected' => $value2]);
  358. }
  359. return true;
  360. }
  361. /**
  362. * Assert that two values are not equal (using ==).
  363. *
  364. * @param mixed $value1
  365. * @param mixed $value2
  366. * @param string|callable|null $message
  367. *
  368. * @throws AssertionFailedException
  369. */
  370. public static function notEq($value1, $value2, $message = null, ?string $propertyPath = null): bool
  371. {
  372. if ($value1 == $value2) {
  373. $message = \sprintf(
  374. static::generateMessage($message ?: 'Value "%s" was not expected to be equal to value "%s".'),
  375. static::stringify($value1),
  376. static::stringify($value2)
  377. );
  378. throw static::createException($value1, $message, static::INVALID_NOT_EQ, $propertyPath, ['expected' => $value2]);
  379. }
  380. return true;
  381. }
  382. /**
  383. * Assert that two values are not the same (using ===).
  384. *
  385. * @param mixed $value1
  386. * @param mixed $value2
  387. * @param string|callable|null $message
  388. * @param string|null $propertyPath
  389. *
  390. * @psalm-template ExpectedType
  391. * @psalm-param ExpectedType $value2
  392. * @psalm-assert !=ExpectedType $value1
  393. *
  394. * @return bool
  395. *
  396. * @throws AssertionFailedException
  397. */
  398. public static function notSame($value1, $value2, $message = null, ?string $propertyPath = null): bool
  399. {
  400. if ($value1 === $value2) {
  401. $message = \sprintf(
  402. static::generateMessage($message ?: 'Value "%s" was not expected to be the same as value "%s".'),
  403. static::stringify($value1),
  404. static::stringify($value2)
  405. );
  406. throw static::createException($value1, $message, static::INVALID_NOT_SAME, $propertyPath, ['expected' => $value2]);
  407. }
  408. return true;
  409. }
  410. /**
  411. * Assert that value is not in array of choices.
  412. *
  413. * @param mixed $value
  414. * @param string|callable|null $message
  415. *
  416. * @throws AssertionFailedException
  417. */
  418. public static function notInArray($value, array $choices, $message = null, ?string $propertyPath = null): bool
  419. {
  420. if (true === \in_array($value, $choices)) {
  421. $message = \sprintf(
  422. static::generateMessage($message ?: 'Value "%s" was not expected to be an element of the values: %s'),
  423. static::stringify($value),
  424. static::stringify($choices)
  425. );
  426. throw static::createException($value, $message, static::INVALID_VALUE_IN_ARRAY, $propertyPath, ['choices' => $choices]);
  427. }
  428. return true;
  429. }
  430. /**
  431. * Assert that value is a php integer.
  432. *
  433. * @param mixed $value
  434. * @param string|callable|null $message
  435. * @param string|null $propertyPath
  436. *
  437. * @psalm-assert int $value
  438. *
  439. * @return bool
  440. *
  441. * @throws AssertionFailedException
  442. */
  443. public static function integer($value, $message = null, ?string $propertyPath = null): bool
  444. {
  445. if (!\is_int($value)) {
  446. $message = \sprintf(
  447. static::generateMessage($message ?: 'Value "%s" is not an integer.'),
  448. static::stringify($value)
  449. );
  450. throw static::createException($value, $message, static::INVALID_INTEGER, $propertyPath);
  451. }
  452. return true;
  453. }
  454. /**
  455. * Assert that value is a php float.
  456. *
  457. * @param mixed $value
  458. * @param string|callable|null $message
  459. * @param string|null $propertyPath
  460. *
  461. * @psalm-assert float $value
  462. *
  463. * @return bool
  464. *
  465. * @throws AssertionFailedException
  466. */
  467. public static function float($value, $message = null, ?string $propertyPath = null): bool
  468. {
  469. if (!\is_float($value)) {
  470. $message = \sprintf(
  471. static::generateMessage($message ?: 'Value "%s" is not a float.'),
  472. static::stringify($value)
  473. );
  474. throw static::createException($value, $message, static::INVALID_FLOAT, $propertyPath);
  475. }
  476. return true;
  477. }
  478. /**
  479. * Validates if an integer or integerish is a digit.
  480. *
  481. * @param mixed $value
  482. * @param string|callable|null $message
  483. * @param string|null $propertyPath
  484. *
  485. * @psalm-assert =numeric $value
  486. *
  487. * @return bool
  488. *
  489. * @throws AssertionFailedException
  490. */
  491. public static function digit($value, $message = null, ?string $propertyPath = null): bool
  492. {
  493. if (!\ctype_digit((string)$value)) {
  494. $message = \sprintf(
  495. static::generateMessage($message ?: 'Value "%s" is not a digit.'),
  496. static::stringify($value)
  497. );
  498. throw static::createException($value, $message, static::INVALID_DIGIT, $propertyPath);
  499. }
  500. return true;
  501. }
  502. /**
  503. * Assert that value is a php integer'ish.
  504. *
  505. * @param mixed $value
  506. * @param string|callable|null $message
  507. *
  508. * @throws AssertionFailedException
  509. */
  510. public static function integerish($value, $message = null, ?string $propertyPath = null): bool
  511. {
  512. if (
  513. \is_resource($value) ||
  514. \is_object($value) ||
  515. \is_bool($value) ||
  516. \is_null($value) ||
  517. \is_array($value) ||
  518. (\is_string($value) && '' == $value) ||
  519. (
  520. \strval(\intval($value)) !== \strval($value) &&
  521. \strval(\intval($value)) !== \strval(\ltrim($value, '0')) &&
  522. '' !== \strval(\intval($value)) &&
  523. '' !== \strval(\ltrim($value, '0'))
  524. )
  525. ) {
  526. $message = \sprintf(
  527. static::generateMessage($message ?: 'Value "%s" is not an integer or a number castable to integer.'),
  528. static::stringify($value)
  529. );
  530. throw static::createException($value, $message, static::INVALID_INTEGERISH, $propertyPath);
  531. }
  532. return true;
  533. }
  534. /**
  535. * Assert that value is php boolean.
  536. *
  537. * @param mixed $value
  538. * @param string|callable|null $message
  539. * @param string|null $propertyPath
  540. *
  541. * @psalm-assert bool $value
  542. *
  543. * @return bool
  544. *
  545. * @throws AssertionFailedException
  546. */
  547. public static function boolean($value, $message = null, ?string $propertyPath = null): bool
  548. {
  549. if (!\is_bool($value)) {
  550. $message = \sprintf(
  551. static::generateMessage($message ?: 'Value "%s" is not a boolean.'),
  552. static::stringify($value)
  553. );
  554. throw static::createException($value, $message, static::INVALID_BOOLEAN, $propertyPath);
  555. }
  556. return true;
  557. }
  558. /**
  559. * Assert that value is a PHP scalar.
  560. *
  561. * @param mixed $value
  562. * @param string|callable|null $message
  563. * @param string|null $propertyPath
  564. *
  565. * @psalm-assert scalar $value
  566. *
  567. * @return bool
  568. *
  569. * @throws AssertionFailedException
  570. */
  571. public static function scalar($value, $message = null, ?string $propertyPath = null): bool
  572. {
  573. if (!\is_scalar($value)) {
  574. $message = \sprintf(
  575. static::generateMessage($message ?: 'Value "%s" is not a scalar.'),
  576. static::stringify($value)
  577. );
  578. throw static::createException($value, $message, static::INVALID_SCALAR, $propertyPath);
  579. }
  580. return true;
  581. }
  582. /**
  583. * Assert that value is not empty.
  584. *
  585. * @param mixed $value
  586. * @param string|callable|null $message
  587. * @param string|null $propertyPath
  588. *
  589. * @psalm-assert !empty $value
  590. *
  591. * @return bool
  592. *
  593. * @throws AssertionFailedException
  594. */
  595. public static function notEmpty($value, $message = null, ?string $propertyPath = null): bool
  596. {
  597. if (empty($value)) {
  598. $message = \sprintf(
  599. static::generateMessage($message ?: 'Value "%s" is empty, but non empty value was expected.'),
  600. static::stringify($value)
  601. );
  602. throw static::createException($value, $message, static::VALUE_EMPTY, $propertyPath);
  603. }
  604. return true;
  605. }
  606. /**
  607. * Assert that value is empty.
  608. *
  609. * @param mixed $value
  610. * @param string|callable|null $message
  611. * @param string|null $propertyPath
  612. *
  613. * @psalm-assert empty $value
  614. *
  615. * @return bool
  616. *
  617. * @throws AssertionFailedException
  618. */
  619. public static function noContent($value, $message = null, ?string $propertyPath = null): bool
  620. {
  621. if (!empty($value)) {
  622. $message = \sprintf(
  623. static::generateMessage($message ?: 'Value "%s" is not empty, but empty value was expected.'),
  624. static::stringify($value)
  625. );
  626. throw static::createException($value, $message, static::VALUE_NOT_EMPTY, $propertyPath);
  627. }
  628. return true;
  629. }
  630. /**
  631. * Assert that value is null.
  632. *
  633. * @param mixed $value
  634. * @param string|callable|null $message
  635. * @param string|null $propertyPath
  636. *
  637. * @psalm-assert null $value
  638. *
  639. * @return bool
  640. */
  641. public static function null($value, $message = null, ?string $propertyPath = null): bool
  642. {
  643. if (null !== $value) {
  644. $message = \sprintf(
  645. static::generateMessage($message ?: 'Value "%s" is not null, but null value was expected.'),
  646. static::stringify($value)
  647. );
  648. throw static::createException($value, $message, static::VALUE_NOT_NULL, $propertyPath);
  649. }
  650. return true;
  651. }
  652. /**
  653. * Assert that value is not null.
  654. *
  655. * @param mixed $value
  656. * @param string|callable|null $message
  657. * @param string|null $propertyPath
  658. *
  659. * @psalm-assert !null $value
  660. *
  661. * @return bool
  662. *
  663. * @throws AssertionFailedException
  664. */
  665. public static function notNull($value, $message = null, ?string $propertyPath = null): bool
  666. {
  667. if (null === $value) {
  668. $message = \sprintf(
  669. static::generateMessage($message ?: 'Value "%s" is null, but non null value was expected.'),
  670. static::stringify($value)
  671. );
  672. throw static::createException($value, $message, static::VALUE_NULL, $propertyPath);
  673. }
  674. return true;
  675. }
  676. /**
  677. * Assert that value is a string.
  678. *
  679. * @param mixed $value
  680. * @param string|callable|null $message
  681. * @param string|null $propertyPath
  682. *
  683. * @psalm-assert string $value
  684. *
  685. * @return bool
  686. *
  687. * @throws AssertionFailedException
  688. */
  689. public static function string($value, $message = null, ?string $propertyPath = null)
  690. {
  691. if (!\is_string($value)) {
  692. $message = \sprintf(
  693. static::generateMessage($message ?: 'Value "%s" expected to be string, type %s given.'),
  694. static::stringify($value),
  695. \gettype($value)
  696. );
  697. throw static::createException($value, $message, static::INVALID_STRING, $propertyPath);
  698. }
  699. return true;
  700. }
  701. /**
  702. * Assert that value matches a regex.
  703. *
  704. * @param mixed $value
  705. * @param string $pattern
  706. * @param string|callable|null $message
  707. * @param string|null $propertyPath
  708. *
  709. * @psalm-assert =string $value
  710. *
  711. * @return bool
  712. *
  713. * @throws AssertionFailedException
  714. */
  715. public static function regex($value, $pattern, $message = null, ?string $propertyPath = null): bool
  716. {
  717. static::string($value, $message, $propertyPath);
  718. if (!\preg_match($pattern, $value)) {
  719. $message = \sprintf(
  720. static::generateMessage($message ?: 'Value "%s" does not match expression.'),
  721. static::stringify($value)
  722. );
  723. throw static::createException($value, $message, static::INVALID_REGEX, $propertyPath, ['pattern' => $pattern]);
  724. }
  725. return true;
  726. }
  727. /**
  728. * Assert that value does not match a regex.
  729. *
  730. * @param mixed $value
  731. * @param string $pattern
  732. * @param string|callable|null $message
  733. * @param string|null $propertyPath
  734. *
  735. * @psalm-assert !=string $value
  736. *
  737. * @throws AssertionFailedException
  738. */
  739. public static function notRegex($value, $pattern, $message = null, ?string $propertyPath = null): bool
  740. {
  741. static::string($value, $message, $propertyPath);
  742. if (\preg_match($pattern, $value)) {
  743. $message = \sprintf(
  744. static::generateMessage($message ?: 'Value "%s" matches expression.'),
  745. static::stringify($value)
  746. );
  747. throw static::createException($value, $message, static::INVALID_NOT_REGEX, $propertyPath, ['pattern' => $pattern]);
  748. }
  749. return true;
  750. }
  751. /**
  752. * Assert that string has a given length.
  753. *
  754. * @param mixed $value
  755. * @param int $length
  756. * @param string|callable|null $message
  757. * @param string|null $propertyPath
  758. * @param string $encoding
  759. *
  760. * @psalm-assert =string $value
  761. *
  762. * @return bool
  763. *
  764. * @throws AssertionFailedException
  765. */
  766. public static function length($value, $length, $message = null, ?string $propertyPath = null, $encoding = 'utf8'): bool
  767. {
  768. static::string($value, $message, $propertyPath);
  769. if (\mb_strlen($value, $encoding) !== $length) {
  770. $message = \sprintf(
  771. static::generateMessage($message ?: 'Value "%s" has to be %d exactly characters long, but length is %d.'),
  772. static::stringify($value),
  773. $length,
  774. \mb_strlen($value, $encoding)
  775. );
  776. throw static::createException($value, $message, static::INVALID_LENGTH, $propertyPath, ['length' => $length, 'encoding' => $encoding]);
  777. }
  778. return true;
  779. }
  780. /**
  781. * Assert that a string is at least $minLength chars long.
  782. *
  783. * @param mixed $value
  784. * @param int $minLength
  785. * @param string|callable|null $message
  786. * @param string|null $propertyPath
  787. * @param string $encoding
  788. *
  789. * @psalm-assert =string $value
  790. *
  791. * @return bool
  792. *
  793. * @throws AssertionFailedException
  794. */
  795. public static function minLength($value, $minLength, $message = null, ?string $propertyPath = null, $encoding = 'utf8'): bool
  796. {
  797. static::string($value, $message, $propertyPath);
  798. if (\mb_strlen($value, $encoding) < $minLength) {
  799. $message = \sprintf(
  800. static::generateMessage($message ?: 'Value "%s" is too short, it should have at least %d characters, but only has %d characters.'),
  801. static::stringify($value),
  802. $minLength,
  803. \mb_strlen($value, $encoding)
  804. );
  805. throw static::createException($value, $message, static::INVALID_MIN_LENGTH, $propertyPath, ['min_length' => $minLength, 'encoding' => $encoding]);
  806. }
  807. return true;
  808. }
  809. /**
  810. * Assert that string value is not longer than $maxLength chars.
  811. *
  812. * @param mixed $value
  813. * @param int $maxLength
  814. * @param string|callable|null $message
  815. * @param string|null $propertyPath
  816. * @param string $encoding
  817. *
  818. * @psalm-assert =string $value
  819. *
  820. * @return bool
  821. *
  822. * @throws AssertionFailedException
  823. */
  824. public static function maxLength($value, $maxLength, $message = null, ?string $propertyPath = null, $encoding = 'utf8'): bool
  825. {
  826. static::string($value, $message, $propertyPath);
  827. if (\mb_strlen($value, $encoding) > $maxLength) {
  828. $message = \sprintf(
  829. static::generateMessage($message ?: 'Value "%s" is too long, it should have no more than %d characters, but has %d characters.'),
  830. static::stringify($value),
  831. $maxLength,
  832. \mb_strlen($value, $encoding)
  833. );
  834. throw static::createException($value, $message, static::INVALID_MAX_LENGTH, $propertyPath, ['max_length' => $maxLength, 'encoding' => $encoding]);
  835. }
  836. return true;
  837. }
  838. /**
  839. * Assert that string length is between min and max lengths.
  840. *
  841. * @param mixed $value
  842. * @param int $minLength
  843. * @param int $maxLength
  844. * @param string|callable|null $message
  845. * @param string|null $propertyPath
  846. * @param string $encoding
  847. *
  848. * @psalm-assert =string $value
  849. *
  850. * @return bool
  851. *
  852. * @throws AssertionFailedException
  853. */
  854. public static function betweenLength($value, $minLength, $maxLength, $message = null, ?string $propertyPath = null, $encoding = 'utf8'): bool
  855. {
  856. static::string($value, $message, $propertyPath);
  857. static::minLength($value, $minLength, $message, $propertyPath, $encoding);
  858. static::maxLength($value, $maxLength, $message, $propertyPath, $encoding);
  859. return true;
  860. }
  861. /**
  862. * Assert that string starts with a sequence of chars.
  863. *
  864. * @param mixed $string
  865. * @param string $needle
  866. * @param string|callable|null $message
  867. * @param string|null $propertyPath
  868. * @param string $encoding
  869. *
  870. * @psalm-assert =string $string
  871. *
  872. * @return bool
  873. *
  874. * @throws AssertionFailedException
  875. */
  876. public static function startsWith($string, $needle, $message = null, ?string $propertyPath = null, $encoding = 'utf8'): bool
  877. {
  878. static::string($string, $message, $propertyPath);
  879. if (0 !== \mb_strpos($string, $needle, 0, $encoding)) {
  880. $message = \sprintf(
  881. static::generateMessage($message ?: 'Value "%s" does not start with "%s".'),
  882. static::stringify($string),
  883. static::stringify($needle)
  884. );
  885. throw static::createException($string, $message, static::INVALID_STRING_START, $propertyPath, ['needle' => $needle, 'encoding' => $encoding]);
  886. }
  887. return true;
  888. }
  889. /**
  890. * Assert that string ends with a sequence of chars.
  891. *
  892. * @param mixed $string
  893. * @param string $needle
  894. * @param string|callable|null $message
  895. * @param string|null $propertyPath
  896. * @param string $encoding
  897. *
  898. * @psalm-assert =string $string
  899. *
  900. * @return bool
  901. *
  902. * @throws AssertionFailedException
  903. */
  904. public static function endsWith($string, $needle, $message = null, ?string $propertyPath = null, $encoding = 'utf8'): bool
  905. {
  906. static::string($string, $message, $propertyPath);
  907. $stringPosition = \mb_strlen($string, $encoding) - \mb_strlen($needle, $encoding);
  908. if (\mb_strripos($string, $needle, 0, $encoding) !== $stringPosition) {
  909. $message = \sprintf(
  910. static::generateMessage($message ?: 'Value "%s" does not end with "%s".'),
  911. static::stringify($string),
  912. static::stringify($needle)
  913. );
  914. throw static::createException($string, $message, static::INVALID_STRING_END, $propertyPath, ['needle' => $needle, 'encoding' => $encoding]);
  915. }
  916. return true;
  917. }
  918. /**
  919. * Assert that string contains a sequence of chars.
  920. *
  921. * @param mixed $string
  922. * @param string $needle
  923. * @param string|callable|null $message
  924. * @param string|null $propertyPath
  925. * @param string $encoding
  926. *
  927. * @psalm-assert =string $string
  928. *
  929. * @return bool
  930. *
  931. * @throws AssertionFailedException
  932. */
  933. public static function contains($string, $needle, $message = null, ?string $propertyPath = null, $encoding = 'utf8'): bool
  934. {
  935. static::string($string, $message, $propertyPath);
  936. if (false === \mb_strpos($string, $needle, 0, $encoding)) {
  937. $message = \sprintf(
  938. static::generateMessage($message ?: 'Value "%s" does not contain "%s".'),
  939. static::stringify($string),
  940. static::stringify($needle)
  941. );
  942. throw static::createException($string, $message, static::INVALID_STRING_CONTAINS, $propertyPath, ['needle' => $needle, 'encoding' => $encoding]);
  943. }
  944. return true;
  945. }
  946. /**
  947. * Assert that string does not contains a sequence of chars.
  948. *
  949. * @param mixed $string
  950. * @param string $needle
  951. * @param string|callable|null $message
  952. * @param string|null $propertyPath
  953. * @param string $encoding
  954. *
  955. * @psalm-assert =string $string
  956. *
  957. * @return bool
  958. *
  959. * @throws AssertionFailedException
  960. */
  961. public static function notContains($string, $needle, $message = null, ?string $propertyPath = null, $encoding = 'utf8'): bool
  962. {
  963. static::string($string, $message, $propertyPath);
  964. if (false !== \mb_strpos($string, $needle, 0, $encoding)) {
  965. $message = \sprintf(
  966. static::generateMessage($message ?: 'Value "%s" contains "%s".'),
  967. static::stringify($string),
  968. static::stringify($needle)
  969. );
  970. throw static::createException($string, $message, static::INVALID_STRING_NOT_CONTAINS, $propertyPath, ['needle' => $needle, 'encoding' => $encoding]);
  971. }
  972. return true;
  973. }
  974. /**
  975. * Assert that value is in array of choices.
  976. *
  977. * @param mixed $value
  978. * @param string|callable|null $message
  979. *
  980. * @throws AssertionFailedException
  981. */
  982. public static function choice($value, array $choices, $message = null, ?string $propertyPath = null): bool
  983. {
  984. if (!\in_array($value, $choices, true)) {
  985. $message = \sprintf(
  986. static::generateMessage($message ?: 'Value "%s" is not an element of the valid values: %s'),
  987. static::stringify($value),
  988. \implode(', ', \array_map([\get_called_class(), 'stringify'], $choices))
  989. );
  990. throw static::createException($value, $message, static::INVALID_CHOICE, $propertyPath, ['choices' => $choices]);
  991. }
  992. return true;
  993. }
  994. /**
  995. * Assert that value is in array of choices.
  996. *
  997. * This is an alias of {@see choice()}.
  998. *
  999. * @param mixed $value
  1000. * @param string|callable|null $message
  1001. *
  1002. * @throws AssertionFailedException
  1003. */
  1004. public static function inArray($value, array $choices, $message = null, ?string $propertyPath = null): bool
  1005. {
  1006. return static::choice($value, $choices, $message, $propertyPath);
  1007. }
  1008. /**
  1009. * Assert that value is numeric.
  1010. *
  1011. * @param mixed $value
  1012. * @param string|callable|null $message
  1013. * @param string|null $propertyPath
  1014. *
  1015. * @psalm-assert numeric $value
  1016. *
  1017. * @return bool
  1018. *
  1019. * @throws AssertionFailedException
  1020. */
  1021. public static function numeric($value, $message = null, ?string $propertyPath = null): bool
  1022. {
  1023. if (!\is_numeric($value)) {
  1024. $message = \sprintf(
  1025. static::generateMessage($message ?: 'Value "%s" is not numeric.'),
  1026. static::stringify($value)
  1027. );
  1028. throw static::createException($value, $message, static::INVALID_NUMERIC, $propertyPath);
  1029. }
  1030. return true;
  1031. }
  1032. /**
  1033. * Assert that value is a resource.
  1034. *
  1035. * @param mixed $value
  1036. * @param string|callable|null $message
  1037. * @param string|null $propertyPath
  1038. *
  1039. * @psalm-assert resource $value
  1040. *
  1041. * @return bool
  1042. */
  1043. public static function isResource($value, $message = null, ?string $propertyPath = null): bool
  1044. {
  1045. if (!\is_resource($value)) {
  1046. $message = \sprintf(
  1047. static::generateMessage($message ?: 'Value "%s" is not a resource.'),
  1048. static::stringify($value)
  1049. );
  1050. throw static::createException($value, $message, static::INVALID_RESOURCE, $propertyPath);
  1051. }
  1052. return true;
  1053. }
  1054. /**
  1055. * Assert that value is an array.
  1056. *
  1057. * @param mixed $value
  1058. * @param string|callable|null $message
  1059. * @param string|null $propertyPath
  1060. *
  1061. * @psalm-assert array $value
  1062. *
  1063. * @return bool
  1064. *
  1065. * @throws AssertionFailedException
  1066. */
  1067. public static function isArray($value, $message = null, ?string $propertyPath = null): bool
  1068. {
  1069. if (!\is_array($value)) {
  1070. $message = \sprintf(
  1071. static::generateMessage($message ?: 'Value "%s" is not an array.'),
  1072. static::stringify($value)
  1073. );
  1074. throw static::createException($value, $message, static::INVALID_ARRAY, $propertyPath);
  1075. }
  1076. return true;
  1077. }
  1078. /**
  1079. * Assert that value is an array or a traversable object.
  1080. *
  1081. * @param mixed $value
  1082. * @param string|callable|null $message
  1083. * @param string|null $propertyPath
  1084. *
  1085. * @psalm-assert iterable $value
  1086. *
  1087. * @return bool
  1088. *
  1089. * @throws AssertionFailedException
  1090. */
  1091. public static function isTraversable($value, $message = null, ?string $propertyPath = null): bool
  1092. {
  1093. if (!\is_array($value) && !$value instanceof Traversable) {
  1094. $message = \sprintf(
  1095. static::generateMessage($message ?: 'Value "%s" is not an array and does not implement Traversable.'),
  1096. static::stringify($value)
  1097. );
  1098. throw static::createException($value, $message, static::INVALID_TRAVERSABLE, $propertyPath);
  1099. }
  1100. return true;
  1101. }
  1102. /**
  1103. * Assert that value is an array or an array-accessible object.
  1104. *
  1105. * @param mixed $value
  1106. * @param string|callable|null $message
  1107. *
  1108. * @throws AssertionFailedException
  1109. */
  1110. public static function isArrayAccessible($value, $message = null, ?string $propertyPath = null): bool
  1111. {
  1112. if (!\is_array($value) && !$value instanceof ArrayAccess) {
  1113. $message = \sprintf(
  1114. static::generateMessage($message ?: 'Value "%s" is not an array and does not implement ArrayAccess.'),
  1115. static::stringify($value)
  1116. );
  1117. throw static::createException($value, $message, static::INVALID_ARRAY_ACCESSIBLE, $propertyPath);
  1118. }
  1119. return true;
  1120. }
  1121. /**
  1122. * Assert that value is countable.
  1123. *
  1124. * @param mixed $value
  1125. * @param string|callable|null $message
  1126. * @param string|null $propertyPath
  1127. *
  1128. * @psalm-assert countable $value
  1129. *
  1130. * @return bool
  1131. *
  1132. * @throws AssertionFailedException
  1133. */
  1134. public static function isCountable($value, $message = null, ?string $propertyPath = null): bool
  1135. {
  1136. if (\function_exists('is_countable')) {
  1137. $assert = \is_countable($value);
  1138. } else {
  1139. $assert = \is_array($value) || $value instanceof Countable || $value instanceof ResourceBundle || $value instanceof SimpleXMLElement;
  1140. }
  1141. if (!$assert) {
  1142. $message = \sprintf(
  1143. static::generateMessage($message ?: 'Value "%s" is not an array and does not implement Countable.'),
  1144. static::stringify($value)
  1145. );
  1146. throw static::createException($value, $message, static::INVALID_COUNTABLE, $propertyPath);
  1147. }
  1148. return true;
  1149. }
  1150. /**
  1151. * Assert that key exists in an array.
  1152. *
  1153. * @param mixed $value
  1154. * @param string|int $key
  1155. * @param string|callable|null $message
  1156. *
  1157. * @throws AssertionFailedException
  1158. */
  1159. public static function keyExists($value, $key, $message = null, ?string $propertyPath = null): bool
  1160. {
  1161. static::isArray($value, $message, $propertyPath);
  1162. if (!\array_key_exists($key, $value)) {
  1163. $message = \sprintf(
  1164. static::generateMessage($message ?: 'Array does not contain an element with key "%s"'),
  1165. static::stringify($key)
  1166. );
  1167. throw static::createException($value, $message, static::INVALID_KEY_EXISTS, $propertyPath, ['key' => $key]);
  1168. }
  1169. return true;
  1170. }
  1171. /**
  1172. * Assert that key does not exist in an array.
  1173. *
  1174. * @param mixed $value
  1175. * @param string|int $key
  1176. * @param string|callable|null $message
  1177. *
  1178. * @throws AssertionFailedException
  1179. */
  1180. public static function keyNotExists($value, $key, $message = null, ?string $propertyPath = null): bool
  1181. {
  1182. static::isArray($value, $message, $propertyPath);
  1183. if (\array_key_exists($key, $value)) {
  1184. $message = \sprintf(
  1185. static::generateMessage($message ?: 'Array contains an element with key "%s"'),
  1186. static::stringify($key)
  1187. );
  1188. throw static::createException($value, $message, static::INVALID_KEY_NOT_EXISTS, $propertyPath, ['key' => $key]);
  1189. }
  1190. return true;
  1191. }
  1192. /**
  1193. * Assert that values in array are unique (using strict equality).
  1194. *
  1195. * @param mixed[] $values
  1196. * @param string|callable|null $message
  1197. *
  1198. * @throws AssertionFailedException
  1199. */
  1200. public static function uniqueValues(array $values, $message = null, ?string $propertyPath = null): bool
  1201. {
  1202. foreach ($values as $key => $value) {
  1203. if (\array_search($value, $values, true) !== $key) {
  1204. $message = \sprintf(
  1205. static::generateMessage($message ?: 'Value "%s" occurs more than once in array'),
  1206. static::stringify($value)
  1207. );
  1208. throw static::createException($value, $message, static::INVALID_UNIQUE_VALUES, $propertyPath, ['value' => $value]);
  1209. }
  1210. }
  1211. return true;
  1212. }
  1213. /**
  1214. * Assert that key exists in an array/array-accessible object using isset().
  1215. *
  1216. * @param mixed $value
  1217. * @param string|int $key
  1218. * @param string|callable|null $message
  1219. *
  1220. * @throws AssertionFailedException
  1221. */
  1222. public static function keyIsset($value, $key, $message = null, ?string $propertyPath = null): bool
  1223. {
  1224. static::isArrayAccessible($value, $message, $propertyPath);
  1225. if (!isset($value[$key])) {
  1226. $message = \sprintf(
  1227. static::generateMessage($message ?: 'The element with key "%s" was not found'),
  1228. static::stringify($key)
  1229. );
  1230. throw static::createException($value, $message, static::INVALID_KEY_ISSET, $propertyPath, ['key' => $key]);
  1231. }
  1232. return true;
  1233. }
  1234. /**
  1235. * Assert that key exists in an array/array-accessible object and its value is not empty.
  1236. *
  1237. * @param mixed $value
  1238. * @param string|int $key
  1239. * @param string|callable|null $message
  1240. *
  1241. * @throws AssertionFailedException
  1242. */
  1243. public static function notEmptyKey($value, $key, $message = null, ?string $propertyPath = null): bool
  1244. {
  1245. static::keyIsset($value, $key, $message, $propertyPath);
  1246. static::notEmpty($value[$key], $message, $propertyPath);
  1247. return true;
  1248. }
  1249. /**
  1250. * Assert that value is not blank.
  1251. *
  1252. * @param mixed $value
  1253. * @param string|callable|null $message
  1254. *
  1255. * @throws AssertionFailedException
  1256. */
  1257. public static function notBlank($value, $message = null, ?string $propertyPath = null): bool
  1258. {
  1259. if (false === $value || (empty($value) && '0' != $value) || (\is_string($value) && '' === \trim($value))) {
  1260. $message = \sprintf(
  1261. static::generateMessage($message ?: 'Value "%s" is blank, but was expected to contain a value.'),
  1262. static::stringify($value)
  1263. );
  1264. throw static::createException($value, $message, static::INVALID_NOT_BLANK, $propertyPath);
  1265. }
  1266. return true;
  1267. }
  1268. /**
  1269. * Assert that value is instance of given class-name.
  1270. *
  1271. * @param mixed $value
  1272. * @param string $className
  1273. * @param string|callable|null $message
  1274. * @param string|null $propertyPath
  1275. *
  1276. * @psalm-template ExpectedType of object
  1277. * @psalm-param class-string<ExpectedType> $className
  1278. * @psalm-assert ExpectedType $value
  1279. *
  1280. * @return bool
  1281. *
  1282. * @throws AssertionFailedException
  1283. */
  1284. public static function isInstanceOf($value, $className, $message = null, ?string $propertyPath = null): bool
  1285. {
  1286. if (!($value instanceof $className)) {
  1287. $message = \sprintf(
  1288. static::generateMessage($message ?: 'Class "%s" was expected to be instanceof of "%s" but is not.'),
  1289. static::stringify($value),
  1290. $className
  1291. );
  1292. throw static::createException($value, $message, static::INVALID_INSTANCE_OF, $propertyPath, ['class' => $className]);
  1293. }
  1294. return true;
  1295. }
  1296. /**
  1297. * Assert that value is not instance of given class-name.
  1298. *
  1299. * @param mixed $value
  1300. * @param string $className
  1301. * @param string|callable|null $message
  1302. * @param string|null $propertyPath
  1303. *
  1304. * @psalm-template ExpectedType of object
  1305. * @psalm-param class-string<ExpectedType> $className
  1306. * @psalm-assert !ExpectedType $value
  1307. *
  1308. * @return bool
  1309. *
  1310. * @throws AssertionFailedException
  1311. */
  1312. public static function notIsInstanceOf($value, $className, $message = null, ?string $propertyPath = null): bool
  1313. {
  1314. if ($value instanceof $className) {
  1315. $message = \sprintf(
  1316. static::generateMessage($message ?: 'Class "%s" was not expected to be instanceof of "%s".'),
  1317. static::stringify($value),
  1318. $className
  1319. );
  1320. throw static::createException($value, $message, static::INVALID_NOT_INSTANCE_OF, $propertyPath, ['class' => $className]);
  1321. }
  1322. return true;
  1323. }
  1324. /**
  1325. * Assert that value is subclass of given class-name.
  1326. *
  1327. * @param mixed $value
  1328. * @param string $className
  1329. * @param string|callable|null $message
  1330. *
  1331. * @throws AssertionFailedException
  1332. */
  1333. public static function subclassOf($value, $className, $message = null, ?string $propertyPath = null): bool
  1334. {
  1335. if (!\is_subclass_of($value, $className)) {
  1336. $message = \sprintf(
  1337. static::generateMessage($message ?: 'Class "%s" was expected to be subclass of "%s".'),
  1338. static::stringify($value),
  1339. $className
  1340. );
  1341. throw static::createException($value, $message, static::INVALID_SUBCLASS_OF, $propertyPath, ['class' => $className]);
  1342. }
  1343. return true;
  1344. }
  1345. /**
  1346. * Assert that value is in range of numbers.
  1347. *
  1348. * @param mixed $value
  1349. * @param mixed $minValue
  1350. * @param mixed $maxValue
  1351. * @param string|callable|null $message
  1352. * @param string|null $propertyPath
  1353. *
  1354. * @psalm-assert =numeric $value
  1355. *
  1356. * @return bool
  1357. *
  1358. * @throws AssertionFailedException
  1359. */
  1360. public static function range($value, $minValue, $maxValue, $message = null, ?string $propertyPath = null): bool
  1361. {
  1362. static::numeric($value, $message, $propertyPath);
  1363. if ($value < $minValue || $value > $maxValue) {
  1364. $message = \sprintf(
  1365. static::generateMessage($message ?: 'Number "%s" was expected to be at least "%d" and at most "%d".'),
  1366. static::stringify($value),
  1367. static::stringify($minValue),
  1368. static::stringify($maxValue)
  1369. );
  1370. throw static::createException($value, $message, static::INVALID_RANGE, $propertyPath, ['min' => $minValue, 'max' => $maxValue]);
  1371. }
  1372. return true;
  1373. }
  1374. /**
  1375. * Assert that a value is at least as big as a given limit.
  1376. *
  1377. * @param mixed $value
  1378. * @param mixed $minValue
  1379. * @param string|callable|null $message
  1380. * @param string|null $propertyPath
  1381. *
  1382. * @psalm-assert =numeric $value
  1383. *
  1384. * @return bool
  1385. *
  1386. * @throws AssertionFailedException
  1387. */
  1388. public static function min($value, $minValue, $message = null, ?string $propertyPath = null): bool
  1389. {
  1390. static::numeric($value, $message, $propertyPath);
  1391. if ($value < $minValue) {
  1392. $message = \sprintf(
  1393. static::generateMessage($message ?: 'Number "%s" was expected to be at least "%s".'),
  1394. static::stringify($value),
  1395. static::stringify($minValue)
  1396. );
  1397. throw static::createException($value, $message, static::INVALID_MIN, $propertyPath, ['min' => $minValue]);
  1398. }
  1399. return true;
  1400. }
  1401. /**
  1402. * Assert that a number is smaller as a given limit.
  1403. *
  1404. * @param mixed $value
  1405. * @param mixed $maxValue
  1406. * @param string|callable|null $message
  1407. * @param string|null $propertyPath
  1408. *
  1409. * @psalm-assert =numeric $value
  1410. *
  1411. * @return bool
  1412. *
  1413. * @throws AssertionFailedException
  1414. */
  1415. public static function max($value, $maxValue, $message = null, ?string $propertyPath = null): bool
  1416. {
  1417. static::numeric($value, $message, $propertyPath);
  1418. if ($value > $maxValue) {
  1419. $message = \sprintf(
  1420. static::generateMessage($message ?: 'Number "%s" was expected to be at most "%s".'),
  1421. static::stringify($value),
  1422. static::stringify($maxValue)
  1423. );
  1424. throw static::createException($value, $message, static::INVALID_MAX, $propertyPath, ['max' => $maxValue]);
  1425. }
  1426. return true;
  1427. }
  1428. /**
  1429. * Assert that a file exists.
  1430. *
  1431. * @param string $value
  1432. * @param string|callable|null $message
  1433. *
  1434. * @throws AssertionFailedException
  1435. */
  1436. public static function file($value, $message = null, ?string $propertyPath = null): bool
  1437. {
  1438. static::string($value, $message, $propertyPath);
  1439. static::notEmpty($value, $message, $propertyPath);
  1440. if (!\is_file($value)) {
  1441. $message = \sprintf(
  1442. static::generateMessage($message ?: 'File "%s" was expected to exist.'),
  1443. static::stringify($value)
  1444. );
  1445. throw static::createException($value, $message, static::INVALID_FILE, $propertyPath);
  1446. }
  1447. return true;
  1448. }
  1449. /**
  1450. * Assert that a directory exists.
  1451. *
  1452. * @param string $value
  1453. * @param string|callable|null $message
  1454. *
  1455. * @throws AssertionFailedException
  1456. */
  1457. public static function directory($value, $message = null, ?string $propertyPath = null): bool
  1458. {
  1459. static::string($value, $message, $propertyPath);
  1460. if (!\is_dir($value)) {
  1461. $message = \sprintf(
  1462. static::generateMessage($message ?: 'Path "%s" was expected to be a directory.'),
  1463. static::stringify($value)
  1464. );
  1465. throw static::createException($value, $message, static::INVALID_DIRECTORY, $propertyPath);
  1466. }
  1467. return true;
  1468. }
  1469. /**
  1470. * Assert that the value is something readable.
  1471. *
  1472. * @param string $value
  1473. * @param string|callable|null $message
  1474. *
  1475. * @throws AssertionFailedException
  1476. */
  1477. public static function readable($value, $message = null, ?string $propertyPath = null): bool
  1478. {
  1479. static::string($value, $message, $propertyPath);
  1480. if (!\is_readable($value)) {
  1481. $message = \sprintf(
  1482. static::generateMessage($message ?: 'Path "%s" was expected to be readable.'),
  1483. static::stringify($value)
  1484. );
  1485. throw static::createException($value, $message, static::INVALID_READABLE, $propertyPath);
  1486. }
  1487. return true;
  1488. }
  1489. /**
  1490. * Assert that the value is something writeable.
  1491. *
  1492. * @param string $value
  1493. * @param string|callable|null $message
  1494. *
  1495. * @throws AssertionFailedException
  1496. */
  1497. public static function writeable($value, $message = null, ?string $propertyPath = null): bool
  1498. {
  1499. static::string($value, $message, $propertyPath);
  1500. if (!\is_writable($value)) {
  1501. $message = \sprintf(
  1502. static::generateMessage($message ?: 'Path "%s" was expected to be writeable.'),
  1503. static::stringify($value)
  1504. );
  1505. throw static::createException($value, $message, static::INVALID_WRITEABLE, $propertyPath);
  1506. }
  1507. return true;
  1508. }
  1509. /**
  1510. * Assert that value is an email address (using input_filter/FILTER_VALIDATE_EMAIL).
  1511. *
  1512. * @param mixed $value
  1513. * @param string|callable|null $message
  1514. * @param string|null $propertyPath
  1515. *
  1516. * @psalm-assert =string $value
  1517. *
  1518. * @return bool
  1519. *
  1520. * @throws AssertionFailedException
  1521. */
  1522. public static function email($value, $message = null, ?string $propertyPath = null): bool
  1523. {
  1524. static::string($value, $message, $propertyPath);
  1525. if (!\filter_var($value, FILTER_VALIDATE_EMAIL)) {
  1526. $message = \sprintf(
  1527. static::generateMessage($message ?: 'Value "%s" was expected to be a valid e-mail address.'),
  1528. static::stringify($value)
  1529. );
  1530. throw static::createException($value, $message, static::INVALID_EMAIL, $propertyPath);
  1531. }
  1532. return true;
  1533. }
  1534. /**
  1535. * Assert that value is an URL.
  1536. *
  1537. * This code snipped was taken from the Symfony project and modified to the special demands of this method.
  1538. *
  1539. * @param mixed $value
  1540. * @param string|callable|null $message
  1541. * @param string|null $propertyPath
  1542. *
  1543. * @psalm-assert =string $value
  1544. *
  1545. * @return bool
  1546. *
  1547. * @throws AssertionFailedException
  1548. *
  1549. * @see https://github.com/symfony/Validator/blob/master/Constraints/UrlValidator.php
  1550. * @see https://github.com/symfony/Validator/blob/master/Constraints/Url.php
  1551. */
  1552. public static function url($value, $message = null, ?string $propertyPath = null): bool
  1553. {
  1554. static::string($value, $message, $propertyPath);
  1555. $protocols = ['http', 'https'];
  1556. $pattern = '~^
  1557. (%s):// # protocol
  1558. (([\.\pL\pN-]+:)?([\.\pL\pN-]+)@)? # basic auth
  1559. (
  1560. ([\pL\pN\pS\-\.])+(\.?([\pL\pN]|xn\-\-[\pL\pN-]+)+\.?) # a domain name
  1561. | # or
  1562. \d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3} # an IP address
  1563. | # or
  1564. \[
  1565. (?:(?:(?:(?:(?:(?:(?:[0-9a-f]{1,4})):){6})(?:(?:(?:(?:(?:[0-9a-f]{1,4})):(?:(?:[0-9a-f]{1,4})))|(?:(?:(?:(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9]))\.){3}(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9])))))))|(?:(?:::(?:(?:(?:[0-9a-f]{1,4})):){5})(?:(?:(?:(?:(?:[0-9a-f]{1,4})):(?:(?:[0-9a-f]{1,4})))|(?:(?:(?:(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9]))\.){3}(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9])))))))|(?:(?:(?:(?:(?:[0-9a-f]{1,4})))?::(?:(?:(?:[0-9a-f]{1,4})):){4})(?:(?:(?:(?:(?:[0-9a-f]{1,4})):(?:(?:[0-9a-f]{1,4})))|(?:(?:(?:(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9]))\.){3}(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9])))))))|(?:(?:(?:(?:(?:(?:[0-9a-f]{1,4})):){0,1}(?:(?:[0-9a-f]{1,4})))?::(?:(?:(?:[0-9a-f]{1,4})):){3})(?:(?:(?:(?:(?:[0-9a-f]{1,4})):(?:(?:[0-9a-f]{1,4})))|(?:(?:(?:(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9]))\.){3}(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9])))))))|(?:(?:(?:(?:(?:(?:[0-9a-f]{1,4})):){0,2}(?:(?:[0-9a-f]{1,4})))?::(?:(?:(?:[0-9a-f]{1,4})):){2})(?:(?:(?:(?:(?:[0-9a-f]{1,4})):(?:(?:[0-9a-f]{1,4})))|(?:(?:(?:(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9]))\.){3}(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9])))))))|(?:(?:(?:(?:(?:(?:[0-9a-f]{1,4})):){0,3}(?:(?:[0-9a-f]{1,4})))?::(?:(?:[0-9a-f]{1,4})):)(?:(?:(?:(?:(?:[0-9a-f]{1,4})):(?:(?:[0-9a-f]{1,4})))|(?:(?:(?:(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9]))\.){3}(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9])))))))|(?:(?:(?:(?:(?:(?:[0-9a-f]{1,4})):){0,4}(?:(?:[0-9a-f]{1,4})))?::)(?:(?:(?:(?:(?:[0-9a-f]{1,4})):(?:(?:[0-9a-f]{1,4})))|(?:(?:(?:(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9]))\.){3}(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9])))))))|(?:(?:(?:(?:(?:(?:[0-9a-f]{1,4})):){0,5}(?:(?:[0-9a-f]{1,4})))?::)(?:(?:[0-9a-f]{1,4})))|(?:(?:(?:(?:(?:(?:[0-9a-f]{1,4})):){0,6}(?:(?:[0-9a-f]{1,4})))?::))))
  1566. \] # an IPv6 address
  1567. )
  1568. (:[0-9]+)? # a port (optional)
  1569. (?:/ (?:[\pL\pN\-._\~!$&\'()*+,;=:@]|%%[0-9A-Fa-f]{2})* )* # a path
  1570. (?:\? (?:[\pL\pN\-._\~!$&\'\[\]()*+,;=:@/?]|%%[0-9A-Fa-f]{2})* )? # a query (optional)
  1571. (?:\# (?:[\pL\pN\-._\~!$&\'()*+,;=:@/?]|%%[0-9A-Fa-f]{2})* )? # a fragment (optional)
  1572. $~ixu';
  1573. $pattern = \sprintf($pattern, \implode('|', $protocols));
  1574. if (!\preg_match($pattern, $value)) {
  1575. $message = \sprintf(
  1576. static::generateMessage($message ?: 'Value "%s" was expected to be a valid URL starting with http or https'),
  1577. static::stringify($value)
  1578. );
  1579. throw static::createException($value, $message, static::INVALID_URL, $propertyPath);
  1580. }
  1581. return true;
  1582. }
  1583. /**
  1584. * Assert that value is alphanumeric.
  1585. *
  1586. * @param mixed $value
  1587. * @param string|callable|null $message
  1588. *
  1589. * @throws AssertionFailedException
  1590. */
  1591. public static function alnum($value, $message = null, ?string $propertyPath = null): bool
  1592. {
  1593. try {
  1594. static::regex($value, '(^([a-zA-Z]{1}[a-zA-Z0-9]*)$)', $message, $propertyPath);
  1595. } catch (Throwable $e) {
  1596. $message = \sprintf(
  1597. static::generateMessage($message ?: 'Value "%s" is not alphanumeric, starting with letters and containing only letters and numbers.'),
  1598. static::stringify($value)
  1599. );
  1600. throw static::createException($value, $message, static::INVALID_ALNUM, $propertyPath);
  1601. }
  1602. return true;
  1603. }
  1604. /**
  1605. * Assert that the value is boolean True.
  1606. *
  1607. * @param mixed $value
  1608. * @param string|callable|null $message
  1609. * @param string|null $propertyPath
  1610. *
  1611. * @psalm-assert true $value
  1612. *
  1613. * @return bool
  1614. *
  1615. * @throws AssertionFailedException
  1616. */
  1617. public static function true($value, $message = null, ?string $propertyPath = null): bool
  1618. {
  1619. if (true !== $value) {
  1620. $message = \sprintf(
  1621. static::generateMessage($message ?: 'Value "%s" is not TRUE.'),
  1622. static::stringify($value)
  1623. );
  1624. throw static::createException($value, $message, static::INVALID_TRUE, $propertyPath);
  1625. }
  1626. return true;
  1627. }
  1628. /**
  1629. * Assert that the value is boolean False.
  1630. *
  1631. * @param mixed $value
  1632. * @param string|callable|null $message
  1633. * @param string|null $propertyPath
  1634. *
  1635. * @psalm-assert false $value
  1636. *
  1637. * @return bool
  1638. *
  1639. * @throws AssertionFailedException
  1640. */
  1641. public static function false($value, $message = null, ?string $propertyPath = null): bool
  1642. {
  1643. if (false !== $value) {
  1644. $message = \sprintf(
  1645. static::generateMessage($message ?: 'Value "%s" is not FALSE.'),
  1646. static::stringify($value)
  1647. );
  1648. throw static::createException($value, $message, static::INVALID_FALSE, $propertyPath);
  1649. }
  1650. return true;
  1651. }
  1652. /**
  1653. * Assert that the class exists.
  1654. *
  1655. * @param mixed $value
  1656. * @param string|callable|null $message
  1657. * @param string|null $propertyPath
  1658. *
  1659. * @psalm-assert class-string $value
  1660. *
  1661. * @return bool
  1662. *
  1663. * @throws AssertionFailedException
  1664. */
  1665. public static function classExists($value, $message = null, ?string $propertyPath = null): bool
  1666. {
  1667. if (!\class_exists($value)) {
  1668. $message = \sprintf(
  1669. static::generateMessage($message ?: 'Class "%s" does not exist.'),
  1670. static::stringify($value)
  1671. );
  1672. throw static::createException($value, $message, static::INVALID_CLASS, $propertyPath);
  1673. }
  1674. return true;
  1675. }
  1676. /**
  1677. * Assert that the interface exists.
  1678. *
  1679. * @param mixed $value
  1680. * @param string|callable|null $message
  1681. * @param string|null $propertyPath
  1682. *
  1683. * @psalm-assert class-string $value
  1684. *
  1685. * @return bool
  1686. *
  1687. * @throws AssertionFailedException
  1688. */
  1689. public static function interfaceExists($value, $message = null, ?string $propertyPath = null): bool
  1690. {
  1691. if (!\interface_exists($value)) {
  1692. $message = \sprintf(
  1693. static::generateMessage($message ?: 'Interface "%s" does not exist.'),
  1694. static::stringify($value)
  1695. );
  1696. throw static::createException($value, $message, static::INVALID_INTERFACE, $propertyPath);
  1697. }
  1698. return true;
  1699. }
  1700. /**
  1701. * Assert that the class implements the interface.
  1702. *
  1703. * @param mixed $class
  1704. * @param string $interfaceName
  1705. * @param string|callable|null $message
  1706. *
  1707. * @throws AssertionFailedException
  1708. */
  1709. public static function implementsInterface($class, $interfaceName, $message = null, ?string $propertyPath = null): bool
  1710. {
  1711. try {
  1712. $reflection = new ReflectionClass($class);
  1713. if (!$reflection->implementsInterface($interfaceName)) {
  1714. $message = \sprintf(
  1715. static::generateMessage($message ?: 'Class "%s" does not implement interface "%s".'),
  1716. static::stringify($class),
  1717. static::stringify($interfaceName)
  1718. );
  1719. throw static::createException($class, $message, static::INTERFACE_NOT_IMPLEMENTED, $propertyPath, ['interface' => $interfaceName]);
  1720. }
  1721. } catch (ReflectionException $e) {
  1722. $message = \sprintf(
  1723. static::generateMessage($message ?: 'Class "%s" failed reflection.'),
  1724. static::stringify($class)
  1725. );
  1726. throw static::createException($class, $message, static::INTERFACE_NOT_IMPLEMENTED, $propertyPath, ['interface' => $interfaceName]);
  1727. }
  1728. return true;
  1729. }
  1730. /**
  1731. * Assert that the given string is a valid json string.
  1732. *
  1733. * NOTICE:
  1734. * Since this does a json_decode to determine its validity
  1735. * you probably should consider, when using the variable
  1736. * content afterwards, just to decode and check for yourself instead
  1737. * of using this assertion.
  1738. *
  1739. * @param mixed $value
  1740. * @param string|callable|null $message
  1741. * @param string|null $propertyPath
  1742. *
  1743. * @psalm-assert =string $value
  1744. *
  1745. * @return bool
  1746. *
  1747. * @throws AssertionFailedException
  1748. */
  1749. public static function isJsonString($value, $message = null, ?string $propertyPath = null): bool
  1750. {
  1751. if (null === \json_decode($value) && JSON_ERROR_NONE !== \json_last_error()) {
  1752. $message = \sprintf(
  1753. static::generateMessage($message ?: 'Value "%s" is not a valid JSON string.'),
  1754. static::stringify($value)
  1755. );
  1756. throw static::createException($value, $message, static::INVALID_JSON_STRING, $propertyPath);
  1757. }
  1758. return true;
  1759. }
  1760. /**
  1761. * Assert that the given string is a valid UUID.
  1762. *
  1763. * Uses code from {@link https://github.com/ramsey/uuid} that is MIT licensed.
  1764. *
  1765. * @param string $value
  1766. * @param string|callable|null $message
  1767. *
  1768. * @throws AssertionFailedException
  1769. */
  1770. public static function uuid($value, $message = null, ?string $propertyPath = null): bool
  1771. {
  1772. $value = \str_replace(['urn:', 'uuid:', '{', '}'], '', $value);
  1773. if ('00000000-0000-0000-0000-000000000000' === $value) {
  1774. return true;
  1775. }
  1776. 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)) {
  1777. $message = \sprintf(
  1778. static::generateMessage($message ?: 'Value "%s" is not a valid UUID.'),
  1779. static::stringify($value)
  1780. );
  1781. throw static::createException($value, $message, static::INVALID_UUID, $propertyPath);
  1782. }
  1783. return true;
  1784. }
  1785. /**
  1786. * Assert that the given string is a valid E164 Phone Number.
  1787. *
  1788. * @see https://en.wikipedia.org/wiki/E.164
  1789. *
  1790. * @param string $value
  1791. * @param string|callable|null $message
  1792. *
  1793. * @throws AssertionFailedException
  1794. */
  1795. public static function e164($value, $message = null, ?string $propertyPath = null): bool
  1796. {
  1797. if (!\preg_match('/^\+?[1-9]\d{1,14}$/', $value)) {
  1798. $message = \sprintf(
  1799. static::generateMessage($message ?: 'Value "%s" is not a valid E164.'),
  1800. static::stringify($value)
  1801. );
  1802. throw static::createException($value, $message, static::INVALID_E164, $propertyPath);
  1803. }
  1804. return true;
  1805. }
  1806. /**
  1807. * Assert that the count of countable is equal to count.
  1808. *
  1809. * @param array|Countable|ResourceBundle|SimpleXMLElement $countable
  1810. * @param int $count
  1811. * @param string|callable|null $message
  1812. * @param string|null $propertyPath
  1813. *
  1814. * @return bool
  1815. *
  1816. * @throws AssertionFailedException
  1817. */
  1818. public static function count($countable, $count, $message = null, ?string $propertyPath = null): bool
  1819. {
  1820. if ($count !== \count($countable)) {
  1821. $message = \sprintf(
  1822. static::generateMessage($message ?: 'List does not contain exactly %d elements (%d given).'),
  1823. static::stringify($count),
  1824. static::stringify(\count($countable))
  1825. );
  1826. throw static::createException($countable, $message, static::INVALID_COUNT, $propertyPath, ['count' => $count]);
  1827. }
  1828. return true;
  1829. }
  1830. /**
  1831. * Assert that the countable have at least $count elements.
  1832. *
  1833. * @param array|Countable|ResourceBundle|SimpleXMLElement $countable
  1834. * @param int $count
  1835. * @param string|callable|null $message
  1836. *
  1837. * @throws AssertionFailedException
  1838. */
  1839. public static function minCount($countable, $count, $message = null, ?string $propertyPath = null): bool
  1840. {
  1841. if ($count > \count($countable)) {
  1842. $message = \sprintf(
  1843. static::generateMessage($message ?: 'List should have at least %d elements, but has %d elements.'),
  1844. static::stringify($count),
  1845. static::stringify(\count($countable))
  1846. );
  1847. throw static::createException($countable, $message, static::INVALID_MIN_COUNT, $propertyPath, ['count' => $count]);
  1848. }
  1849. return true;
  1850. }
  1851. /**
  1852. * Assert that the countable have at most $count elements.
  1853. *
  1854. * @param array|Countable|ResourceBundle|SimpleXMLElement $countable
  1855. * @param int $count
  1856. * @param string|callable|null $message
  1857. *
  1858. * @throws AssertionFailedException
  1859. */
  1860. public static function maxCount($countable, $count, $message = null, ?string $propertyPath = null): bool
  1861. {
  1862. if ($count < \count($countable)) {
  1863. $message = \sprintf(
  1864. static::generateMessage($message ?: 'List should have at most %d elements, but has %d elements.'),
  1865. static::stringify($count),
  1866. static::stringify(\count($countable))
  1867. );
  1868. throw static::createException($countable, $message, static::INVALID_MAX_COUNT, $propertyPath, ['count' => $count]);
  1869. }
  1870. return true;
  1871. }
  1872. /**
  1873. * static call handler to implement:
  1874. * - "null or assertion" delegation
  1875. * - "all" delegation.
  1876. *
  1877. * @param string $method
  1878. * @param array $args
  1879. *
  1880. * @return bool|mixed
  1881. *
  1882. * @throws AssertionFailedException
  1883. */
  1884. public static function __callStatic($method, $args)
  1885. {
  1886. if (0 === \strpos($method, 'nullOr')) {
  1887. if (!\array_key_exists(0, $args)) {
  1888. throw new BadMethodCallException('Missing the first argument.');
  1889. }
  1890. if (null === $args[0]) {
  1891. return true;
  1892. }
  1893. $method = \substr($method, 6);
  1894. return \call_user_func_array([\get_called_class(), $method], $args);
  1895. }
  1896. if (0 === \strpos($method, 'all')) {
  1897. if (!\array_key_exists(0, $args)) {
  1898. throw new BadMethodCallException('Missing the first argument.');
  1899. }
  1900. static::isTraversable($args[0]);
  1901. $method = \substr($method, 3);
  1902. $values = \array_shift($args);
  1903. $calledClass = \get_called_class();
  1904. foreach ($values as $value) {
  1905. \call_user_func_array([$calledClass, $method], \array_merge([$value], $args));
  1906. }
  1907. return true;
  1908. }
  1909. throw new BadMethodCallException('No assertion Assertion#'.$method.' exists.');
  1910. }
  1911. /**
  1912. * Determines if the values array has every choice as key and that this choice has content.
  1913. *
  1914. * @param string|callable|null $message
  1915. *
  1916. * @throws AssertionFailedException
  1917. */
  1918. public static function choicesNotEmpty(array $values, array $choices, $message = null, ?string $propertyPath = null): bool
  1919. {
  1920. static::notEmpty($values, $message, $propertyPath);
  1921. foreach ($choices as $choice) {
  1922. static::notEmptyKey($values, $choice, $message, $propertyPath);
  1923. }
  1924. return true;
  1925. }
  1926. /**
  1927. * Determines that the named method is defined in the provided object.
  1928. *
  1929. * @param string $value
  1930. * @param mixed $object
  1931. * @param string|callable|null $message
  1932. *
  1933. * @throws AssertionFailedException
  1934. */
  1935. public static function methodExists($value, $object, $message = null, ?string $propertyPath = null): bool
  1936. {
  1937. static::isObject($object, $message, $propertyPath);
  1938. if (!\method_exists($object, $value)) {
  1939. $message = \sprintf(
  1940. static::generateMessage($message ?: 'Expected "%s" does not exist in provided object.'),
  1941. static::stringify($value)
  1942. );
  1943. throw static::createException($value, $message, static::INVALID_METHOD, $propertyPath, ['object' => \get_class($object)]);
  1944. }
  1945. return true;
  1946. }
  1947. /**
  1948. * Determines that the provided value is an object.
  1949. *
  1950. * @param mixed $value
  1951. * @param string|callable|null $message
  1952. * @param string|null $propertyPath
  1953. *
  1954. * @psalm-assert object $value
  1955. *
  1956. * @return bool
  1957. *
  1958. * @throws AssertionFailedException
  1959. */
  1960. public static function isObject($value, $message = null, ?string $propertyPath = null): bool
  1961. {
  1962. if (!\is_object($value)) {
  1963. $message = \sprintf(
  1964. static::generateMessage($message ?: 'Provided "%s" is not a valid object.'),
  1965. static::stringify($value)
  1966. );
  1967. throw static::createException($value, $message, static::INVALID_OBJECT, $propertyPath);
  1968. }
  1969. return true;
  1970. }
  1971. /**
  1972. * Determines if the value is less than given limit.
  1973. *
  1974. * @param mixed $value
  1975. * @param mixed $limit
  1976. * @param string|callable|null $message
  1977. *
  1978. * @throws AssertionFailedException
  1979. */
  1980. public static function lessThan($value, $limit, $message = null, ?string $propertyPath = null): bool
  1981. {
  1982. if ($value >= $limit) {
  1983. $message = \sprintf(
  1984. static::generateMessage($message ?: 'Provided "%s" is not less than "%s".'),
  1985. static::stringify($value),
  1986. static::stringify($limit)
  1987. );
  1988. throw static::createException($value, $message, static::INVALID_LESS, $propertyPath, ['limit' => $limit]);
  1989. }
  1990. return true;
  1991. }
  1992. /**
  1993. * Determines if the value is less or equal than given limit.
  1994. *
  1995. * @param mixed $value
  1996. * @param mixed $limit
  1997. * @param string|callable|null $message
  1998. *
  1999. * @throws AssertionFailedException
  2000. */
  2001. public static function lessOrEqualThan($value, $limit, $message = null, ?string $propertyPath = null): bool
  2002. {
  2003. if ($value > $limit) {
  2004. $message = \sprintf(
  2005. static::generateMessage($message ?: 'Provided "%s" is not less or equal than "%s".'),
  2006. static::stringify($value),
  2007. static::stringify($limit)
  2008. );
  2009. throw static::createException($value, $message, static::INVALID_LESS_OR_EQUAL, $propertyPath, ['limit' => $limit]);
  2010. }
  2011. return true;
  2012. }
  2013. /**
  2014. * Determines if the value is greater than given limit.
  2015. *
  2016. * @param mixed $value
  2017. * @param mixed $limit
  2018. * @param string|callable|null $message
  2019. *
  2020. * @throws AssertionFailedException
  2021. */
  2022. public static function greaterThan($value, $limit, $message = null, ?string $propertyPath = null): bool
  2023. {
  2024. if ($value <= $limit) {
  2025. $message = \sprintf(
  2026. static::generateMessage($message ?: 'Provided "%s" is not greater than "%s".'),
  2027. static::stringify($value),
  2028. static::stringify($limit)
  2029. );
  2030. throw static::createException($value, $message, static::INVALID_GREATER, $propertyPath, ['limit' => $limit]);
  2031. }
  2032. return true;
  2033. }
  2034. /**
  2035. * Determines if the value is greater or equal than given limit.
  2036. *
  2037. * @param mixed $value
  2038. * @param mixed $limit
  2039. * @param string|callable|null $message
  2040. *
  2041. * @throws AssertionFailedException
  2042. */
  2043. public static function greaterOrEqualThan($value, $limit, $message = null, ?string $propertyPath = null): bool
  2044. {
  2045. if ($value < $limit) {
  2046. $message = \sprintf(
  2047. static::generateMessage($message ?: 'Provided "%s" is not greater or equal than "%s".'),
  2048. static::stringify($value),
  2049. static::stringify($limit)
  2050. );
  2051. throw static::createException($value, $message, static::INVALID_GREATER_OR_EQUAL, $propertyPath, ['limit' => $limit]);
  2052. }
  2053. return true;
  2054. }
  2055. /**
  2056. * Assert that a value is greater or equal than a lower limit, and less than or equal to an upper limit.
  2057. *
  2058. * @param mixed $value
  2059. * @param mixed $lowerLimit
  2060. * @param mixed $upperLimit
  2061. * @param string|callable|null $message
  2062. * @param string $propertyPath
  2063. *
  2064. * @throws AssertionFailedException
  2065. */
  2066. public static function between($value, $lowerLimit, $upperLimit, $message = null, ?string $propertyPath = null): bool
  2067. {
  2068. if ($lowerLimit > $value || $value > $upperLimit) {
  2069. $message = \sprintf(
  2070. static::generateMessage($message ?: 'Provided "%s" is neither greater than or equal to "%s" nor less than or equal to "%s".'),
  2071. static::stringify($value),
  2072. static::stringify($lowerLimit),
  2073. static::stringify($upperLimit)
  2074. );
  2075. throw static::createException($value, $message, static::INVALID_BETWEEN, $propertyPath, ['lower' => $lowerLimit, 'upper' => $upperLimit]);
  2076. }
  2077. return true;
  2078. }
  2079. /**
  2080. * Assert that a value is greater than a lower limit, and less than an upper limit.
  2081. *
  2082. * @param mixed $value
  2083. * @param mixed $lowerLimit
  2084. * @param mixed $upperLimit
  2085. * @param string|callable|null $message
  2086. * @param string $propertyPath
  2087. *
  2088. * @throws AssertionFailedException
  2089. */
  2090. public static function betweenExclusive($value, $lowerLimit, $upperLimit, $message = null, ?string $propertyPath = null): bool
  2091. {
  2092. if ($lowerLimit >= $value || $value >= $upperLimit) {
  2093. $message = \sprintf(
  2094. static::generateMessage($message ?: 'Provided "%s" is neither greater than "%s" nor less than "%s".'),
  2095. static::stringify($value),
  2096. static::stringify($lowerLimit),
  2097. static::stringify($upperLimit)
  2098. );
  2099. throw static::createException($value, $message, static::INVALID_BETWEEN_EXCLUSIVE, $propertyPath, ['lower' => $lowerLimit, 'upper' => $upperLimit]);
  2100. }
  2101. return true;
  2102. }
  2103. /**
  2104. * Assert that extension is loaded.
  2105. *
  2106. * @param mixed $value
  2107. * @param string|callable|null $message
  2108. *
  2109. * @throws AssertionFailedException
  2110. */
  2111. public static function extensionLoaded($value, $message = null, ?string $propertyPath = null): bool
  2112. {
  2113. if (!\extension_loaded($value)) {
  2114. $message = \sprintf(
  2115. static::generateMessage($message ?: 'Extension "%s" is required.'),
  2116. static::stringify($value)
  2117. );
  2118. throw static::createException($value, $message, static::INVALID_EXTENSION, $propertyPath);
  2119. }
  2120. return true;
  2121. }
  2122. /**
  2123. * Assert that date is valid and corresponds to the given format.
  2124. *
  2125. * @param string $value
  2126. * @param string $format supports all of the options date(), except for the following:
  2127. * N, w, W, t, L, o, B, a, A, g, h, I, O, P, Z, c, r
  2128. * @param string|callable|null $message
  2129. *
  2130. * @throws AssertionFailedException
  2131. *
  2132. * @see http://php.net/manual/function.date.php#refsect1-function.date-parameters
  2133. */
  2134. public static function date($value, $format, $message = null, ?string $propertyPath = null): bool
  2135. {
  2136. static::string($value, $message, $propertyPath);
  2137. static::string($format, $message, $propertyPath);
  2138. $dateTime = DateTime::createFromFormat('!'.$format, $value);
  2139. if (false === $dateTime || $value !== $dateTime->format($format)) {
  2140. $message = \sprintf(
  2141. static::generateMessage($message ?: 'Date "%s" is invalid or does not match format "%s".'),
  2142. static::stringify($value),
  2143. static::stringify($format)
  2144. );
  2145. throw static::createException($value, $message, static::INVALID_DATE, $propertyPath, ['format' => $format]);
  2146. }
  2147. return true;
  2148. }
  2149. /**
  2150. * Assert that the value is an object, or a class that exists.
  2151. *
  2152. * @param mixed $value
  2153. * @param string|callable|null $message
  2154. *
  2155. * @throws AssertionFailedException
  2156. */
  2157. public static function objectOrClass($value, $message = null, ?string $propertyPath = null): bool
  2158. {
  2159. if (!\is_object($value)) {
  2160. static::classExists($value, $message, $propertyPath);
  2161. }
  2162. return true;
  2163. }
  2164. /**
  2165. * Assert that the value is an object or class, and that the property exists.
  2166. *
  2167. * @param mixed $value
  2168. * @param string $property
  2169. * @param string|callable|null $message
  2170. *
  2171. * @throws AssertionFailedException
  2172. */
  2173. public static function propertyExists($value, $property, $message = null, ?string $propertyPath = null): bool
  2174. {
  2175. static::objectOrClass($value);
  2176. if (!\property_exists($value, $property)) {
  2177. $message = \sprintf(
  2178. static::generateMessage($message ?: 'Class "%s" does not have property "%s".'),
  2179. static::stringify($value),
  2180. static::stringify($property)
  2181. );
  2182. throw static::createException($value, $message, static::INVALID_PROPERTY, $propertyPath, ['property' => $property]);
  2183. }
  2184. return true;
  2185. }
  2186. /**
  2187. * Assert that the value is an object or class, and that the properties all exist.
  2188. *
  2189. * @param mixed $value
  2190. * @param string|callable|null $message
  2191. *
  2192. * @throws AssertionFailedException
  2193. */
  2194. public static function propertiesExist($value, array $properties, $message = null, ?string $propertyPath = null): bool
  2195. {
  2196. static::objectOrClass($value);
  2197. static::allString($properties, $message, $propertyPath);
  2198. $invalidProperties = [];
  2199. foreach ($properties as $property) {
  2200. if (!\property_exists($value, $property)) {
  2201. $invalidProperties[] = $property;
  2202. }
  2203. }
  2204. if ($invalidProperties) {
  2205. $message = \sprintf(
  2206. static::generateMessage($message ?: 'Class "%s" does not have these properties: %s.'),
  2207. static::stringify($value),
  2208. static::stringify(\implode(', ', $invalidProperties))
  2209. );
  2210. throw static::createException($value, $message, static::INVALID_PROPERTY, $propertyPath, ['properties' => $properties]);
  2211. }
  2212. return true;
  2213. }
  2214. /**
  2215. * Assert comparison of two versions.
  2216. *
  2217. * @param string $version1
  2218. * @param string $operator
  2219. * @param string $version2
  2220. * @param string|callable|null $message
  2221. *
  2222. * @throws AssertionFailedException
  2223. */
  2224. public static function version($version1, $operator, $version2, $message = null, ?string $propertyPath = null): bool
  2225. {
  2226. static::notEmpty($operator, 'versionCompare operator is required and cannot be empty.');
  2227. if (true !== \version_compare($version1, $version2, $operator)) {
  2228. $message = \sprintf(
  2229. static::generateMessage($message ?: 'Version "%s" is not "%s" version "%s".'),
  2230. static::stringify($version1),
  2231. static::stringify($operator),
  2232. static::stringify($version2)
  2233. );
  2234. throw static::createException($version1, $message, static::INVALID_VERSION, $propertyPath, ['operator' => $operator, 'version' => $version2]);
  2235. }
  2236. return true;
  2237. }
  2238. /**
  2239. * Assert on PHP version.
  2240. *
  2241. * @param string $operator
  2242. * @param mixed $version
  2243. * @param string|callable|null $message
  2244. *
  2245. * @throws AssertionFailedException
  2246. */
  2247. public static function phpVersion($operator, $version, $message = null, ?string $propertyPath = null): bool
  2248. {
  2249. static::defined('PHP_VERSION');
  2250. return static::version(PHP_VERSION, $operator, $version, $message, $propertyPath);
  2251. }
  2252. /**
  2253. * Assert that extension is loaded and a specific version is installed.
  2254. *
  2255. * @param string $extension
  2256. * @param string $operator
  2257. * @param mixed $version
  2258. * @param string|callable|null $message
  2259. *
  2260. * @throws AssertionFailedException
  2261. */
  2262. public static function extensionVersion($extension, $operator, $version, $message = null, ?string $propertyPath = null): bool
  2263. {
  2264. static::extensionLoaded($extension, $message, $propertyPath);
  2265. return static::version(\phpversion($extension), $operator, $version, $message, $propertyPath);
  2266. }
  2267. /**
  2268. * Determines that the provided value is callable.
  2269. *
  2270. * @param mixed $value
  2271. * @param string|callable|null $message
  2272. * @param string|null $propertyPath
  2273. *
  2274. * @psalm-assert callable $value
  2275. *
  2276. * @return bool
  2277. *
  2278. * @throws AssertionFailedException
  2279. */
  2280. public static function isCallable($value, $message = null, ?string $propertyPath = null): bool
  2281. {
  2282. if (!\is_callable($value)) {
  2283. $message = \sprintf(
  2284. static::generateMessage($message ?: 'Provided "%s" is not a callable.'),
  2285. static::stringify($value)
  2286. );
  2287. throw static::createException($value, $message, static::INVALID_CALLABLE, $propertyPath);
  2288. }
  2289. return true;
  2290. }
  2291. /**
  2292. * Assert that the provided value is valid according to a callback.
  2293. *
  2294. * If the callback returns `false` the assertion will fail.
  2295. *
  2296. * @param mixed $value
  2297. * @param callable $callback
  2298. * @param string|callable|null $message
  2299. *
  2300. * @throws AssertionFailedException
  2301. */
  2302. public static function satisfy($value, $callback, $message = null, ?string $propertyPath = null): bool
  2303. {
  2304. static::isCallable($callback);
  2305. if (false === \call_user_func($callback, $value)) {
  2306. $message = \sprintf(
  2307. static::generateMessage($message ?: 'Provided "%s" is invalid according to custom rule.'),
  2308. static::stringify($value)
  2309. );
  2310. throw static::createException($value, $message, static::INVALID_SATISFY, $propertyPath);
  2311. }
  2312. return true;
  2313. }
  2314. /**
  2315. * Assert that value is an IPv4 or IPv6 address
  2316. * (using input_filter/FILTER_VALIDATE_IP).
  2317. *
  2318. * @param string $value
  2319. * @param int|null $flag
  2320. * @param string|callable|null $message
  2321. *
  2322. * @throws AssertionFailedException
  2323. *
  2324. * @see http://php.net/manual/filter.filters.flags.php
  2325. */
  2326. public static function ip($value, $flag = null, $message = null, ?string $propertyPath = null): bool
  2327. {
  2328. static::string($value, $message, $propertyPath);
  2329. if ($flag === null) {
  2330. $filterVarResult = \filter_var($value, FILTER_VALIDATE_IP);
  2331. } else {
  2332. $filterVarResult = \filter_var($value, FILTER_VALIDATE_IP, $flag);
  2333. }
  2334. if (!$filterVarResult) {
  2335. $message = \sprintf(
  2336. static::generateMessage($message ?: 'Value "%s" was expected to be a valid IP address.'),
  2337. static::stringify($value)
  2338. );
  2339. throw static::createException($value, $message, static::INVALID_IP, $propertyPath, ['flag' => $flag]);
  2340. }
  2341. return true;
  2342. }
  2343. /**
  2344. * Assert that value is an IPv4 address
  2345. * (using input_filter/FILTER_VALIDATE_IP).
  2346. *
  2347. * @param string $value
  2348. * @param int|null $flag
  2349. * @param string|callable|null $message
  2350. *
  2351. * @throws AssertionFailedException
  2352. *
  2353. * @see http://php.net/manual/filter.filters.flags.php
  2354. */
  2355. public static function ipv4($value, $flag = null, $message = null, ?string $propertyPath = null): bool
  2356. {
  2357. static::ip($value, $flag | FILTER_FLAG_IPV4, static::generateMessage($message ?: 'Value "%s" was expected to be a valid IPv4 address.'), $propertyPath);
  2358. return true;
  2359. }
  2360. /**
  2361. * Assert that value is an IPv6 address
  2362. * (using input_filter/FILTER_VALIDATE_IP).
  2363. *
  2364. * @param string $value
  2365. * @param int|null $flag
  2366. * @param string|callable|null $message
  2367. *
  2368. * @throws AssertionFailedException
  2369. *
  2370. * @see http://php.net/manual/filter.filters.flags.php
  2371. */
  2372. public static function ipv6($value, $flag = null, $message = null, ?string $propertyPath = null): bool
  2373. {
  2374. static::ip($value, $flag | FILTER_FLAG_IPV6, static::generateMessage($message ?: 'Value "%s" was expected to be a valid IPv6 address.'), $propertyPath);
  2375. return true;
  2376. }
  2377. /**
  2378. * Assert that a constant is defined.
  2379. *
  2380. * @param mixed $constant
  2381. * @param string|callable|null $message
  2382. */
  2383. public static function defined($constant, $message = null, ?string $propertyPath = null): bool
  2384. {
  2385. if (!\defined($constant)) {
  2386. $message = \sprintf(static::generateMessage($message ?: 'Value "%s" expected to be a defined constant.'), $constant);
  2387. throw static::createException($constant, $message, static::INVALID_CONSTANT, $propertyPath);
  2388. }
  2389. return true;
  2390. }
  2391. /**
  2392. * Assert that a constant is defined.
  2393. *
  2394. * @param string $value
  2395. * @param string|callable|null $message
  2396. *
  2397. * @throws AssertionFailedException
  2398. */
  2399. public static function base64($value, $message = null, ?string $propertyPath = null): bool
  2400. {
  2401. if (false === \base64_decode($value, true)) {
  2402. $message = \sprintf(static::generateMessage($message ?: 'Value "%s" is not a valid base64 string.'), $value);
  2403. throw static::createException($value, $message, static::INVALID_BASE64, $propertyPath);
  2404. }
  2405. return true;
  2406. }
  2407. /**
  2408. * Helper method that handles building the assertion failure exceptions.
  2409. * They are returned from this method so that the stack trace still shows
  2410. * the assertions method.
  2411. *
  2412. * @param mixed $value
  2413. * @param string|callable|null $message
  2414. * @param int $code
  2415. *
  2416. * @return mixed
  2417. */
  2418. protected static function createException($value, $message, $code, $propertyPath = null, array $constraints = [])
  2419. {
  2420. $exceptionClass = static::$exceptionClass;
  2421. return new $exceptionClass($message, $code, $propertyPath, $value, $constraints);
  2422. }
  2423. /**
  2424. * Make a string version of a value.
  2425. *
  2426. * @param mixed $value
  2427. */
  2428. protected static function stringify($value): string
  2429. {
  2430. $result = \gettype($value);
  2431. if (\is_bool($value)) {
  2432. $result = $value ? '<TRUE>' : '<FALSE>';
  2433. } elseif (\is_scalar($value)) {
  2434. $val = (string)$value;
  2435. if (\mb_strlen($val) > 100) {
  2436. $val = \mb_substr($val, 0, 97).'...';
  2437. }
  2438. $result = $val;
  2439. } elseif (\is_array($value)) {
  2440. $result = '<ARRAY>';
  2441. } elseif (\is_object($value)) {
  2442. $result = \get_class($value);
  2443. } elseif (\is_resource($value)) {
  2444. $result = \get_resource_type($value);
  2445. } elseif (null === $value) {
  2446. $result = '<NULL>';
  2447. }
  2448. return $result;
  2449. }
  2450. /**
  2451. * Generate the message.
  2452. *
  2453. * @param string|callable|null $message
  2454. */
  2455. protected static function generateMessage($message): string
  2456. {
  2457. if (\is_callable($message)) {
  2458. $traces = \debug_backtrace(0);
  2459. $parameters = [];
  2460. try {
  2461. $reflection = new ReflectionClass($traces[1]['class']);
  2462. $method = $reflection->getMethod($traces[1]['function']);
  2463. foreach ($method->getParameters() as $index => $parameter) {
  2464. if ('message' !== $parameter->getName()) {
  2465. $parameters[$parameter->getName()] = \array_key_exists($index, $traces[1]['args'])
  2466. ? $traces[1]['args'][$index]
  2467. : $parameter->getDefaultValue();
  2468. }
  2469. }
  2470. $parameters['::assertion'] = \sprintf('%s%s%s', $traces[1]['class'], $traces[1]['type'], $traces[1]['function']);
  2471. $message = \call_user_func_array($message, [$parameters]);
  2472. } // @codeCoverageIgnoreStart
  2473. catch (Throwable $exception) {
  2474. $message = \sprintf('Unable to generate message : %s', $exception->getMessage());
  2475. } // @codeCoverageIgnoreEnd
  2476. }
  2477. return (string)$message;
  2478. }
  2479. }