Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
30 / 30
100.00% covered (success)
100.00%
3 / 3
CRAP
100.00% covered (success)
100.00%
1 / 1
DynamicDefaultValues
100.00% covered (success)
100.00%
30 / 30
100.00% covered (success)
100.00%
3 / 3
9
100.00% covered (success)
100.00%
1 / 1
 __construct
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 applyDynamicDefaults
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 computeDefaultFor
100.00% covered (success)
100.00%
25 / 25
100.00% covered (success)
100.00%
1 / 1
6
1<?php
2
3namespace MediaWiki\Settings;
4
5use LogicException;
6use MediaWiki\Settings\Config\ConfigBuilder;
7use MediaWiki\Settings\Config\ConfigSchema;
8
9class DynamicDefaultValues {
10
11    private ConfigSchema $configSchema;
12
13    /**
14     * @var array
15     */
16    private $declarations;
17
18    /**
19     * @param ConfigSchema $configSchema
20     */
21    public function __construct( ConfigSchema $configSchema ) {
22        $this->configSchema = $configSchema;
23        $this->declarations = $this->configSchema->getDynamicDefaults();
24    }
25
26    /**
27     * Compute dynamic defaults for settings that have them defined.
28     *
29     * @param ConfigBuilder $configBuilder
30     *
31     * @return void
32     */
33    public function applyDynamicDefaults( ConfigBuilder $configBuilder ): void {
34        $alreadyComputed = [];
35
36        foreach ( $this->declarations as $key => $unused ) {
37            $this->computeDefaultFor( $key, $configBuilder, $alreadyComputed );
38        }
39    }
40
41    /**
42     * Compute dynamic default for a setting, recursively computing any dependencies.
43     *
44     * @param string $key Name of setting
45     * @param ConfigBuilder $configBuilder
46     * @param array &$alreadyComputed Map whose keys are the names of settings whose dynamic
47     *   defaults have already been computed
48     * @param array $currentlyComputing Ordered map whose keys are the names of settings whose
49     *   dynamic defaults are currently being computed, for cycle detection.
50     */
51    private function computeDefaultFor(
52        string $key,
53        ConfigBuilder $configBuilder,
54        array &$alreadyComputed = [],
55        array $currentlyComputing = []
56    ): void {
57        if ( !isset( $this->declarations[ $key ] ) || isset( $alreadyComputed[ $key ] ) ) {
58            return;
59        }
60        if ( isset( $currentlyComputing[ $key ] ) ) {
61            throw new LogicException(
62                'Cyclic dependency when computing dynamic default: ' .
63                implode( ' -> ', array_keys( $currentlyComputing ) ) . " -> $key"
64            );
65        }
66        if (
67            $configBuilder->get( $key ) !==
68            $this->configSchema->getDefaultFor( $key )
69        ) {
70            // Default was already overridden, nothing more to do
71            $alreadyComputed[ $key ] = true;
72
73            return;
74        }
75
76        $currentlyComputing[ $key ] = true;
77
78        $callback = $this->declarations[ $key ]['callback'];
79        $argNames = $this->declarations[ $key ]['use'] ?? [];
80        $args = [];
81
82        foreach ( $argNames as $argName ) {
83            $this->computeDefaultFor(
84                $argName,
85                $configBuilder,
86                $alreadyComputed,
87                $currentlyComputing
88            );
89            $args[] = $configBuilder->get( $argName );
90        }
91
92        $configBuilder->set( $key, $callback( ...$args ) );
93
94        $alreadyComputed[ $key ] = true;
95    }
96}