Test Failed
Push — main ( ed15c3...2f7161 )
by Paul
12:26 queued 06:03
created

OptionManager::normalize()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 13
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 1
Bugs 1 Features 0
Metric Value
eloc 10
dl 0
loc 13
ccs 0
cts 6
cp 0
rs 9.9332
c 1
b 1
f 0
cc 2
nc 2
nop 1
crap 6
1
<?php
2
3
namespace GeminiLabs\SiteReviews\Database;
4
5
use GeminiLabs\SiteReviews\Helper;
6
use GeminiLabs\SiteReviews\Helpers\Arr;
7
use GeminiLabs\SiteReviews\Helpers\Cast;
8
use GeminiLabs\SiteReviews\Helpers\Str;
9
use GeminiLabs\SiteReviews\Modules\Migrate;
10
11
/**
12
 * @method array  getArray(string $path = '', $fallback = [])
13
 * @method bool   getBool(string $path = '', $fallback = false)
14
 * @method float  getFloat(string $path = '', $fallback = 0.0)
15
 * @method int    getInt(string $path = '', $fallback = 0)
16 34
 * @method string getString(string $path = '', $fallback = '')
17
 */
18 34
class OptionManager
19 34
{
20
    /**
21
     * @return mixed
22
     */
23
    public function __call(string $method, array $args = [])
24 31
    {
25
        if (!str_starts_with($method, 'get')) {
26 31
            throw new \BadMethodCallException("Method [$method] does not exist.");
27 31
        }
28 31
        $cast = strtolower((string) substr($method, 3));
29
        if (!in_array($cast, ['array', 'bool', 'float', 'int', 'string'])) {
30 31
            throw new \BadMethodCallException("Method [$method] does not exist.");
31 31
        }
32
        $path = Arr::getAs('string', $args, 0);
33
        $fallback = Arr::get($args, 1);
34
        return call_user_func([$this, 'get'], $path, $fallback, $cast);
35
    }
36 1
37
    public function all(): array
38 1
    {
39
        $settings = Arr::consolidate(glsr()->retrieve('settings'));
40
        if (empty($settings)) {
41 4
            $settings = $this->reset();
42
        }
43 4
        return $settings;
44 4
    }
45 4
46 4
    public static function databaseKey(?int $version = null): string
47 4
    {
48 4
        $versions = static::databaseKeys();
49
        if (null === $version) {
50
            $version = glsr()->version('major');
51 4
        }
52 4
        if (array_key_exists($version, $versions)) {
53
            return $versions[$version];
54
        }
55
        return '';
56
    }
57
58
    public static function databaseKeys(): array
59
    {
60
        $keys = [];
61
        $slug = Str::snakeCase(glsr()->id);
62 34
        $version = intval(glsr()->version('major')) + 1;
63
        while (--$version) {
64 34
            if ($version >= 7) {
65 34
                $keys[$version] = $slug; // remove version from settings key in versions >= 7.0
66 34
            } elseif (1 === $version) {
67 34
                $keys[$version] = sprintf('geminilabs_%s_settings', $slug);
68 34
            } elseif (2 === $version) {
69
                $keys[$version] = sprintf('geminilabs_%s-v%s', $slug, $version);
70 34
            } else {
71
                $keys[$version] = sprintf('%s_v%s', $slug, $version);
72
            }
73
        }
74
        return $keys;
75
    }
76
77
    public static function flushSettingsCache(): void
78
    {
79
        $alloptions = wp_load_alloptions(true);
80
        $flushed = false;
81
        foreach (static::databaseKeys() as $option) {
82
            if (isset($alloptions[$option])) {
83
                unset($alloptions[$option]);
84 24
                $flushed = true;
85
            }
86 24
        }
87
        if ($flushed) {
88
            wp_cache_set('alloptions', $alloptions, 'options');
89
        }
90
    }
91
92 21
    /**
93
     * @param mixed $fallback
94 21
     *
95
     * @return mixed
96
     */
97
    public function get(string $path = '', $fallback = '', string $cast = '')
98
    {
99
        $option = Arr::get($this->all(), $path, $fallback);
100
        $path = ltrim(Str::removePrefix($path, 'settings'), '.');
101
        if (!empty($path)) {
102 31
            $path = str_replace('.', '/', $path);
103
            $option = glsr()->filter("option/{$path}", $option);
104 31
        }
105 31
        return Cast::to($cast, $option);
106
    }
107
108
    /**
109
     * This is used when exporting the settings.
110
     */
111
    public function json(): string
112
    {
113
        $all = $this->all();
114
        $all['extra'] = glsr()->filterArray('export/settings/extra', []); // allow addons to export additional data
115
        return (string) wp_json_encode($all, JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_TAG | JSON_NUMERIC_CHECK | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
116
    }
117
118
    public function mergeDefaults(array $defaults): void
119
    {
120
        $saved = Arr::consolidate($this->wp(static::databaseKey(), []));
121
        $defaults = Arr::flatten(Arr::getAs('array', $defaults, 'settings'));
122
        $settings = Arr::flatten(Arr::getAs('array', $saved, 'settings'));
123
        if (empty($defaults) || empty(array_diff_key($defaults, $settings))) {
124
            return;
125
        }
126
        $settings = shortcode_atts($defaults, $settings);
127
        $settings = Arr::unflatten($settings);
128
        $settings['strings'] = Arr::consolidate(Arr::get($saved, 'settings.strings'));
129
        $saved['settings'] = $settings;
130
        $this->replace($saved);
131
    }
132
133
    public function normalize(array $data = []): array
134
    {
135
        $settings = $this->kses($data);
136
        $strings = Arr::get($settings, 'settings.strings', []);
137
        if (!empty(glsr()->settings)) { // access the property directly to prevent an infinite loop
138
            $defaults = glsr()->defaults(); // @phpstan-ignore-line
139
            $defaults = Arr::flatten($defaults);
140
            $settings = Arr::flatten($settings);
141
            $settings = shortcode_atts($defaults, $settings);
142
            $settings = Arr::unflatten($settings);
143
        }
144
        $settings = Arr::set($settings, 'settings.strings', $strings);
145
        return $settings;
146
    }
147
148
    public function previous(): array
149
    {
150
        static::flushSettingsCache();
151
        foreach (static::databaseKeys() as $version => $databaseKey) {
152
            $settings = Arr::consolidate(get_option($databaseKey));
153 31
            if (!empty(array_filter(Arr::getAs('array', $settings, 'settings')))) {
154
                return $settings;
155 31
            }
156 31
        }
157 1
        return [];
158 1
    }
159 1
160
    public function replace(array $settings): bool
161 31
    {
162 31
        if (empty($settings)) {
163
            return false;
164
        }
165
        $settings = $this->normalize($settings);
166
        if (!update_option(static::databaseKey(), $settings)) {
167
            return false;
168
        }
169 31
        $this->reset();
170
        return true;
171 31
    }
172 31
173
    public function reset(): array
174 31
    {
175 31
        $settings = Arr::consolidate($this->wp(static::databaseKey(), []));
176
        if (empty($settings)) {
177 31
            delete_option(static::databaseKey());
178 31
            // glsr(Migrate::class)->reset(); // Do this to migrate any previous version settings
179 31
        }
180
        $settings = $this->normalize($settings);
181
        glsr()->store('settings', $settings);
182
        return $settings;
183
    }
184 31
185
    /**
186 31
     * @param mixed $value
187 31
     */
188 31
    public function set(string $path, $value = ''): bool
189 31
    {
190 31
        $settings = $this->all();
191 31
        $settings = Arr::set($settings, $path, $value);
192 31
        $settings = $this->normalize($settings);
193
        if (!update_option(static::databaseKey(), $settings)) {
194 31
            return false;
195
        }
196
        glsr()->store('settings', $settings);
197 31
        return true;
198
    }
199
200
    public function updateVersion(): void
201
    {
202
        $version = $this->get('version', '0.0.0');
203
        if (glsr()->version !== $version) {
204
            $this->set('version', glsr()->version);
205
            $this->set('version_upgraded_from', $version);
206
        }
207
    }
208
209
    /**
210
     * @param mixed $fallback
211
     *
212
     * @return mixed
213
     */
214
    public function wp(string $path, $fallback = '', string $cast = '')
215
    {
216
        $option = get_option($path, $fallback);
217
        return Cast::to($cast, Helper::ifEmpty($option, $fallback, $strict = true));
218
    }
219
220
    public function kses(array $data): array
221
    {
222
        $data = Arr::flatten($data);
223
        array_walk($data, function (&$value) {
224
            if (is_string($value)) {
225
                $value = wp_kses($value, wp_kses_allowed_html('post'));
226
            }
227
        });
228
        $data = Arr::unflatten($data);
229
        return $data;
230
    }
231
}
232