Passed
Push — master ( 2a2a20...6f21b5 )
by Alexey
02:23
created

src/Mutator/MutatorResolver.php (1 issue)

1
<?php
2
/**
3
 * This code is licensed under the BSD 3-Clause License.
4
 *
5
 * Copyright (c) 2017, Maks Rafalko
6
 * All rights reserved.
7
 *
8
 * Redistribution and use in source and binary forms, with or without
9
 * modification, are permitted provided that the following conditions are met:
10
 *
11
 * * Redistributions of source code must retain the above copyright notice, this
12
 *   list of conditions and the following disclaimer.
13
 *
14
 * * Redistributions in binary form must reproduce the above copyright notice,
15
 *   this list of conditions and the following disclaimer in the documentation
16
 *   and/or other materials provided with the distribution.
17
 *
18
 * * Neither the name of the copyright holder nor the names of its
19
 *   contributors may be used to endorse or promote products derived from
20
 *   this software without specific prior written permission.
21
 *
22
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
23
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
25
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
26
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
28
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
29
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32
 */
33
34
declare(strict_types=1);
35
36
namespace Infection\Mutator;
37
38
use function array_key_exists;
39
use function array_merge_recursive;
40
use function class_exists;
41
use function count;
42
use InvalidArgumentException;
43
use function Safe\sprintf;
44
use stdClass;
45
46
/**
47
 * @internal
48
 */
49
final class MutatorResolver
50
{
51
    private const GLOBAL_IGNORE_SETTING = 'global-ignore';
52
53
    /**
54
     * Resolves the given hashmap of enabled, disabled or configured mutators
55
     * and profiles into a hashmap of mutator raw settings by their mutator
56
     * class name.
57
     *
58
     * @param array<string, bool|stdClass> $mutatorSettings
59
     *
60
     * @return array<class-string<Mutator&ConfigurableMutator>, mixed[]>
0 ignored issues
show
Documentation Bug introduced by
The doc comment array<class-string<Mutat...rableMutator>, mixed[]> at position 2 could not be parsed: Unknown type name 'class-string' at position 2 in array<class-string<Mutator&ConfigurableMutator>, mixed[]>.
Loading history...
61
     */
62
    public function resolve(array $mutatorSettings): array
63
    {
64
        $mutators = [];
65
66
        $globalSettings = [];
67
68
        foreach ($mutatorSettings as $mutatorOrProfileOrGlobalSettingKey => $setting) {
69
            if ($mutatorOrProfileOrGlobalSettingKey === self::GLOBAL_IGNORE_SETTING) {
70
                /** @var string[] $globalSetting */
71
                $globalSetting = $setting;
72
73
                $globalSettings = ['ignore' => $globalSetting];
74
                unset($mutatorSettings[self::GLOBAL_IGNORE_SETTING]);
75
76
                break;
77
            }
78
        }
79
80
        foreach ($mutatorSettings as $mutatorOrProfile => $setting) {
81
            $resolvedSettings = self::resolveSettings($setting, $globalSettings);
82
83
            if (array_key_exists($mutatorOrProfile, ProfileList::ALL_PROFILES)) {
84
                self::registerFromProfile(
85
                    $mutatorOrProfile,
86
                    $resolvedSettings,
87
                    $mutators
88
                );
89
90
                continue;
91
            }
92
93
            if (array_key_exists($mutatorOrProfile, ProfileList::ALL_MUTATORS)) {
94
                self::registerFromName(
95
                    $mutatorOrProfile,
96
                    $resolvedSettings,
97
                    $mutators
98
                );
99
100
                continue;
101
            }
102
103
            throw new InvalidArgumentException(sprintf(
104
                'The profile or mutator "%s" was not recognized.',
105
                $mutatorOrProfile
106
            ));
107
        }
108
109
        return $mutators;
110
    }
111
112
    /**
113
     * @param stdClass|bool $settings
114
     * @param array<string, string[]> $globalSettings
115
     *
116
     * @return array<string, mixed[]>|bool
117
     */
118
    private static function resolveSettings($settings, array $globalSettings)
119
    {
120
        if ($settings === false) {
121
            return false;
122
        }
123
124
        if (count($globalSettings) === 0) {
125
            return (array) $settings;
126
        }
127
128
        if ($settings === true) {
129
            return $globalSettings;
130
        }
131
132
        return array_merge_recursive($globalSettings, (array) $settings);
133
    }
134
135
    /**
136
     * @param array<string, mixed[]>|bool $settings
137
     * @param array<string, array<string, string>> $mutators
138
     */
139
    private static function registerFromProfile(
140
        string $profile,
141
        $settings,
142
        array &$mutators
143
    ): void {
144
        foreach (ProfileList::ALL_PROFILES[$profile] as $mutatorOrProfile) {
145
            /** @var string $mutatorOrProfile */
146
147
            // A profile may refer to another collection of profiles
148
            if (array_key_exists($mutatorOrProfile, ProfileList::ALL_PROFILES)) {
149
                self::registerFromProfile(
150
                    $mutatorOrProfile,
151
                    $settings,
152
                    $mutators
153
                );
154
155
                continue;
156
            }
157
158
            if (class_exists($mutatorOrProfile, true)) {
159
                self::registerFromClass(
160
                    $mutatorOrProfile,
161
                    $settings,
162
                    $mutators
163
                );
164
165
                continue;
166
            }
167
168
            throw new InvalidArgumentException(sprintf(
169
                'The "%s" profile contains the "%s" mutator which was '
170
                . 'not recognized.',
171
                $profile,
172
                $mutatorOrProfile
173
            ));
174
        }
175
    }
176
177
    /**
178
     * @param array<string, mixed[]>|bool $settings
179
     * @param array<string, array<string, string>> $mutators
180
     */
181
    private static function registerFromName(
182
        string $mutator,
183
        $settings,
184
        array &$mutators
185
    ): void {
186
        if (!array_key_exists($mutator, ProfileList::ALL_MUTATORS)) {
187
            throw new InvalidArgumentException(sprintf(
188
                'The "%s" mutator/profile was not recognized.',
189
                $mutator
190
            ));
191
        }
192
193
        self::registerFromClass(
194
            ProfileList::ALL_MUTATORS[$mutator],
195
            $settings,
196
            $mutators
197
        );
198
    }
199
200
    /**
201
     * @param array<string, string[]>|bool $settings
202
     * @param array<string, string[]> $mutators
203
     */
204
    private static function registerFromClass(
205
        string $mutatorClassName,
206
        $settings,
207
        array &$mutators
208
    ): void {
209
        if ($settings === false) {
210
            unset($mutators[$mutatorClassName]);
211
        } else {
212
            $mutators[$mutatorClassName] = (array) $settings;
213
        }
214
    }
215
}
216