Passed
Push — develop ( 6e51fd...682124 )
by Paul
14:04
created

Shortcode::normalizeHide()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 3
c 1
b 0
f 0
dl 0
loc 5
ccs 0
cts 4
cp 0
rs 10
cc 1
nc 1
nop 1
crap 2
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 defaults(): DefaultsAbstract
74
    {
75
        $classname = str_replace('Shortcodes\\', 'Defaults\\', get_class($this));
76
        $classname = str_replace('Shortcode', 'Defaults', $classname);
77
        return glsr($classname);
78
    }
79
80
    public function hasVisibleFields(array $args = []): bool
81
    {
82
        if (!empty($args)) {
83
            $this->normalize($args);
84
        }
85
        $defaults = $this->options('hide');
86
        $hide = $this->args['hide'] ?? [];
87
        $hide = array_flip(Arr::consolidate($hide));
88
        unset($defaults['if_empty'], $hide['if_empty']);
89
        return !empty(array_diff_key($defaults, $hide));
90
    }
91
92
    public function normalize(array $args, string $from = ''): ShortcodeContract
93
    {
94
        $this->args = [];
95
        $this->from = ($args['from'] ?? $from) ?: $this->from;
96
        $args = glsr()->filterArray("shortcode/args/{$this->tag}", $args, $this);
97
        $args = glsr()->filterArray('shortcode/args', $args, $this);
98
        $args = $this->defaults()->unguardedRestrict($args);
99
        foreach ($args as $key => $value) {
100
            $method = Helper::buildMethodName('normalize', $key);
101
            if (method_exists($this, $method)) {
102
                $value = call_user_func([$this, $method], $value);
103
            }
104
            $this->args[$key] = $value;
105
        }
106
        return $this;
107
    }
108
109
    /**
110
     * Returns the options for a shortcode setting. Results are filtered
111
     * by the "site-reviews/shortcode/options/{$options}" hook.
112
     */
113 8
    public function options(string $option, array $args = []): array
114
    {
115 8
        $args['option'] = $option;
116 8
        $args['shortcode'] = $this->tag;
117 8
        return call_user_func([glsr(ShortcodeOptionManager::class), $option], $args);
118
    }
119
120
    public function register(): void
121
    {
122
        $shortcode = (new \ReflectionClass($this))->getShortName();
123
        $shortcode = Str::snakeCase($shortcode);
124
        $shortcode = str_replace('_shortcode', '', $shortcode);
125
        add_shortcode($shortcode, fn ($atts) => $this->build($atts));
126
        glsr()->alias($shortcode, fn () => glsr(get_class($this)));
127
        glsr()->append('shortcodes', get_class($this), $shortcode);
128
    }
129
130
    /**
131
     * Returns the filtered shortcode settings configuration.
132
     */
133
    public function settings(): array
134
    {
135
        $config = $this->config();
136
        $config = glsr()->filterArray("shortcode/config/{$this->tag}", $config, $this);
137
        $config = glsr()->filterArray('shortcode/config', $config, $this);
138
        return $config;
139
    }
140
141 8
    public function tag(): string
142
    {
143 8
        $shortName = (new \ReflectionClass($this))->getShortName();
144 8
        return Str::snakeCase(str_replace('Shortcode', '', $shortName));
145
    }
146
147
    /**
148
     * @param array $args The unmodified $args as passed to the build method
149
     */
150
    public function wrap(string $html, array $args = []): string
151
    {
152
        $attributes = [
153
            'class' => $this->classAttr($args['class'] ?? '', isWrapper: true),
154
        ];
155
        $attributes = glsr()->filterArray('shortcode/wrap/attributes', $attributes, $args, $this);
156
        $attributes = array_map('esc_attr', $attributes);
157
        return glsr(Builder::class)->div($html, $attributes);
158
    }
159
160
    protected function classAttr(string $attr, bool $isWrapper = false): string
161
    {
162
        $prefixes = [
163
            'has-custom-',
164
            'has-text-align-',
165
            'is-custom-',
166
            'is-style-',
167
            'items-justified-',
168
        ];
169
        $classes = array_filter(explode(' ', trim($attr)));
170
        $rootClasses = [
171
            glsr(Style::class)->styleClasses(),
172
        ];
173
        $wrapClasses = [
174
            Str::dashCase("{$this->from}-{$this->tag}"),
175
        ];
176
        foreach ($classes as $class) {
177
            $isPrefixed = !empty(array_filter($prefixes, fn ($p) => str_starts_with($class, $p)));
178
            if ($isPrefixed) {
179
                $wrapClasses[] = $class;
180
                continue;
181
            }
182
            $rootClasses[] = $class;
183
        }
184
        return glsr(Sanitizer::class)->sanitizeAttrClass(
185
            implode(' ', $isWrapper ? $wrapClasses : $rootClasses)
186
        );
187
    }
188
189
    /**
190
     * Returns the unfiltered shortcode settings configuration.
191
     */
192
    abstract protected function config(): array;
193
194
    protected function debug(array $data = []): void
195
    {
196
        if (empty($this->args['debug']) || 'shortcode' !== $this->from) {
197
            return;
198
        }
199
        $data = wp_parse_args($data, [
200
            'args' => $this->args,
201
            'shortcode' => $this->tag,
202
        ]);
203
        ksort($data);
204
        ob_start();
205
        glsr_debug($data);
206
        $this->debug = ob_get_clean();
207
    }
208
209
    protected function hideOptions(): array
210
    {
211
        return [];
212
    }
213
214
    /**
215
     * @param string $value
216
     */
217
    protected function normalizeAssignedPosts($value): string
218
    {
219
        $values = Cast::toArray($value);
220
        $postTypes = [];
221
        foreach ($values as $postType) {
222
            if (!is_numeric($postType) && post_type_exists((string) $postType)) {
223
                $postTypes[] = $postType;
224
            }
225
        }
226
        $values = glsr(Sanitizer::class)->sanitizePostIds($values);
227
        $values = glsr(Multilingual::class)->getPostIdsForAllLanguages($values);
228
        $values = array_merge($values, $postTypes);
229
        return implode(',', $values);
230
    }
231
232
    /**
233
     * @param string $value
234
     */
235
    protected function normalizeAssignedTerms($value): string
236
    {
237
        $values = glsr(Sanitizer::class)->sanitizeTermIds($value);
238
        $values = glsr(Multilingual::class)->getTermIdsForAllLanguages($values);
239
        return implode(',', $values);
240
    }
241
242
    /**
243
     * @param string $value
244
     */
245
    protected function normalizeAssignedUsers($value): string
246
    {
247
        $values = glsr(Sanitizer::class)->sanitizeUserIds($value);
248
        return implode(',', $values);
249
    }
250
251
    protected function normalizeClass(string $value): string
252
    {
253
        return $this->classAttr($value, isWrapper: false);
254
    }
255
256
    /**
257
     * @param string|array $value
258
     */
259
    protected function normalizeHide($value): array
260
    {
261
        $hideKeys = array_keys($this->options('hide'));
262
        return array_filter(Cast::toArray($value),
263
            fn ($value) => in_array($value, $hideKeys)
264
        );
265
    }
266
}
267