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