Completed
Pull Request — master (#15)
by
unknown
04:36
created

TextExtension::getName()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 4
ccs 0
cts 0
cp 0
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 0
crap 2
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 25
    public function getFilters()
24
    {
25
        return [
26 25
            new \Twig_SimpleFilter('paragraph', [$this, 'paragraph'], ['pre_escape' => 'html', 'is_safe' => ['html']]),
27 25
            new \Twig_SimpleFilter('line', [$this, 'line']),
28 25
            new \Twig_SimpleFilter('less', [$this, 'less'], ['pre_escape' => 'html', 'is_safe' => ['html']]),
29 25
            new \Twig_SimpleFilter('truncate', [$this, 'truncate'], ['pre_escape' => 'html', 'is_safe' => ['html']]),
30 25
            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 2
    public function paragraph($value)
41
    {
42 2
        if (!isset($value)) {
43 1
            return null;
44
        }
45
        
46 1
        return '<p>' . preg_replace(['~\n(\s*)\n\s*~', '~(?<!</p>)\n\s*~'], ["</p>\n\$1<p>", "<br>\n"], trim($value)) .
47 1
            '</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 4
    public function line($value, $line = 1)
58
    {
59 4
        if (!isset($value)) {
60 1
            return null;
61
        }
62
        
63 3
        $lines = explode("\n", $value);
64
        
65 3
        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 4
    public function less($value, $replace = '...', $break = '<!-- pagebreak -->')
77
    {
78 4
        if (!isset($value)) {
79 1
            return null;
80
        }
81
        
82 3
        $pos = stripos($value, $break);
83 3
        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 4
    public function truncate($value, $length, $replace = '...')
95
    {
96 4
        if (!isset($value)) {
97 1
            return null;
98
        }
99
        
100 3
        return strlen($value) <= $length ? $value : substr($value, 0, $length - strlen($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
     * @return string
112
     */
113 6
    protected function linkifyHttp($protocol, $text, array &$links, $attr, $mode)
114
    {
115 6
        $regexp = $mode != 'all'
116 5
            ? '~(?:(https?)://([^\s<>]+)|(?<!\w@)\b(www\.[^\s<>]+?\.[^\s<>]+))(?<![\.,:;\?!\'"\|])~i'
117 6
            : '~(?:(https?)://([^\s<>]+)|(?<!\w@)\b([^\s<>@]+?\.[^\s<>]+)(?<![\.,:]))~i';
118
        
119 6
        return preg_replace_callback($regexp, function ($match) use ($protocol, &$links, $attr) {
120 5
            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...
Coding Style Best Practice introduced by
It is generally a best practice to always use braces with control structures.

Adding braces to control structures avoids accidental mistakes as your code changes:

// Without braces (not recommended)
if (true)
    doSomething();

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

Let’s take a look at an example:

// Bad
$a = 5; $b = 6; $c = 7;

// Good
$a = 5;
$b = 6;
$c = 7;
Loading history...
206 9
                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...
Coding Style introduced by
It is generally recommended to place each PHP statement on a line by itself.

Let’s take a look at an example:

// Bad
$a = 5; $b = 6; $c = 7;

// Good
$a = 5;
$b = 6;
$c = 7;
Loading history...
207 10
                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...
Coding Style introduced by
It is generally recommended to place each PHP statement on a line by itself.

Let’s take a look at an example:

// Bad
$a = 5; $b = 6; $c = 7;

// Good
$a = 5;
$b = 6;
$c = 7;
Loading history...
208
            }
209
        }
210
        
211
        // Insert all link
212 10
        return preg_replace_callback('/<(\d+)>/', function ($match) use (&$links) {
213 10
            return $links[$match[1] - 1];
214 10
        }, $text);
215
    }
216
}
217