Issues (9)

src/ShortcodeCompiler.php (4 issues)

1
<?php
2
3
4
namespace ThinkOne\NovaFlexibleContentFieldShortcode;
5
6
use Illuminate\Support\Str;
7
8
class ShortcodeCompiler
9
{
10
    protected string $keyName;
11
12
    protected array $shortcodeData;
13
14
    protected array $presentersMap;
15
16
    /**
17
     * ShortcodeCompiler constructor.
18
     *
19
     * @param array $shortcodeData
20
     * @param array $shortcodeMap
21
     * @param string $keyName
22
     */
23 6
    public function __construct(array $shortcodeData = [], array $presentersMap = [], ?string $keyName = null)
24
    {
25 6
        $this->setKeyName($keyName ?? config('nfc-shortcode.key'));
26 6
        $this->shortcodeData = $shortcodeData;
27
28 6
        $this->presentersMap = array_merge(
29 6
            config('nfc-shortcode.presenters'),
30 6
            $presentersMap
31 6
        );
32
    }
33
34 6
    public static function make(...$arguments)
35
    {
36 6
        return new static(...$arguments);
0 ignored issues
show
$arguments is expanded, but the parameter $shortcodeData of ThinkOne\NovaFlexibleCon...Compiler::__construct() does not expect variable arguments. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

36
        return new static(/** @scrutinizer ignore-type */ ...$arguments);
Loading history...
37
    }
38
39
    /**
40
     * @param string $keyName
41
     *
42
     * @return ShortcodeCompiler
43
     */
44 6
    public function setKeyName(string $keyName): self
45
    {
46 6
        $this->keyName = $keyName;
47
48 6
        return $this;
49
    }
50
51 6
    protected function regex()
52
    {
53 6
        return '\[\s*' . $this->keyName . '\s*=\s*[\'"]([a-zA-z0-9]*)[\'"].*\]';
54
    }
55
56 6
    public function renderShortcodes($value)
57
    {
58 6
        $pattern = $this->regex();
59 6
        preg_match_all("/{$pattern}/mU", $value, $matches);
60 6
        if ($matches && !empty($matches[0])) {
61 6
            foreach ($matches[0] as $i => $shortcode) {
62 6
                $value = Str::replaceFirst($shortcode, $this->render($matches[0][ $i ], $matches[1][ $i ]), $value);
63
            }
64
        }
65
66 3
        return $value;
67
    }
68
69
    /**
70
     * @throws ShortcodeException
71
     */
72 6
    protected function render($shortcode, $key): string
73
    {
74 6
        $data          = collect($this->shortcodeData);
0 ignored issues
show
$this->shortcodeData of type array is incompatible with the type Illuminate\Contracts\Support\Arrayable expected by parameter $value of collect(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

74
        $data          = collect(/** @scrutinizer ignore-type */ $this->shortcodeData);
Loading history...
75 6
        $shortcodeData = $data->firstWhere('key', $key);
76 6
        if (!$shortcodeData) {
77 1
            return '';
78
        }
79
80 5
        $presenter = $this->findPresenter($shortcodeData['layout'], $key);
81 5
        if (!$presenter) {
82 1
            return '';
83
        }
84
85
        /** @var ShortcodePresenter $presenter */
86 4
        $presenter = new $presenter();
87 4
        if (!($presenter instanceof ShortcodePresenter)) {
0 ignored issues
show
$presenter is always a sub-type of ThinkOne\NovaFlexibleCon...code\ShortcodePresenter.
Loading history...
88 2
            throw new ShortcodeException('presenter should implement ShortcodePresenter');
89
        }
90
91 2
        return $presenter->render($key, $shortcodeData['attributes'], $this->parseAttributes($shortcode));
92
    }
93
94
    /**
95
     * Find Presenter
96
     *
97
     * @param string $slug
98
     * @param string $key
99
     *
100
     * @return string|null
101
     */
102 5
    protected function findPresenter(string $slug, string $key): ?string
103
    {
104 5
        if (!empty($this->presentersMap[ $key ])) {
105 1
            return $this->presentersMap[ $key ];
106
        }
107 4
        if (!empty($this->presentersMap[ $slug ])) {
108 3
            return $this->presentersMap[ $slug ];
109
        }
110
111 1
        return null;
112
    }
113
114
    /**
115
     * Parse the shortcode attributes
116
     *
117
     * @return array
118
     */
119 2
    protected function parseAttributes($text)
120
    {
121 2
        $text = trim($text, '[]');
122
        // decode attribute values
123 2
        $text = htmlspecialchars_decode($text, ENT_QUOTES);
124
125 2
        $attributes = [];
126
        // attributes pattern
127 2
        $pattern = '/(\w+)\s*=\s*"([^"]*)"(?:\s|$)|(\w+)\s*=\s*\'([^\']*)\'(?:\s|$)|(\w+)\s*=\s*([^\s\'"]+)(?:\s|$)|"([^"]*)"(?:\s|$)|(\S+)(?:\s|$)/';
128
        // Match
129 2
        if (preg_match_all($pattern, preg_replace('/[\x{00a0}\x{200b}]+/u', ' ', $text), $match, PREG_SET_ORDER)) {
130 2
            foreach ($match as $m) {
131 2
                if (!empty($m[1])) {
132 2
                    $attributes[ strtolower($m[1]) ] = stripcslashes($m[2]);
133
                } elseif (!empty($m[3])) {
134
                    $attributes[ strtolower($m[3]) ] = stripcslashes($m[4]);
135
                } elseif (!empty($m[5])) {
136
                    $attributes[ strtolower($m[5]) ] = stripcslashes($m[6]);
137
                } elseif (isset($m[7]) && strlen($m[7])) {
138
                    $attributes[] = stripcslashes($m[7]);
139
                } elseif (isset($m[8])) {
140
                    $attributes[] = stripcslashes($m[8]);
141
                }
142
            }
143
        } else {
144
            $attributes = ltrim($text);
145
        }
146
147 2
        $attributes = is_array($attributes) ? $attributes : [ $attributes ];
0 ignored issues
show
The condition is_array($attributes) is always true.
Loading history...
148 2
        if (isset($attributes[ $this->keyName ])) {
149 2
            unset($attributes[ $this->keyName ]);
150
        }
151
152 2
        return $attributes;
153
    }
154
}
155