Completed
Pull Request — master (#529)
by Daniel
04:04
created

UrlFormatter::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 2
dl 0
loc 5
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace Coyote\Services\Parser\Parsers;
4
5
use Collective\Html\HtmlBuilder;
6
use TRegx\SafeRegex\Exception\BacktrackLimitPregException;
7
use TRegx\SafeRegex\preg;
8
9
class UrlFormatter
10
{
11
    // http://daringfireball.net/2010/07/improved_regex_for_matching_urls
12
    private const REGEXP_URL = '#(?i)\b((?:https?://|www\d{0,3}[.]|[a-z0-9.\-]+[.][a-z]{2,4}/)(?:[^\s()<>]+|\(([^\s()<>]+|(\([^\s()<>]+\)))*\))+(?:\(([^\s()<>]+|(\([^\s()<>]+\)))*\)|[^\s`!()\[\]{};:\'".,<>?«»“”‘’]))#u';
13
14
    private const TITLE_LEN = 64;
15
16
    /** @var string */
17
    private $host;
18
    /** @var HtmlBuilder */
19
    private $html;
20
21
    public function __construct(string $host, HtmlBuilder $html)
22
    {
23
        $this->host = $host;
24
        $this->html = $html;
25
    }
26
27
    public function parse(string $text): string
28
    {
29
        try {
30
            return preg::replace_callback(self::REGEXP_URL, function (array $match): string {
31
                $url = $match[0];
32
                if (pattern('^[\w]+?://', 'i')->fails($url)) {
33
                    return $this->processLink('http://' . $url, $url);
34
                }
35
                return $this->processLink($url, $url);
36
            }, $text);
37
        } catch (BacktrackLimitPregException $exception) {
38
            // regexp posiada buga, nie parsuje poprawnie URL-i jezeli zawiera on (
39
            // poki co nie mam rozwiazania na ten problem, dlatego zwracamy nieprzeprasowany tekst
40
            // w przypadku bledu
41
            // Dokłądniej mówiąc to nie parsuje żadnego urla w poście, jeśli przynajmniej jeden z nich ma (
42
            return $text;
43
        }
44
    }
45
46
    private function processLink(string $url, string $title): string
47
    {
48
        $quoted = htmlspecialchars($title, ENT_QUOTES, 'UTF-8', false); // doesn't app('html') take care of this?
49
        return $this->html->link($url, $this->truncate($quoted));
50
    }
51
52
    private function truncate(string $text): string
53
    {
54
        if (mb_strlen($text) < self::TITLE_LEN) {
55
            return $text;
56
        }
57
        if ($this->host === parse_url($text, PHP_URL_HOST)) {
58
            return $text;
59
        }
60
        return $this->truncateToLengthWith($text, self::TITLE_LEN, '[...]');
61
    }
62
63
    private function truncateToLengthWith(string $text, int $length, string $substitute): string
64
    {
65
        $padding = ($length - mb_strlen($substitute)) / 2;
66
        return mb_substr($text, 0, $padding) . $substitute . mb_substr($text, -$padding);
67
    }
68
}
69