Completed
Push — master ( fd5325...d7e193 )
by Schlaefer
05:54 queued 03:00
created

JbbCodeAutolinkVisitor::_atUserLink()   B

Complexity

Conditions 10
Paths 5

Size

Total Lines 53

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 10
nc 5
nop 1
dl 0
loc 53
rs 7.1587
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
declare(strict_types=1);
4
5
/**
6
 * Saito - The Threaded Web Forum
7
 *
8
 * @copyright Copyright (c) the Saito Project Developers
9
 * @link https://github.com/Schlaefer/Saito
10
 * @license http://opensource.org/licenses/MIT
11
 */
12
13
namespace Plugin\BbcodeParser\src\Lib\jBBCode\Visitors;
14
15
use Plugin\BbcodeParser\src\Lib\Helper\UrlParserTrait;
16
17
/**
18
 * Handles all implicit linking in a text (autolink URLs, tags, ...)
19
 */
20
class JbbCodeAutolinkVisitor extends JbbCodeTextVisitor
21
{
22
    use UrlParserTrait;
23
24
    protected $_disallowedTags = ['code'];
25
26
    /**
27
     * {@inheritDoc}
28
     */
29
    protected function _processTextNode($string, $node)
30
    {
31
        // don't auto-link in url tags; problem is that 'urlWithAttributes' definition
32
        // reuses 'url' tag with ParseContent = true
33
        if ($node->getParent()->getTagName() === 'url') {
34
            return $string;
35
        }
36
        $string = $this->hashLink($string);
37
        $string = $this->atUserLink($string);
38
39
        return $this->autolink($string);
40
    }
41
42
    /**
43
     * Links @<username> to the user's profile.
44
     *
45
     * @param string $string The Text to be parsed.
46
     * @return string The text with usernames linked.
47
     */
48
    protected function atUserLink(string $string): string
49
    {
50
        $tags = [];
51
52
        /*
53
         * - '\pP' all unicode punctuation marks
54
         * - '<' if nl2br has taken place whatchout for <br /> linebreaks
55
         */
56
        $hasTags = preg_match_all('/(\s|^)@([^\s\pP<]+)/m', $string, $tags);
57
        if (!$hasTags) {
58
            return $string;
59
        }
60
61
        // would be cleaner to pass userlist by value, but for performance reasons
62
        // we only query the db if we actually have any @ tags
63
        $users = $this->_sOptions->get('UserList')->get();
64
        sort($users);
65
        $names = [];
66
        if (empty($tags[2]) === false) {
67
            $tags = $tags[2];
68
            foreach ($tags as $tag) {
69
                if (in_array($tag, $users)) {
70
                    $names[$tag] = 1;
71
                } else {
72
                    $continue = 0;
73
                    foreach ($users as $user) {
74
                        if (mb_strpos($user, $tag) === 0) {
75
                            $names[$user] = 1;
76
                            $continue = true;
77
                        }
78
                        if ($continue === false) {
79
                            break;
80
                        } elseif ($continue !== 0) {
81
                            $continue = false;
82
                        }
83
                    }
84
                }
85
            }
86
        }
87
        krsort($names);
88
        $baseUrl = $this->_sOptions->get('webroot') . $this->_sOptions->get('atBaseUrl');
89
        foreach ($names as $name => $v) {
90
            $title = urlencode($name);
91
            $link = $this->_url(
92
                $baseUrl . $title,
93
                "@$name",
94
                false
95
            );
96
            $string = str_replace("@$name", $link, $string);
97
        }
98
99
        return $string;
100
    }
101
102
    /**
103
     * Autolinks URLs not surrounded by explicit URL-tags for user-convenience.
104
     *
105
     * @param string $string The text to be parsed for URLs.
106
     * @return string The text with URLs linked.
107
     */
108
    protected function autolink(string $string): string
109
    {
110
        $replace = function (array $matches): string {
111
            // don't link locally
112
            if (strpos($matches['element'], 'file://') !== false) {
113
                return $matches['element'];
114
            }
115
116
            // exclude punctuation at end of sentence from URLs
117
            $ignoredEndChars = implode('|', [',', '\?', ',', '\.', '\)', '!']);
118
            preg_match(
119
                '/(?P<element>.*?)(?P<suffix>' . $ignoredEndChars . ')?$/',
120
                $matches['element'],
121
                $m
122
            );
123
            // keep ['element'] and ['suffix'] and include ['prefix']; (array) for phpstan
124
            $matches = (array)($m + $matches);
125
126
            if (strpos($matches['element'], '://') === false) {
127
                $matches['element'] = 'http://' . $matches['element'];
128
            }
129
            $matches += [
130
                'prefix' => '',
131
                'suffix' => ''
132
            ];
133
134
            $url = $this->_url(
135
                $matches['element'],
136
                $matches['element'],
137
                false,
138
                true
139
            );
140
141
            return $matches['prefix'] . $url . $matches['suffix'];
142
        };
143
144
        //# autolink http://urls
145
        $string = preg_replace_callback(
146
            "#(?<=^|[\n (])(?P<element>[\w]+?://.*?[^ \"\n\r\t<]*)#is",
147
            $replace,
148
            $string
149
        );
150
151
        //# autolink without http://, i.e. www.foo.bar/baz
152
        $string = preg_replace_callback(
153
            "#(?P<prefix>^|[\n (])(?P<element>(www|ftp)\.[\w\-]+\.[\w\-.\~]+(?:/[^ \"\t\n\r<]*)?)#is",
154
            $replace,
155
            $string
156
        );
157
158
        //# autolink email
159
        $string = preg_replace_callback(
160
            "#(?<=^|[\n ])(?P<content>([a-z0-9&\-_.]+?)@([\w\-]+\.([\w\-\.]+\.)*[\w]+))#i",
161
            function ($matches) {
162
                return $this->_email($matches['content']);
163
            },
164
            $string
165
        );
166
167
        return $string;
168
    }
169
170
    /**
171
     * Links #<posting-ID> to that posting.
172
     *
173
     * @param string $string Text to be parsed for #<id>.
174
     * @return string Text containing hash-links.
175
     */
176
    protected function hashLink(string $string): string
177
    {
178
        $baseUrl = $this->_sOptions->get('webroot') . $this->_sOptions->get('hashBaseUrl');
179
        $string = preg_replace_callback(
180
            '/(?<=\s|^|]|\()(?<tag>#)(?<element>\d+)(?!\w)/',
181
            function (array $m) use ($baseUrl): string {
182
                $hash = $m['element'];
183
184
                return $this->_url($baseUrl . $hash, '#' . $hash);
185
            },
186
            $string
187
        );
188
189
        return $string;
190
    }
191
}
192