PageRenderTime 41ms CodeModel.GetById 16ms RepoModel.GetById 1ms app.codeStats 0ms

/core/lib/Drupal/Component/Assertion/Inspector.php

http://github.com/drupal/drupal
PHP | 420 lines | 148 code | 25 blank | 247 comment | 26 complexity | a6d225f34d408ba886109cba523c87c0 MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.1
  1. <?php
  2. namespace Drupal\Component\Assertion;
  3. use Traversable;
  4. /**
  5. * Generic inspections for the assert() statement.
  6. *
  7. * This is a static function collection for inspecting variable contents. All
  8. * functions in this collection check a variable against an assertion about its
  9. * structure.
  10. *
  11. * Example call:
  12. * @code
  13. * assert(Inspector::assertAllStrings($array));
  14. * @endcode
  15. *
  16. * @ingroup php_assert
  17. */
  18. class Inspector {
  19. /**
  20. * Asserts argument can be traversed with foreach.
  21. *
  22. * @param mixed $traversable
  23. * Variable to be examined.
  24. *
  25. * @return bool
  26. * TRUE if $traversable can be traversed with foreach.
  27. */
  28. public static function assertTraversable($traversable) {
  29. return is_array($traversable) || $traversable instanceof Traversable;
  30. }
  31. /**
  32. * Asserts callback returns TRUE for each member of a traversable.
  33. *
  34. * This is less memory intensive than using array_filter() to build a second
  35. * array and then comparing the arrays. Many of the other functions in this
  36. * collection alias this function passing a specific callback to make the
  37. * code more readable.
  38. *
  39. * @param callable $callable
  40. * Callback function.
  41. * @param mixed $traversable
  42. * Variable to be examined.
  43. *
  44. * @return bool
  45. * TRUE if $traversable can be traversed and $callable returns TRUE on
  46. * all members.
  47. *
  48. * @see http://php.net/manual/language.types.callable.php
  49. */
  50. public static function assertAll(callable $callable, $traversable) {
  51. if (static::assertTraversable($traversable)) {
  52. foreach ($traversable as $member) {
  53. if (!$callable($member)) {
  54. return FALSE;
  55. }
  56. }
  57. return TRUE;
  58. }
  59. return FALSE;
  60. }
  61. /**
  62. * Asserts that all members are strings.
  63. *
  64. * Use this only if it is vital that the members not be objects, otherwise
  65. * test with ::assertAllStringable().
  66. *
  67. * @param mixed $traversable
  68. * Variable to be examined.
  69. *
  70. * @return bool
  71. * TRUE if $traversable can be traversed and all members are strings.
  72. */
  73. public static function assertAllStrings($traversable) {
  74. return static::assertAll('is_string', $traversable);
  75. }
  76. /**
  77. * Asserts all members are strings or objects with magic __toString() method.
  78. *
  79. * @param mixed $traversable
  80. * Variable to be examined.
  81. *
  82. * @return bool
  83. * TRUE if $traversable can be traversed and all members are strings or
  84. * objects with __toString().
  85. */
  86. public static function assertAllStringable($traversable) {
  87. if (static::assertTraversable($traversable)) {
  88. foreach ($traversable as $member) {
  89. if (!static::assertStringable($member)) {
  90. return FALSE;
  91. }
  92. }
  93. return TRUE;
  94. }
  95. return FALSE;
  96. }
  97. /**
  98. * Asserts argument is a string or an object castable to a string.
  99. *
  100. * Use this instead of is_string() alone unless the argument being an object
  101. * in any way will cause a problem.
  102. *
  103. * @param mixed $string
  104. * Variable to be examined
  105. *
  106. * @return bool
  107. * TRUE if $string is a string or an object castable to a string.
  108. */
  109. public static function assertStringable($string) {
  110. return is_string($string) || (is_object($string) && method_exists($string, '__toString'));
  111. }
  112. /**
  113. * Asserts that all members are arrays.
  114. *
  115. * @param mixed $traversable
  116. * Variable to be examined.
  117. *
  118. * @return bool
  119. * TRUE if $traversable can be traversed and all members are arrays.
  120. */
  121. public static function assertAllArrays($traversable) {
  122. return static::assertAll('is_array', $traversable);
  123. }
  124. /**
  125. * Asserts that the array is strict.
  126. *
  127. * What PHP calls arrays are more formally called maps in most other
  128. * programming languages. A map is a datatype that associates values to keys.
  129. * The term 'strict array' here refers to a 0-indexed array in the classic
  130. * sense found in programming languages other than PHP.
  131. *
  132. * @param mixed $array
  133. * Variable to be examined.
  134. *
  135. * @return bool
  136. * TRUE if $traversable is a 0-indexed array.
  137. *
  138. * @see http://php.net/manual/language.types.array.php
  139. */
  140. public static function assertStrictArray($array) {
  141. if (!is_array($array)) {
  142. return FALSE;
  143. }
  144. $i = 0;
  145. foreach (array_keys($array) as $key) {
  146. if ($i !== $key) {
  147. return FALSE;
  148. }
  149. $i++;
  150. }
  151. return TRUE;
  152. }
  153. /**
  154. * Asserts all members are strict arrays.
  155. *
  156. * @param mixed $traversable
  157. * Variable to be examined.
  158. *
  159. * @return bool
  160. * TRUE if $traversable can be traversed and all members are strict arrays.
  161. *
  162. * @see ::assertStrictArray
  163. */
  164. public static function assertAllStrictArrays($traversable) {
  165. return static::assertAll([__CLASS__, 'assertStrictArray'], $traversable);
  166. }
  167. /**
  168. * Asserts all given keys exist in every member array.
  169. *
  170. * Drupal has several data structure arrays that require certain keys be set.
  171. * You can overload this function to specify a list of required keys. All
  172. * of the keys must be set for this method to return TRUE.
  173. *
  174. * As an example, this assertion tests for the keys of a theme registry.
  175. *
  176. * @code
  177. * assert(Inspector::assertAllHaveKey(
  178. * $arrayToTest, "type", "theme path", "function", "template", "variables", "render element", "preprocess functions"));
  179. * @endcode
  180. *
  181. * Note: If a method requires certain keys to be present it will usually be
  182. * specific about the data types for the values of those keys. Therefore it
  183. * will be best to write a specific test for it. Such tests are either bound
  184. * to the object that uses them, or are collected into one assertion set for
  185. * the package.
  186. *
  187. * @param mixed $traversable
  188. * Variable to be examined.
  189. * @param string ...
  190. * Keys to be searched for.
  191. *
  192. * @return bool
  193. * TRUE if $traversable can be traversed and all members have all keys.
  194. */
  195. public static function assertAllHaveKey($traversable) {
  196. $args = func_get_args();
  197. unset($args[0]);
  198. if (static::assertTraversable($traversable)) {
  199. foreach ($traversable as $member) {
  200. foreach ($args as $key) {
  201. if (!array_key_exists($key, $member)) {
  202. return FALSE;
  203. }
  204. }
  205. }
  206. return TRUE;
  207. }
  208. return FALSE;
  209. }
  210. /**
  211. * Asserts that all members are integer values.
  212. *
  213. * @param mixed $traversable
  214. * Variable to be examined.
  215. *
  216. * @return bool
  217. * TRUE if $traversable can be traversed and all members are integers.
  218. */
  219. public static function assertAllIntegers($traversable) {
  220. return static::assertAll('is_int', $traversable);
  221. }
  222. /**
  223. * Asserts that all members are float values.
  224. *
  225. * @param mixed $traversable
  226. * Variable to be examined.
  227. *
  228. * @return bool
  229. * TRUE if $traversable can be traversed and all members are floating point
  230. * numbers.
  231. */
  232. public static function assertAllFloat($traversable) {
  233. return static::assertAll('is_float', $traversable);
  234. }
  235. /**
  236. * Asserts that all members are callable.
  237. *
  238. * @param mixed $traversable
  239. * Variable to be examined.
  240. *
  241. * @return bool
  242. * TRUE if $traversable can be traversed and all members are callable.
  243. */
  244. public static function assertAllCallable($traversable) {
  245. return static::assertAll('is_callable', $traversable);
  246. }
  247. /**
  248. * Asserts that all members are not empty.
  249. *
  250. * @param mixed $traversable
  251. * Variable to be examined.
  252. *
  253. * @return bool
  254. * TRUE if $traversable can be traversed and all members not empty.
  255. */
  256. public static function assertAllNotEmpty($traversable) {
  257. if (static::assertTraversable($traversable)) {
  258. foreach ($traversable as $member) {
  259. if (empty($member)) {
  260. return FALSE;
  261. }
  262. }
  263. return TRUE;
  264. }
  265. return FALSE;
  266. }
  267. /**
  268. * Asserts all members are numeric data types or strings castable to such.
  269. *
  270. * @param mixed $traversable
  271. * Variable to be examined.
  272. *
  273. * @return bool
  274. * TRUE if $traversable can be traversed and all members are numeric.
  275. */
  276. public static function assertAllNumeric($traversable) {
  277. return static::assertAll('is_numeric', $traversable);
  278. }
  279. /**
  280. * Asserts that all members are strings that contain the specified string.
  281. *
  282. * This runs faster than the regular expression equivalent.
  283. *
  284. * @param string $pattern
  285. * The needle to find.
  286. * @param mixed $traversable
  287. * Variable to examine.
  288. * @param bool $case_sensitive
  289. * TRUE to use strstr(), FALSE to use stristr() which is case insensitive.
  290. *
  291. * @return bool
  292. * TRUE if $traversable can be traversed and all members are strings
  293. * containing $pattern.
  294. */
  295. public static function assertAllMatch($pattern, $traversable, $case_sensitive = FALSE) {
  296. if (static::assertTraversable($traversable)) {
  297. if ($case_sensitive) {
  298. foreach ($traversable as $member) {
  299. if (!(is_string($member) && strstr($member, $pattern))) {
  300. return FALSE;
  301. }
  302. }
  303. }
  304. else {
  305. foreach ($traversable as $member) {
  306. if (!(is_string($member) && stristr($member, $pattern))) {
  307. return FALSE;
  308. }
  309. }
  310. }
  311. return TRUE;
  312. }
  313. return FALSE;
  314. }
  315. /**
  316. * Asserts that all members are strings matching a regular expression.
  317. *
  318. * @param string $pattern
  319. * Regular expression string to find.
  320. * @param mixed $traversable
  321. * Variable to be examined.
  322. *
  323. * @return bool
  324. * TRUE if $traversable can be traversed and all members are strings
  325. * matching $pattern.
  326. */
  327. public static function assertAllRegularExpressionMatch($pattern, $traversable) {
  328. if (static::assertTraversable($traversable)) {
  329. foreach ($traversable as $member) {
  330. if (!is_string($member)) {
  331. return FALSE;
  332. }
  333. if (!preg_match($pattern, $member)) {
  334. return FALSE;
  335. }
  336. }
  337. return TRUE;
  338. }
  339. return FALSE;
  340. }
  341. /**
  342. * Asserts that all members are objects.
  343. *
  344. * When testing if a collection is composed of objects those objects should
  345. * be given a common interface to implement and the test should be written to
  346. * search for just that interface. While this method will allow tests for
  347. * just object status or for multiple classes and interfaces this was done to
  348. * allow tests to be written for existing code without altering it. Only use
  349. * this method in that manner when testing code from third party vendors.
  350. *
  351. * Here are some examples:
  352. * @code
  353. * // Just test all are objects, like a cache.
  354. * assert(Inspector::assertAllObjects($collection));
  355. *
  356. * // Test if traversable objects (arrays won't pass this)
  357. * assert(Inspector::assertAllObjects($collection, '\\Traversable'));
  358. *
  359. * // Test for the Foo class or Bar\None interface
  360. * assert(Inspector::assertAllObjects($collection, '\\Foo', '\\Bar\\None'));
  361. * @endcode
  362. *
  363. * @param mixed $traversable
  364. * Variable to be examined.
  365. * @param string ...
  366. * Classes and interfaces to test objects against.
  367. *
  368. * @return bool
  369. * TRUE if $traversable can be traversed and all members are objects with
  370. * at least one of the listed classes or interfaces.
  371. */
  372. public static function assertAllObjects($traversable) {
  373. $args = func_get_args();
  374. unset($args[0]);
  375. if (static::assertTraversable($traversable)) {
  376. foreach ($traversable as $member) {
  377. if (count($args) > 0) {
  378. foreach ($args as $instance) {
  379. if ($member instanceof $instance) {
  380. // We're continuing to the next member on the outer loop.
  381. // @see http://php.net/continue
  382. continue 2;
  383. }
  384. }
  385. return FALSE;
  386. }
  387. elseif (!is_object($member)) {
  388. return FALSE;
  389. }
  390. }
  391. return TRUE;
  392. }
  393. return FALSE;
  394. }
  395. }