Completed
Push — master ( 640397...4ec2f8 )
by Mikael
02:52
created

TextFilter   F

Complexity

Total Complexity 66

Size/Duplication

Total Lines 697
Duplicated Lines 7.75 %

Coupling/Cohesion

Components 2
Dependencies 8

Test Coverage

Coverage 71.43%

Importance

Changes 0
Metric Value
wmc 66
lcom 2
cbo 8
dl 54
loc 697
ccs 130
cts 182
cp 0.7143
rs 3.023
c 0
b 0
f 0

23 Methods

Rating   Name   Duplication   Size   Complexity  
A doFilter() 0 30 4
A setFilterConfig() 8 8 2
A getFilterConfig() 10 10 3
A setMeta() 0 4 1
A getFilters() 0 4 1
A hasFilter() 0 4 1
A addToFrontmatter() 0 13 4
C parseFactory() 14 75 17
A parse() 0 14 2
A addExcerpt() 0 6 1
B extractFrontMatter() 0 27 7
A jsonFrontMatter() 0 17 3
A yamlFrontMatter() 0 13 2
A yamlParse() 0 27 5
A getTitleFromFirstH1() 11 11 2
A getTitleFromFirstHeader() 11 11 2
A bbcode2html() 0 22 1
A makeClickable() 0 10 1
A syntaxHighlightJs() 0 11 3
A purify() 0 10 1
A markdown() 0 9 1
A nl2br() 0 4 1
A htmlentities() 0 4 1

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like TextFilter 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 TextFilter, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace Anax\TextFilter;
4
5
/**
6
 * Extract frontmatter from text and pass text through a set of filter to
7
 * format and extract information from the text.
8
 */
