23
23
*/
24
24
class Optionsimplements \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 ($ optionTypesas $ 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 ($ acceptedValuesas $ 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 ($ optionTypesas $ 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 =$ valueinstanceof \DateTime ||$ valueinstanceof \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 (!$ valueinstanceof \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 ($ optionsas $ 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