Completed
Push — master ( 2c646e...6b4b2f )
by Asif
01:02
created

ShortcodeCompiler   B

Complexity

Total Complexity 43

Size/Duplication

Total Lines 382
Duplicated Lines 0 %

Coupling/Cohesion

Components 2
Dependencies 2

Importance

Changes 0
Metric Value
wmc 43
lcom 2
cbo 2
dl 0
loc 382
rs 8.96
c 0
b 0
f 0

24 Methods

Rating   Name   Duplication   Size   Complexity  
A attachData() 0 4 1
A viewData() 0 5 1
A getData() 0 4 1
A strip() 0 9 2
A getStrip() 0 4 1
A setStrip() 0 4 1
A stripTag() 0 8 3
A getRegistered() 0 4 1
A enable() 0 4 1
A disable() 0 4 1
A add() 0 4 1
A compile() 0 17 5
A hasShortcodes() 0 4 1
A parseToken() 0 9 2
A renderShortcodes() 0 6 1
A render() 0 16 1
A compileShortcode() 0 14 1
A setMatches() 0 4 1
A getName() 0 4 1
A getContent() 0 5 1
A getCallback() 0 20 3
B parseAttributes() 0 30 10
A getShortcodeNames() 0 4 1
A getRegex() 0 6 1

How to fix   Complexity   

Complex Class

Complex classes like ShortcodeCompiler often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use ShortcodeCompiler, and based on these observations, apply Extract Interface, too.

