23
23
*/
24
24
class Options implements \ArrayAccess, \Iterator, \Countable
25
25
{
26
+ /**
27
+ * Whether to format {@link \DateTime} objects as RFC-3339 dates
28
+ * during exceptions ("Y-m-d H:i:s").
29
+ *
30
+ * @var int
31
+ */
32
+ const PRETTY_DATE = 1 ;
33
+
34
+ /**
35
+ * Whether to cast objects with a "__toString()" method to strings during exceptions.
36
+ *
37
+ * @var int
38
+ */
39
+ const OBJECT_TO_STRING = 2 ;
40
+
26
41
/**
27
42
* A list of option values.
43
+ *
28
44
* @var array
29
45
*/
30
46
private $ options = array ();
31
47
32
48
/**
33
49
* A list of normalizer closures.
50
+ *
34
51
* @var array
35
52
*/
36
53
private $ normalizers = array ();
37
54
38
55
/**
39
56
* A list of closures for evaluating lazy options.
57
+ *
40
58
* @var array
41
59
*/
42
60
private $ lazy = array ();
43
61
44
62
/**
45
63
* A list containing the currently locked options.
64
+ *
46
65
* @var array
47
66
*/
48
67
private $ lock = array ();
@@ -228,7 +247,7 @@ public static function validateTypes(array $options, array $acceptedTypes)
228
247
continue ;
229
248
}
230
249
231
- $ value = $ options [$ option ];
250
+ $ value = $ options [$ option ];
232
251
$ optionTypes = (array ) $ optionTypes ;
233
252
234
253
foreach ($ optionTypes as $ type ) {
@@ -241,17 +260,11 @@ public static function validateTypes(array $options, array $acceptedTypes)
241
260
}
242
261
}
243
262
244
- $ printableValue = is_object ($ value )
245
- ? get_class ($ value )
246
- : (is_array ($ value )
247
- ? 'Array '
248
- : (string ) $ value );
249
-
250
263
throw new InvalidOptionsException (sprintf (
251
264
'The option "%s" with value "%s" is expected to be of type "%s" ' ,
252
265
$ option ,
253
- $ printableValue ,
254
- implode ('", " ' , $ optionTypes )
266
+ self :: formatValue ( $ value ) ,
267
+ implode ('", " ' , self :: formatTypesOf ( $ optionTypes) )
255
268
));
256
269
}
257
270
}
@@ -275,7 +288,7 @@ public static function validateValues(array $options, array $acceptedValues)
275
288
foreach ($ acceptedValues as $ option => $ optionValues ) {
276
289
if (array_key_exists ($ option , $ options )) {
277
290
if (is_array ($ optionValues ) && !in_array ($ options [$ option ], $ optionValues , true )) {
278
- throw new InvalidOptionsException (sprintf ('The option "%s" has the value "%s", but is expected to be one of "%s" ' , $ option , $ options [$ option ], implode ('", " ' , $ optionValues )));
291
+ throw new InvalidOptionsException (sprintf ('The option "%s" has the value "%s", but is expected to be one of "%s" ' , $ option , $ options [$ option ], implode ('", " ' , self :: formatValues ( $ optionValues) )));
279
292
}
280
293
281
294
if (is_callable ($ optionValues ) && !call_user_func ($ optionValues , $ options [$ option ])) {
@@ -285,6 +298,120 @@ public static function validateValues(array $options, array $acceptedValues)
285
298
}
286
299
}
287
300
301
+ /**
302
+ * Returns a string representation of the type of the value.
303
+ *
304
+ * @param mixed $value
305
+ *
306
+ * @return string
307
+ */
308
+ private static function formatTypeOf ($ value )
309
+ {
310
+ return is_object ($ value ) ? get_class ($ value ) : gettype ($ value );
311
+ }
312
+
313
+ /**
314
+ * @param array $optionTypes
315
+ *
316
+ * @return array
317
+ */
318
+ private static function formatTypesOf (array $ optionTypes )
319
+ {
320
+ foreach ($ optionTypes as $ x => $ type ) {
321
+ $ optionTypes [$ x ] = self ::formatTypeOf ($ type );
322
+ }
323
+
324
+ return $ optionTypes ;
325
+ }
326
+
327
+ /**
328
+ * Returns a string representation of the value.
329
+ *
330
+ * This method returns the equivalent PHP tokens for most scalar types
331
+ * (i.e. "false" for false, "1" for 1 etc.). Strings are always wrapped
332
+ * in double quotes ("). Objects, arrays and resources are formatted as
333
+ * "object", "array" and "resource". If the parameter $prettyDateTime
334
+ * is set to true, {@link \DateTime} objects will be formatted as
335
+ * RFC-3339 dates ("Y-m-d H:i:s").
336
+ *
337
+ * @param mixed $value The value to format as string
338
+ * @param int $format A bitwise combination of the format
339
+ * constants in this class
340
+ *
341
+ * @return string The string representation of the passed value
342
+ */
343
+ private static function formatValue ($ value , $ format = 0 )
344
+ {
345
+ $ isDateTime = $ value instanceof \DateTime || $ value instanceof \DateTimeInterface;
346
+ if (($ format & self ::PRETTY_DATE ) && $ isDateTime ) {
347
+ if (class_exists ('IntlDateFormatter ' )) {
348
+ $ locale = \Locale::getDefault ();
349
+ $ formatter = new \IntlDateFormatter ($ locale , \IntlDateFormatter::MEDIUM , \IntlDateFormatter::SHORT );
350
+ // neither the native nor the stub IntlDateFormatter support
351
+ // DateTimeImmutable as of yet
352
+ if (!$ value instanceof \DateTime) {
353
+ $ value = new \DateTime (
354
+ $ value ->format ('Y-m-d H:i:s.u e ' ),
355
+ $ value ->getTimezone ()
356
+ );
357
+ }
358
+
359
+ return $ formatter ->format ($ value );
360
+ }
361
+
362
+ return $ value ->format ('Y-m-d H:i:s ' );
363
+ }
364
+
365
+ if (is_object ($ value )) {
366
+ if ($ format & self ::OBJECT_TO_STRING && method_exists ($ value , '__toString ' )) {
367
+ return $ value ->__toString ();
368
+ }
369
+
370
+ return 'object ' ;
371
+ }
372
+
373
+ if (is_array ($ value )) {
374
+ return 'array ' ;
375
+ }
376
+
377
+ if (is_string ($ value )) {
378
+ return '" ' .$ value .'" ' ;
379
+ }
380
+
381
+ if (is_resource ($ value )) {
382
+ return 'resource ' ;
383
+ }
384
+
385
+ if (null === $ value ) {
386
+ return 'null ' ;
387
+ }
388
+
389
+ if (false === $ value ) {
390
+ return 'false ' ;
391
+ }
392
+
393
+ if (true === $ value ) {
394
+ return 'true ' ;
395
+ }
396
+
397
+ return (string ) $ value ;
398
+ }
399
+
400
+ /**
401
+ * Returns a string representation of a list of values.
402
+ *
403
+ * @param array $values
404
+ * @param bool $prettyDateTime
405
+ *
406
+ * @return string
407
+ */
408
+ private static function formatValues (array $ values , $ prettyDateTime = false )
409
+ {
410
+ return array_map (function ($ value ) use ($ prettyDateTime ) {
411
+ return self ::formatValue ($ value , $ prettyDateTime );
412
+ }, $ values );
413
+ }
414
+
288
415
/**
289
416
* Constructs a new object with a set of default options.
290
417
*
@@ -382,8 +509,8 @@ public function replace(array $options)
382
509
throw new OptionDefinitionException ('Options cannot be replaced anymore once options have been read. ' );
383
510
}
384
511
385
- $ this ->options = array ();
386
- $ this ->lazy = array ();
512
+ $ this ->options = array ();
513
+ $ this ->lazy = array ();
387
514
$ this ->normalizers = array ();
388
515
389
516
foreach ($ options as $ option => $ value ) {
@@ -424,7 +551,7 @@ public function overload($option, $value)
424
551
$ reflClosure = is_array ($ value )
425
552
? new \ReflectionMethod ($ value [0 ], $ value [1 ])
426
553
: new \ReflectionFunction ($ value );
427
- $ params = $ reflClosure ->getParameters ();
554
+ $ params = $ reflClosure ->getParameters ();
428
555
429
556
if (isset ($ params [0 ]) && null !== ($ class = $ params [0 ]->getClass ()) && __CLASS__ === $ class ->name ) {
430
557
// Initialize the option if no previous value exists
@@ -487,7 +614,7 @@ public function get($option)
487
614
*
488
615
* @param string $option The option name.
489
616
*
490
- * @return bool Whether the option exists.
617
+ * @return bool Whether the option exists.
491
618
*/
492
619
public function has ($ option )
493
620
{
@@ -527,8 +654,8 @@ public function clear()
527
654
throw new OptionDefinitionException ('Options cannot be cleared anymore once options have been read. ' );
528
655
}
529
656
530
- $ this ->options = array ();
531
- $ this ->lazy = array ();
657
+ $ this ->options = array ();
658
+ $ this ->lazy = array ();
532
659
$ this ->normalizers = array ();
533
660
}
534
661
@@ -567,7 +694,7 @@ public function all()
567
694
*
568
695
* @param string $option The option name.
569
696
*
570
- * @return bool Whether the option exists.
697
+ * @return bool Whether the option exists.
571
698
*
572
699
* @see \ArrayAccess::offsetExists()
573
700
*/
@@ -746,7 +873,7 @@ private function normalizeOption($option)
746
873
/** @var \Closure $normalizer */
747
874
$ normalizer = $ this ->normalizers [$ option ];
748
875
749
- $ this ->lock [$ option ] = true ;
876
+ $ this ->lock [$ option ] = true ;
750
877
$ this ->options [$ option ] = $ normalizer ($ this , array_key_exists ($ option , $ this ->options ) ? $ this ->options [$ option ] : null );
751
878
unset($ this ->lock [$ option ]);
752
879
0 commit comments