Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
31 / 31
100.00% covered (success)
100.00%
7 / 7
CRAP
100.00% covered (success)
100.00%
1 / 1
DataProvider
100.00% covered (success)
100.00%
31 / 31
100.00% covered (success)
100.00%
7 / 7
15
100.00% covered (success)
100.00%
1 / 1
 validateConfiguration
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
2
 overrideDefaultsWithConfigRecursive
100.00% covered (success)
100.00%
11 / 11
100.00% covered (success)
100.00%
1 / 1
5
 enhanceConfigPreValidation
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
2
 addAutocomputedProperties
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 processStoreStatus
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
3
 loadValidConfiguration
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 loadValidConfigurationUncached
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2
3namespace MediaWiki\Extension\CommunityConfiguration\Provider;
4
5use StatusValue;
6use stdClass;
7
8class DataProvider extends AbstractProvider {
9
10    /**
11     * Process a StatusValue returned from IConfigurationStore
12     *
13     * This ensures the configuration in $storeStatus is valid.
14     *
15     * @param stdClass $config
16     * @return StatusValue
17     */
18    private function validateConfiguration( stdClass $config ): StatusValue {
19        $validationStatus = $this->getValidator()->validatePermissively( $config );
20        if ( !$validationStatus->isOK() ) {
21            return $validationStatus;
22        }
23
24        return $validationStatus->setResult( true, $config );
25    }
26
27    private function overrideDefaultsWithConfigRecursive( stdClass $defaults, stdClass $config ): stdClass {
28        $merged = clone $defaults;
29
30        foreach ( $config as $configPropertyName => $configPropertyValue ) {
31            if ( property_exists( $merged, $configPropertyName ) ) {
32                if ( is_object( $merged->$configPropertyName ) && is_object( $configPropertyValue ) ) {
33                    $merged->$configPropertyName = $this->overrideDefaultsWithConfigRecursive(
34                        $merged->$configPropertyName,
35                        $configPropertyValue
36                    );
37                } else {
38                    // REVIEW: In particular, this uses any existing config value for an array as is.
39                    // It does not apply defaults to fields in the array elements.
40                    // Not even when those elements are objects.
41                    $merged->$configPropertyName = $configPropertyValue;
42                }
43            } else {
44                $merged->$configPropertyName = $configPropertyValue;
45            }
46        }
47
48        return $merged;
49    }
50
51    private function enhanceConfigPreValidation( stdClass $config ): stdClass {
52        // enhance $config with defaults (if possible)
53        if ( !$this->getValidator()->areSchemasSupported() ) {
54            return $config;
55        }
56
57        $defaultsMap = $this->getValidator()->getSchemaBuilder()->getDefaultsMap();
58
59        $config = $this->overrideDefaultsWithConfigRecursive( $defaultsMap, $config );
60
61        return $config;
62    }
63
64    protected function addAutocomputedProperties( stdClass $config ): stdClass {
65        return $config;
66    }
67
68    /**
69     * Process a store status
70     *
71     * Common logic for both loadValidConfiguration() and loadValidConfigurationUncached().
72     *
73     * This function:
74     *     (1) Enhances config with defaults
75     *     (2) Validates the configuration against the schema
76     *
77     * @param StatusValue $storeStatus Result of IConfigurationStore::loadConfiguration(Uncached)
78     * @return StatusValue
79     */
80    private function processStoreStatus( StatusValue $storeStatus ): StatusValue {
81        if ( !$storeStatus->isOK() ) {
82            return $storeStatus;
83        }
84
85        $result = $this->validateConfiguration(
86            $this->enhanceConfigPreValidation( $storeStatus->getValue() )
87        );
88        if ( !$result->isOK() ) {
89            // an issue occurred, return the StatusValue
90            return $result;
91        }
92
93        return $result->setResult( true, $this->addAutocomputedProperties( $result->getValue() ) );
94    }
95
96    /**
97     * @inheritDoc
98     */
99    public function loadValidConfiguration(): StatusValue {
100        return $this->processStoreStatus( $this->getStore()->loadConfiguration() );
101    }
102
103    /**
104     * @inheritDoc
105     */
106    public function loadValidConfigurationUncached(): StatusValue {
107        return $this->processStoreStatus( $this->getStore()->loadConfigurationUncached() );
108    }
109}