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.
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 ();
@@ -155,7 +174,7 @@ public static function validateNames(array $options, $acceptedOptions, $namesAsK
155
174
ksort ($ diff );
156
175
157
176
throw new InvalidOptionsException (sprintf (
158
- (count ($ diff ) > 1 ? 'The options "%s" do not exist. ' : 'The option "%s" does not exist. ' ). ' Known options are: "%s" ' ,
177
+ (count ($ diff ) > 1 ? 'The options "%s" do not exist. ' : 'The option "%s" does not exist. ' ) . ' Known options are: "%s" ' ,
159
178
implode ('", " ' , array_keys ($ diff )),
160
179
implode ('", " ' , array_keys ($ acceptedOptions ))
161
180
));
@@ -228,11 +247,11 @@ 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 ) {
235
- $ isFunction = 'is_ ' . $ type ;
254
+ $ isFunction = 'is_ ' . $ type ;
236
255
237
256
if (function_exists ($ isFunction ) && $ isFunction ($ value )) {
238
257
continue 2 ;
@@ -244,88 +263,151 @@ public static function validateTypes(array $options, array $acceptedTypes)
244
263
throw new InvalidOptionsException (sprintf (
245
264
'The option "%s" with value "%s" is expected to be of type "%s" ' ,
246
265
$ option ,
247
- self ::createPrintableValue ($ value ),
248
- implode ('", " ' , array_map ( function ( $ optionType ) { return self ::createPrintableType ( $ optionType ); }, $ optionTypes ))
266
+ self ::formatValue ($ value ),
267
+ implode ('", " ' , self ::formatTypesOf ( $ optionTypes ))
249
268
));
250
269
}
251
270
}
252
271
253
272
/**
254
- * Attempts to convert a given value into a (short) string or integer.
273
+ * Validates that the given option values match the accepted values and
274
+ * throws an exception otherwise.
275
+ *
276
+ * @param array $options A list of option names and values
277
+ * @param array $acceptedValues A mapping of option names to accepted option
278
+ * values. The option values must be given as
279
+ * arrays
280
+ *
281
+ * @throws InvalidOptionsException If any of the values does not match the
282
+ * accepted values of the option
283
+ *
284
+ * @since 2.6
285
+ */
286
+ public static function validateValues (array $ options , array $ acceptedValues )
287
+ {
288
+ foreach ($ acceptedValues as $ option => $ optionValues ) {
289
+ if (array_key_exists ($ option , $ options )) {
290
+ if (is_array ($ optionValues ) && !in_array ($ options [$ option ], $ optionValues , true )) {
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 ))));
292
+ }
293
+
294
+ if (is_callable ($ optionValues ) && !call_user_func ($ optionValues , $ options [$ option ])) {
295
+ throw new InvalidOptionsException (sprintf ('The option "%s" has the value "%s", which it is not valid ' , $ option , $ options [$ option ]));
296
+ }
297
+ }
298
+ }
299
+ }
300
+
301
+ /**
302
+ * Returns a string representation of the type of the value.
303
+ *
304
+ * @param mixed $value
255
305
*
256
- * @param mixed $value The value to convert.
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
257
315
*
258
- * @return string The printable value.
316
+ * @return array
259
317
*/
260
- private static function createPrintableValue ( $ value )
318
+ private static function formatTypesOf ( array $ optionTypes )
261
319
{
320
+ return array_map (function ($ optionType ) {
321
+ return self ::formatTypeOf ($ optionType );
322
+ }, $ optionTypes );
323
+ }
324
+
325
+ /**
326
+ * Returns a string representation of the value.
327
+ *
328
+ * This method returns the equivalent PHP tokens for most scalar types
329
+ * (i.e. "false" for false, "1" for 1 etc.). Strings are always wrapped
330
+ * in double quotes ("). Objects, arrays and resources are formatted as
331
+ * "object", "array" and "resource". If the parameter $prettyDateTime
332
+ * is set to true, {@link \DateTime} objects will be formatted as
333
+ * RFC-3339 dates ("Y-m-d H:i:s").
334
+ *
335
+ * @param mixed $value The value to format as string
336
+ * @param int $format A bitwise combination of the format
337
+ * constants in this class
338
+ *
339
+ * @return string The string representation of the passed value
340
+ */
341
+ private static function formatValue ($ value , $ format = 0 )
342
+ {
343
+ $ isDateTime = $ value instanceof \DateTime || $ value instanceof \DateTimeInterface;
344
+ if (($ format & self ::PRETTY_DATE ) && $ isDateTime ) {
345
+ if (class_exists ('IntlDateFormatter ' )) {
346
+ $ locale = \Locale::getDefault ();
347
+ $ formatter = new \IntlDateFormatter ($ locale , \IntlDateFormatter::MEDIUM , \IntlDateFormatter::SHORT );
348
+ // neither the native nor the stub IntlDateFormatter support
349
+ // DateTimeImmutable as of yet
350
+ if (!$ value instanceof \DateTime) {
351
+ $ value = new \DateTime (
352
+ $ value ->format ('Y-m-d H:i:s.u e ' ),
353
+ $ value ->getTimezone ()
354
+ );
355
+ }
356
+
357
+ return $ formatter ->format ($ value );
358
+ }
359
+
360
+ return $ value ->format ('Y-m-d H:i:s ' );
361
+ }
362
+
262
363
if (is_object ($ value )) {
263
- return get_class ($ value );
364
+ if ($ format & self ::OBJECT_TO_STRING && method_exists ($ value , '__toString ' )) {
365
+ return $ value ->__toString ();
366
+ }
367
+
368
+ return 'object ' ;
264
369
}
265
370
266
371
if (is_array ($ value )) {
267
- return 'Array ' ;
372
+ return 'array ' ;
373
+ }
374
+
375
+ if (is_string ($ value )) {
376
+ return '" ' . $ value . '" ' ;
377
+ }
378
+
379
+ if (is_resource ($ value )) {
380
+ return 'resource ' ;
268
381
}
269
382
270
383
if (null === $ value ) {
271
384
return 'null ' ;
272
385
}
273
386
274
- if (is_string ( $ value) ) {
275
- return '" ' . $ value . ' " ' ;
387
+ if (false === $ value ) {
388
+ return 'false ' ;
276
389
}
277
390
278
- if (is_bool ( $ value) ) {
279
- return ( string ) var_export ( $ value , true ) ;
391
+ if (true === $ value ) {
392
+ return ' true ' ;
280
393
}
281
394
282
395
return (string ) $ value ;
283
396
}
284
397
285
398
/**
286
- * Attempts to convert a given value's type into a printable string .
399
+ * Returns a string representation of a list of values .
287
400
*
288
- * @param mixed $value The value to convert.
401
+ * @param array $values
402
+ * @param bool $prettyDateTime
289
403
*
290
- * @return string Objects get returned as their classname,
291
- * all other values are put through gettype().
404
+ * @return string
292
405
*/
293
- private static function createPrintableType ( $ value )
406
+ private static function formatValues ( array $ values , $ prettyDateTime = false )
294
407
{
295
- if (is_object ($ value )) {
296
- return get_class ($ value );
297
- } else {
298
- return gettype ($ value );
299
- }
300
- }
301
-
302
- /**
303
- * Validates that the given option values match the accepted values and
304
- * throws an exception otherwise.
305
- *
306
- * @param array $options A list of option names and values
307
- * @param array $acceptedValues A mapping of option names to accepted option
308
- * values. The option values must be given as
309
- * arrays
310
- *
311
- * @throws InvalidOptionsException If any of the values does not match the
312
- * accepted values of the option
313
- *
314
- * @since 2.6
315
- */
316
- public static function validateValues (array $ options , array $ acceptedValues )
317
- {
318
- foreach ($ acceptedValues as $ option => $ optionValues ) {
319
- if (array_key_exists ($ option , $ options )) {
320
- if (is_array ($ optionValues ) && !in_array ($ options [$ option ], $ optionValues , true )) {
321
- throw new InvalidOptionsException (sprintf ('The option "%s" has the value "%s", but is expected to be one of "%s" ' , $ option , $ options [$ option ], implode ('", " ' , $ optionValues )));
322
- }
323
-
324
- if (is_callable ($ optionValues ) && !call_user_func ($ optionValues , $ options [$ option ])) {
325
- throw new InvalidOptionsException (sprintf ('The option "%s" has the value "%s", which it is not valid ' , $ option , $ options [$ option ]));
326
- }
327
- }
328
- }
408
+ return array_map (function ($ value ) use ($ prettyDateTime ) {
409
+ return self ::formatValue ($ value , $ prettyDateTime );
410
+ }, $ values );
329
411
}
330
412
331
413
/**
@@ -425,8 +507,8 @@ public function replace(array $options)
425
507
throw new OptionDefinitionException ('Options cannot be replaced anymore once options have been read. ' );
426
508
}
427
509
428
- $ this ->options = array ();
429
- $ this ->lazy = array ();
510
+ $ this ->options = array ();
511
+ $ this ->lazy = array ();
430
512
$ this ->normalizers = array ();
431
513
432
514
foreach ($ options as $ option => $ value ) {
@@ -467,7 +549,7 @@ public function overload($option, $value)
467
549
$ reflClosure = is_array ($ value )
468
550
? new \ReflectionMethod ($ value [0 ], $ value [1 ])
469
551
: new \ReflectionFunction ($ value );
470
- $ params = $ reflClosure ->getParameters ();
552
+ $ params = $ reflClosure ->getParameters ();
471
553
472
554
if (isset ($ params [0 ]) && null !== ($ class = $ params [0 ]->getClass ()) && __CLASS__ === $ class ->name ) {
473
555
// Initialize the option if no previous value exists
@@ -530,7 +612,7 @@ public function get($option)
530
612
*
531
613
* @param string $option The option name.
532
614
*
533
- * @return bool Whether the option exists.
615
+ * @return bool Whether the option exists.
534
616
*/
535
617
public function has ($ option )
536
618
{
@@ -570,8 +652,8 @@ public function clear()
570
652
throw new OptionDefinitionException ('Options cannot be cleared anymore once options have been read. ' );
571
653
}
572
654
573
- $ this ->options = array ();
574
- $ this ->lazy = array ();
655
+ $ this ->options = array ();
656
+ $ this ->lazy = array ();
575
657
$ this ->normalizers = array ();
576
658
}
577
659
@@ -610,7 +692,7 @@ public function all()
610
692
*
611
693
* @param string $option The option name.
612
694
*
613
- * @return bool Whether the option exists.
695
+ * @return bool Whether the option exists.
614
696
*
615
697
* @see \ArrayAccess::offsetExists()
616
698
*/
@@ -789,7 +871,7 @@ private function normalizeOption($option)
789
871
/** @var \Closure $normalizer */
790
872
$ normalizer = $ this ->normalizers [$ option ];
791
873
792
- $ this ->lock [$ option ] = true ;
874
+ $ this ->lock [$ option ] = true ;
793
875
$ this ->options [$ option ] = $ normalizer ($ this , array_key_exists ($ option , $ this ->options ) ? $ this ->options [$ option ] : null );
794
876
unset($ this ->lock [$ option ]);
795
877
0 commit comments