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 }
|