Functions::slug()   B
last analyzed

Complexity

Conditions 4
Paths 4

Size

Total Lines 337
Code Lines 297

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 13
CRAP Score 4

Importance

Changes 0
Metric Value
cc 4
eloc 297
nc 4
nop 2
dl 0
loc 337
ccs 13
cts 13
cp 1
crap 4
rs 8
c 0
b 0
f 0

How to fix   Long Method   

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
namespace __\Traits;
4
5
trait Functions
6
{
7
8
    /**
9
     * Create a web friendly URL slug from a string.
10
     *
11
     * Although supported, transliteration is discouraged because
12
     *     1) most web browsers support UTF-8 characters in URLs
13
     *     2) transliteration causes a loss of information
14
     *
15
     * @usage __::slug('Jakieś zdanie z dużą ilością obcych znaków!');
16
     *        >> 'jakies-zdanie-z-duza-iloscia-obcych-znakow'
17
     *
18
     * @param string $str     string to generate slug from
19
     * @param array  $options method options which includes: delimiter, limit, lowercase, replacements, transliterate
20
     *
21
     * @return string
22
     */
23 1
    public static function slug(string $str, array $options = []): string
24
    {
25
        // Make sure string is in UTF-8 and strip invalid UTF-8 characters
26 1
        $str = mb_convert_encoding((string)$str, 'UTF-8', mb_list_encodings());
27
28
        $defaults = [
29 1
            'delimiter' => '-',
30
            'limit' => null,
31
            'lowercase' => true,
32
            'replacements' => [],
33
            'transliterate' => true,
34
        ];
35
36
        // Merge options
37 1
        $options = array_merge($defaults, $options);
38
39
        $char_map = [
40
            // Latin
41 1
            'À' => 'A',
42
            'Á' => 'A',
43
            'Â' => 'A',
44
            'Ã' => 'A',
45
            'Ä' => 'A',
46
            'Å' => 'A',
47
            'Æ' => 'AE',
48
            'Ç' => 'C',
49
            'È' => 'E',
50
            'É' => 'E',
51
            'Ê' => 'E',
52
            'Ë' => 'E',
53
            'Ì' => 'I',
54
            'Í' => 'I',
55
            'Î' => 'I',
56
            'Ï' => 'I',
57
            'Ð' => 'D',
58
            'Ñ' => 'N',
59
            'Ò' => 'O',
60
            'Ó' => 'O',
61
            'Ô' => 'O',
62
            'Õ' => 'O',
63
            'Ö' => 'O',
64
            'Ő' => 'O',
65
            'Ø' => 'O',
66
            'Ù' => 'U',
67
            'Ú' => 'U',
68
            'Û' => 'U',
69
            'Ü' => 'U',
70
            'Ű' => 'U',
71
            'Ý' => 'Y',
72
            'Þ' => 'TH',
73
            'ß' => 'ss',
74
            'à' => 'a',
75
            'á' => 'a',
76
            'â' => 'a',
77
            'ã' => 'a',
78
            'ä' => 'a',
79
            'å' => 'a',
80
            'æ' => 'ae',
81
            'ç' => 'c',
82
            'è' => 'e',
83
            'é' => 'e',
84
            'ê' => 'e',
85
            'ë' => 'e',
86
            'ì' => 'i',
87
            'í' => 'i',
88
            'î' => 'i',
89
            'ï' => 'i',
90
            'ð' => 'd',
91
            'ñ' => 'n',
92
            'ò' => 'o',
93
            'ó' => 'o',
94
            'ô' => 'o',
95
            'õ' => 'o',
96
            'ö' => 'o',
97
            'ő' => 'o',
98
            'ø' => 'o',
99
            'ù' => 'u',
100
            'ú' => 'u',
101
            'û' => 'u',
102
            'ü' => 'u',
103
            'ű' => 'u',
104
            'ý' => 'y',
105
            'þ' => 'th',
106
            'ÿ' => 'y',
107
108
            // Latin symbols
109
            '©' => '(c)',
110
111
            // Greek
112
            'Α' => 'A',
113
            'Β' => 'B',
114
            'Γ' => 'G',
115
            'Δ' => 'D',
116
            'Ε' => 'E',
117
            'Ζ' => 'Z',
118
            'Η' => 'H',
119
            'Θ' => '8',
120
            'Ι' => 'I',
121
            'Κ' => 'K',
122
            'Λ' => 'L',
123
            'Μ' => 'M',
124
            'Ν' => 'N',
125
            'Ξ' => '3',
126
            'Ο' => 'O',
127
            'Π' => 'P',
128
            'Ρ' => 'R',
129
            'Σ' => 'S',
130
            'Τ' => 'T',
131
            'Υ' => 'Y',
132
            'Φ' => 'F',
133
            'Χ' => 'X',
134
            'Ψ' => 'PS',
135
            'Ω' => 'W',
136
            'Ά' => 'A',
137
            'Έ' => 'E',
138
            'Ί' => 'I',
139
            'Ό' => 'O',
140
            'Ύ' => 'Y',
141
            'Ή' => 'H',
142
            'Ώ' => 'W',
143
            'Ϊ' => 'I',
144
            'Ϋ' => 'Y',
145
            'α' => 'a',
146
            'β' => 'b',
147
            'γ' => 'g',
148
            'δ' => 'd',
149
            'ε' => 'e',
150
            'ζ' => 'z',
151
            'η' => 'h',
152
            'θ' => '8',
153
            'ι' => 'i',
154
            'κ' => 'k',
155
            'λ' => 'l',
156
            'μ' => 'm',
157
            'ν' => 'n',
158
            'ξ' => '3',
159
            'ο' => 'o',
160
            'π' => 'p',
161
            'ρ' => 'r',
162
            'σ' => 's',
163
            'τ' => 't',
164
            'υ' => 'y',
165
            'φ' => 'f',
166
            'χ' => 'x',
167
            'ψ' => 'ps',
168
            'ω' => 'w',
169
            'ά' => 'a',
170
            'έ' => 'e',
171
            'ί' => 'i',
172
            'ό' => 'o',
173
            'ύ' => 'y',
174
            'ή' => 'h',
175
            'ώ' => 'w',
176
            'ς' => 's',
177
            'ϊ' => 'i',
178
            'ΰ' => 'y',
179
            'ϋ' => 'y',
180
            'ΐ' => 'i',
181
182
            // Turkish
183
            'Ş' => 'S',
184
            'İ' => 'I',
185
            'Ç' => 'C',
186
            'Ü' => 'U',
187
            'Ö' => 'O',
188
            'Ğ' => 'G',
189
            'ş' => 's',
190
            'ı' => 'i',
191
            'ç' => 'c',
192
            'ü' => 'u',
193
            'ö' => 'o',
194
            'ğ' => 'g',
195
196
            // Russian
197
            'А' => 'A',
198
            'Б' => 'B',
199
            'В' => 'V',
200
            'Г' => 'G',
201
            'Д' => 'D',
202
            'Е' => 'E',
203
            'Ё' => 'Yo',
204
            'Ж' => 'Zh',
205
            'З' => 'Z',
206
            'И' => 'I',
207
            'Й' => 'J',
208
            'К' => 'K',
209
            'Л' => 'L',
210
            'М' => 'M',
211
            'Н' => 'N',
212
            'О' => 'O',
213
            'П' => 'P',
214
            'Р' => 'R',
215
            'С' => 'S',
216
            'Т' => 'T',
217
            'У' => 'U',
218
            'Ф' => 'F',
219
            'Х' => 'H',
220
            'Ц' => 'C',
221
            'Ч' => 'Ch',
222
            'Ш' => 'Sh',
223
            'Щ' => 'Sh',
224
            'Ъ' => '',
225
            'Ы' => 'Y',
226
            'Ь' => '',
227
            'Э' => 'E',
228
            'Ю' => 'Yu',
229
            'Я' => 'Ya',
230
            'а' => 'a',
231
            'б' => 'b',
232
            'в' => 'v',
233
            'г' => 'g',
234
            'д' => 'd',
235
            'е' => 'e',
236
            'ё' => 'yo',
237
            'ж' => 'zh',
238
            'з' => 'z',
239
            'и' => 'i',
240
            'й' => 'j',
241
            'к' => 'k',
242
            'л' => 'l',
243
            'м' => 'm',
244
            'н' => 'n',
245
            'о' => 'o',
246
            'п' => 'p',
247
            'р' => 'r',
248
            'с' => 's',
249
            'т' => 't',
250
            'у' => 'u',
251
            'ф' => 'f',
252
            'х' => 'h',
253
            'ц' => 'c',
254
            'ч' => 'ch',
255
            'ш' => 'sh',
256
            'щ' => 'sh',
257
            'ъ' => '',
258
            'ы' => 'y',
259
            'ь' => '',
260
            'э' => 'e',
261
            'ю' => 'yu',
262
            'я' => 'ya',
263
264
            // Ukrainian
265
            'Є' => 'Ye',
266
            'І' => 'I',
267
            'Ї' => 'Yi',
268
            'Ґ' => 'G',
269
            'є' => 'ye',
270
            'і' => 'i',
271
            'ї' => 'yi',
272
            'ґ' => 'g',
273
274
            // Czech
275
            'Č' => 'C',
276
            'Ď' => 'D',
277
            'Ě' => 'E',
278
            'Ň' => 'N',
279
            'Ř' => 'R',
280
            'Š' => 'S',
281
            'Ť' => 'T',
282
            'Ů' => 'U',
283
            'Ž' => 'Z',
284
            'č' => 'c',
285
            'ď' => 'd',
286
            'ě' => 'e',
287
            'ň' => 'n',
288
            'ř' => 'r',
289
            'š' => 's',
290
            'ť' => 't',
291
            'ů' => 'u',
292
            'ž' => 'z',
293
294
            // Polish
295
            'Ą' => 'A',
296
            'Ć' => 'C',
297
            'Ę' => 'e',
298
            'Ł' => 'L',
299
            'Ń' => 'N',
300
            'Ó' => 'o',
301
            'Ś' => 'S',
302
            'Ź' => 'Z',
303
            'Ż' => 'Z',
304
            'ą' => 'a',
305
            'ć' => 'c',
306
            'ę' => 'e',
307
            'ł' => 'l',
308
            'ń' => 'n',
309
            'ó' => 'o',
310
            'ś' => 's',
311
            'ź' => 'z',
312
            'ż' => 'z',
313
314
            // Latvian
315
            'Ā' => 'A',
316
            'Č' => 'C',
317
            'Ē' => 'E',
318
            'Ģ' => 'G',
319
            'Ī' => 'i',
320
            'Ķ' => 'k',
321
            'Ļ' => 'L',
322
            'Ņ' => 'N',
323
            'Š' => 'S',
324
            'Ū' => 'u',
325
            'Ž' => 'Z',
326
            'ā' => 'a',
327
            'č' => 'c',
328
            'ē' => 'e',
329
            'ģ' => 'g',
330
            'ī' => 'i',
331
            'ķ' => 'k',
332
            'ļ' => 'l',
333
            'ņ' => 'n',
334
            'š' => 's',
335
            'ū' => 'u',
336
            'ž' => 'z',
337
        ];
338
339
        // Make custom replacements
340 1
        $str = preg_replace(array_keys($options['replacements']), $options['replacements'], $str);
341
342
        // Transliterate characters to ASCII
343 1
        if ($options['transliterate']) {
344 1
            $str = str_replace(array_keys($char_map), $char_map, $str);
345
        }
346
347
        // Replace non-alphanumeric characters with our delimiter
348 1
        $str = preg_replace('/[^\p{L}\p{Nd}]+/u', $options['delimiter'], $str);
349
350
        // Remove duplicate delimiters
351 1
        $str = preg_replace('/(' . preg_quote($options['delimiter'], '/') . '){2,}/', '$1', $str);
352
353
        // Truncate slug to max. characters
354 1
        $str = mb_substr($str, 0, ($options['limit'] ? $options['limit'] : mb_strlen($str, 'UTF-8')), 'UTF-8');
355
356
        // Remove delimiter from ends
357 1
        $str = trim($str, $options['delimiter']);
358
359 1
        return $options['lowercase'] ? mb_strtolower($str, 'UTF-8') : $str;
360
    }
361
362
    /**
363
     * Find the urls inside a string a put them inside anchor tags.
364
     *
365
     * @usage __::urlify("I love https://google.com");
366
     *        >> 'I love <a href="https://google.com">google.com</a>'
367
     *
368
     * @param string $string
369
     *
370
     * @return string|mixed
371
     */
372 1
    public static function urlify(string $string)
373
    {
374
        /* Proposed by:
375
         * Søren Løvborg
376
         * http://stackoverflow.com/users/136796/soren-lovborg
377
         * http://stackoverflow.com/questions/17900004/turn-plain-text-urls-into-active-links-using-php/17900021#17900021
378
         */
379
380 1
        $rexProtocol = '(https?://)?';
381 1
        $rexDomain = '((?:[-a-zA-Z0-9]{1,63}\.)+[-a-zA-Z0-9]{2,63}|(?:[0-9]{1,3}\.){3}[0-9]{1,3})';
382 1
        $rexPort = '(:[0-9]{1,5})?';
383 1
        $rexPath = '(/[!$-/0-9:;=@_\':;!a-zA-Z\x7f-\xff]*?)?';
384 1
        $rexQuery = '(\?[!$-/0-9:;=@_\':;!a-zA-Z\x7f-\xff]+?)?';
385 1
        $rexFragment = '(#[!$-/0-9:;=@_\':;!a-zA-Z\x7f-\xff]+?)?';
386
387 1
        return preg_replace_callback(
388 1
            "&\\b$rexProtocol$rexDomain$rexPort$rexPath$rexQuery$rexFragment(?=[?.!,;:\"]?(\s|$))&",
389
            function ($match) {
390 1
                $completeUrl = $match[1] ? $match[0] : "http://{$match[0]}";
391
392 1
                return '<a href="' . $completeUrl . '">' . $match[2] . $match[3] . $match[4] . '</a>';
393 1
            },
394 1
            htmlspecialchars($string)
395
        );
396
    }
397
398
    /**
399
     * Truncate string based on count of words
400
     *
401
     * @usage $string = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque et mi orci.';
402
     *        __::truncate($string);
403
     *        >> 'Lorem ipsum dolor sit amet, consectetur...'
404
     *
405
     * @param string  $text  text to truncate
406
     * @param integer $limit limit of words
407
     *
408
     * @return string
409
     */
410
411 1
    public static function truncate(string $text, int $limit = 40): string
412
    {
413 1
        if (str_word_count($text, 0) > $limit) {
414 1
            $words = (array)str_word_count($text, 2);
415 1
            $pos = array_keys($words);
416 1
            $text = mb_substr($text, 0, $pos[$limit], 'UTF-8') . '...';
417
        }
418
419 1
        return $text;
420
    }
421
}
422