CTextFilter   A
last analyzed

Complexity

Total Complexity 27

Size/Duplication

Total Lines 304
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 3

Test Coverage

Coverage 13.56%

Importance

Changes 7
Bugs 1 Features 1
Metric Value
c 7
b 1
f 1
dl 0
loc 304
ccs 16
cts 118
cp 0.1356
rs 10
wmc 27
lcom 1
cbo 3

11 Methods

Rating   Name   Duplication   Size   Complexity  
A bbcode2html() 0 22 1
A markdown() 0 4 1
A nl2br() 0 4 1
A shortCodeBaseurl() 0 4 1
A shortCodeRelurl() 0 4 1
A shortCodeAsset() 0 4 1
B doFilter() 0 26 3
A makeClickable() 0 10 1
A shortCodeInit() 0 18 3
C shortCodeFigure() 0 48 9
B shortCode() 0 37 5
1
<?php
2
3
namespace Anax\Content;
4
5
/**
6
 * Filter and format content.
7
 *
8
 */
9
class CTextFilter
10
{
11
    use \Anax\TConfigure,
12
        \Anax\DI\TInjectionAware;
13
14
15
16
    /**
17
     * Call each filter.
18
     *
19
     * @param string $text    the text to filter.
20
     * @param string $filters as comma separated list of filter.
21
     *
22
     * @return string the formatted text.
23
     */
24 1
    public function doFilter($text, $filters)
25
    {
26
        // Define all valid filters with their callback function.
27
        $callbacks = array(
28 1
            'bbcode'    => 'bbcode2html',
29 1
            'clickable' => 'makeClickable',
30 1
            'markdown'  => 'markdown',
31 1
            'nl2br'     => 'nl2br',
32 1
            'shortcode' => 'shortCode',
33 1
        );
34
35
        // Make an array of the comma separated string $filters
36 1
        $filter = preg_replace('/\s/', '', explode(',', $filters));
37
38
        // For each filter, call its function with the $text as parameter.
39 1
        foreach ($filter as $key) {
40
41 1
            if (isset($callbacks[$key])) {
42 1
                $text = call_user_func_array([$this, $callbacks[$key]], [$text]);
43 1
            } else {
44
                throw new \Exception("The filter '$filter' is not a valid filter string.");
45
            }
46 1
        }
47
48 1
        return $text;
49
    }
50
51
52
53
    /**
54
     * Helper, BBCode formatting converting to HTML.
55
     *
56
     * @param string $text The text to be converted.
57
     *
58
     * @return string the formatted text.
59
     *
60
     * @link http://dbwebb.se/coachen/reguljara-uttryck-i-php-ger-bbcode-formattering
61
     */
62
    public function bbcode2html($text)
63
    {
64
        $search = [
65
            '/\[b\](.*?)\[\/b\]/is',
66
            '/\[i\](.*?)\[\/i\]/is',
67
            '/\[u\](.*?)\[\/u\]/is',
68
            '/\[img\](https?.*?)\[\/img\]/is',
69
            '/\[url\](https?.*?)\[\/url\]/is',
70
            '/\[url=(https?.*?)\](.*?)\[\/url\]/is'
71
        ];
72
73
        $replace = [
74
            '<strong>$1</strong>',
75
            '<em>$1</em>',
76
            '<u>$1</u>',
77
            '<img src="$1" />',
78
            '<a href="$1">$1</a>',
79
            '<a href="$1">$2</a>'
80
        ];
81
82
        return preg_replace($search, $replace, $text);
83
    }
84
85
86
87
    /**
88
     * Make clickable links from URLs in text.
89
     *
90
     * @param string $text the text that should be formatted.
91
     *
92
     * @return string with formatted anchors.
93
     *
94
     * @link http://dbwebb.se/coachen/lat-php-funktion-make-clickable-automatiskt-skapa-klickbara-lankar
95
     */
96
    public function makeClickable($text)
97
    {
98
        return preg_replace_callback(
99
            '#\b(?<![href|src]=[\'"])https?://[^\s()<>]+(?:\([\w\d]+\)|([^[:punct:]\s]|/))#',
100
            function ($matches) {
101
                return "<a href=\'{$matches[0]}\'>{$matches[0]}</a>";
102
            },
103
            $text
104
        );
105
    }
106
107
108
109
    /**
110
     * Format text according to Markdown syntax.
111
     *
112
     * @param string $text the text that should be formatted.
113
     *
114
     * @return string as the formatted html-text.
115
     *
116
     * @link http://dbwebb.se/coachen/skriv-for-webben-med-markdown-och-formattera-till-html-med-php
117
     */
118
    public function markdown($text)
119
    {
120
        return \Michelf\MarkdownExtra::defaultTransform($text);
121
    }
122
123
124
125
    /**
126
     * For convenience access to nl2br
127
     *
128
     * @param string $text text to be converted.
129
     *
130
     * @return string the formatted text.
131
     */
132 1
    public function nl2br($text)
133
    {
134 1
        return nl2br($text);
135
    }
136
137
138
139
    /**
140
     * Shortcode to to quicker format text as HTML.
141
     *
142
     * @param string $text text to be converted.
143
     *
144
     * @return string the formatted text.
145
     */
146
    public function shortCode($text)
147
    {
148
        $patterns = [
149
            '/\[(FIGURE)[\s+](.+)\]/',
150
            '/\[(BASEURL)\]/',
151
            '/\[(RELURL)\]/',
152
            '/\[(ASSET)\]/',
153
        ];
154
155
        return preg_replace_callback(
156
            $patterns,
157
            function ($matches) {
158
                switch ($matches[1]) {
159
160
                    case 'FIGURE':
161
                        return CTextFilter::shortCodeFigure($matches[2]);
162
                        break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
163
164
                    case 'BASEURL':
165
                        return CTextFilter::shortCodeBaseurl();
166
                        break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
167
168
                    case 'RELURL':
169
                        return CTextFilter::shortCodeRelurl();
170
                        break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
171
172
                    case 'ASSET':
173
                        return CTextFilter::shortCodeAsset();
174
                        break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
175
176
                    default:
177
                        return "{$matches[1]} is unknown shortcode.";
178
                }
179
            },
180
            $text
181
        );
182
    }
183
184
185
186
    /**
187
    * Init shortcode handling by preparing the option list to an array, for those using arguments.
188
    *
189
    * @param string $options for the shortcode.
190
    *
191
    * @return array with all the options.
192
    */
193
    protected static function shortCodeInit($options)
194
    {
195
        preg_match_all('/[a-zA-Z0-9]+="[^"]+"|\S+/', $options, $matches);
196
197
        $res = [];
198
        foreach ($matches[0] as $match) {
199
            $pos = strpos($match, '=');
200
            if ($pos == false) {
0 ignored issues
show
Bug Best Practice introduced by
It seems like you are loosely comparing $pos of type integer to the boolean false. If you are specifically checking for 0, consider using something more explicit like === 0 instead.
Loading history...
201
                $res[$match] = true;
202
            } else {
203
                $key = substr($match, 0, $pos);
204
                $val = trim(substr($match, $pos+1), '"');
205
                $res[$key] = $val;
206
            }
207
        }
208
209
        return $res;
210
    }
211
212
213
214
    /**
215
     * Shortcode for <figure>.
216
     *
217
     * Usage example: [FIGURE src="img/home/me.jpg" caption="Me" alt="Bild på mig" nolink="nolink"]
218
     *
219
     * @param string $options for the shortcode.
220
     *
221
     * @return array with all the options.
222
     */
223
    protected static function shortCodeFigure($options)
224
    {
225
        extract(
226
            array_merge(
0 ignored issues
show
Bug introduced by
array_merge(array('id' =...hortCodeInit($options)) cannot be passed to extract() as the parameter $var_array expects a reference.
Loading history...
227
                [
228
                    'id' => null,
229
                    'class' => null,
230
                    'src' => null,
231
                    'title' => null,
232
                    'alt' => null,
233
                    'caption' => null,
234
                    'href' => null,
235
                    'nolink' => false,
236
                ],
237
                CTextFilter::shortCodeInit($options)
238
            ),
239
            EXTR_SKIP
240
        );
241
242
        $id = $id ? " id='$id'" : null;
243
        $class = $class ? " class='figure $class'" : " class='figure'";
244
        $title = $title ? " title='$title'" : null;
245
246
        if (!$alt && $caption) {
247
            $alt = $caption;
248
        }
249
250
        if (!$href) {
251
            $pos = strpos($src, '?');
252
            $href = $pos ? substr($src, 0, $pos) : $src;
253
        }
254
255
        $a_start = null;
256
        $a_end = null;
257
        if (!$nolink) {
258
            $a_start = "<a href='{$href}'>";
259
            $a_end = "</a>";
260
        }
261
262
        $html = <<<EOD
263
<figure{$id}{$class}>
264
{$a_start}<img src='{$src}' alt='{$alt}'{$title}/>{$a_end}
265
<figcaption markdown=1>{$caption}</figcaption>
266
</figure>
267
EOD;
268
269
        return $html;
270
    }
271
272
273
274
    /**
275
     * Shortcode for adding BASEURL to links.
276
     *
277
     * Usage example: [BASEURL]
278
     *
279
     * @return array with all the options.
280
     */
281
    protected function shortCodeBaseurl()
282
    {
283
        return $this->di->url->create() . "/";
284
    }
285
286
287
288
    /**
289
     * Shortcode for adding RELURL to links.
290
     *
291
     * Usage example: [RELURL]
292
     *
293
     * @return array with all the options.
294
     */
295
    protected function shortCodeRelurl()
296
    {
297
        return $this->di->url->createRelative() . "/";
298
    }
299
300
301
    /**
302
    * Shortcode for adding RELURL to links.
303
    *
304
    * Usage example: [RELURL]
305
    *
306
    * @return array with all the options.
307
    */
308
    protected function shortCodeAsset()
309
    {
310
        return $this->di->url->asset() . "/";
311
    }
312
}
313