Completed
Push — master ( 205cf7...842449 )
by Arnold
02:56
created

TextExtension::linkifyOther()   A

Complexity

Conditions 4
Paths 6

Size

Total Lines 15
Code Lines 10

Duplication

Lines 4
Ratio 26.67 %

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 4
loc 15
rs 9.2
cc 4
eloc 10
nc 6
nop 5
1
<?php
2
3
namespace Jasny\Twig;
4
5
/**
6
 * Text functions for Twig.
7
 */
8
class TextExtension extends \Twig_Extension
9
{
10
    /**
11
     * Return extension name
12
     * 
13
     * @return string
14
     */
15
    public function getName()
16
    {
17
        return 'jasny/text';
18
    }
19
    
20
    /**
21
     * {@inheritdoc}
22
     */
23
    public function getFilters()
24
    {
25
        return [
26
            new \Twig_SimpleFilter('paragraph', [$this, 'paragraph'], ['pre_escape' => 'html', 'is_safe' => ['html']]),
27
            new \Twig_SimpleFilter('line', [$this, 'line']),
28
            new \Twig_SimpleFilter('less', [$this, 'less'], ['pre_escape' => 'html', 'is_safe' => ['html']]),
29
            new \Twig_SimpleFilter('truncate', [$this, 'truncate'], ['pre_escape' => 'html', 'is_safe' => ['html']]),
30
            new \Twig_SimpleFilter('linkify', [$this, 'linkify'], ['pre_escape' => 'html', 'is_safe' => ['html']])
31
        ];
32
    }
33
34
    /**
35
     * Add paragraph and line breaks to text.
36
     * 
37
     * @param string $value
38
     * @return string
39
     */
40
    public function paragraph($value)
41
    {
42
        if (!isset($value)) {
43
            return null;
44
        }
45
        
46
        return '<p>' . preg_replace(['~\n(\s*)\n\s*~', '~(?<!</p>)\n\s*~'], ["</p>\n\$1<p>", "<br>\n"], trim($value)) .
47
            '</p>';
48
    }
49
50
    /**
51
     * Get a single line
52
     * 
53
     * @param string $value 
54
     * @param int    $line   Line number (starts at 1)
55
     * @return string
56
     */
57
    public function line($value, $line = 1)
58
    {
59
        if (!isset($value)) {
60
            return null;
61
        }
62
        
63
        $lines = explode("\n", $value);
64
        
65
        return isset($lines[$line - 1]) ? $lines[$line - 1] : null;
66
    }
67
    
68
    /**
69
     * Cut of text on a pagebreak.
70
     * 
71
     * @param string $value
72
     * @param string $replace
73
     * @param string $break
74
     * @return string
75
     */
76 View Code Duplication
    public function less($value, $replace = '...', $break = '<!-- pagebreak -->')
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...
77
    {
78
        if (!isset($value)) {
79
            return null;
80
        }
81
        
82
        $pos = stripos($value, $break);
83
        return $pos === false ? $value : substr($value, 0, $pos) . $replace;
84
    }
85
86
    /**
87
     * Cut of text if it's to long.
88
     * 
89
     * @param string $value
90
     * @param int    $length
91
     * @param string $replace
92
     * @return string
93
     */
94 View Code Duplication
    public function truncate($value, $length, $replace = '...')
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...
95
    {
96
        if (!isset($value)) {
97
            return null;
98
        }
99
        
100
        return strlen($value) <= $length ? $value : substr($value, 0, $length - strip_tags($replace)) . $replace;
101
    }
102
    
103
    /**
104
     * Linkify a HTTP(S) link.
105
     * 
106
     * @param string $protocol  'http' or 'https'
107
     * @param string $text
108
     * @param array  $links     OUTPUT
109
     * @param string $attr
110
     * @param string $mode
111
     */
112
    protected function linkifyHttp($protocol, $text, array &$links, $attr, $mode)
113
    {
114
        $regexp = $mode != 'all'
115
            ? '~(?:(https?)://([^\s<>]+)|(?<!\w@)\b(www\.[^\s<>]+?\.[^\s<>]+))(?<![\.,:;\?!\'"\|])~i'
116
            : '~(?:(https?)://([^\s<>]+)|(?<!\w@)\b([^\s<>@]+?\.[^\s<>]+)(?<![\.,:]))~i';
117
        
118
        return preg_replace_callback($regexp, function ($match) use ($protocol, &$links, $attr) {
119
            if ($match[1]) $protocol = $match[1];
0 ignored issues
show
Bug introduced by
Consider using a different name than the imported variable $protocol, or did you forget to import by reference?

It seems like you are assigning to a variable which was imported through a use statement which was not imported by reference.

For clarity, we suggest to use a different name or import by reference depending on whether you would like to have the change visibile in outer-scope.

Change not visible in outer-scope

$x = 1;
$callable = function() use ($x) {
    $x = 2; // Not visible in outer scope. If you would like this, how
            // about using a different variable name than $x?
};

$callable();
var_dump($x); // integer(1)

Change visible in outer-scope

$x = 1;
$callable = function() use (&$x) {
    $x = 2;
};

$callable();
var_dump($x); // integer(2)
Loading history...
120
            $link = $match[2] ?: $match[3];
121
            
122
            return '<' . array_push($links, '<a' . $attr . ' href="' . $protocol . '://' . $link  . '">'
123
                . rtrim($link, '/') . '</a>') . '>';
124
        }, $text);
125
    }
126
    
127
    /**
128
     * Linkify a mail link.
129
     * 
130
     * @param string $text
131
     * @param array  $links     OUTPUT
132
     * @param string $attr
133
     */
134
    protected function linkifyMail($text, array &$links, $attr)
135
    {
136
        $regexp = '~([^\s<>]+?@[^\s<>]+?\.[^\s<>]+)(?<![\.,:;\?!\'"\|])~';
137
        
138 View Code Duplication
        return preg_replace_callback($regexp, function ($match) use (&$links, $attr) {
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...
139
            return '<' . array_push($links, '<a' . $attr . ' href="mailto:' . $match[1]  . '">' . $match[1] . '</a>')
140
                . '>';
141
        }, $text);
142
    }
143
    
144
    
145
    /**
146
     * Linkify a link.
147
     * 
148
     * @param string $protocol
149
     * @param string $text
150
     * @param array  $links     OUTPUT
151
     * @param string $attr
152
     * @param string $mode
153
     */
154
    protected function linkifyOther($protocol, $text, array &$links, $attr, $mode)
155
    {
156
        if (strpos($protocol, ':') === false) {
157
            $protocol .= in_array($protocol, ['ftp', 'tftp', 'ssh', 'scp'])  ? '://' : ':';
158
        }
159
        
160
        $regexp = $mode != 'all'
161
            ? '~' . preg_quote($protocol, '~') . '([^\s<>]+)(?<![\.,:;\?!\'"\|])~i'
162
            : '~([^\s<>]+)(?<![\.,:])~i';
163
        
164 View Code Duplication
        return preg_replace_callback($regexp, function ($match) use ($protocol, &$links, $attr) {
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...
165
            return '<' . array_push($links, '<a' . $attr . ' href="' . $protocol . $match[1]  . '">' . $match[1]
166
                . '</a>') . '>';
167
        }, $text);
168
    }
169
    
170
    /**
171
     * Turn all URLs in clickable links.
172
     * 
173
     * @param string $value
174
     * @param array  $protocols   'http'/'https', 'mail' and also 'ftp', 'scp', 'tel', etc
175
     * @param array  $attributes  HTML attributes for the link
176
     * @param string $mode        normal or all
177
     * @return string
178
     */
179
    public function linkify($value, $protocols = ['http', 'mail'], array $attributes = [], $mode = 'normal')
180
    {
181
        if (!isset($value)) {
182
            return null;
183
        }
184
        
185
        // Link attributes
186
        $attr = '';
187
        foreach ($attributes as $key => $val) {
188
            $attr .= ' ' . $key . '="' . htmlentities($val) . '"';
189
        }
190
        
191
        $links = [];
192
        
193
        // Extract existing links and tags
194
        $text = preg_replace_callback('~(<a .*?>.*?</a>|<.*?>)~i', function ($match) use (&$links) {
195
            return '<' . array_push($links, $match[1]) . '>';
196
        }, $value);
197
        
198
        // Extract text links for each protocol
199
        foreach ((array)$protocols as $protocol) {
200
            switch ($protocol) {
201
                case 'http':
202
                case 'https':   $text = $this->linkifyHttp($protocol, $text, $links, $attr, $mode); break;
0 ignored issues
show
Coding Style introduced by
The case body in a switch statement must start on the line following the statement.

According to the PSR-2, the body of a case statement must start on the line immediately following the case statement.

switch ($expr) {
case "A":
    doSomething(); //right
    break;
case "B":

    doSomethingElse(); //wrong
    break;

}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
Coding Style introduced by
Terminating statement must be on a line by itself

As per the PSR-2 coding standard, the break (or other terminating) statement must be on a line of its own.

switch ($expr) {
     case "A":
         doSomething();
         break; //wrong
     case "B":
         doSomething();
         break; //right
     case "C:":
         doSomething();
         return true; //right
 }

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
203
                case 'mail':    $text = $this->linkifyMail($text, $links, $attr); break;
0 ignored issues
show
Coding Style introduced by
The case body in a switch statement must start on the line following the statement.

According to the PSR-2, the body of a case statement must start on the line immediately following the case statement.

switch ($expr) {
case "A":
    doSomething(); //right
    break;
case "B":

    doSomethingElse(); //wrong
    break;

}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
Coding Style introduced by
Terminating statement must be on a line by itself

As per the PSR-2 coding standard, the break (or other terminating) statement must be on a line of its own.

switch ($expr) {
     case "A":
         doSomething();
         break; //wrong
     case "B":
         doSomething();
         break; //right
     case "C:":
         doSomething();
         return true; //right
 }

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
204
                default:        $text = $this->linkifyOther($protocol, $text, $links, $attr, $mode); break;
0 ignored issues
show
Coding Style introduced by
The default body in a switch statement must start on the line following the statement.

According to the PSR-2, the body of a default statement must start on the line immediately following the statement.

switch ($expr) {
    default:
        doSomething(); //right
        break;
}


switch ($expr) {
    default:

        doSomething(); //wrong
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
Coding Style introduced by
Terminating statement must be on a line by itself

As per the PSR-2 coding standard, the break (or other terminating) statement must be on a line of its own.

switch ($expr) {
     case "A":
         doSomething();
         break; //wrong
     case "B":
         doSomething();
         break; //right
     case "C:":
         doSomething();
         return true; //right
 }

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
205
            }
206
        }
207
        
208
        // Insert all link
209
        return preg_replace_callback('/<(\d+)>/', function ($match) use (&$links) {
210
            return $links[$match[1] - 1];
211
        }, $text);
212
    }
213
}
214