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,61 +263,12 @@ 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
- /**
254
- * Attempts to convert a given value into a (short) string or integer.
255
- *
256
- * @param mixed $value The value to convert.
257
- *
258
- * @return string The printable value.
259
- */
260
- private static function createPrintableValue ($ value )
261
- {
262
- if (is_object ($ value )) {
263
- return get_class ($ value );
264
- }
265
-
266
- if (is_array ($ value )) {
267
- return 'Array ' ;
268
- }
269
-
270
- if (null === $ value ) {
271
- return 'null ' ;
272
- }
273
-
274
- if (is_string ($ value )) {
275
- return '" ' .$ value .'" ' ;
276
- }
277
-
278
- if (is_bool ($ value )) {
279
- return (string ) var_export ($ value , true );
280
- }
281
-
282
- return (string ) $ value ;
283
- }
284
-
285
- /**
286
- * Attempts to convert a given value's type into a printable string.
287
- *
288
- * @param mixed $value The value to convert.
289
- *
290
- * @return string Objects get returned as their classname,
291
- * all other values are put through gettype().
292
- */
293
- private static function createPrintableType ($ value )
294
- {
295
- if (is_object ($ value )) {
296
- return get_class ($ value );
297
- } else {
298
- return gettype ($ value );
299
- }
300
- }
301
-
302
272
/**
303
273
* Validates that the given option values match the accepted values and
304
274
* throws an exception otherwise.
@@ -318,7 +288,7 @@ public static function validateValues(array $options, array $acceptedValues)
318
288
foreach ($ acceptedValues as $ option => $ optionValues ) {
319
289
if (array_key_exists ($ option , $ options )) {
320
290
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 )));
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) )));
322
292
}
323
293
324
294
if (is_callable ($ optionValues ) && !call_user_func ($ optionValues , $ options [$ option ])) {
@@ -328,6 +298,118 @@ public static function validateValues(array $options, array $acceptedValues)
328
298
}
329
299
}
330
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
+ 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
+
363
+ if (is_object ($ value )) {
364
+ if ($ format & self ::OBJECT_TO_STRING && method_exists ($ value , '__toString ' )) {
365
+ return $ value ->__toString ();
366
+ }
367
+
368
+ return 'object ' ;
369
+ }
370
+
371
+ if (is_array ($ value )) {
372
+ return 'array ' ;
373
+ }
374
+
375
+ if (is_string ($ value )) {
376
+ return '" ' . $ value . '" ' ;
377
+ }
378
+
379
+ if (is_resource ($ value )) {
380
+ return 'resource ' ;
381
+ }
382
+
383
+ if (null === $ value ) {
384
+ return 'null ' ;
385
+ }
386
+
387
+ if (false === $ value ) {
388
+ return 'false ' ;
389
+ }
390
+
391
+ if (true === $ value ) {
392
+ return 'true ' ;
393
+ }
394
+
395
+ return (string ) $ value ;
396
+ }
397
+
398
+ /**
399
+ * Returns a string representation of a list of values.
400
+ *
401
+ * @param array $values
402
+ * @param bool $prettyDateTime
403
+ *
404
+ * @return string
405
+ */
406
+ private static function formatValues (array $ values , $ prettyDateTime = false )
407
+ {
408
+ return array_map (function ($ value ) use ($ prettyDateTime ) {
409
+ return self ::formatValue ($ value , $ prettyDateTime );
410
+ }, $ values );
411
+ }
412
+
331
413
/**
332
414
* Constructs a new object with a set of default options.
333
415
*
@@ -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