Test Failed
Push — develop ( c4a2cb...83a5b5 )
by Paul
07:40
created

Shortcode   A

Complexity

Total Complexity 32

Size/Duplication

Total Lines 236
Duplicated Lines 0 %

Test Coverage

Coverage 8.18%

Importance

Changes 7
Bugs 1 Features 1
Metric Value
wmc 32
eloc 108
c 7
b 1
f 1
dl 0
loc 236
ccs 9
cts 110
cp 0.0818
rs 9.84

20 Methods

Rating   Name   Duplication   Size   Complexity  
A attributes() 0 14 1
A __construct() 0 8 1
A build() 0 7 1
A debug() 0 13 3
A buildBlock() 0 3 1
A normalize() 0 15 4
A register() 0 10 2
A tag() 0 4 1
A buildShortcode() 0 3 1
A displayOptions() 0 3 1
A normalizeHide() 0 5 1
A normalizeAssignedUsers() 0 4 1
A normalizeAssignedTerms() 0 5 1
A hideOptions() 0 3 1
A normalizeLabels() 0 18 3
A defaults() 0 5 1
A normalizeAssignedPosts() 0 13 4
A settings() 0 6 1
A hasVisibleFields() 0 10 2
A options() 0 5 1
1
<?php
2
3
namespace GeminiLabs\SiteReviews\Shortcodes;
4
5
use GeminiLabs\SiteReviews\Contracts\ShortcodeContract;
6
use GeminiLabs\SiteReviews\Database\ShortcodeOptionManager;
7
use GeminiLabs\SiteReviews\Defaults\DefaultsAbstract;
8
use GeminiLabs\SiteReviews\Helper;
9
use GeminiLabs\SiteReviews\Helpers\Arr;
10
use GeminiLabs\SiteReviews\Helpers\Cast;
11
use GeminiLabs\SiteReviews\Helpers\Str;
12
use GeminiLabs\SiteReviews\Modules\Html\Builder;
13
use GeminiLabs\SiteReviews\Modules\Multilingual;
14
use GeminiLabs\SiteReviews\Modules\Rating;
15
use GeminiLabs\SiteReviews\Modules\Sanitizer;
16
use GeminiLabs\SiteReviews\Modules\Style;
17
18
abstract class Shortcode implements ShortcodeContract
19
{
20
    public array $args;
21
    public string $debug;
22
    public string $description;
23
    public string $from;
24 8
    public string $name;
25
    public string $tag;
26 8
27
    public function __construct()
28
    {
29
        $this->args = [];
30
        $this->debug = '';
31
        $this->description = $this->description();
32
        $this->from = '';
33
        $this->name = $this->name();
34
        $this->tag = $this->tag();
35
    }
36
37
    public function attributes(array $values, string $from = 'function'): array
38
    {
39
        $attributes = $this->defaults()->dataAttributes($values);
40
        $attributes = wp_parse_args($attributes, [
41
            'class' => glsr(Style::class)->styleClasses(),
42
            'data-from' => $from,
43
            'data-shortcode' => $this->tag,
44
            'id' => Arr::get($values, 'id'),
45
        ]);
46
        unset($attributes['data-id']);
47
        unset($attributes['data-form_id']);
48
        $attributes = glsr()->filterArray("shortcode/{$this->tag}/attributes", $attributes, $this);
49
        $attributes = array_map('esc_attr', $attributes);
50
        return $attributes;
51
    }
52
53
    public function build($args = [], string $from = 'shortcode'): string
54
    {
55
        $this->normalize(wp_parse_args($args), $from);
56
        $template = $this->buildTemplate();
57
        $attributes = $this->attributes($this->args, $from);
58
        $html = glsr(Builder::class)->div($template, $attributes);
59
        return sprintf('%s%s', $this->debug, $html);
60
    }
61
62
    /**
63
     * @param string|array $args
64
     */
65
    public function buildBlock($args = []): string
66
    {
67
        return $this->build(wp_parse_args($args), 'block');
68
    }
69
70
    /**
71
     * @param string|array $args
72
     */
73
    public function buildShortcode($args = []): string
74
    {
75
        return $this->build(wp_parse_args($args), 'shortcode');
76
    }
77
78
    public function defaults(): DefaultsAbstract
79
    {
80
        $classname = str_replace('Shortcodes\\', 'Defaults\\', get_class($this));
81
        $classname = str_replace('Shortcode', 'Defaults', $classname);
82
        return glsr($classname);
0 ignored issues
show
Bug Best Practice introduced by
The expression return glsr($classname) could return the type callable which is incompatible with the type-hinted return GeminiLabs\SiteReviews\Defaults\DefaultsAbstract. Consider adding an additional type-check to rule them out.
Loading history...
83 8
    }
84
85 8
    public function hasVisibleFields(array $args = []): bool
86 8
    {
87
        if (!empty($args)) {
88
            $this->normalize($args);
89
        }
90
        $defaults = $this->options('hide');
91
        $hide = $this->args['hide'] ?? [];
92
        $hide = array_flip(Arr::consolidate($hide));
93
        unset($defaults['if_empty'], $hide['if_empty']);
94
        return !empty(array_diff_key($defaults, $hide));
95
    }
96
97
    public function normalize(array $args, string $from = ''): ShortcodeContract
98
    {
99
        if (!empty($from)) {
100
            $this->from = $from;
101
        }
102
        $args = glsr()->filterArray('shortcode/args', $args, $this->tag);
103
        $args = $this->defaults()->unguardedRestrict($args);
104
        foreach ($args as $key => &$value) {
105
            $method = Helper::buildMethodName('normalize', $key);
106
            if (method_exists($this, $method)) {
107
                $value = call_user_func([$this, $method], $value, $args);
108
            }
109
        }
110
        $this->args = $args;
111
        return $this;
112
    }
113
114
    /**
115
     * Returns the options for a shortcode setting. Results are filtered
116
     * by the "site-reviews/shortcode/options/{$options}" hook.
117
     */
118
    public function options(string $option, array $args = []): array
119
    {
120
        $args['option'] = $option;
121
        $args['shortcode'] = $this->tag;
122
        return call_user_func([glsr(ShortcodeOptionManager::class), $option], $args);
123
    }
124
125
    public function register(): void
126
    {
127
        if (!function_exists('add_shortcode')) {
128
            return;
129
        }
130
        $shortcode = (new \ReflectionClass($this))->getShortName();
131
        $shortcode = Str::snakeCase($shortcode);
132
        $shortcode = str_replace('_shortcode', '', $shortcode);
133
        add_shortcode($shortcode, fn ($atts) => $this->build($atts));
134
        glsr()->append('shortcodes', get_class($this), $shortcode);
135
    }
136
137
    /**
138
     * Returns the filtered shortcode settings configuration.
139
     */
140
    public function settings(): array
141
    {
142
        $config = $this->config();
143
        $config = glsr()->filterArray("shortcode/{$this->tag}/config", $config, $this);
144
        $config = glsr()->filterArray('shortcode/config', $config, $this->tag, $this);
145
        return $config;
146
    }
147
148
    public function tag(): string
149
    {
150
        return Str::snakeCase(
151
            str_replace('Shortcode', '', (new \ReflectionClass($this))->getShortName())
152
        );
153
    }
154
155
    /**
156
     * Returns the unfiltered shortcode settings configuration.
157
     */
158
    abstract protected function config(): array;
159
160
    protected function debug(array $data = []): void
161
    {
162
        if (empty($this->args['debug']) || 'shortcode' !== $this->from) {
163
            return;
164
        }
165
        $data = wp_parse_args($data, [
166
            'args' => $this->args,
167
            'shortcode' => $this->tag,
168
        ]);
169
        ksort($data);
170
        ob_start();
171
        glsr_debug($data);
172
        $this->debug = ob_get_clean();
173
    }
174
175
    protected function displayOptions(): array
176
    {
177
        return [];
178
    }
179
180
    protected function hideOptions(): array
181
    {
182
        return [];
183
    }
184
185
    /**
186
     * @param string $value
187
     */
188
    protected function normalizeAssignedPosts($value): string
189
    {
190
        $values = Cast::toArray($value);
191
        $postTypes = [];
192
        foreach ($values as $postType) {
193
            if (!is_numeric($postType) && post_type_exists((string) $postType)) {
194
                $postTypes[] = $postType;
195
            }
196
        }
197
        $values = glsr(Sanitizer::class)->sanitizePostIds($values);
198
        $values = glsr(Multilingual::class)->getPostIdsForAllLanguages($values);
199
        $values = array_merge($values, $postTypes);
200
        return implode(',', $values);
201
    }
202
203
    /**
204
     * @param string $value
205
     */
206
    protected function normalizeAssignedTerms($value): string
207
    {
208
        $values = glsr(Sanitizer::class)->sanitizeTermIds($value);
209
        $values = glsr(Multilingual::class)->getTermIdsForAllLanguages($values);
210
        return implode(',', $values);
211
    }
212
213
    /**
214
     * @param string $value
215
     */
216
    protected function normalizeAssignedUsers($value): string
217 8
    {
218
        $values = glsr(Sanitizer::class)->sanitizeUserIds($value);
219 8
        return implode(',', $values);
220 8
    }
221 8
222
    /**
223
     * @param string|array $value
224
     */
225
    protected function normalizeHide($value): array
226
    {
227
        $hideKeys = array_keys($this->options('hide'));
228
        return array_filter(Cast::toArray($value),
229
            fn ($value) => in_array($value, $hideKeys)
230
        );
231
    }
232
233
    /**
234
     * @param string $value
235
     */
236
    protected function normalizeLabels($value): array
237
    {
238
        $defaults = [
239
            __('Excellent', 'site-reviews'),
240
            __('Very good', 'site-reviews'),
241
            __('Average', 'site-reviews'),
242
            __('Poor', 'site-reviews'),
243
            __('Terrible', 'site-reviews'),
244
        ];
245
        $maxRating = Rating::max();
246
        $defaults = array_pad(array_slice($defaults, 0, $maxRating), $maxRating, '');
247
        $labels = array_map('trim', explode(',', $value));
248
        foreach ($defaults as $i => $label) {
249
            if (!empty($labels[$i])) {
250
                $defaults[$i] = $labels[$i];
251
            }
252
        }
253
        return array_combine(range($maxRating, 1), $defaults);
254
    }
255
}
256