Str   B
last analyzed

Complexity

Total Complexity 45

Size/Duplication

Total Lines 338
Duplicated Lines 0 %

Test Coverage

Coverage 92.39%

Importance

Changes 5
Bugs 0 Features 0
Metric Value
eloc 112
dl 0
loc 338
ccs 85
cts 92
cp 0.9239
rs 8.8
c 5
b 0
f 0
wmc 45

11 Methods

Rating   Name   Duplication   Size   Complexity  
A camel() 0 4 1
A moustaches() 0 8 1
A snake() 0 9 1
A normalise() 0 7 1
A pascal() 0 7 1
C plural() 0 42 16
A truncate() 0 19 4
A bool() 0 14 5
C singular() 0 39 12
A join() 0 7 2
A token() 0 5 1

How to fix   Complexity   

Complex Class

Complex classes like Str often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Str, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace Ronanchilvers\Utility;
4
5
/**
6
 * Useful string manipulation methods
7
 *
8
 * NB: This class only handles english strings - it is not aware of other languages
9
 *
10
 * @author Ronan Chilvers <[email protected]>
11
 */
12
class Str
13
{
14
    /**
15
     * @var array
16
     */
17
    static protected $irregulars = [
18
        'child'  => 'children',
19
        'foot'   => 'feet',
20
        'goose'  => 'geese',
21
        'man'    => 'men',
22
        'mouse'  => 'mice',
23
        'person' => 'people',
24
        'sheep'  => 'sheep',
25
        'tooth'  => 'teeth',
26
    ];
27
28
    /**
29
     * @var array
30
     */
31
    static protected $uncountable = [
32
        'sheep',
33
        'fish',
34
        'deer',
35
        'series',
36
        'species',
37
        'money',
38
        'rice',
39
        'information',
40
        'equipment'
41
    ];
42
43
    /**
44
     * @var array
45
     */
46
    static protected $vowels = [
47
        'a',
48
        'e',
49
        'i',
50
        'o',
51
        'u'
52
    ];
53
54
    /**
55
     * Pluralise a string
56
     *
57
     * [1.0] 'ies' rule    (ends in a consonant + y : baby/lady)
58
     * [2.0] 'ves' rule    (ends in f or fe : leaf/knife) --- roof : rooves (correct but old english, roofs is ok).
59
     * [3.1] 'es' rule 1   (ends in a consonant + o : volcano/mango)
60
     * [3.2] 'es' rule 2   (ends in ch, sh, s, ss, x, z : match/dish/bus/glass/fox/buzz)
61
     * [4.1] 's' rule 1    (ends in a vowel + y or o : boy/radio)
62
     * [4.2] 's' rule 2    (ends in other than above : cat/ball)
63
     *
64
     * @param string $string The singular noun to pluralise
65
     * @param int $count
66
     * @param string $plural
67
     * @return string
68
     * @author Ronan Chilvers <[email protected]>
69
     */
70 132
    static public function plural($string, $count = 2, $plural = false)
71
    {
72 132
        $string = trim($string);
73 132
        if (in_array($string, static::$uncountable) && false === $plural) {
74 18
            return $string;
75
        }
76
77 114
        if (empty($string) || $count == 1) {
78 57
            return $string;
79
        }
80
81 57
        if (false !== $plural) {
82 33
            return $plural;
83
        }
84
85 24
        $string = mb_strtolower($string);
86 24
        if (isset(static::$irregulars[$string])) {
87 7
            return static::$irregulars[$string];
88
        }
89
90
        // [1.0]
91 17
        if (!in_array(mb_substr($string, -2, 1), static::$vowels) && 'y' == mb_substr($string, -1)) {
92 2
            return mb_substr($string, 0, -1) . 'ies';
93
        }
94
95
        // [2.0]
96 15
        if ('f' == mb_substr($string, -1) || 'fe' == mb_substr($string, -2)) {
97 2
            $length = ('e' == mb_substr($string, -1)) ? -2 : -1 ;
98 2
            return mb_substr($string, 0, $length) . 'ves';
99
        }
100
101
        // [3.1]
102 13
        if (!in_array(mb_substr($string, -2, 1), static::$vowels) && 'o' == mb_substr($string, -1)) {
103 1
            return $string . 'es';
104
        }
105
106
        // [3.2]
107 12
        if (in_array(mb_substr($string, -2), ['ch', 'sh', 'ss']) || in_array(mb_substr($string, -1), ['s', 'x', 'z'])) {
108 7
            return $string . 'es';
109
        }
110
111 5
        return $string . 's';
112
    }
113
114
    /**
115
     * Singularise a string
116
     *
117
     * [1.0] 'ies' rule    (ends in a consonant + y : baby/lady)
118
     * [2.0] 'ves' rule    (ends in f or fe : leaf/knife) --- roof : rooves (correct but old english, roofs is ok).
119
     * [3.1] 'es' rule 1   (ends in a consonant + o : volcano/mango)
120
     * [3.2] 'es' rule 2   (ends in ch, sh, s, ss, x, z : match/dish/bus/glass/fox/buzz)
121
     * [4.1] 's' rule 1    (ends in a vowel + y or o : boy/radio)
122
     * [4.2] 's' rule 2    (ends in other than above : cat/ball)
123
     *
124
     * @param string $string The singular noun to singularise
125
     * @param int $count
126
     * @param string $singular
127
     * @return string
128
     * @author Ronan Chilvers <[email protected]>
129
     */
130 129
    static public function singular($string, $count = 1, $singular = false)
131
    {
132 129
        $string = trim($string);
133 129
        if (in_array($string, static::$uncountable) && false === $singular) {
134 18
            return $string;
135
        }
136
137 111
        if (empty($string) || $count !== 1) {
138 56
            return $string;
139
        }
140
141 55
        if (false !== $singular) {
142 32
            return $singular;
143
        }
144
145 23
        $singulars = array_flip(static::$irregulars);
146 23
        $string = mb_strtolower($string);
147 23
        if (isset($singulars[$string])) {
148 7
            return $singulars[$string];
149
        }
150
151
        // [1.0]
152 16
        if ('ies' == mb_substr($string, -3)) {
153 2
            return mb_substr($string, 0, -3) . 'y';
154
        }
155
156
        // [2.0]
157 14
        if ('ves' == mb_substr($string, -3)) {
158 2
            if (in_array(mb_substr($string, -4, 1), static::$vowels)) {
159 1
                return mb_substr($string, 0, -3) . 'f';
160
            }
161
        }
162
163
        // [3.1, 3.2]
164 13
        if ('ves' != mb_substr($string, -3) && 'es' == mb_substr($string, -2)) {
165 8
            return mb_substr($string, 0, -2);
166
        }
167
168 5
        return rtrim($string, 's');
169
    }
170
171
    /**
172
     * Convert a string to PascalCase
173
     *
174
     * Pascal case is upper camel case, or camel case with the first
175
     * letter uppercased.
176
     *
177
     * @param string $string
178
     * @param array $allowed Allowed characters that won't be compressed
179
     * @return string
180
     * @author Ronan Chilvers <[email protected]>
181
     */
182 4
    static public function pascal($string, $allowed = [])
183
    {
184 4
        $string = mb_strtolower($string);
185 4
        $string = preg_replace('#[^a-z0-9' . implode('', $allowed) . ']+#', ' ', $string);
186 4
        $string = str_replace(' ', '', ucwords($string));
187
188 4
        return $string;
189
    }
190
191
    /**
192
     * Convert a string to camelCase
193
     *
194
     * We're taking camel case here to mean lower camel case, ie: the first
195
     * letter is lower case.
196
     *
197
     * @param string $string
198
     * @param array $allowed Allowed characters that won't be compressed
199
     * @return string
200
     * @author Ronan Chilvers <[email protected]>
201
     */
202 2
    static public function camel($string, $allowed = [])
203
    {
204 2
        return lcfirst(
205 2
            static::pascal($string, $allowed)
206
        );
207
    }
208
209
    /**
210
     * Snake case a string
211
     *
212
     * This method snake cases phrases and can also convert camelCase
213
     * and PascalCase strings to snake case.
214
     *
215
     * @param string $string
216
     * @param array $allowed Allowed characters that won't be compressed
217
     * @return string
218
     * @author Ronan Chilvers <[email protected]>
219
     */
220 4
    static public function snake($string, $allowed = [])
221
    {
222
        // $string = mb_strtolower($string);
223 4
        $string = preg_replace('#([A-Z]{1})#', ' $1', $string);
224 4
        $string = mb_strtolower(trim($string));
225 4
        $string = preg_replace('#[^a-z0-9' . implode('', $allowed) . ']{1,}#', ' ', $string);
226 4
        $string = str_replace(' ', '_', $string);
227
228 4
        return $string;
229
    }
230
231
    /**
232
     * Truncate a string to a given length, optionally respecting word
233
     * boundaries
234
     *
235
     * @param string $string The string to truncate
236
     * @param int $length The length to truncate to
237
     * @param string $suffix Suffix to tag on the end of the string
238
     * @param boolean $words Respect word boundaries
239
     * @return string
240
     * @author Ronan Chilvers <[email protected]>
241
     */
242 5
    static public function truncate($string, $length, $suffix = '...', $words = false)
243
    {
244 5
        if ($length > mb_strlen($string)) {
245 1
            return $string;
246
        }
247 4
        if (false === $words) {
248 2
            return mb_substr($string, 0, $length - mb_strlen($suffix)) . $suffix;
249
        }
250 2
        $matches = [];
251 2
        preg_match(
252 2
            "/(^.{1," . ($length - mb_strlen($suffix)) . "})(?=\s|$).*/u",
253
            $string,
254
            $matches
255
        );
256 2
        if (!isset($matches[1])) {
257 1
            return mb_substr($string, 0, $length - mb_strlen($suffix)) . $suffix;
258
        }
259
260 1
        return $matches[1] . $suffix;
261
    }
262
263
    /**
264
     * Generate a long random string suitable for use as a token
265
     *
266
     * @param int $length
267
     * @return string
268
     * @author Ronan Chilvers <[email protected]>
269
     */
270 1
    static public function token($length = 64)
271
    {
272 1
        return substr(bin2hex(
273 1
            random_bytes($length)
274
        ), 0, $length);
275
    }
276
277
    /**
278
     * Create a string by joining an arbitrary number of other strings with a separator
279
     *
280
     * @param string $seperator
281
     * @param string $piece...
282
     * @author Ronan Chilvers <[email protected]>
283
     */
284 2
    static public function join($seperator, ...$pieces)
285
    {
286 2
        foreach ($pieces as &$piece) {
287 2
            $piece = trim($piece, $seperator);
288
        }
289
290 2
        return implode($seperator, $pieces);
291
    }
292
293
    /**
294
     * Convert a string to a boolean value
295
     *
296
     * @param string
297
     * @return boolean
298
     * @author Ronan Chilvers <[email protected]>
299
     */
300 6
    static public function bool($string)
301
    {
302 6
        if (true === $string) {
303
            return true;
304
        }
305
        switch ($string) {
306
307 6
            case '1':
308 5
            case 'yes':
309 4
            case 'true':
310 3
                return true;
311
312
            default:
313 3
                return false;
314
315
        }
316
    }
317
318
    /**
319
     * Simple moustaches templating
320
     *
321
     * @param string $template
322
     * @param array $params
323
     * @return string
324
     * @author Ronan Chilvers <[email protected]>
325
     */
326
    static public function moustaches(string $template, array $params)
327
    {
328
        $keys = array_map(function ($value) {
329
            return '{'.$value.'}';
330
        }, array_keys($params));
331
        $values = array_values($params);
332
333
        return str_replace($keys, $values, $template);
334
    }
335
336
    /**
337
     * Normalise a string into a key with only hyphens and lowercase alphanumeric characters
338
     *
339
     * @param string $string
340
     * @return string
341
     * @author Ronan Chilvers <[email protected]>
342
     */
343 3
    static public function normalise($string): string
344
    {
345 3
        $string = strtolower($string);
346 3
        $string = preg_replace('/[^A-z0-9\s-]+/', '', $string);
347 3
        $string = preg_replace('/[\s-]{1,}/', '-', $string);
348
349 3
        return $string;
350
    }
351
}
352