1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
|
<?php
namespace MediaWiki\Config;
use InvalidArgumentException;
use Wikimedia\Assert\Assert;
/**
* A class for passing options to services. It can be constructed from a Config, and in practice
* most options will be taken from site configuration, but they don't have to be. The options passed
* are copied and will not reflect subsequent updates to site configuration (assuming they're not
* objects).
*
* Services that take this type as a parameter to their constructor should specify a list of the
* keys they expect to receive in an array. The convention is to make it a public const called
* CONSTRUCTOR_OPTIONS. In the constructor, they should call assertRequiredOptions() to make sure
* that they weren't passed too few or too many options. This way it's clear what each class
* depends on, and that it's getting passed the correct set of options. (This means there are no
* optional options. This makes sense for services, since they shouldn't be constructed by
* outside code.)
*
* @newable since 1.36
*
* @since 1.34
*/
class ServiceOptions {
/** @var string[] */
private $keys;
/** @var array */
private $options = [];
/**
* @stable to call since 1.36
*
* @param string[] $keys Which keys to extract from $sources
* @param Config|ServiceOptions|array ...$sources Each source is either a Config object or an array. If the
* same key is present in two sources, the first one takes precedence. Keys that are not in
* $keys are ignored.
* @throws InvalidArgumentException if one of $keys is not found in any of $sources
*/
public function __construct( array $keys, ...$sources ) {
$this->keys = $keys;
foreach ( $keys as $key ) {
foreach ( $sources as $source ) {
if ( $source instanceof Config ) {
if ( $source->has( $key ) ) {
$this->options[$key] = $source->get( $key );
continue 2;
}
} elseif ( $source instanceof ServiceOptions ) {
if ( array_key_exists( $key, $source->options ) ) {
$this->options[$key] = $source->get( $key );
continue 2;
}
} elseif ( array_key_exists( $key, $source ) ) {
$this->options[$key] = $source[$key];
continue 2;
}
}
throw new InvalidArgumentException( "Key \"$key\" not found in input sources" );
}
}
/**
* Assert that the list of options provided in this instance exactly match $expectedKeys,
* without regard for order.
*
* @param string[] $expectedKeys
*/
public function assertRequiredOptions( array $expectedKeys ) {
if ( $this->keys !== $expectedKeys ) {
$extraKeys = array_diff( $this->keys, $expectedKeys );
$missingKeys = array_diff( $expectedKeys, $this->keys );
Assert::precondition( !$extraKeys && !$missingKeys,
(
$extraKeys
? 'Unsupported options passed: ' . implode( ', ', $extraKeys ) . '!'
: ''
) . ( $extraKeys && $missingKeys ? ' ' : '' ) . (
$missingKeys
? 'Required options missing: ' . implode( ', ', $missingKeys ) . '!'
: ''
)
);
}
}
/**
* @param string $key
* @return mixed
*/
public function get( $key ) {
if ( !array_key_exists( $key, $this->options ) ) {
throw new InvalidArgumentException( "Unrecognized option \"$key\"" );
}
return $this->options[$key];
}
}
|