1
<?php namespace Webwizo\Shortcodes\Compilers;
2
3
use Illuminate\Support\Str;
4
5
class ShortcodeCompiler
6
{
7
8
    /**
9
     * Enabled state
10
     *
11
     * @var boolean
12
     */
13
    protected $enabled = false;
14
15
    /**
16
     * Enable strip state
17
     *
18
     * @var boolean
19
     */
20
    protected $strip = false;
21
22
    /**
23
     * @var
24
     */
25
    protected $matches;
26
27
    /**
28
     * Registered laravel-shortcodes
29
     *
30
     * @var array
31
     */
32
    protected $registered = [];
33
34
    /**
35
     * Attached View Data
36
     *
37
     * @var array
38
     */
39
    protected $data = [];
40
    
41
    protected $_viewData;
42
43
    /**
44
     * Enable
45
     *
46
     * @return void
47
     */
48
    public function enable()
49
    {
50
        $this->enabled = true;
51
    }
52
53
    /**
54
     * Disable
55
     *
56
     * @return void
57
     */
58
    public function disable()
59
    {
60
        $this->enabled = false;
61
    }
62
63
    /**
64
     * Add a new shortcode
65
     *
66
     * @param string          $name
67
     * @param callable|string $callback
68
     */
69
    public function add($name, $callback)
70
    {
71
        $this->registered[$name] = $callback;
72
    }
73
74
    public function attachData($data)
75
    {
76
        $this->data = $data;
77
    }
78
79
    /**
80
     * Compile the contents
81
     *
82
     * @param  string $value
83
     *
84
     * @return string
85
     */
86
    public function compile($value)
87
    {
88
        // Only continue is laravel-shortcodes have been registered
89
        if (!$this->enabled || !$this->hasShortcodes()) {
90
            return $value;
91
        }
92
        // Set empty result
93
        $result = '';
94
        // Here we will loop through all of the tokens returned by the Zend lexer and
95
        // parse each one into the corresponding valid PHP. We will then have this
96
        // template as the correctly rendered PHP that can be rendered natively.
97
        foreach (token_get_all($value) as $token) {
98
            $result .= is_array($token) ? $this->parseToken($token) : $token;
99
        }
100
101
        return $result;
102
    }
103
104
    /**
105
     * Check if laravel-shortcodes have been registered
106
     *
107
     * @return boolean
108
     */
109
    public function hasShortcodes()
110
    {
111
        return !empty($this->registered);
112
    }
113
114
    /**
115
     * Parse the tokens from the template.
116
     *
117
     * @param  array $token
118
     *
119
     * @return string
120
     */
121
    protected function parseToken($token)
122
    {
123
        list($id, $content) = $token;
124
        if ($id == T_INLINE_HTML) {
125
            $content = $this->renderShortcodes($content);
126
        }
127
128
        return $content;
129
    }
130
131
    /**
132
     * Render laravel-shortcodes
133
     *
134
     * @param  string $value
135
     *
136
     * @return string
137
     */
138
    protected function renderShortcodes($value)
139
    {
140
        $pattern = $this->getRegex();
141
142
        return preg_replace_callback("/{$pattern}/s", [$this, 'render'], $value);
143
    }
144
    
145
    // get view data
146
    public function viewData( $viewData )
147
    {
148
        $this->_viewData = $viewData;
149
        return $this;
150
    }
151
152
    /**
153
     * Render the current calld shortcode.
154
     *
155
     * @param  array $matches
156
     *
157
     * @return string
158
     */
159
    public function render($matches)
160
    {
161
        // Compile the shortcode
162
        $compiled = $this->compileShortcode($matches);
163
        $name = $compiled->getName();
164
        $viewData = $this->_viewData;
165
166
        // Render the shortcode through the callback
167
        return call_user_func_array($this->getCallback($name), [
168
            $compiled,
169
            $compiled->getContent(),
170
            $this,
171
            $name,
172
            $viewData
173
        ]);
174
    }
175
176
    /**
177
     * Get Compiled Attributes.
178
     *
179
     * @param $matches
180
     *
181
     * @return \Webwizo\Shortcodes\Shortcode
182
     */
183
    protected function compileShortcode($matches)
184
    {
185
        // Set matches
186
        $this->setMatches($matches);
187
        // pars the attributes
188
        $attributes = $this->parseAttributes($this->matches[3]);
189
190
        // return shortcode instance
191
        return new Shortcode(
192
            $this->getName(),
193
            $attributes,
194
            $this->getContent()
195
        );
196
    }
197
198
    /**
199
     * Set the matches
200
     *
201
     * @param array $matches
202
     */
203
    protected function setMatches($matches = [])
204
    {
205
        $this->matches = $matches;
206
    }
207
208
    /**
209
     * Return the shortcode name
210
     *
211
     * @return string
212
     */
213
    public function getName()
214
    {
215
        return $this->matches[2];
216
    }
217
218
    /**
219
     * Return the shortcode content
220
     *
221
     * @return string
222
     */
223
    public function getContent()
224
    {
225
        // Compile the content, to support nested laravel-shortcodes
226
        return $this->compile($this->matches[5]);
227
    }
228
229
    /**
230
     * Return the view data
231
     *
232
     * @return array
233
     */
234
    public function getData()
235
    {
236
        return $this->data;
237
    }
238
239
240
    /**
241
     * Get the callback for the current shortcode (class or callback)
242
     *
243
     * @param  string $name
244
     *
245
     * @return callable|array
246
     */
247
    public function getCallback($name)
248
    {
249
        // Get the callback from the laravel-shortcodes array
250
        $callback = $this->registered[$name];
251
        // if is a string
252
        if (is_string($callback)) {
253
            // Parse the callback
254
            list($class, $method) = Str::parseCallback($callback, 'register');
255
            // If the class exist
256
            if (class_exists($class)) {
257
                // return class and method
258
                return [
259
                    app($class),
260
                    $method
261
                ];
262
            }
263
        }
264
265
        return $callback;
266
    }
267
268
    /**
269
     * Parse the shortcode attributes
270
     *
271
     * @author Wordpress
272
     * @return array
273
     */
274
    protected function parseAttributes($text)
275
    {
276
        // decode attribute values
277
        $text = htmlspecialchars_decode($text, ENT_QUOTES);
278
279
        $attributes = [];
280
        // attributes pattern
281
        $pattern = '/(\w+)\s*=\s*"([^"]*)"(?:\s|$)|(\w+)\s*=\s*\'([^\']*)\'(?:\s|$)|(\w+)\s*=\s*([^\s\'"]+)(?:\s|$)|"([^"]*)"(?:\s|$)|(\S+)(?:\s|$)/';
282
        // Match
283
        if (preg_match_all($pattern, preg_replace('/[\x{00a0}\x{200b}]+/u', " ", $text), $match, PREG_SET_ORDER)) {
284
            foreach ($match as $m) {
285
                if (!empty($m[1])) {
286
                    $attributes[strtolower($m[1])] = stripcslashes($m[2]);
287
                } elseif (!empty($m[3])) {
288
                    $attributes[strtolower($m[3])] = stripcslashes($m[4]);
289
                } elseif (!empty($m[5])) {
290
                    $attributes[strtolower($m[5])] = stripcslashes($m[6]);
291
                } elseif (isset($m[7]) && strlen($m[7])) {
292
                    $attributes[] = stripcslashes($m[7]);
293
                } elseif (isset($m[8])) {
294
                    $attributes[] = stripcslashes($m[8]);
295
                }
296
            }
297
        } else {
298
            $attributes = ltrim($text);
299
        }
300
301
        // return attributes
302
        return is_array($attributes) ? $attributes : [$attributes];
303
    }
304
305
    /**
306
     * Get shortcode names
307
     *
308
     * @return string
309
     */
310
    protected function getShortcodeNames()
311
    {
312
        return join('|', array_map('preg_quote', array_keys($this->registered)));
313
    }
314
315
    /**
316
     * Get shortcode regex.
317
     *
318
     * @author Wordpress
319
     * @return string
320
     */
321
    protected function getRegex()
322
    {
323
        $shortcodeNames = $this->getShortcodeNames();
324
325
        return "\\[(\\[?)($shortcodeNames)(?![\\w-])([^\\]\\/]*(?:\\/(?!\\])[^\\]\\/]*)*?)(?:(\\/)\\]|\\](?:([^\\[]*+(?:\\[(?!\\/\\2\\])[^\\[]*+)*+)\\[\\/\\2\\])?)(\\]?)";
326
    }
327
328
    /**
329
     * Remove all shortcode tags from the given content.
330
     *
331
     * @param string $content Content to remove shortcode tags.
332
     *
333
     * @return string Content without shortcode tags.
334
     */
335
    public function strip($content)
336
    {
337
        if (empty($this->registered)) {
338
            return $content;
339
        }
340
        $pattern = $this->getRegex();
341
342
        return preg_replace_callback("/{$pattern}/s", [$this, 'stripTag'], $content);
343
    }
344
345
    /**
346
     * @return boolean
347
     */
348
    public function getStrip()
349
    {
350
        return $this->strip;
351
    }
352
353
    /**
354
     * @param boolean $strip
355
     */
356
    public function setStrip($strip)
357
    {
358
        $this->strip = $strip;
359
    }
360
361
    /**
362
     * Remove shortcode tag
363
     *
364
     * @param type $m
365
     *
366
     * @return string Content without shortcode tag.
367
     */
368
    protected function stripTag($m)
369
    {
370
        if ($m[1] == '[' && $m[6] == ']') {
371
            return substr($m[0], 1, -1);
372
        }
373
374
        return $m[1] . $m[6];
375
    }
376
    
377
    /**
378
     * Get registered shortcodes
379
     *
380
     * @return array shortcode tags.
381
     */
382
    public function getRegistered()
383
    {
384
        return $this->registered;
385
    }
386
}
387