9
class TextFilter
10
{
11
    use TTextUtilities,
12
        TShortcode;
13
14
15
16
    /**
17
     * Supported filters.
18
     */
19
    private $filters = [
20
        // Supported since 1.2
21
        "frontmatter" => "Anax\TextFilter\Filter\Frontmatter",
22
        "variable"    => "Anax\TextFilter\Filter\Variable",
23
24
        // Supported before 1.2
25
        "bbcode" => "",
26
        "clickable" => "",
27
        "shortcode" => "",
28
        "markdown" => "",
29
//        "geshi",
30
        "nl2br" => "",
31
        "htmlentities" => "",
32
        "purify" => "",
33
        "titlefromh1" => "",
34
        "titlefromheader" => "",
35
        "anchor4Header" => "",
36
37
        // @deprecated after 1.2
38
        "jsonfrontmatter" => "", // @deprecated replaced with frontmatter since 1.2
39
        "yamlfrontmatter" => "", // @deprecated replaced with frontmatter since 1.2
40
     ];
41
42
43
44
     /**
45
      * Current document parsed.
46
      */
47
    private $current;
48
49
50
51
    /**
52
     * Configuration for individual filters.
53
     */
54
    private $config = [];
55
56
57
58
    /**
59
     * Hold meta information for filters to use.
60
     */
61
    private $meta = [];
62
63
64
65
    /**
66
     * Call each filter.
67
     *
68
     * @deprecated deprecated since version 1.2 mosbth/textfilter in favour of parse().
69
     *
70
     * @param string       $text    the text to filter.
71
     * @param string|array $filters as comma separated list of filter,
72
     *                              or filters sent in as array.
73
     *
74
     * @return string the formatted text.
75
     */
76 11
    public function doFilter($text, $filters)
77
    {
78
        // Define all valid filters with their callback function.
79
        $callbacks = [
80 11
            'bbcode'    => 'bbcode2html',
81
            'clickable' => 'makeClickable',
82
            'shortcode' => 'shortCode',
83
            'markdown'  => 'markdown',
84
            'nl2br'     => 'nl2br',
85
            'purify'    => 'purify',
86
        ];
87
    
88
        // Make an array of the comma separated string $filters
89 11
        if (is_array($filters)) {
90 2
            $filter = $filters;
91
        } else {
92 9
            $filters = strtolower($filters);
93 9
            $filter = preg_replace('/\s/', '', explode(',', $filters));
94
        }
95
    
96
        // For each filter, call its function with the $text as parameter.
97 11
        foreach ($filter as $key) {
98 11
            if (!isset($callbacks[$key])) {
99 1
                throw new Exception("The filter '$filters' is not a valid filter string due to '$key'.");
100
            }
101 10
            $text = call_user_func_array([$this, $callbacks[$key]], [$text]);
102
        }
103
    
104 10
        return $text;
105
    }
106
107
108
109
    /**
110
     * Set configuration for a certain filter.
111
     *
112
     * @param string $filter the label of the filter to set configuration for.
113
     * @param array  $config the configuration as an array.
114
     *
115
     * @return void
116
     */
117 9 View Code Duplication
    public function setFilterConfig($filter, $config)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
118
    {
119 9
        if (!$this->hasFilter($filter)) {
120 1
            throw new Exception("No such filter '$filter' exists.");
121
        }
122
123 8
        $this->config[$filter] = $config;
124 8
    }
125
126
127
128
    /**
129
     * Get configuration for a certain filter.
130
     *
131
     * @param string $filter the label of the filter to get configuration for.
132
     * @param array  $config the configuration as an array.
0 ignored issues
show
Bug introduced by
There is no parameter named $config. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
133
     *
134
     * @return array the configuration as an array or empty array.
135
     */
136 10 View Code Duplication
    public function getFilterConfig($filter)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
137
    {
138 10
        if (!$this->hasFilter($filter)) {
139 1
            throw new Exception("No such filter '$filter' exists.");
140
        }
141
142 9
        return isset($this->config[$filter])
143 8
            ? $this->config[$filter]
144 9
            : [];
145
    }
146
147
148
149
    /**
150
     * Set meta information that some filters can use.
151
     *
152
     * @param array $meta values for filters to use.
153
     *
154
     * @return void
155
     */
156
    public function setMeta($meta)
157
    {
158
        return $this->meta = $meta;
159
    }
160
161
162
163
    /**
164
     * Return an array of all filters supported.
165
     *
166
     * @return array with strings of filters supported.
167
     */
168
    public function getFilters()
169
    {
170
        return $this->filters;
171
    }
172
173
174
175
    /**
176
     * Check if filter is supported.
177
     *
178
     * @param string $filter to use.
179
     *
180
     * @throws mos/TextFilter/Exception  when filter does not exists.
181
     *
182
     * @return boolean true if filter exists, false othwerwise.
183
     */
184 14
    public function hasFilter($filter)
185
    {
186 14
        return array_key_exists($filter, $this->filters);
187
    }
188
189
190
191
    /**
192
     * Add array items to frontmatter.
193
     *
194
     * @deprecated since 1.2, replaced with filter\Frontmatter.
195
     *
196
     * @param array|null $matter key value array with items to add
197
     *                           or null if empty.
198
     *
199
     * @return $this
200
     */
201 2
    private function addToFrontmatter($matter)
202
    {
203 2
        if (empty($matter) || !is_array($matter)) {
204 1
            return $this;
205
        }
206
207 2
        if (is_null($this->current->frontmatter)) {
208
            $this->current->frontmatter = [];
209
        }
210
211 2
        $this->current->frontmatter = array_merge($this->current->frontmatter, $matter);
212 2
        return $this;
213
    }
214
215
216
217
    /**
218
     * Call a specific filter and store its details.
219
     *
220
     * @param string $filter to use.
221
     *
222
     * @throws mos/TextFilter/Exception when filter does not exists.
223
     *
224
     * @return string the formatted text.
225
     */
226 9
    private function parseFactory($filter)
227
    {
228
        // Define single tasks filter with a callback.
229
        $callbacks = [
230 9
            "bbcode"    => "bbcode2html",
231
            "clickable" => "makeClickable",
232
            "shortcode" => "shortCode",
233
            "markdown"  => "markdown",
234
            //"geshi"     => "syntaxHighlightGeSHi",
235
            "nl2br"     => "nl2br",
236
            "htmlentities" => "htmlentities",
237
            "purify"    => "purify",
238
            'anchor4Header' => 'createAnchor4Header',
239
        ];
240
241
        // Do the specific filter
242 9
        $text = $this->current->text;
243 9
        $frontmatter = $this->current->frontmatter;
244
        switch ($filter) {
245 9
            case "frontmatter":
246 5
            case "variable":
247 6
                $filterObject = new $this->filters[$filter]();
248 6
                $config = $this->getFilterConfig("$filter");
249 6
                $res = $filterObject->parse($text, $frontmatter, $config);
250 6
                $this->current->text = $res["text"];
251 6
                $this->current->frontmatter = $res["frontmatter"];
252 6
                break;
253
254 3
            case "jsonfrontmatter":
255 2
                $res = $this->jsonFrontMatter($text);
0 ignored issues
show
Deprecated Code introduced by
The method Anax\TextFilter\TextFilter::jsonFrontMatter() has been deprecated with message: since 1.2, replaced with filter\Frontmatter.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
256 2
                $this->current->text = $res["text"];
257 2
                $this->addToFrontmatter($res["frontmatter"]);
0 ignored issues
show
Deprecated Code introduced by
The method Anax\TextFilter\TextFilter::addToFrontmatter() has been deprecated with message: since 1.2, replaced with filter\Frontmatter.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
258 2
                break;
259
260 2
            case "yamlfrontmatter":
261
                $res = $this->yamlFrontMatter($text);
0 ignored issues
show
Deprecated Code introduced by
The method Anax\TextFilter\TextFilter::yamlFrontMatter() has been deprecated with message: since 1.2, replaced with filter\Frontmatter.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
262
                $this->current->text = $res["text"];
263
                $this->addToFrontmatter($res["frontmatter"]);
0 ignored issues
show
Bug introduced by
It seems like $res['frontmatter'] can also be of type string; however, Anax\TextFilter\TextFilter::addToFrontmatter() does only seem to accept array|null, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
Deprecated Code introduced by
The method Anax\TextFilter\TextFilter::addToFrontmatter() has been deprecated with message: since 1.2, replaced with filter\Frontmatter.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
264
                break;
265
266 2 View Code Duplication
            case "titlefromh1":
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
267 1
                $title = $this->getTitleFromFirstH1($text);
268 1
                $this->current->text = $text;
269 1
                if (!isset($this->current->frontmatter["title"])) {
270 1
                    $this->addToFrontmatter(["title" => $title]);
0 ignored issues
show
Deprecated Code introduced by
The method Anax\TextFilter\TextFilter::addToFrontmatter() has been deprecated with message: since 1.2, replaced with filter\Frontmatter.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
271
                }
272 1
                break;
273
274 2 View Code Duplication
            case "titlefromheader":
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
275
                $title = $this->getTitleFromFirstHeader($text);
276
                $this->current->text = $text;
277
                if (!isset($this->current->frontmatter["title"])) {
278
                    $this->addToFrontmatter(["title" => $title]);
0 ignored issues
show
Deprecated Code introduced by
The method Anax\TextFilter\TextFilter::addToFrontmatter() has been deprecated with message: since 1.2, replaced with filter\Frontmatter.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
279
                }
280
                break;
281
282 2
            case "bbcode":
283 2
            case "clickable":
284 2
            case "shortcode":
285 2
            case "markdown":
286
            //case "geshi":
287 1
            case "nl2br":
288 1
            case "htmlentities":
289 1
            case "purify":
290
            case "anchor4Header":
291 2
                $this->current->text = call_user_func_array(
292 2
                    [$this, $callbacks[$filter]],
293 2
                    [$text]
294
                );
295 2
                break;
296
297
            default:
298
                throw new Exception("The filter '$filter' is not a valid filter     string.");
299
        }
300 9
    }
301
302
303
304
    /**
305
     * Call each filter and return array with details of the formatted content.
306
     *
307
     * @param string $text   the text to filter.
308
     * @param array  $filter array of filters to use.
309
     *
310
     * @throws Anax\TextFilter\Exception  when filter does not exists.
311
     *
312
     * @return array with the formatted text and additional details.
313
     */
314 11
    public function parse($text, $filter)
315
    {
316 11
        $this->current = new \stdClass();
317 11
        $this->current->frontmatter = [];
318 11
        $this->current->text = $text;
319
320 11
        foreach ($filter as $key) {
321 9
            $this->parseFactory($key);
322
        }
323
324 11
        $this->current->text = $this->getUntilStop($this->current->text);
325
326 11
        return $this->current;
327
    }
328
329
330
331
    /**
332
     * Add excerpt as short version of text if available.
333
     *
334
     * @param object &$current same structure as returned by parse().
335
     *
336
     * @return void.
0 ignored issues
show
Documentation introduced by
The doc-type void. could not be parsed: Unknown type name "void." at position 0. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
337
     */
338 2
    public function addExcerpt($current)
339
    {
340 2
        list($excerpt, $hasMore) = $this->getUntilMore($current->text);
341 2
        $current->excerpt = $excerpt;
342 2
        $current->hasMore = $hasMore;
343 2
    }
344
345
346
347
    /**
348
     * Extract front matter from text.
349
     *
350
     * @deprecated since 1.2, replaced with filter\Frontmatter.
351
     *
352
     * @param string $text       the text to be parsed.
353
     * @param string $startToken the start token.
354
     * @param string $stopToken  the stop token.
355
     *
356
     * @return array with the formatted text and the front matter.
357
     */
358 2
    private function extractFrontMatter($text, $startToken, $stopToken)
359
    {
360 2
        $tokenLength = strlen($startToken);
361
362 2
        $start = strpos($text, $startToken);
363
        // Is a valid start?
364 2
        if ($start !== false && $start !== 0) {
365
            if ($text[$start - 1] !== "\n") {
366
                $start = false;
367
            }
368
        }
369
370 2
        $frontmatter = null;
371 2
        if ($start !== false) {
372 2
            $stop = strpos($text, $stopToken, $tokenLength - 1);
373
374 2
            if ($stop !== false && $text[$stop - 1] === "\n") {
375 2
                $length = $stop - ($start + $tokenLength);
376
377 2
                $frontmatter = substr($text, $start + $tokenLength, $length);
378 2
                $textStart = substr($text, 0, $start);
379 2
                $text = $textStart . substr($text, $stop + $tokenLength);
380
            }
381
        }
382
383 2
        return [$text, $frontmatter];
384
    }
385
386
387
388
    /**
389
     * Extract JSON front matter from text.
390
     *
391
     * @deprecated since 1.2, replaced with filter\Frontmatter.
392
     *
393
     * @param string $text the text to be parsed.
394
     *
395
     * @return array with the formatted text and the front matter.
396
     */
397 2
    public function jsonFrontMatter($text)
398
    {
399 2
        list($text, $frontmatter) = $this->extractFrontMatter($text, "{{{\n", "}}}\n");
0 ignored issues
show
Deprecated Code introduced by
The method Anax\TextFilter\TextFilter::extractFrontMatter() has been deprecated with message: since 1.2, replaced with filter\Frontmatter.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
400
401 2
        if (!empty($frontmatter)) {
402 2
            $frontmatter = json_decode($frontmatter, true);
403
404 2
            if (is_null($frontmatter)) {
405
                throw new Exception("Failed parsing JSON frontmatter.");
406
            }
407
        }
408
409
        return [
410 2
            "text" => $text,
411 2
            "frontmatter" => $frontmatter
412
        ];
413
    }
414
415
416
417
    /**
418
     * Extract YAML front matter from text.
419
     *
420
     * @deprecated since 1.2, replaced with filter\Frontmatter.
421
     *
422
     * @param string $text the text to be parsed.
423
     *
424
     * @return array with the formatted text and the front matter.
425
     */
426
    public function yamlFrontMatter($text)
427
    {
428
        list($text, $frontmatter) = $this->extractFrontMatter($text, "---\n", "...\n");
0 ignored issues
show
Deprecated Code introduced by
The method Anax\TextFilter\TextFilter::extractFrontMatter() has been deprecated with message: since 1.2, replaced with filter\Frontmatter.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
429
430
        if (!empty($frontmatter)) {
431
            $frontmatter = $this->yamlParse("---\n$frontmatter...\n");
0 ignored issues
show
Deprecated Code introduced by
The method Anax\TextFilter\TextFilter::yamlParse() has been deprecated with message: since 1.2, replaced with filter\Frontmatter.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
432
        }
433
434
        return [
435
            "text" => $text,
436
            "frontmatter" => $frontmatter
437
        ];
438
    }
439
440
441
442
    /**
443
     * Extract YAML front matter from text, use one of several available
444
     * implementations of a YAML parser.
445
     *
446
     * @deprecated since 1.2, replaced with filter\Frontmatter.
447
     *
448
     * @param string $text the text to be parsed.
449
     *
450
     * @throws: Exception when parsing frontmatter fails.
451
     *
452
     * @return array with the formatted text and the front matter.
453
     */
454
    public function yamlParse($text)
455
    {
456
        if (function_exists("yaml_parse")) {
457
            // Prefer php5-yaml extension
458
            $parsed = yaml_parse($text);
459
460
            if ($parsed === false) {
461
                throw new Exception("Failed parsing YAML frontmatter.");
462
            }
463
464
            return $parsed;
465
        }
466
467
        if (method_exists("Symfony\Component\Yaml\Yaml", "parse")) {
468
            // symfony/yaml
469
            $parsed = \Symfony\Component\Yaml\Yaml::parse($text);
470
            return $parsed;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $parsed; (null|Symfony\Component\Y...e|string|array|stdClass) is incompatible with the return type documented by Anax\TextFilter\TextFilter::yamlParse of type array.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
471
        }
472
473
        if (function_exists("spyc_load")) {
474
            // mustangostang/spyc
475
            $parsed = spyc_load($text);
476
            return $parsed;
477
        }
478
479
        throw new Exception("Could not find support for YAML.");
480
    }
481
482
483
484
    /**
485
     * Get the title from the first H1.
486
     *
487
     * @param string $text the text to be parsed.
488
     *
489
     * @return string|null with the title, if its found.
490
     */
491 1 View Code Duplication
    public function getTitleFromFirstH1($text)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
492
    {
493 1
        $matches = [];
494 1
        $title = null;
495
496 1
        if (preg_match("#<h1.*?>(.*)</h1>#", $text, $matches)) {
497 1
            $title = strip_tags($matches[1]);
498
        }
499
500 1
        return $title;
501
    }
502
503
504
505
    /**
506
     * Get the title from the first header.
507
     *
508
     * @param string $text the text to be parsed.
509
     *
510
     * @return string|null with the title, if its found.
511
     */
512 View Code Duplication
    public function getTitleFromFirstHeader($text)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
513
    {
514
        $matches = [];
515
        $title = null;
516
517
        if (preg_match("#<h[1-6].*?>(.*)</h[1-6]>#", $text, $matches)) {
518
            $title = strip_tags($matches[1]);
519
        }
520
521
        return $title;
522
    }
523
524
525
526
    /**
527
     * Helper, BBCode formatting converting to HTML.
528
     *
529
     * @param string $text The text to be converted.
530
     *
531
     * @return string the formatted text.
532
     *
533
     * @link http://dbwebb.se/coachen/reguljara-uttryck-i-php-ger-bbcode-formattering
534
     */
535 3
    public function bbcode2html($text)
536
    {
537
        $search = [
538 3
            '/\[b\](.*?)\[\/b\]/is',
539
            '/\[i\](.*?)\[\/i\]/is',
540
            '/\[u\](.*?)\[\/u\]/is',
541
            '/\[img\](https?.*?)\[\/img\]/is',
542
            '/\[url\](https?.*?)\[\/url\]/is',
543
            '/\[url=(https?.*?)\](.*?)\[\/url\]/is'
544
        ];
545
546
        $replace = [
547 3
            '<strong>$1</strong>',
548
            '<em>$1</em>',
549
            '<u>$1</u>',
550
            '<img src="$1" />',
551
            '<a href="$1">$1</a>',
552
            '<a href="$1">$2</a>'
553
        ];
554
555 3
        return preg_replace($search, $replace, $text);
556
    }
557
558
559
560
    /**
561
     * Make clickable links from URLs in text.
562
     *
563
     * @param string $text the text that should be formatted.
564
     *
565
     * @return string with formatted anchors.
566
     *
567
     * @link http://dbwebb.se/coachen/lat-php-funktion-make-clickable-automatiskt-skapa-klickbara-lankar
568
     */
569 1
    public function makeClickable($text)
570
    {
571 1
        return preg_replace_callback(
572 1
            '#\b(?<![href|src]=[\'"])https?://[^\s()<>]+(?:\([\w\d]+\)|([^[:punct:]\s]|/))#',
573 1
            function ($matches) {
574 1
                return "<a href='{$matches[0]}'>{$matches[0]}</a>";
575 1
            },
576 1
            $text
577
        );
578
    }
579
580
581
582
    /**
583
     * Syntax highlighter using GeSHi http://qbnz.com/highlighter/.
584
     *
585
     * @param string $text     text to be converted.
586
     * @param string $language which language to use for highlighting syntax.
587
     *
588
     * @return string the formatted text.
589
     */
590
     /*
591
    public function syntaxHighlightGeSHi($text, $language = "text")
592
    {
593
        $language = $language ?: "text";
594
        //$language = ($language === 'html') ? 'html4strict' : $language;
595
        $language = ($language === 'html') ? 'html5' : $language;
596
597
        $geshi = new \GeSHi($text, $language);
598
        $geshi->set_overall_class('geshi');
599
        $geshi->enable_classes('geshi');
600
        //$geshi->set_header_type(GESHI_HEADER_PRE_VALID);
601
        //$geshi->enable_line_numbers(GESHI_NORMAL_LINE_NUMBERS);
602
        $code = $geshi->parse_code();
603
604
        //echo "<pre>$language\n$code\n", $geshi->get_stylesheet(false) , "</pre>"; exit;
605
606
        // Replace last &nbsp;</pre>, -strlen("&nbsp;</pre>") == 12
607
        $length = strlen("&nbsp;</pre>");
608
        if (substr($code, -$length) == "&nbsp;</pre>") {
609
            $code = substr_replace($code, "</pre>", -$length);
610
        }
611
612
        return $code;
613
    }
614
*/
615
616
617
618
    /**
619
     * Syntax highlighter using highlight.php, a port of highlight.js
620
     * https://packagist.org/packages/scrivo/highlight.php.
621
     *
622
     * @param string $text     text to be converted.
623
     * @param string $language which language to use for highlighting syntax.
624
     *
625
     * @return string the formatted text.
626
     */
627
    public function syntaxHighlightJs($text, $language = "text")
628
    {
629
        if ($language === "text" || empty($language)) {
630
            return "<pre class=\"hljs\">" . htmlentities($text) . "</pre>";
631
        }
632
633
        $highlight = new \Highlight\Highlighter();
634
        $res = $highlight->highlight($language, $text);
635
636
        return "<pre class=\"hljs\">$res->value</pre>";
637
    }
638
639
640
641
    /**
642
     * Format text according to HTML Purifier.
643
     *
644
     * @param string $text that should be formatted.
645
     *
646
     * @return string as the formatted html-text.
647
     */
648 1
    public function purify($text)
649
    {
650 1
        $config   = \HTMLPurifier_Config::createDefault();
651 1
        $config->set("Cache.DefinitionImpl", null);
652
        //$config->set('Cache.SerializerPath', '/home/user/absolute/path');
653
654 1
        $purifier = new \HTMLPurifier($config);
655
    
656 1
        return $purifier->purify($text);
657
    }
658
659
660
661
    /**
662
     * Format text according to Markdown syntax.
663
     *
664
     * @param string $text the text that should be formatted.
665
     *
666
     * @return string as the formatted html-text.
667
     */
668 8
    public function markdown($text)
669
    {
670 8
        $text = \Michelf\MarkdownExtra::defaultTransform($text);
671 8
        $text = \Michelf\SmartyPantsTypographer::defaultTransform(
672 8
            $text,
673 8
            "2"
674
        );
675 8
        return $text;
676
    }
677
678
679
680
    /**
681
     * For convenience access to nl2br
682
     *
683
     * @param string $text text to be converted.
684
     *
685
     * @return string the formatted text.
686
     */
687 1
    public function nl2br($text)
688
    {
689 1
        return nl2br($text);
690
    }
691
692
693
694
    /**
695
     * For convenience access to htmlentities
696
     *
697
     * @param string $text text to be converted.
698
     *
699
     * @return string the formatted text.
700
     */
701
    public function htmlentities($text)
702
    {
703
        return htmlentities($text);
704
    }
705
}
706