Completed
Push — master ( f8a254...22b9ab )
by Yuri
02:05
created

Formatter::nameCase()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 21
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 11
CRAP Score 4

Importance

Changes 1
Bugs 0 Features 1
Metric Value
c 1
b 0
f 1
dl 0
loc 21
ccs 11
cts 11
cp 1
rs 9.0534
cc 4
eloc 10
nc 3
nop 2
crap 4
1
<?php namespace Tamtamchik\NameCase;
2
3
/**
4
 * Class Formatter.
5
 */
6
class Formatter
7
{
8
    // Default options.
9
    private static $options = [
10
        'lazy'    => true,
11
        'irish'   => true,
12
        'spanish' => true,
13
    ];
14
15
    // Irish exceptions.
16
    private static $exceptions = [
17
        '\bMacEdo'     => 'Macedo',
18
        '\bMacEvicius' => 'Macevicius',
19
        '\bMacHado'    => 'Machado',
20
        '\bMacHar'     => 'Machar',
21
        '\bMacHin'     => 'Machin',
22
        '\bMacHlin'    => 'Machlin',
23
        '\bMacIas'     => 'Macias',
24
        '\bMacIulis'   => 'Maciulis',
25
        '\bMacKie'     => 'Mackie',
26
        '\bMacKle'     => 'Mackle',
27
        '\bMacKlin'    => 'Macklin',
28
        '\bMacKmin'    => 'Mackmin',
29
        '\bMacQuarie'  => 'Macquarie',
30
    ];
31
32
    // General replacements.
33
    private static $replacements = [
34
        '\bAl(?=\s+\w)'         => 'al',        // al Arabic or forename Al.
35
        '\b(Bin|Binti|Binte)\b' => 'bin',       // bin, binti, binte Arabic
36
        '\bAp\b'                => 'ap',        // ap Welsh.
37
        '\bBen(?=\s+\w)'        => 'ben',       // ben Hebrew or forename Ben.
38
        '\bDell([ae])\b'        => 'dell\1',    // della and delle Italian.
39
        '\bD([aeiou])\b'        => 'd\1',       // da, de, di Italian; du French; do Brasil
40
        '\bD([ao]s)\b'          => 'd\1',       // das, dos Brasileiros
41
        '\bDe([lr])\b'          => 'de\1',      // del Italian; der Dutch/Flemish.
42
        '\bEl\b'                => 'el',        // el Greek or El Spanish.
43
        '\bLa\b'                => 'la',        // la French or La Spanish.
44
        '\bL([eo])\b'           => 'l\1',       // lo Italian; le French.
45
        '\bVan(?=\s+\w)'        => 'van',       // van German or forename Van.
46
        '\bVon\b'               => 'von',       // von Dutch/Flemish
47
    ];
48
49
    // Spanish conjunctions.
50
    private static $conjunctions = ["Y", "E", "I"];
51
52
    // Roman letters regexp.
53
    private static $romanRegex = '\b((?:[Xx]{1,3}|[Xx][Ll]|[Ll][Xx]{0,3})?(?:[Ii]{1,3}|[Ii][VvXx]|[Vv][Ii]{0,3})?)\b';
54
55 3
    public function __construct(array $options = [])
56
    {
57 3
        self::$options = array_merge(self::$options, $options);
58 3
    }
59
60
    /**
61
     * Main function for NameCase.
62
     *
63
     * @param string $string
64
     * @param array  $options
65
     *
66
     * @return string
67
     */
68 12
    public static function nameCase($string = '', array $options = [])
69
    {
70 12
        self::$options = array_merge(self::$options, $options);
71
72
        // Do not do anything if string is mixed and lazy option is true.
73 12
        if (self::$options['lazy'] && self::skipMixed($string)) return $string;
74
75
        // Capitalize
76 12
        $string = self::capitalize($string);
77 12
        $string = self::updateIrish($string);
78
79
        // Fixes for "son (daughter) of" etc
80 12
        foreach (self::$replacements as $pattern => $replacement) {
81 12
            $string = mb_ereg_replace($pattern, $replacement, $string);
82 12
        }
83
84 12
        $string = self::updateRoman($string);
85 12
        $string = self::fixConjunction($string);
86
87 12
        return $string;
88
    }
89
90
    /**
91
     * Capitalize first letters.
92
     *
93
     * @param string $string
94
     *
95
     * @return string
96
     */
97 12
    private static function capitalize($string)
98
    {
99 12
        $string = mb_strtolower($string);
100
101
        $string = mb_ereg_replace_callback('\b\w', function ($matches) {
102 12
            return mb_strtoupper($matches[0]);
103 12
        }, $string);
104
105
        // Lowercase 's
106
        $string = mb_ereg_replace_callback('\'\w\b', function ($matches) {
107 9
            return mb_strtolower($matches[0]);
108 12
        }, $string);
109
110 12
        return $string;
111
    }
112
113
    /**
114
     * Skip if string is mixed case.
115
     *
116
     * @param string $string
117
     *
118
     * @return bool
119
     */
120 12
    private static function skipMixed($string)
121
    {
122 12
        $firstLetterLower = $string[0] == mb_strtolower($string[0]);
123 12
        $allLowerOrUpper  = (mb_strtolower($string) == $string || mb_strtoupper($string) == $string);
124
125 12
        return ! ($firstLetterLower || $allLowerOrUpper);
126
    }
127
128
    /**
129
     * Update for Irish names.
130
     *
131
     * @param string $string
132
     *
133
     * @return string
134
     */
135 12
    private static function updateIrish($string)
136
    {
137 12
        if ( ! self::$options['irish']) return $string;
138
139 12
        if (mb_ereg_match('.*?\bMac[A-Za-z]{2,}[^aciozj]\b', $string) || mb_ereg_match('.*?\bMc', $string)) {
140 9
            $string = self::updateMac($string);
141 9
        }
142
143 12
        return mb_ereg_replace('Macmurdo', 'MacMurdo', $string);
144
    }
145
146
    /**
147
     * Fix Spanish conjunctions.
148
     *
149
     * @param string $string
150
     *
151
     * @return string
152
     */
153 12
    private static function fixConjunction($string)
154
    {
155 12
        if ( ! self::$options['spanish']) return $string;
156
157 12
        foreach (self::$conjunctions as $conjunction) {
158 12
            $string = mb_ereg_replace('\b' . $conjunction . '\b', mb_strtolower($conjunction), $string);
159 12
        }
160
161 12
        return $string;
162
    }
163
164
    /**
165
     * Fix roman numeral names.
166
     *
167
     * @param string $string
168
     *
169
     * @return string
170
     */
171 12
    private static function updateRoman($string)
172
    {
173
        return mb_ereg_replace_callback(self::$romanRegex, function ($matches) {
174 12
            return mb_strtoupper($matches[0]);
175 12
        }, $string);
176
    }
177
178
    /**
179
     * Updates irish Mac & Mc.
180
     *
181
     * @param string $string
182
     *
183
     * @return string
184
     */
185
    private static function updateMac($string)
186
    {
187 9
        $string = mb_ereg_replace_callback('\b(Ma?c)([A-Za-z]+)', function ($matches) {
188 9
            return $matches[1] . mb_strtoupper(mb_substr($matches[2], 0, 1)) . mb_substr($matches[2], 1);
189 9
        }, $string);
190
191
        // Now fix "Mac" exceptions
192 9
        foreach (self::$exceptions as $pattern => $replacement) {
193 9
            $string = mb_ereg_replace($pattern, $replacement, $string);
194 9
        }
195
196 9
        return $string;
197
    }
198
}
199