Passed
Push — feature/6.x ( 53903f...32797a )
by Schlaefer
05:38 queued 02:15
created

JbbCodeAutolinkVisitor::atUserLink()   B

Complexity

Conditions 10
Paths 5

Size

Total Lines 52
Code Lines 32

Duplication

Lines 0
Ratio 0 %

Importance

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