Passed
Push — develop ( bf5e1a...a01811 )
by Paul
14:43
created

Shortcode   A

Complexity

Total Complexity 37

Size/Duplication

Total Lines 259
Duplicated Lines 0 %

Test Coverage

Coverage 9.52%

Importance

Changes 9
Bugs 1 Features 0
Metric Value
wmc 37
eloc 124
c 9
b 1
f 0
dl 0
loc 259
ccs 14
cts 147
cp 0.0952
rs 9.44

22 Methods

Rating   Name   Duplication   Size   Complexity  
A attributes() 0 16 2
A __construct() 0 8 1
A build() 0 14 3
A buildCallback() 0 4 1
A debug() 0 13 3
A normalize() 0 15 4
A register() 0 8 1
A tag() 0 4 1
A classAttr() 0 21 4
A normalizeHide() 0 5 1
A normalizeAssignedUsers() 0 4 1
A normalizeAssignedTerms() 0 5 1
A wrap() 0 3 1
A hideOptions() 0 3 1
A defaults() 0 5 1
A normalizeAssignedPosts() 0 13 4
A settings() 0 6 1
A normalizeClass() 0 3 1
A hasVisibleFields() 0 10 2
A options() 0 5 1
A enqueue() 0 2 1
A wrapperAttributes() 0 10 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\Sanitizer;
15
use GeminiLabs\SiteReviews\Modules\Style;
16
17
abstract class Shortcode implements ShortcodeContract
18
{
19
    public array $args;
20
    public string $debug;
21
    public string $description;
22
    public string $from;
23
    public string $name;
24
    public string $tag;
25
26 8
    public function __construct()
27
    {
28 8
        $this->args = [];
29 8
        $this->debug = '';
30 8
        $this->description = $this->description();
31 8
        $this->from = '';
32 8
        $this->name = $this->name();
33 8
        $this->tag = $this->tag();
34
    }
35
36
    /**
37
     * The attributes added to the unwrapped rendered root HTML element.
38
     */
39
    public function attributes(array $values, string $from = 'function'): array
40
    {
41
        $attributes = $this->defaults()->dataAttributes($values);
42
        $attributes = wp_parse_args($attributes, [
43
            'class' => $this->classAttr($values['class'] ?? '', isWrapper: false),
44
            'data-from' => ($values['from'] ?? '') ?: $from,
45
            'data-shortcode' => $this->tag,
46
            'id' => $values['id'] ?? '',
47
        ]);
48
        unset($attributes['data-class']);
49
        unset($attributes['data-id']);
50
        unset($attributes['data-form_id']);
51
        $attributes = glsr()->filterArray("shortcode/attributes/{$this->tag}", $attributes, $this);
52
        $attributes = glsr()->filterArray('shortcode/attributes', $attributes, $this);
53
        $attributes = array_map('esc_attr', $attributes);
54
        return $attributes;
55
    }
56
57
    public function build(array $args = [], string $from = 'shortcode', bool $isWrapped = true): string
58
    {
59
        $this->normalize($args, $from);
60
        $template = $this->buildTemplate();
61
        if (empty($template)) {
62
            return '';
63
        }
64
        $attributes = $this->attributes($this->args, $this->from);
65
        $html = glsr(Builder::class)->div($template, $attributes);
66
        $rendered = $this->debug.$html;
67
        if ($isWrapped) {
68
            return $this->wrap($rendered, $args); // pass the original $args here
69
        }
70
        return $rendered;
71
    }
72
73
    public function buildCallback(array $args = []): string
74
    {
75
        $this->enqueue();
76
        return $this->build($args);
77
    }
78
79
    public function classAttr(string $classAttr, bool $isWrapper = false): string
80
    {
81
        $prefixes = [
82
            'has-', 'is-', 'items-',
83
        ];
84
        $rootClasses = [
85
            glsr(Style::class)->styleClasses(),
86
        ];
87
        $wrapClasses = [
88
            Str::dashCase("{$this->from}-{$this->tag}"),
89
        ];
90
        foreach (array_filter(explode(' ', $classAttr)) as $class) {
91
            $isPrefixed = !empty(array_filter($prefixes, fn ($p) => str_starts_with($class, $p)));
92
            if ($isPrefixed) {
93
                $wrapClasses[] = $class;
94
                continue;
95
            }
96
            $rootClasses[] = $class;
97
        }
98
        $classes = $isWrapper ? $wrapClasses : $rootClasses;
99
        return glsr(Sanitizer::class)->sanitizeAttrClass(implode(' ', $classes));
100
    }
101
102
    public function defaults(): DefaultsAbstract
103
    {
104
        $classname = str_replace('Shortcodes\\', 'Defaults\\', get_class($this));
105
        $classname = str_replace('Shortcode', 'Defaults', $classname);
106
        return glsr($classname);
107
    }
108
109
    public function enqueue(): void
110
    {
111
    }
112
113
    public function hasVisibleFields(array $args = []): bool
114
    {
115
        if (!empty($args)) {
116
            $this->normalize($args);
117
        }
118
        $defaults = $this->options('hide');
119
        $hide = $this->args['hide'] ?? [];
120
        $hide = array_flip(Arr::consolidate($hide));
121
        unset($defaults['if_empty'], $hide['if_empty']);
122
        return !empty(array_diff_key($defaults, $hide));
123
    }
124
125
    public function normalize(array $args, string $from = ''): ShortcodeContract
126
    {
127
        $this->args = [];
128
        $this->from = ($args['from'] ?? $from) ?: $this->from;
129
        $args = glsr()->filterArray("shortcode/args/{$this->tag}", $args, $this);
130
        $args = glsr()->filterArray('shortcode/args', $args, $this);
131
        $args = $this->defaults()->unguardedRestrict($args);
132
        foreach ($args as $key => $value) {
133
            $method = Helper::buildMethodName('normalize', $key);
134
            if (method_exists($this, $method)) {
135
                $value = call_user_func([$this, $method], $value);
136
            }
137
            $this->args[$key] = $value;
138
        }
139
        return $this;
140
    }
141
142
    /**
143
     * Returns the options for a shortcode setting. Results are filtered
144
     * by the "site-reviews/shortcode/options/{$options}" hook.
145
     */
146 8
    public function options(string $option, array $args = []): array
147
    {
148 8
        $args['option'] = $option;
149 8
        $args['shortcode'] = $this->tag;
150 8
        return glsr(ShortcodeOptionManager::class)->get($option, $args);
151
    }
152
153
    public function register(): void
154
    {
155
        $shortcode = (new \ReflectionClass($this))->getShortName();
156
        $shortcode = Str::snakeCase($shortcode);
157
        $shortcode = str_replace('_shortcode', '', $shortcode);
158
        add_shortcode($shortcode, [$this, 'buildCallback']);
159
        glsr()->alias($shortcode, fn () => glsr(get_class($this)));
160
        glsr()->append('shortcodes', get_class($this), $shortcode);
161
    }
162
163
    /**
164
     * Returns the filtered shortcode settings configuration.
165
     */
166
    public function settings(): array
167
    {
168
        $config = $this->config();
169
        $config = glsr()->filterArray("shortcode/config/{$this->tag}", $config, $this);
170
        $config = glsr()->filterArray('shortcode/config', $config, $this);
171
        return $config;
172
    }
173
174 8
    public function tag(): string
175
    {
176 8
        $shortName = (new \ReflectionClass($this))->getShortName();
177 8
        return Str::snakeCase(str_replace('Shortcode', '', $shortName));
178
    }
179
180
    /**
181
     * @param array $args The unmodified $args as passed to the build method
182
     */
183
    public function wrap(string $html, array $args = []): string
184
    {
185
        return glsr(Builder::class)->div($html, $this->wrapperAttributes($args));
186
    }
187
188
    /**
189
     * Returns the unfiltered shortcode settings configuration.
190
     */
191
    abstract protected function config(): array;
192
193
    protected function debug(array $data = []): void
194
    {
195
        if (empty($this->args['debug']) || 'shortcode' !== $this->from) {
196
            return;
197
        }
198
        $data = wp_parse_args($data, [
199
            'args' => $this->args,
200
            'shortcode' => $this->tag,
201
        ]);
202
        ksort($data);
203
        ob_start();
204
        glsr_debug($data);
205
        $this->debug = ob_get_clean();
206
    }
207
208
    protected function hideOptions(): array
209
    {
210
        return [];
211
    }
212
213
    /**
214
     * @param string $value
215
     */
216
    protected function normalizeAssignedPosts($value): string
217
    {
218
        $values = Cast::toArray($value);
219
        $postTypes = [];
220
        foreach ($values as $postType) {
221
            if (!is_numeric($postType) && post_type_exists((string) $postType)) {
222
                $postTypes[] = $postType;
223
            }
224
        }
225
        $values = glsr(Sanitizer::class)->sanitizePostIds($values);
226
        $values = glsr(Multilingual::class)->getPostIdsForAllLanguages($values);
227
        $values = array_merge($values, $postTypes);
228
        return implode(',', $values);
229
    }
230
231
    /**
232
     * @param string $value
233
     */
234
    protected function normalizeAssignedTerms($value): string
235
    {
236
        $values = glsr(Sanitizer::class)->sanitizeTermIds($value);
237
        $values = glsr(Multilingual::class)->getTermIdsForAllLanguages($values);
238
        return implode(',', $values);
239
    }
240
241
    /**
242
     * @param string $value
243
     */
244
    protected function normalizeAssignedUsers($value): string
245
    {
246
        $values = glsr(Sanitizer::class)->sanitizeUserIds($value);
247
        return implode(',', $values);
248
    }
249
250
    protected function normalizeClass(string $value): string
251
    {
252
        return $this->classAttr($value, isWrapper: false);
253
    }
254
255
    /**
256
     * @param string|array $value
257
     */
258
    protected function normalizeHide($value): array
259
    {
260
        $hideKeys = array_keys($this->options('hide'));
261
        return array_filter(Cast::toArray($value),
262
            fn ($value) => in_array($value, $hideKeys)
263
        );
264
    }
265
266
    protected function wrapperAttributes(array $args): array
267
    {
268
        $attributes = [
269
            'class' => $this->classAttr($args['class'] ?? '', isWrapper: true),
270
            'style' => $args['style'] ?? '',
271
        ];
272
        $attributes = glsr()->filterArray('shortcode/wrap/attributes', $attributes, $args, $this);
273
        return array_filter([
274
            'class' => glsr(Sanitizer::class)->sanitizeAttrClass($attributes['class'] ?? ''),
275
            'style' => glsr(Sanitizer::class)->sanitizeAttrStyle($attributes['style'] ?? ''),
276
        ]);
277
    }
278
}
279