GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Completed
Push — master ( 935081...a89aa8 )
by Carlos
04:20
created

Pinyin   A

Complexity

Total Complexity 35

Size/Duplication

Total Lines 410
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 0

Test Coverage

Coverage 93.38%

Importance

Changes 32
Bugs 9 Features 0
Metric Value
wmc 35
c 32
b 9
f 0
lcom 1
cbo 0
dl 0
loc 410
ccs 127
cts 136
cp 0.9338
rs 9

20 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 5 1
A __clone() 0 3 1
A getInstance() 0 8 2
A set() 0 4 1
A settings() 0 4 1
A trans() 0 6 1
A letter() 0 8 1
B parse() 0 37 5
A appends() 0 9 2
B getFirstLetters() 0 20 6
A string2pinyin() 0 17 3
A formatWords() 0 8 2
A formatDictPinyin() 0 8 1
A containChinese() 0 4 1
A justChinese() 0 4 1
A prepare() 0 8 1
A delimit() 0 9 1
A removeTone() 0 9 1
A addAccents() 0 8 1
B addAccentsCallback() 0 43 2
1
<?php
2
3
/**
4
 * Pinyin.php.
5
 *
6
 * For the full copyright and license information, please view the LICENSE
7
 * file that was distributed with this source code.
8
 *
9
 * @author    overtrue <[email protected]>
10
 * @copyright 2015 overtrue <[email protected]>
11
 *
12
 * @link      https://github.com/overtrue/pinyin
13
 * @link      http://overtrue.me
14
 */
15
16
namespace Overtrue\Pinyin;
17
18
/**
19
 * Chinese to pinyin translator.
20
 *
21
 * @example
22
 * <pre>
23
 *      echo \Overtrue\Pinyin\Pinyin::trans('带着希望去旅行,比到达终点更美好'), "\n";
24
 *      //output: "dài zhe xī wàng qù lǔ xíng bǐ dào dá zhōng diǎn gèng měi hǎo"
25
 * </pre>
26
 */
