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