Passed
Push — develop ( becc1e...262a87 )
by Paul
15:25
created

Shortcode::options()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 1

Importance

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