annotate core/lib/Drupal/Component/Assertion/Inspector.php @ 19:fa3358dc1485 tip

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