27
class Pinyin
28
{
29
    /**
30
     * Dictionary.
31
     *
32
     * @var array
33
     */
34
    protected static $dictionary = array();
35
36
    /**
37
     * Settings.
38
     *
39
     * @var array
40
     */
41
    protected static $settings = array(
42
                                  'delimiter' => ' ',
43
                                  'accent' => true,
44
                                  'only_chinese' => false,
45
                                  'uppercase' => false,
46
                                  'charset' => 'UTF-8'  // GB2312,UTF-8
47
                                 );
48
    /**
49
     * Internal charset used by this package.
50
     *
51
     * @var string
52
     */
53
    protected static $internalCharset = 'UTF-8';
54
55
    /**
56
     * The instance.
57
     *
58
     * @var \Overtrue\Pinyin\Pinyin
59
     */
60
    private static $_instance;
61
62
    /**
63
     * Constructor.
64
     *
65
     * set dictionary path.
66
     */
67 1
    private function __construct()
68
    {
69 1
        $list = json_decode(file_get_contents(dirname(__DIR__).'/data/dict.php'), true);
70 1
        static::appends($list);
71 1
    }
72
73
    /**
74
     * Disable clone.
75
     */
76
    private function __clone()
77
    {
78
    }
79
80
    /**
81
     * Get class instance.
82
     *
83
     * @return \Overtrue\Pinyin\Pinyin
84
     */
85 17
    public static function getInstance()
86
    {
87 17
        if (is_null(self::$_instance)) {
88 1
            self::$_instance = new static();
89 1
        }
90
91 17
        return self::$_instance;
92
    }
93
94
    /**
95
     * Setter.
96
     *
97
     * @param string $key
98
     * @param mixed  $value
99
     */
100 4
    public static function set($key, $value)
101
    {
102 4
        static::$settings[$key] = $value;
103 4
    }
104
105
    /**
106
     * Global settings.
107
     *
108
     * @param array $settings settings.
109
     */
110
    public static function settings(array $settings = array())
111
    {
112
        static::$settings = array_merge(static::$settings, $settings);
113
    }
114
115
    /**
116
     * Chinese to pinyin.
117
     *
118
     * @param string $string   source string.
119
     * @param array  $settings settings.
120
     *
121
     * @return string
122
     */
123 15
    public static function trans($string, array $settings = array())
124
    {
125 15
        $parsed = self::parse($string, $settings);
126
127 15
        return $parsed['pinyin'];
128
    }
129
130
    /**
131
     * Get first letters of string.
132
     *
133
     * @param string $string   source string.
134
     * @param string $settings settings
135
     *
136
     * @return string
137
     */
138 2
    public static function letter($string, array $settings = array())
139
    {
140 2
        $settings = array_merge($settings, array('accent' => false, 'only_chinese' => true));
141
142 2
        $parsed = self::parse($string, $settings);
143
144 2
        return $parsed['letter'];
145
    }
146
147
    /**
148
     * Parse the string to pinyin.
149
     *
150
     * Overtrue\Pinyin\Pinyin::parse('带着梦想旅行');
151
     *
152
     * @param string $string
153
     * @param array  $settings
154
     *
155
     * @return array
156
     */
157 17
    public static function parse($string, array $settings = array())
158
    {
159 17
        $instance = static::getInstance();
160 17
        $raw      = $string;
161
162 17
        $settings = array_merge(self::$settings, $settings);
163
164
        // add charset set
165 17
        if (!empty($settings['charset']) && $settings['charset'] != static::$internalCharset) {
166
            $string = iconv($settings['charset'], static::$internalCharset, $string);
167
        }
168
169
        // remove non-Chinese char.
170 17
        if ($settings['only_chinese']) {
171 2
            $string = $instance->justChinese($string);
172 2
        }
173
174 17
        $source = $instance->string2pinyin($string);
175
176
        // add accents
177 17
        if ($settings['accent']) {
178 13
            $pinyin = $instance->addAccents($source);
179 13
        } else {
180 7
            $pinyin = $instance->removeTone($source);
181
        }
182
183
        //add delimiter
184 17
        $delimitedPinyin = $instance->delimit($pinyin, $settings['delimiter']);
185
186
        $return = array(
187 17
                   'src' => $raw,
188 17
                   'pinyin' => stripslashes($delimitedPinyin),
189 17
                   'letter' => stripslashes($instance->getFirstLetters($source, $settings)),
190 17
                  );
191
192 17
        return $return;
193
    }
194
195
    /**
196
     * Add custom words.
197
     *
198
     * @param array $appends
199
     */
200 2
    public static function appends(array $appends)
201
    {
202 2
        $list = static::formatWords($appends);
203
204 2
        foreach ($list as $key => $value) {
205 2
            $firstChar = mb_substr($key, 0, 1, static::$internalCharset);
206 2
            self::$dictionary[$firstChar][$key] = $value;
207 2
        }
208 2
    }
209
210
    /**
211
     * Get first letters from pinyin.
212
     *
213
     * @param string $pinyin
214
     * @param array  $settings
215
     *
216
     * @return string
217
     */
218 17
    protected function getFirstLetters($pinyin, $settings)
219
    {
220 17
        $letterCase = $settings['uppercase'] ? 'strtoupper' : 'strtolower';
221
222 17
        $letters = array();
223
224 17
        foreach (explode(' ', $pinyin) as $word) {
225 17
            if (empty($word)) {
226 2
                continue;
227
            }
228
229 17
            $ord = ord(strtolower($word{0}));
230
231 17
            if ($ord >= 97 && $ord <= 122) {
232 17
                $letters[] = $letterCase($word{0});
233 17
            }
234 17
        }
235
236 17
        return implode($settings['delimiter'], $letters);
237
    }
238
239
    /**
240
     * Replace string to pinyin.
241
     *
242
     * @param string $string
243
     *
244
     * @return string
245
     */
246 17
    protected function string2pinyin($string)
247
    {
248 17
        $preparedString = $this->prepare($string);
249 17
        $count = mb_strlen($preparedString, static::$internalCharset);
250 17
        $dictionary = array();
251
252 17
        $i = 0;
253 17
        while ($i < $count) {
254 17
            $char = mb_substr($preparedString, $i++, 1, static::$internalCharset);
255 17
            $pinyinGroup = isset(self::$dictionary[$char]) ? self::$dictionary[$char] : array();
256 17
            $dictionary = array_merge($dictionary, $pinyinGroup);
257 17
        }
258
259 17
        $pinyin = strtr($preparedString, $dictionary);
260
261 17
        return trim(str_replace('  ', ' ', $pinyin));
262
    }
263
264
    /**
265
     * Format user's words.
266
     *
267
     * @param array $words
268
     *
269
     * @return array
270
     */
271 2
    public static function formatWords($words)
272
    {
273 2
        foreach ($words as $word => $pinyin) {
274 2
            $words[$word] = static::formatDictPinyin($pinyin);
275 2
        }
276
277 2
        return $words;
278
    }
279
280
    /**
281
     * Format pinyin to lowercase.
282
     *
283
     * @param string $pinyin pinyin string.
284
     *
285
     * @return string
286
     */
287 2
    protected static function formatDictPinyin($pinyin)
288
    {
289 2
        $pinyin = trim($pinyin);
290
291 2
        return preg_replace_callback('/[a-z]{1,}:?\d{1}\s?/i', function ($matches) {
292 2
            return strtolower($matches[0]);
293 2
        }, " {$pinyin} ");
294
    }
295
296
    /**
297
     * Check if the string has Chinese characters.
298
     *
299
     * @param string $string string to check.
300
     *
301
     * @return int
302
     */
303
    protected function containChinese($string)
304
    {
305
        return preg_match('/\p{Han}+/u', $string);
306
    }
307
308
    /**
309
     * Remove the non-Chinese characters.
310
     *
311
     * @param string $string source string.
312
     *
313
     * @return string
314
     */
315 2
    public function justChinese($string)
316
    {
317 2
        return preg_replace('/[^\p{Han}]/u', '', $string);
318
    }
319
320
    /**
321
     * Prepare the string.
322
     *
323
     * @param string $string source string.
324
     *
325
     * @return string
326
     */
327 17
    protected function prepare($string)
328
    {
329
        $pattern = array(
330 17
                '/([A-z])(\d)/' => '$1\\\\\2', // test4 => test\\4
331 17
            );
332
333 17
        return preg_replace(array_keys($pattern), $pattern, $string);
334
    }
335
336
    /**
337
     * Add delimiter.
338
     *
339
     * @param string $string
340
     * @param string $delimiter
341
     *
342
     * @return string
343
     */
344 17
    protected function delimit($string, $delimiter = '')
345
    {
346 17
        $defaultEncoding = mb_regex_encoding();
347 17
        mb_regex_encoding(static::$internalCharset);
348 17
        $string = mb_ereg_replace('\s+', strval($delimiter), trim($string));
349 17
        mb_regex_encoding($defaultEncoding);
350
351 17
        return $string;
352
    }
353
354
    /**
355
     * Remove tone.
356
     *
357
     * @param string $string string with tone.
358
     *
359
     * @return string
360
     */
361 7
    protected function removeTone($string)
362
    {
363
        $replacement = array(
364 7
                        '/u:/' => 'u',
365 7
                        '/([a-z])[1-5]/i' => '\\1',
366 7
                       );
367
368 7
        return preg_replace(array_keys($replacement), $replacement, $string);
369
    }
370
371
    /**
372
     * Credits for these 2 functions go to Bouke Versteegh, who shared these
373
     * at http://stackoverflow.com/questions/1598856/convert-numbered-to-accentuated-pinyin.
374
     *
375
     * @param string $string The pinyin string with tone numbers, i.e. "ni3 hao3"
376
     *
377
     * @return string The formatted string with tone marks, i.e.
378
     */
379 13
    protected function addAccents($string)
380
    {
381
        // find words with a number behind them, and replace with callback fn.
382 13
        return str_replace('u:', 'ü', preg_replace_callback(
383 13
            '~([a-zA-ZüÜ]+\:?)([1-5])~',
384 13
            array($this, 'addAccentsCallback'),
385 13
            $string));
386
    }
387
388
    /**
389
     * Helper callback.
390
     *
391
     * @param array $match
392
     */
393 13
    protected function addAccentsCallback($match)
394
    {
395 13
        static $accentmap = null;
396
397 13
        if ($accentmap === null) {
398
            // where to place the accent marks
399
            $stars = 'a* e* i* o* u* ü* ü* '.
400 1
                     'A* E* I* O* U* Ü* '.
401 1
                     'a*i a*o e*i ia* ia*o ie* io* iu* '.
402 1
                     'A*I A*O E*I IA* IA*O IE* IO* IU* '.
403 1
                     'o*u ua* ua*i ue* ui* uo* üe* '.
404 1
                     'O*U UA* UA*I UE* UI* UO* ÜE*';
405
            $nostars = 'a e i o u u: ü '.
406 1
                       'A E I O U Ü '.
407 1
                       'ai ao ei ia iao ie io iu '.
408 1
                       'AI AO EI IA IAO IE IO IU '.
409 1
                       'ou ua uai ue ui uo üe '.
410 1
                       'OU UA UAI UE UI UO ÜE';
411
412
            // build an array like array('a' => 'a*') and store statically
413 1
            $accentmap = array_combine(explode(' ', $nostars), explode(' ', $stars));
414 1
        }
415
416 13
        $vowels = array('a*', 'e*', 'i*', 'o*', 'u*', 'ü*', 'A*', 'E*', 'I*', 'O*', 'U*', 'Ü*');
417
418
        $pinyin = array(
419 13
            1 => array('ā', 'ē', 'ī', 'ō', 'ū', 'ǖ', 'Ā', 'Ē', 'Ī', 'Ō', 'Ū', 'Ǖ'),
420 13
            2 => array('á', 'é', 'í', 'ó', 'ú', 'ǘ', 'Á', 'É', 'Í', 'Ó', 'Ú', 'Ǘ'),
421 13
            3 => array('ǎ', 'ě', 'ǐ', 'ǒ', 'ǔ', 'ǚ', 'Ǎ', 'Ě', 'Ǐ', 'Ǒ', 'Ǔ', 'Ǚ'),
422 13
            4 => array('à', 'è', 'ì', 'ò', 'ù', 'ǜ', 'À', 'È', 'Ì', 'Ò', 'Ù', 'Ǜ'),
423 13
            5 => array('a', 'e', 'i', 'o', 'u', 'ü', 'A', 'E', 'I', 'O', 'U', 'Ü'),
424 13
        );
425
426 13
        list(, $word, $tone) = $match;
427
428
        // add star to vowelcluster
429 13
        $word = strtr($word, $accentmap);
0 ignored issues
show
Bug introduced by
It seems like $accentmap can also be of type null; however, strtr() does only seem to accept array, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
430
431
        // replace starred letter with accented
432 13
        $word = str_replace($vowels, $pinyin[$tone], $word);
433
434 13
        return $word;
435
    }
436
}//end class
437
438