Completed
Push — next ( 5f2bc0...cef70f )
by Thomas
25s queued 12s
created

Net_IDNA2   F

Complexity

Total Complexity 224

Size/Duplication

Total Lines 3361
Duplicated Lines 1.22 %

Coupling/Cohesion

Components 1
Dependencies 1

Importance

Changes 0
Metric Value
dl 41
loc 3361
rs 0.8
c 0
b 0
f 0
wmc 224
lcom 1
cbo 1

25 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 14 4
C setParams() 0 49 13
F encode() 21 84 21
D decode() 12 81 22
B _unparse_url() 0 26 7
F _encode() 0 122 28
C _decode() 0 66 13
A _adapt() 0 11 3
A _encodeDigit() 0 4 1
A _decodeDigit() 0 6 4
D _nameprep() 0 89 23
A _hangulDecompose() 0 19 4
C _hangulCompose() 0 56 12
A _getCombiningClass() 0 4 2
B _applyCannonicalOrdering() 0 35 7
B _combine() 0 41 9
D _utf8_to_ucs4() 8 73 22
B _ucs4_to_utf8() 0 59 8
A _ucs4_to_ucs4_string() 0 11 2
A _ucs4_string_to_ucs4() 0 26 5
A _showHex() 0 12 3
A _showBitmask() 0 20 5
A _byteLength() 0 8 2
A getInstance() 0 4 1
A singleton() 0 14 3

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like Net_IDNA2 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. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

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 Net_IDNA2, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
// {{{ license
4
5
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4 foldmethod=marker: */
6
//
7
// +----------------------------------------------------------------------+
8
// | This library is free software; you can redistribute it and/or modify |
9
// | it under the terms of the GNU Lesser General Public License as       |
10
// | published by the Free Software Foundation; either version 2.1 of the |
11
// | License, or (at your option) any later version.                      |
12
// |                                                                      |
13
// | This library is distributed in the hope that it will be useful, but  |
14
// | WITHOUT ANY WARRANTY; without even the implied warranty of           |
15
// | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU    |
16
// | Lesser General Public License for more details.                      |
17
// |                                                                      |
18
// | You should have received a copy of the GNU Lesser General Public     |
19
// | License along with this library; if not, write to the Free Software  |
20
// | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 |
21
// | USA.                                                                 |
22
// +----------------------------------------------------------------------+
23
//
24
25
// }}}
26
require_once __DIR__ . '/IDNA2/Exception.php';
27
require_once __DIR__ . '/IDNA2/Exception/Nameprep.php';
28
// adjusted paths for OC distribution
29
30
/**
31
 * Encode/decode Internationalized Domain Names.
32
 * The class allows to convert internationalized domain names
33
 * (see RFC 3490 for details) as they can be used with various registries worldwide
34
 * to be translated between their original (localized) form and their encoded form
35
 * as it will be used in the DNS (Domain Name System).
36
 * The class provides two public methods, encode() and decode(), which do exactly
37
 * what you would expect them to do. You are allowed to use complete domain names,
38
 * simple strings and complete email addresses as well. That means, that you might
39
 * use any of the following notations:
40
 * - www.n�rgler.com
41
 * - xn--nrgler-wxa
42
 * - xn--brse-5qa.xn--knrz-1ra.info
43
 * Unicode input might be given as either UTF-8 string, UCS-4 string or UCS-4
44
 * array. Unicode output is available in the same formats.
45
 * You can select your preferred format via {@link set_paramter()}.
46
 * ACE input and output is always expected to be ASCII.
47
 *
48
 * @author  Markus Nix <[email protected]>
49
 * @author  Matthias Sommerfeld <[email protected]>
50
 * @author  Stefan Neufeind <[email protected]>
51
 * @version $Id: IDNA2.php 305344 2010-11-14 23:52:42Z neufeind $
52
 */
53
class Net_IDNA2
54
{
55
    // {{{ npdata
56
    /**
57
     * These Unicode codepoints are
58
     * mapped to nothing, See RFC3454 for details
59
     *
60
     * @static
61
     * @var array
62
     * @access private
63
     */
64
    private static $_np_map_nothing = [
65
        0xAD,
66
        0x34F,
67
        0x1806,
68
        0x180B,
69
        0x180C,
70
        0x180D,
71
        0x200B,
72
        0x200C,
73
        0x200D,
74
        0x2060,
75
        0xFE00,
76
        0xFE01,
77
        0xFE02,
78
        0xFE03,
79
        0xFE04,
80
        0xFE05,
81
        0xFE06,
82
        0xFE07,
83
        0xFE08,
84
        0xFE09,
85
        0xFE0A,
86
        0xFE0B,
87
        0xFE0C,
88
        0xFE0D,
89
        0xFE0E,
90
        0xFE0F,
91
        0xFEFF,
92
    ];
93
94
    /**
95
     * Prohibited codepints
96
     *
97
     * @static
98
     * @var array
99
     * @access private
100
     */
101
    private static $_general_prohibited = [
102
        0,
103
        1,
104
        2,
105
        3,
106
        4,
107
        5,
108
        6,
109
        7,
110
        8,
111
        9,
112
        0xA,
113
        0xB,
114
        0xC,
115
        0xD,
116
        0xE,
117
        0xF,
118
        0x10,
119
        0x11,
120
        0x12,
121
        0x13,
122
        0x14,
123
        0x15,
124
        0x16,
125
        0x17,
126
        0x18,
127
        0x19,
128
        0x1A,
129
        0x1B,
130
        0x1C,
131
        0x1D,
132
        0x1E,
133
        0x1F,
134
        0x20,
135
        0x21,
136
        0x22,
137
        0x23,
138
        0x24,
139
        0x25,
140
        0x26,
141
        0x27,
142
        0x28,
143
        0x29,
144
        0x2A,
145
        0x2B,
146
        0x2C,
147
        0x2F,
148
        0x3B,
149
        0x3C,
150
        0x3D,
151
        0x3E,
152
        0x3F,
153
        0x40,
154
        0x5B,
155
        0x5C,
156
        0x5D,
157
        0x5E,
158
        0x5F,
159
        0x60,
160
        0x7B,
161
        0x7C,
162
        0x7D,
163
        0x7E,
164
        0x7F,
165
        0x3002,
166
    ];
167
168
    /**
169
     * Codepints prohibited by Nameprep
170
     *
171
     * @static
172
     * @var array
173
     * @access private
174
     */
175
    private static $_np_prohibit = [
176
        0xA0,
177
        0x1680,
178
        0x2000,
179
        0x2001,
180
        0x2002,
181
        0x2003,
182
        0x2004,
183
        0x2005,
184
        0x2006,
185
        0x2007,
186
        0x2008,
187
        0x2009,
188
        0x200A,
189
        0x200B,
190
        0x202F,
191
        0x205F,
192
        0x3000,
193
        0x6DD,
194
        0x70F,
195
        0x180E,
196
        0x200C,
197
        0x200D,
198
        0x2028,
199
        0x2029,
200
        0xFEFF,
201
        0xFFF9,
202
        0xFFFA,
203
        0xFFFB,
204
        0xFFFC,
205
        0xFFFE,
206
        0xFFFF,
207
        0x1FFFE,
208
        0x1FFFF,
209
        0x2FFFE,
210
        0x2FFFF,
211
        0x3FFFE,
212
        0x3FFFF,
213
        0x4FFFE,
214
        0x4FFFF,
215
        0x5FFFE,
216
        0x5FFFF,
217
        0x6FFFE,
218
        0x6FFFF,
219
        0x7FFFE,
220
        0x7FFFF,
221
        0x8FFFE,
222
        0x8FFFF,
223
        0x9FFFE,
224
        0x9FFFF,
225
        0xAFFFE,
226
        0xAFFFF,
227
        0xBFFFE,
228
        0xBFFFF,
229
        0xCFFFE,
230
        0xCFFFF,
231
        0xDFFFE,
232
        0xDFFFF,
233
        0xEFFFE,
234
        0xEFFFF,
235
        0xFFFFE,
236
        0xFFFFF,
237
        0x10FFFE,
238
        0x10FFFF,
239
        0xFFF9,
240
        0xFFFA,
241
        0xFFFB,
242
        0xFFFC,
243
        0xFFFD,
244
        0x340,
245
        0x341,
246
        0x200E,
247
        0x200F,
248
        0x202A,
249
        0x202B,
250
        0x202C,
251
        0x202D,
252
        0x202E,
253
        0x206A,
254
        0x206B,
255
        0x206C,
256
        0x206D,
257
        0x206E,
258
        0x206F,
259
        0xE0001,
260
    ];
261
262
    /**
263
     * Codepoint ranges prohibited by nameprep
264
     *
265
     * @static
266
     * @var array
267
     * @access private
268
     */
269
    private static $_np_prohibit_ranges = [
270
        [0x80, 0x9F],
271
        [0x2060, 0x206F],
272
        [0x1D173, 0x1D17A],
273
        [0xE000, 0xF8FF],
274
        [0xF0000, 0xFFFFD],
275
        [0x100000, 0x10FFFD],
276
        [0xFDD0, 0xFDEF],
277
        [0xD800, 0xDFFF],
278
        [0x2FF0, 0x2FFB],
279
        [0xE0020, 0xE007F],
280
    ];
281
282
    /**
283
     * Replacement mappings (casemapping, replacement sequences, ...)
284
     *
285
     * @static
286
     * @var array
287
     * @access private
288
     */
289
    private static $_np_replacemaps = [
290
        0x41 => [0x61],
291
        0x42 => [0x62],
292
        0x43 => [0x63],
293
        0x44 => [0x64],
294
        0x45 => [0x65],
295
        0x46 => [0x66],
296
        0x47 => [0x67],
297
        0x48 => [0x68],
298
        0x49 => [0x69],
299
        0x4A => [0x6A],
300
        0x4B => [0x6B],
301
        0x4C => [0x6C],
302
        0x4D => [0x6D],
303
        0x4E => [0x6E],
304
        0x4F => [0x6F],
305
        0x50 => [0x70],
306
        0x51 => [0x71],
307
        0x52 => [0x72],
308
        0x53 => [0x73],
309
        0x54 => [0x74],
310
        0x55 => [0x75],
311
        0x56 => [0x76],
312
        0x57 => [0x77],
313
        0x58 => [0x78],
314
        0x59 => [0x79],
315
        0x5A => [0x7A],
316
        0xB5 => [0x3BC],
317
        0xC0 => [0xE0],
318
        0xC1 => [0xE1],
319
        0xC2 => [0xE2],
320
        0xC3 => [0xE3],
321
        0xC4 => [0xE4],
322
        0xC5 => [0xE5],
323
        0xC6 => [0xE6],
324
        0xC7 => [0xE7],
325
        0xC8 => [0xE8],
326
        0xC9 => [0xE9],
327
        0xCA => [0xEA],
328
        0xCB => [0xEB],
329
        0xCC => [0xEC],
330
        0xCD => [0xED],
331
        0xCE => [0xEE],
332
        0xCF => [0xEF],
333
        0xD0 => [0xF0],
334
        0xD1 => [0xF1],
335
        0xD2 => [0xF2],
336
        0xD3 => [0xF3],
337
        0xD4 => [0xF4],
338
        0xD5 => [0xF5],
339
        0xD6 => [0xF6],
340
        0xD8 => [0xF8],
341
        0xD9 => [0xF9],
342
        0xDA => [0xFA],
343
        0xDB => [0xFB],
344
        0xDC => [0xFC],
345
        0xDD => [0xFD],
346
        0xDE => [0xFE],
347
        0xDF => [0x73, 0x73],
348
        0x100 => [0x101],
349
        0x102 => [0x103],
350
        0x104 => [0x105],
351
        0x106 => [0x107],
352
        0x108 => [0x109],
353
        0x10A => [0x10B],
354
        0x10C => [0x10D],
355
        0x10E => [0x10F],
356
        0x110 => [0x111],
357
        0x112 => [0x113],
358
        0x114 => [0x115],
359
        0x116 => [0x117],
360
        0x118 => [0x119],
361
        0x11A => [0x11B],
362
        0x11C => [0x11D],
363
        0x11E => [0x11F],
364
        0x120 => [0x121],
365
        0x122 => [0x123],
366
        0x124 => [0x125],
367
        0x126 => [0x127],
368
        0x128 => [0x129],
369
        0x12A => [0x12B],
370
        0x12C => [0x12D],
371
        0x12E => [0x12F],
372
        0x130 => [0x69, 0x307],
373
        0x132 => [0x133],
374
        0x134 => [0x135],
375
        0x136 => [0x137],
376
        0x139 => [0x13A],
377
        0x13B => [0x13C],
378
        0x13D => [0x13E],
379
        0x13F => [0x140],
380
        0x141 => [0x142],
381
        0x143 => [0x144],
382
        0x145 => [0x146],
383
        0x147 => [0x148],
384
        0x149 => [0x2BC, 0x6E],
385
        0x14A => [0x14B],
386
        0x14C => [0x14D],
387
        0x14E => [0x14F],
388
        0x150 => [0x151],
389
        0x152 => [0x153],
390
        0x154 => [0x155],
391
        0x156 => [0x157],
392
        0x158 => [0x159],
393
        0x15A => [0x15B],
394
        0x15C => [0x15D],
395
        0x15E => [0x15F],
396
        0x160 => [0x161],
397
        0x162 => [0x163],
398
        0x164 => [0x165],
399
        0x166 => [0x167],
400
        0x168 => [0x169],
401
        0x16A => [0x16B],
402
        0x16C => [0x16D],
403
        0x16E => [0x16F],
404
        0x170 => [0x171],
405
        0x172 => [0x173],
406
        0x174 => [0x175],
407
        0x176 => [0x177],
408
        0x178 => [0xFF],
409
        0x179 => [0x17A],
410
        0x17B => [0x17C],
411
        0x17D => [0x17E],
412
        0x17F => [0x73],
413
        0x181 => [0x253],
414
        0x182 => [0x183],
415
        0x184 => [0x185],
416
        0x186 => [0x254],
417
        0x187 => [0x188],
418
        0x189 => [0x256],
419
        0x18A => [0x257],
420
        0x18B => [0x18C],
421
        0x18E => [0x1DD],
422
        0x18F => [0x259],
423
        0x190 => [0x25B],
424
        0x191 => [0x192],
425
        0x193 => [0x260],
426
        0x194 => [0x263],
427
        0x196 => [0x269],
428
        0x197 => [0x268],
429
        0x198 => [0x199],
430
        0x19C => [0x26F],
431
        0x19D => [0x272],
432
        0x19F => [0x275],
433
        0x1A0 => [0x1A1],
434
        0x1A2 => [0x1A3],
435
        0x1A4 => [0x1A5],
436
        0x1A6 => [0x280],
437
        0x1A7 => [0x1A8],
438
        0x1A9 => [0x283],
439
        0x1AC => [0x1AD],
440
        0x1AE => [0x288],
441
        0x1AF => [0x1B0],
442
        0x1B1 => [0x28A],
443
        0x1B2 => [0x28B],
444
        0x1B3 => [0x1B4],
445
        0x1B5 => [0x1B6],
446
        0x1B7 => [0x292],
447
        0x1B8 => [0x1B9],
448
        0x1BC => [0x1BD],
449
        0x1C4 => [0x1C6],
450
        0x1C5 => [0x1C6],
451
        0x1C7 => [0x1C9],
452
        0x1C8 => [0x1C9],
453
        0x1CA => [0x1CC],
454
        0x1CB => [0x1CC],
455
        0x1CD => [0x1CE],
456
        0x1CF => [0x1D0],
457
        0x1D1 => [0x1D2],
458
        0x1D3 => [0x1D4],
459
        0x1D5 => [0x1D6],
460
        0x1D7 => [0x1D8],
461
        0x1D9 => [0x1DA],
462
        0x1DB => [0x1DC],
463
        0x1DE => [0x1DF],
464
        0x1E0 => [0x1E1],
465
        0x1E2 => [0x1E3],
466
        0x1E4 => [0x1E5],
467
        0x1E6 => [0x1E7],
468
        0x1E8 => [0x1E9],
469
        0x1EA => [0x1EB],
470
        0x1EC => [0x1ED],
471
        0x1EE => [0x1EF],
472
        0x1F0 => [0x6A, 0x30C],
473
        0x1F1 => [0x1F3],
474
        0x1F2 => [0x1F3],
475
        0x1F4 => [0x1F5],
476
        0x1F6 => [0x195],
477
        0x1F7 => [0x1BF],
478
        0x1F8 => [0x1F9],
479
        0x1FA => [0x1FB],
480
        0x1FC => [0x1FD],
481
        0x1FE => [0x1FF],
482
        0x200 => [0x201],
483
        0x202 => [0x203],
484
        0x204 => [0x205],
485
        0x206 => [0x207],
486
        0x208 => [0x209],
487
        0x20A => [0x20B],
488
        0x20C => [0x20D],
489
        0x20E => [0x20F],
490
        0x210 => [0x211],
491
        0x212 => [0x213],
492
        0x214 => [0x215],
493
        0x216 => [0x217],
494
        0x218 => [0x219],
495
        0x21A => [0x21B],
496
        0x21C => [0x21D],
497
        0x21E => [0x21F],
498
        0x220 => [0x19E],
499
        0x222 => [0x223],
500
        0x224 => [0x225],
501
        0x226 => [0x227],
502
        0x228 => [0x229],
503
        0x22A => [0x22B],
504
        0x22C => [0x22D],
505
        0x22E => [0x22F],
506
        0x230 => [0x231],
507
        0x232 => [0x233],
508
        0x345 => [0x3B9],
509
        0x37A => [0x20, 0x3B9],
510
        0x386 => [0x3AC],
511
        0x388 => [0x3AD],
512
        0x389 => [0x3AE],
513
        0x38A => [0x3AF],
514
        0x38C => [0x3CC],
515
        0x38E => [0x3CD],
516
        0x38F => [0x3CE],
517
        0x390 => [0x3B9, 0x308, 0x301],
518
        0x391 => [0x3B1],
519
        0x392 => [0x3B2],
520
        0x393 => [0x3B3],
521
        0x394 => [0x3B4],
522
        0x395 => [0x3B5],
523
        0x396 => [0x3B6],
524
        0x397 => [0x3B7],
525
        0x398 => [0x3B8],
526
        0x399 => [0x3B9],
527
        0x39A => [0x3BA],
528
        0x39B => [0x3BB],
529
        0x39C => [0x3BC],
530
        0x39D => [0x3BD],
531
        0x39E => [0x3BE],
532
        0x39F => [0x3BF],
533
        0x3A0 => [0x3C0],
534
        0x3A1 => [0x3C1],
535
        0x3A3 => [0x3C3],
536
        0x3A4 => [0x3C4],
537
        0x3A5 => [0x3C5],
538
        0x3A6 => [0x3C6],
539
        0x3A7 => [0x3C7],
540
        0x3A8 => [0x3C8],
541
        0x3A9 => [0x3C9],
542
        0x3AA => [0x3CA],
543
        0x3AB => [0x3CB],
544
        0x3B0 => [0x3C5, 0x308, 0x301],
545
        0x3C2 => [0x3C3],
546
        0x3D0 => [0x3B2],
547
        0x3D1 => [0x3B8],
548
        0x3D2 => [0x3C5],
549
        0x3D3 => [0x3CD],
550
        0x3D4 => [0x3CB],
551
        0x3D5 => [0x3C6],
552
        0x3D6 => [0x3C0],
553
        0x3D8 => [0x3D9],
554
        0x3DA => [0x3DB],
555
        0x3DC => [0x3DD],
556
        0x3DE => [0x3DF],
557
        0x3E0 => [0x3E1],
558
        0x3E2 => [0x3E3],
559
        0x3E4 => [0x3E5],
560
        0x3E6 => [0x3E7],
561
        0x3E8 => [0x3E9],
562
        0x3EA => [0x3EB],
563
        0x3EC => [0x3ED],
564
        0x3EE => [0x3EF],
565
        0x3F0 => [0x3BA],
566
        0x3F1 => [0x3C1],
567
        0x3F2 => [0x3C3],
568
        0x3F4 => [0x3B8],
569
        0x3F5 => [0x3B5],
570
        0x400 => [0x450],
571
        0x401 => [0x451],
572
        0x402 => [0x452],
573
        0x403 => [0x453],
574
        0x404 => [0x454],
575
        0x405 => [0x455],
576
        0x406 => [0x456],
577
        0x407 => [0x457],
578
        0x408 => [0x458],
579
        0x409 => [0x459],
580
        0x40A => [0x45A],
581
        0x40B => [0x45B],
582
        0x40C => [0x45C],
583
        0x40D => [0x45D],
584
        0x40E => [0x45E],
585
        0x40F => [0x45F],
586
        0x410 => [0x430],
587
        0x411 => [0x431],
588
        0x412 => [0x432],
589
        0x413 => [0x433],
590
        0x414 => [0x434],
591
        0x415 => [0x435],
592
        0x416 => [0x436],
593
        0x417 => [0x437],
594
        0x418 => [0x438],
595
        0x419 => [0x439],
596
        0x41A => [0x43A],
597
        0x41B => [0x43B],
598
        0x41C => [0x43C],
599
        0x41D => [0x43D],
600
        0x41E => [0x43E],
601
        0x41F => [0x43F],
602
        0x420 => [0x440],
603
        0x421 => [0x441],
604
        0x422 => [0x442],
605
        0x423 => [0x443],
606
        0x424 => [0x444],
607
        0x425 => [0x445],
608
        0x426 => [0x446],
609
        0x427 => [0x447],
610
        0x428 => [0x448],
611
        0x429 => [0x449],
612
        0x42A => [0x44A],
613
        0x42B => [0x44B],
614
        0x42C => [0x44C],
615
        0x42D => [0x44D],
616
        0x42E => [0x44E],
617
        0x42F => [0x44F],
618
        0x460 => [0x461],
619
        0x462 => [0x463],
620
        0x464 => [0x465],
621
        0x466 => [0x467],
622
        0x468 => [0x469],
623
        0x46A => [0x46B],
624
        0x46C => [0x46D],
625
        0x46E => [0x46F],
626
        0x470 => [0x471],
627
        0x472 => [0x473],
628
        0x474 => [0x475],
629
        0x476 => [0x477],
630
        0x478 => [0x479],
631
        0x47A => [0x47B],
632
        0x47C => [0x47D],
633
        0x47E => [0x47F],
634
        0x480 => [0x481],
635
        0x48A => [0x48B],
636
        0x48C => [0x48D],
637
        0x48E => [0x48F],
638
        0x490 => [0x491],
639
        0x492 => [0x493],
640
        0x494 => [0x495],
641
        0x496 => [0x497],
642
        0x498 => [0x499],
643
        0x49A => [0x49B],
644
        0x49C => [0x49D],
645
        0x49E => [0x49F],
646
        0x4A0 => [0x4A1],
647
        0x4A2 => [0x4A3],
648
        0x4A4 => [0x4A5],
649
        0x4A6 => [0x4A7],
650
        0x4A8 => [0x4A9],
651
        0x4AA => [0x4AB],
652
        0x4AC => [0x4AD],
653
        0x4AE => [0x4AF],
654
        0x4B0 => [0x4B1],
655
        0x4B2 => [0x4B3],
656
        0x4B4 => [0x4B5],
657
        0x4B6 => [0x4B7],
658
        0x4B8 => [0x4B9],
659
        0x4BA => [0x4BB],
660
        0x4BC => [0x4BD],
661
        0x4BE => [0x4BF],
662
        0x4C1 => [0x4C2],
663
        0x4C3 => [0x4C4],
664
        0x4C5 => [0x4C6],
665
        0x4C7 => [0x4C8],
666
        0x4C9 => [0x4CA],
667
        0x4CB => [0x4CC],
668
        0x4CD => [0x4CE],
669
        0x4D0 => [0x4D1],
670
        0x4D2 => [0x4D3],
671
        0x4D4 => [0x4D5],
672
        0x4D6 => [0x4D7],
673
        0x4D8 => [0x4D9],
674
        0x4DA => [0x4DB],
675
        0x4DC => [0x4DD],
676
        0x4DE => [0x4DF],
677
        0x4E0 => [0x4E1],
678
        0x4E2 => [0x4E3],
679
        0x4E4 => [0x4E5],
680
        0x4E6 => [0x4E7],
681
        0x4E8 => [0x4E9],
682
        0x4EA => [0x4EB],
683
        0x4EC => [0x4ED],
684
        0x4EE => [0x4EF],
685
        0x4F0 => [0x4F1],
686
        0x4F2 => [0x4F3],
687
        0x4F4 => [0x4F5],
688
        0x4F8 => [0x4F9],
689
        0x500 => [0x501],
690
        0x502 => [0x503],
691
        0x504 => [0x505],
692
        0x506 => [0x507],
693
        0x508 => [0x509],
694
        0x50A => [0x50B],
695
        0x50C => [0x50D],
696
        0x50E => [0x50F],
697
        0x531 => [0x561],
698
        0x532 => [0x562],
699
        0x533 => [0x563],
700
        0x534 => [0x564],
701
        0x535 => [0x565],
702
        0x536 => [0x566],
703
        0x537 => [0x567],
704
        0x538 => [0x568],
705
        0x539 => [0x569],
706
        0x53A => [0x56A],
707
        0x53B => [0x56B],
708
        0x53C => [0x56C],
709
        0x53D => [0x56D],
710
        0x53E => [0x56E],
711
        0x53F => [0x56F],
712
        0x540 => [0x570],
713
        0x541 => [0x571],
714
        0x542 => [0x572],
715
        0x543 => [0x573],
716
        0x544 => [0x574],
717
        0x545 => [0x575],
718
        0x546 => [0x576],
719
        0x547 => [0x577],
720
        0x548 => [0x578],
721
        0x549 => [0x579],
722
        0x54A => [0x57A],
723
        0x54B => [0x57B],
724
        0x54C => [0x57C],
725
        0x54D => [0x57D],
726
        0x54E => [0x57E],
727
        0x54F => [0x57F],
728
        0x550 => [0x580],
729
        0x551 => [0x581],
730
        0x552 => [0x582],
731
        0x553 => [0x583],
732
        0x554 => [0x584],
733
        0x555 => [0x585],
734
        0x556 => [0x586],
735
        0x587 => [0x565, 0x582],
736
        0x1E00 => [0x1E01],
737
        0x1E02 => [0x1E03],
738
        0x1E04 => [0x1E05],
739
        0x1E06 => [0x1E07],
740
        0x1E08 => [0x1E09],
741
        0x1E0A => [0x1E0B],
742
        0x1E0C => [0x1E0D],
743
        0x1E0E => [0x1E0F],
744
        0x1E10 => [0x1E11],
745
        0x1E12 => [0x1E13],
746
        0x1E14 => [0x1E15],
747
        0x1E16 => [0x1E17],
748
        0x1E18 => [0x1E19],
749
        0x1E1A => [0x1E1B],
750
        0x1E1C => [0x1E1D],
751
        0x1E1E => [0x1E1F],
752
        0x1E20 => [0x1E21],
753
        0x1E22 => [0x1E23],
754
        0x1E24 => [0x1E25],
755
        0x1E26 => [0x1E27],
756
        0x1E28 => [0x1E29],
757
        0x1E2A => [0x1E2B],
758
        0x1E2C => [0x1E2D],
759
        0x1E2E => [0x1E2F],
760
        0x1E30 => [0x1E31],
761
        0x1E32 => [0x1E33],
762
        0x1E34 => [0x1E35],
763
        0x1E36 => [0x1E37],
764
        0x1E38 => [0x1E39],
765
        0x1E3A => [0x1E3B],
766
        0x1E3C => [0x1E3D],
767
        0x1E3E => [0x1E3F],
768
        0x1E40 => [0x1E41],
769
        0x1E42 => [0x1E43],
770
        0x1E44 => [0x1E45],
771
        0x1E46 => [0x1E47],
772
        0x1E48 => [0x1E49],
773
        0x1E4A => [0x1E4B],
774
        0x1E4C => [0x1E4D],
775
        0x1E4E => [0x1E4F],
776
        0x1E50 => [0x1E51],
777
        0x1E52 => [0x1E53],
778
        0x1E54 => [0x1E55],
779
        0x1E56 => [0x1E57],
780
        0x1E58 => [0x1E59],
781
        0x1E5A => [0x1E5B],
782
        0x1E5C => [0x1E5D],
783
        0x1E5E => [0x1E5F],
784
        0x1E60 => [0x1E61],
785
        0x1E62 => [0x1E63],
786
        0x1E64 => [0x1E65],
787
        0x1E66 => [0x1E67],
788
        0x1E68 => [0x1E69],
789
        0x1E6A => [0x1E6B],
790
        0x1E6C => [0x1E6D],
791
        0x1E6E => [0x1E6F],
792
        0x1E70 => [0x1E71],
793
        0x1E72 => [0x1E73],
794
        0x1E74 => [0x1E75],
795
        0x1E76 => [0x1E77],
796
        0x1E78 => [0x1E79],
797
        0x1E7A => [0x1E7B],
798
        0x1E7C => [0x1E7D],
799
        0x1E7E => [0x1E7F],
800
        0x1E80 => [0x1E81],
801
        0x1E82 => [0x1E83],
802
        0x1E84 => [0x1E85],
803
        0x1E86 => [0x1E87],
804
        0x1E88 => [0x1E89],
805
        0x1E8A => [0x1E8B],
806
        0x1E8C => [0x1E8D],
807
        0x1E8E => [0x1E8F],
808
        0x1E90 => [0x1E91],
809
        0x1E92 => [0x1E93],
810
        0x1E94 => [0x1E95],
811
        0x1E96 => [0x68, 0x331],
812
        0x1E97 => [0x74, 0x308],
813
        0x1E98 => [0x77, 0x30A],
814
        0x1E99 => [0x79, 0x30A],
815
        0x1E9A => [0x61, 0x2BE],
816
        0x1E9B => [0x1E61],
817
        0x1EA0 => [0x1EA1],
818
        0x1EA2 => [0x1EA3],
819
        0x1EA4 => [0x1EA5],
820
        0x1EA6 => [0x1EA7],
821
        0x1EA8 => [0x1EA9],
822
        0x1EAA => [0x1EAB],
823
        0x1EAC => [0x1EAD],
824
        0x1EAE => [0x1EAF],
825
        0x1EB0 => [0x1EB1],
826
        0x1EB2 => [0x1EB3],
827
        0x1EB4 => [0x1EB5],
828
        0x1EB6 => [0x1EB7],
829
        0x1EB8 => [0x1EB9],
830
        0x1EBA => [0x1EBB],
831
        0x1EBC => [0x1EBD],
832
        0x1EBE => [0x1EBF],
833
        0x1EC0 => [0x1EC1],
834
        0x1EC2 => [0x1EC3],
835
        0x1EC4 => [0x1EC5],
836
        0x1EC6 => [0x1EC7],
837
        0x1EC8 => [0x1EC9],
838
        0x1ECA => [0x1ECB],
839
        0x1ECC => [0x1ECD],
840
        0x1ECE => [0x1ECF],
841
        0x1ED0 => [0x1ED1],
842
        0x1ED2 => [0x1ED3],
843
        0x1ED4 => [0x1ED5],
844
        0x1ED6 => [0x1ED7],
845
        0x1ED8 => [0x1ED9],
846
        0x1EDA => [0x1EDB],
847
        0x1EDC => [0x1EDD],
848
        0x1EDE => [0x1EDF],
849
        0x1EE0 => [0x1EE1],
850
        0x1EE2 => [0x1EE3],
851
        0x1EE4 => [0x1EE5],
852
        0x1EE6 => [0x1EE7],
853
        0x1EE8 => [0x1EE9],
854
        0x1EEA => [0x1EEB],
855
        0x1EEC => [0x1EED],
856
        0x1EEE => [0x1EEF],
857
        0x1EF0 => [0x1EF1],
858
        0x1EF2 => [0x1EF3],
859
        0x1EF4 => [0x1EF5],
860
        0x1EF6 => [0x1EF7],
861
        0x1EF8 => [0x1EF9],
862
        0x1F08 => [0x1F00],
863
        0x1F09 => [0x1F01],
864
        0x1F0A => [0x1F02],
865
        0x1F0B => [0x1F03],
866
        0x1F0C => [0x1F04],
867
        0x1F0D => [0x1F05],
868
        0x1F0E => [0x1F06],
869
        0x1F0F => [0x1F07],
870
        0x1F18 => [0x1F10],
871
        0x1F19 => [0x1F11],
872
        0x1F1A => [0x1F12],
873
        0x1F1B => [0x1F13],
874
        0x1F1C => [0x1F14],
875
        0x1F1D => [0x1F15],
876
        0x1F28 => [0x1F20],
877
        0x1F29 => [0x1F21],
878
        0x1F2A => [0x1F22],
879
        0x1F2B => [0x1F23],
880
        0x1F2C => [0x1F24],
881
        0x1F2D => [0x1F25],
882
        0x1F2E => [0x1F26],
883
        0x1F2F => [0x1F27],
884
        0x1F38 => [0x1F30],
885
        0x1F39 => [0x1F31],
886
        0x1F3A => [0x1F32],
887
        0x1F3B => [0x1F33],
888
        0x1F3C => [0x1F34],
889
        0x1F3D => [0x1F35],
890
        0x1F3E => [0x1F36],
891
        0x1F3F => [0x1F37],
892
        0x1F48 => [0x1F40],
893
        0x1F49 => [0x1F41],
894
        0x1F4A => [0x1F42],
895
        0x1F4B => [0x1F43],
896
        0x1F4C => [0x1F44],
897
        0x1F4D => [0x1F45],
898
        0x1F50 => [0x3C5, 0x313],
899
        0x1F52 => [0x3C5, 0x313, 0x300],
900
        0x1F54 => [0x3C5, 0x313, 0x301],
901
        0x1F56 => [0x3C5, 0x313, 0x342],
902
        0x1F59 => [0x1F51],
903
        0x1F5B => [0x1F53],
904
        0x1F5D => [0x1F55],
905
        0x1F5F => [0x1F57],
906
        0x1F68 => [0x1F60],
907
        0x1F69 => [0x1F61],
908
        0x1F6A => [0x1F62],
909
        0x1F6B => [0x1F63],
910
        0x1F6C => [0x1F64],
911
        0x1F6D => [0x1F65],
912
        0x1F6E => [0x1F66],
913
        0x1F6F => [0x1F67],
914
        0x1F80 => [0x1F00, 0x3B9],
915
        0x1F81 => [0x1F01, 0x3B9],
916
        0x1F82 => [0x1F02, 0x3B9],
917
        0x1F83 => [0x1F03, 0x3B9],
918
        0x1F84 => [0x1F04, 0x3B9],
919
        0x1F85 => [0x1F05, 0x3B9],
920
        0x1F86 => [0x1F06, 0x3B9],
921
        0x1F87 => [0x1F07, 0x3B9],
922
        0x1F88 => [0x1F00, 0x3B9],
923
        0x1F89 => [0x1F01, 0x3B9],
924
        0x1F8A => [0x1F02, 0x3B9],
925
        0x1F8B => [0x1F03, 0x3B9],
926
        0x1F8C => [0x1F04, 0x3B9],
927
        0x1F8D => [0x1F05, 0x3B9],
928
        0x1F8E => [0x1F06, 0x3B9],
929
        0x1F8F => [0x1F07, 0x3B9],
930
        0x1F90 => [0x1F20, 0x3B9],
931
        0x1F91 => [0x1F21, 0x3B9],
932
        0x1F92 => [0x1F22, 0x3B9],
933
        0x1F93 => [0x1F23, 0x3B9],
934
        0x1F94 => [0x1F24, 0x3B9],
935
        0x1F95 => [0x1F25, 0x3B9],
936
        0x1F96 => [0x1F26, 0x3B9],
937
        0x1F97 => [0x1F27, 0x3B9],
938
        0x1F98 => [0x1F20, 0x3B9],
939
        0x1F99 => [0x1F21, 0x3B9],
940
        0x1F9A => [0x1F22, 0x3B9],
941
        0x1F9B => [0x1F23, 0x3B9],
942
        0x1F9C => [0x1F24, 0x3B9],
943
        0x1F9D => [0x1F25, 0x3B9],
944
        0x1F9E => [0x1F26, 0x3B9],
945
        0x1F9F => [0x1F27, 0x3B9],
946
        0x1FA0 => [0x1F60, 0x3B9],
947
        0x1FA1 => [0x1F61, 0x3B9],
948
        0x1FA2 => [0x1F62, 0x3B9],
949
        0x1FA3 => [0x1F63, 0x3B9],
950
        0x1FA4 => [0x1F64, 0x3B9],
951
        0x1FA5 => [0x1F65, 0x3B9],
952
        0x1FA6 => [0x1F66, 0x3B9],
953
        0x1FA7 => [0x1F67, 0x3B9],
954
        0x1FA8 => [0x1F60, 0x3B9],
955
        0x1FA9 => [0x1F61, 0x3B9],
956
        0x1FAA => [0x1F62, 0x3B9],
957
        0x1FAB => [0x1F63, 0x3B9],
958
        0x1FAC => [0x1F64, 0x3B9],
959
        0x1FAD => [0x1F65, 0x3B9],
960
        0x1FAE => [0x1F66, 0x3B9],
961
        0x1FAF => [0x1F67, 0x3B9],
962
        0x1FB2 => [0x1F70, 0x3B9],
963
        0x1FB3 => [0x3B1, 0x3B9],
964
        0x1FB4 => [0x3AC, 0x3B9],
965
        0x1FB6 => [0x3B1, 0x342],
966
        0x1FB7 => [0x3B1, 0x342, 0x3B9],
967
        0x1FB8 => [0x1FB0],
968
        0x1FB9 => [0x1FB1],
969
        0x1FBA => [0x1F70],
970
        0x1FBB => [0x1F71],
971
        0x1FBC => [0x3B1, 0x3B9],
972
        0x1FBE => [0x3B9],
973
        0x1FC2 => [0x1F74, 0x3B9],
974
        0x1FC3 => [0x3B7, 0x3B9],
975
        0x1FC4 => [0x3AE, 0x3B9],
976
        0x1FC6 => [0x3B7, 0x342],
977
        0x1FC7 => [0x3B7, 0x342, 0x3B9],
978
        0x1FC8 => [0x1F72],
979
        0x1FC9 => [0x1F73],
980
        0x1FCA => [0x1F74],
981
        0x1FCB => [0x1F75],
982
        0x1FCC => [0x3B7, 0x3B9],
983
        0x1FD2 => [0x3B9, 0x308, 0x300],
984
        0x1FD3 => [0x3B9, 0x308, 0x301],
985
        0x1FD6 => [0x3B9, 0x342],
986
        0x1FD7 => [0x3B9, 0x308, 0x342],
987
        0x1FD8 => [0x1FD0],
988
        0x1FD9 => [0x1FD1],
989
        0x1FDA => [0x1F76],
990
        0x1FDB => [0x1F77],
991
        0x1FE2 => [0x3C5, 0x308, 0x300],
992
        0x1FE3 => [0x3C5, 0x308, 0x301],
993
        0x1FE4 => [0x3C1, 0x313],
994
        0x1FE6 => [0x3C5, 0x342],
995
        0x1FE7 => [0x3C5, 0x308, 0x342],
996
        0x1FE8 => [0x1FE0],
997
        0x1FE9 => [0x1FE1],
998
        0x1FEA => [0x1F7A],
999
        0x1FEB => [0x1F7B],
1000
        0x1FEC => [0x1FE5],
1001
        0x1FF2 => [0x1F7C, 0x3B9],
1002
        0x1FF3 => [0x3C9, 0x3B9],
1003
        0x1FF4 => [0x3CE, 0x3B9],
1004
        0x1FF6 => [0x3C9, 0x342],
1005
        0x1FF7 => [0x3C9, 0x342, 0x3B9],
1006
        0x1FF8 => [0x1F78],
1007
        0x1FF9 => [0x1F79],
1008
        0x1FFA => [0x1F7C],
1009
        0x1FFB => [0x1F7D],
1010
        0x1FFC => [0x3C9, 0x3B9],
1011
        0x20A8 => [0x72, 0x73],
1012
        0x2102 => [0x63],
1013
        0x2103 => [0xB0, 0x63],
1014
        0x2107 => [0x25B],
1015
        0x2109 => [0xB0, 0x66],
1016
        0x210B => [0x68],
1017
        0x210C => [0x68],
1018
        0x210D => [0x68],
1019
        0x2110 => [0x69],
1020
        0x2111 => [0x69],
1021
        0x2112 => [0x6C],
1022
        0x2115 => [0x6E],
1023
        0x2116 => [0x6E, 0x6F],
1024
        0x2119 => [0x70],
1025
        0x211A => [0x71],
1026
        0x211B => [0x72],
1027
        0x211C => [0x72],
1028
        0x211D => [0x72],
1029
        0x2120 => [0x73, 0x6D],
1030
        0x2121 => [0x74, 0x65, 0x6C],
1031
        0x2122 => [0x74, 0x6D],
1032
        0x2124 => [0x7A],
1033
        0x2126 => [0x3C9],
1034
        0x2128 => [0x7A],
1035
        0x212A => [0x6B],
1036
        0x212B => [0xE5],
1037
        0x212C => [0x62],
1038
        0x212D => [0x63],
1039
        0x2130 => [0x65],
1040
        0x2131 => [0x66],
1041
        0x2133 => [0x6D],
1042
        0x213E => [0x3B3],
1043
        0x213F => [0x3C0],
1044
        0x2145 => [0x64],
1045
        0x2160 => [0x2170],
1046
        0x2161 => [0x2171],
1047
        0x2162 => [0x2172],
1048
        0x2163 => [0x2173],
1049
        0x2164 => [0x2174],
1050
        0x2165 => [0x2175],
1051
        0x2166 => [0x2176],
1052
        0x2167 => [0x2177],
1053
        0x2168 => [0x2178],
1054
        0x2169 => [0x2179],
1055
        0x216A => [0x217A],
1056
        0x216B => [0x217B],
1057
        0x216C => [0x217C],
1058
        0x216D => [0x217D],
1059
        0x216E => [0x217E],
1060
        0x216F => [0x217F],
1061
        0x24B6 => [0x24D0],
1062
        0x24B7 => [0x24D1],
1063
        0x24B8 => [0x24D2],
1064
        0x24B9 => [0x24D3],
1065
        0x24BA => [0x24D4],
1066
        0x24BB => [0x24D5],
1067
        0x24BC => [0x24D6],
1068
        0x24BD => [0x24D7],
1069
        0x24BE => [0x24D8],
1070
        0x24BF => [0x24D9],
1071
        0x24C0 => [0x24DA],
1072
        0x24C1 => [0x24DB],
1073
        0x24C2 => [0x24DC],
1074
        0x24C3 => [0x24DD],
1075
        0x24C4 => [0x24DE],
1076
        0x24C5 => [0x24DF],
1077
        0x24C6 => [0x24E0],
1078
        0x24C7 => [0x24E1],
1079
        0x24C8 => [0x24E2],
1080
        0x24C9 => [0x24E3],
1081
        0x24CA => [0x24E4],
1082
        0x24CB => [0x24E5],
1083
        0x24CC => [0x24E6],
1084
        0x24CD => [0x24E7],
1085
        0x24CE => [0x24E8],
1086
        0x24CF => [0x24E9],
1087
        0x3371 => [0x68, 0x70, 0x61],
1088
        0x3373 => [0x61, 0x75],
1089
        0x3375 => [0x6F, 0x76],
1090
        0x3380 => [0x70, 0x61],
1091
        0x3381 => [0x6E, 0x61],
1092
        0x3382 => [0x3BC, 0x61],
1093
        0x3383 => [0x6D, 0x61],
1094
        0x3384 => [0x6B, 0x61],
1095
        0x3385 => [0x6B, 0x62],
1096
        0x3386 => [0x6D, 0x62],
1097
        0x3387 => [0x67, 0x62],
1098
        0x338A => [0x70, 0x66],
1099
        0x338B => [0x6E, 0x66],
1100
        0x338C => [0x3BC, 0x66],
1101
        0x3390 => [0x68, 0x7A],
1102
        0x3391 => [0x6B, 0x68, 0x7A],
1103
        0x3392 => [0x6D, 0x68, 0x7A],
1104
        0x3393 => [0x67, 0x68, 0x7A],
1105
        0x3394 => [0x74, 0x68, 0x7A],
1106
        0x33A9 => [0x70, 0x61],
1107
        0x33AA => [0x6B, 0x70, 0x61],
1108
        0x33AB => [0x6D, 0x70, 0x61],
1109
        0x33AC => [0x67, 0x70, 0x61],
1110
        0x33B4 => [0x70, 0x76],
1111
        0x33B5 => [0x6E, 0x76],
1112
        0x33B6 => [0x3BC, 0x76],
1113
        0x33B7 => [0x6D, 0x76],
1114
        0x33B8 => [0x6B, 0x76],
1115
        0x33B9 => [0x6D, 0x76],
1116
        0x33BA => [0x70, 0x77],
1117
        0x33BB => [0x6E, 0x77],
1118
        0x33BC => [0x3BC, 0x77],
1119
        0x33BD => [0x6D, 0x77],
1120
        0x33BE => [0x6B, 0x77],
1121
        0x33BF => [0x6D, 0x77],
1122
        0x33C0 => [0x6B, 0x3C9],
1123
        0x33C1 => [0x6D, 0x3C9],
1124
        /* 0x33C2  => array(0x61, 0x2E, 0x6D, 0x2E), */
1125
        0x33C3 => [0x62, 0x71],
1126
        0x33C6 => [0x63, 0x2215, 0x6B, 0x67],
1127
        0x33C7 => [0x63, 0x6F, 0x2E],
1128
        0x33C8 => [0x64, 0x62],
1129
        0x33C9 => [0x67, 0x79],
1130
        0x33CB => [0x68, 0x70],
1131
        0x33CD => [0x6B, 0x6B],
1132
        0x33CE => [0x6B, 0x6D],
1133
        0x33D7 => [0x70, 0x68],
1134
        0x33D9 => [0x70, 0x70, 0x6D],
1135
        0x33DA => [0x70, 0x72],
1136
        0x33DC => [0x73, 0x76],
1137
        0x33DD => [0x77, 0x62],
1138
        0xFB00 => [0x66, 0x66],
1139
        0xFB01 => [0x66, 0x69],
1140
        0xFB02 => [0x66, 0x6C],
1141
        0xFB03 => [0x66, 0x66, 0x69],
1142
        0xFB04 => [0x66, 0x66, 0x6C],
1143
        0xFB05 => [0x73, 0x74],
1144
        0xFB06 => [0x73, 0x74],
1145
        0xFB13 => [0x574, 0x576],
1146
        0xFB14 => [0x574, 0x565],
1147
        0xFB15 => [0x574, 0x56B],
1148
        0xFB16 => [0x57E, 0x576],
1149
        0xFB17 => [0x574, 0x56D],
1150
        0xFF21 => [0xFF41],
1151
        0xFF22 => [0xFF42],
1152
        0xFF23 => [0xFF43],
1153
        0xFF24 => [0xFF44],
1154
        0xFF25 => [0xFF45],
1155
        0xFF26 => [0xFF46],
1156
        0xFF27 => [0xFF47],
1157
        0xFF28 => [0xFF48],
1158
        0xFF29 => [0xFF49],
1159
        0xFF2A => [0xFF4A],
1160
        0xFF2B => [0xFF4B],
1161
        0xFF2C => [0xFF4C],
1162
        0xFF2D => [0xFF4D],
1163
        0xFF2E => [0xFF4E],
1164
        0xFF2F => [0xFF4F],
1165
        0xFF30 => [0xFF50],
1166
        0xFF31 => [0xFF51],
1167
        0xFF32 => [0xFF52],
1168
        0xFF33 => [0xFF53],
1169
        0xFF34 => [0xFF54],
1170
        0xFF35 => [0xFF55],
1171
        0xFF36 => [0xFF56],
1172
        0xFF37 => [0xFF57],
1173
        0xFF38 => [0xFF58],
1174
        0xFF39 => [0xFF59],
1175
        0xFF3A => [0xFF5A],
1176
        0x10400 => [0x10428],
1177
        0x10401 => [0x10429],
1178
        0x10402 => [0x1042A],
1179
        0x10403 => [0x1042B],
1180
        0x10404 => [0x1042C],
1181
        0x10405 => [0x1042D],
1182
        0x10406 => [0x1042E],
1183
        0x10407 => [0x1042F],
1184
        0x10408 => [0x10430],
1185
        0x10409 => [0x10431],
1186
        0x1040A => [0x10432],
1187
        0x1040B => [0x10433],
1188
        0x1040C => [0x10434],
1189
        0x1040D => [0x10435],
1190
        0x1040E => [0x10436],
1191
        0x1040F => [0x10437],
1192
        0x10410 => [0x10438],
1193
        0x10411 => [0x10439],
1194
        0x10412 => [0x1043A],
1195
        0x10413 => [0x1043B],
1196
        0x10414 => [0x1043C],
1197
        0x10415 => [0x1043D],
1198
        0x10416 => [0x1043E],
1199
        0x10417 => [0x1043F],
1200
        0x10418 => [0x10440],
1201
        0x10419 => [0x10441],
1202
        0x1041A => [0x10442],
1203
        0x1041B => [0x10443],
1204
        0x1041C => [0x10444],
1205
        0x1041D => [0x10445],
1206
        0x1041E => [0x10446],
1207
        0x1041F => [0x10447],
1208
        0x10420 => [0x10448],
1209
        0x10421 => [0x10449],
1210
        0x10422 => [0x1044A],
1211
        0x10423 => [0x1044B],
1212
        0x10424 => [0x1044C],
1213
        0x10425 => [0x1044D],
1214
        0x1D400 => [0x61],
1215
        0x1D401 => [0x62],
1216
        0x1D402 => [0x63],
1217
        0x1D403 => [0x64],
1218
        0x1D404 => [0x65],
1219
        0x1D405 => [0x66],
1220
        0x1D406 => [0x67],
1221
        0x1D407 => [0x68],
1222
        0x1D408 => [0x69],
1223
        0x1D409 => [0x6A],
1224
        0x1D40A => [0x6B],
1225
        0x1D40B => [0x6C],
1226
        0x1D40C => [0x6D],
1227
        0x1D40D => [0x6E],
1228
        0x1D40E => [0x6F],
1229
        0x1D40F => [0x70],
1230
        0x1D410 => [0x71],
1231
        0x1D411 => [0x72],
1232
        0x1D412 => [0x73],
1233
        0x1D413 => [0x74],
1234
        0x1D414 => [0x75],
1235
        0x1D415 => [0x76],
1236
        0x1D416 => [0x77],
1237
        0x1D417 => [0x78],
1238
        0x1D418 => [0x79],
1239
        0x1D419 => [0x7A],
1240
        0x1D434 => [0x61],
1241
        0x1D435 => [0x62],
1242
        0x1D436 => [0x63],
1243
        0x1D437 => [0x64],
1244
        0x1D438 => [0x65],
1245
        0x1D439 => [0x66],
1246
        0x1D43A => [0x67],
1247
        0x1D43B => [0x68],
1248
        0x1D43C => [0x69],
1249
        0x1D43D => [0x6A],
1250
        0x1D43E => [0x6B],
1251
        0x1D43F => [0x6C],
1252
        0x1D440 => [0x6D],
1253
        0x1D441 => [0x6E],
1254
        0x1D442 => [0x6F],
1255
        0x1D443 => [0x70],
1256
        0x1D444 => [0x71],
1257
        0x1D445 => [0x72],
1258
        0x1D446 => [0x73],
1259
        0x1D447 => [0x74],
1260
        0x1D448 => [0x75],
1261
        0x1D449 => [0x76],
1262
        0x1D44A => [0x77],
1263
        0x1D44B => [0x78],
1264
        0x1D44C => [0x79],
1265
        0x1D44D => [0x7A],
1266
        0x1D468 => [0x61],
1267
        0x1D469 => [0x62],
1268
        0x1D46A => [0x63],
1269
        0x1D46B => [0x64],
1270
        0x1D46C => [0x65],
1271
        0x1D46D => [0x66],
1272
        0x1D46E => [0x67],
1273
        0x1D46F => [0x68],
1274
        0x1D470 => [0x69],
1275
        0x1D471 => [0x6A],
1276
        0x1D472 => [0x6B],
1277
        0x1D473 => [0x6C],
1278
        0x1D474 => [0x6D],
1279
        0x1D475 => [0x6E],
1280
        0x1D476 => [0x6F],
1281
        0x1D477 => [0x70],
1282
        0x1D478 => [0x71],
1283
        0x1D479 => [0x72],
1284
        0x1D47A => [0x73],
1285
        0x1D47B => [0x74],
1286
        0x1D47C => [0x75],
1287
        0x1D47D => [0x76],
1288
        0x1D47E => [0x77],
1289
        0x1D47F => [0x78],
1290
        0x1D480 => [0x79],
1291
        0x1D481 => [0x7A],
1292
        0x1D49C => [0x61],
1293
        0x1D49E => [0x63],
1294
        0x1D49F => [0x64],
1295
        0x1D4A2 => [0x67],
1296
        0x1D4A5 => [0x6A],
1297
        0x1D4A6 => [0x6B],
1298
        0x1D4A9 => [0x6E],
1299
        0x1D4AA => [0x6F],
1300
        0x1D4AB => [0x70],
1301
        0x1D4AC => [0x71],
1302
        0x1D4AE => [0x73],
1303
        0x1D4AF => [0x74],
1304
        0x1D4B0 => [0x75],
1305
        0x1D4B1 => [0x76],
1306
        0x1D4B2 => [0x77],
1307
        0x1D4B3 => [0x78],
1308
        0x1D4B4 => [0x79],
1309
        0x1D4B5 => [0x7A],
1310
        0x1D4D0 => [0x61],
1311
        0x1D4D1 => [0x62],
1312
        0x1D4D2 => [0x63],
1313
        0x1D4D3 => [0x64],
1314
        0x1D4D4 => [0x65],
1315
        0x1D4D5 => [0x66],
1316
        0x1D4D6 => [0x67],
1317
        0x1D4D7 => [0x68],
1318
        0x1D4D8 => [0x69],
1319
        0x1D4D9 => [0x6A],
1320
        0x1D4DA => [0x6B],
1321
        0x1D4DB => [0x6C],
1322
        0x1D4DC => [0x6D],
1323
        0x1D4DD => [0x6E],
1324
        0x1D4DE => [0x6F],
1325
        0x1D4DF => [0x70],
1326
        0x1D4E0 => [0x71],
1327
        0x1D4E1 => [0x72],
1328
        0x1D4E2 => [0x73],
1329
        0x1D4E3 => [0x74],
1330
        0x1D4E4 => [0x75],
1331
        0x1D4E5 => [0x76],
1332
        0x1D4E6 => [0x77],
1333
        0x1D4E7 => [0x78],
1334
        0x1D4E8 => [0x79],
1335
        0x1D4E9 => [0x7A],
1336
        0x1D504 => [0x61],
1337
        0x1D505 => [0x62],
1338
        0x1D507 => [0x64],
1339
        0x1D508 => [0x65],
1340
        0x1D509 => [0x66],
1341
        0x1D50A => [0x67],
1342
        0x1D50D => [0x6A],
1343
        0x1D50E => [0x6B],
1344
        0x1D50F => [0x6C],
1345
        0x1D510 => [0x6D],
1346
        0x1D511 => [0x6E],
1347
        0x1D512 => [0x6F],
1348
        0x1D513 => [0x70],
1349
        0x1D514 => [0x71],
1350
        0x1D516 => [0x73],
1351
        0x1D517 => [0x74],
1352
        0x1D518 => [0x75],
1353
        0x1D519 => [0x76],
1354
        0x1D51A => [0x77],
1355
        0x1D51B => [0x78],
1356
        0x1D51C => [0x79],
1357
        0x1D538 => [0x61],
1358
        0x1D539 => [0x62],
1359
        0x1D53B => [0x64],
1360
        0x1D53C => [0x65],
1361
        0x1D53D => [0x66],
1362
        0x1D53E => [0x67],
1363
        0x1D540 => [0x69],
1364
        0x1D541 => [0x6A],
1365
        0x1D542 => [0x6B],
1366
        0x1D543 => [0x6C],
1367
        0x1D544 => [0x6D],
1368
        0x1D546 => [0x6F],
1369
        0x1D54A => [0x73],
1370
        0x1D54B => [0x74],
1371
        0x1D54C => [0x75],
1372
        0x1D54D => [0x76],
1373
        0x1D54E => [0x77],
1374
        0x1D54F => [0x78],
1375
        0x1D550 => [0x79],
1376
        0x1D56C => [0x61],
1377
        0x1D56D => [0x62],
1378
        0x1D56E => [0x63],
1379
        0x1D56F => [0x64],
1380
        0x1D570 => [0x65],
1381
        0x1D571 => [0x66],
1382
        0x1D572 => [0x67],
1383
        0x1D573 => [0x68],
1384
        0x1D574 => [0x69],
1385
        0x1D575 => [0x6A],
1386
        0x1D576 => [0x6B],
1387
        0x1D577 => [0x6C],
1388
        0x1D578 => [0x6D],
1389
        0x1D579 => [0x6E],
1390
        0x1D57A => [0x6F],
1391
        0x1D57B => [0x70],
1392
        0x1D57C => [0x71],
1393
        0x1D57D => [0x72],
1394
        0x1D57E => [0x73],
1395
        0x1D57F => [0x74],
1396
        0x1D580 => [0x75],
1397
        0x1D581 => [0x76],
1398
        0x1D582 => [0x77],
1399
        0x1D583 => [0x78],
1400
        0x1D584 => [0x79],
1401
        0x1D585 => [0x7A],
1402
        0x1D5A0 => [0x61],
1403
        0x1D5A1 => [0x62],
1404
        0x1D5A2 => [0x63],
1405
        0x1D5A3 => [0x64],
1406
        0x1D5A4 => [0x65],
1407
        0x1D5A5 => [0x66],
1408
        0x1D5A6 => [0x67],
1409
        0x1D5A7 => [0x68],
1410
        0x1D5A8 => [0x69],
1411
        0x1D5A9 => [0x6A],
1412
        0x1D5AA => [0x6B],
1413
        0x1D5AB => [0x6C],
1414
        0x1D5AC => [0x6D],
1415
        0x1D5AD => [0x6E],
1416
        0x1D5AE => [0x6F],
1417
        0x1D5AF => [0x70],
1418
        0x1D5B0 => [0x71],
1419
        0x1D5B1 => [0x72],
1420
        0x1D5B2 => [0x73],
1421
        0x1D5B3 => [0x74],
1422
        0x1D5B4 => [0x75],
1423
        0x1D5B5 => [0x76],
1424
        0x1D5B6 => [0x77],
1425
        0x1D5B7 => [0x78],
1426
        0x1D5B8 => [0x79],
1427
        0x1D5B9 => [0x7A],
1428
        0x1D5D4 => [0x61],
1429
        0x1D5D5 => [0x62],
1430
        0x1D5D6 => [0x63],
1431
        0x1D5D7 => [0x64],
1432
        0x1D5D8 => [0x65],
1433
        0x1D5D9 => [0x66],
1434
        0x1D5DA => [0x67],
1435
        0x1D5DB => [0x68],
1436
        0x1D5DC => [0x69],
1437
        0x1D5DD => [0x6A],
1438
        0x1D5DE => [0x6B],
1439
        0x1D5DF => [0x6C],
1440
        0x1D5E0 => [0x6D],
1441
        0x1D5E1 => [0x6E],
1442
        0x1D5E2 => [0x6F],
1443
        0x1D5E3 => [0x70],
1444
        0x1D5E4 => [0x71],
1445
        0x1D5E5 => [0x72],
1446
        0x1D5E6 => [0x73],
1447
        0x1D5E7 => [0x74],
1448
        0x1D5E8 => [0x75],
1449
        0x1D5E9 => [0x76],
1450
        0x1D5EA => [0x77],
1451
        0x1D5EB => [0x78],
1452
        0x1D5EC => [0x79],
1453
        0x1D5ED => [0x7A],
1454
        0x1D608 => [0x61],
1455
        0x1D609 => [0x62],
1456
        0x1D60A => [0x63],
1457
        0x1D60B => [0x64],
1458
        0x1D60C => [0x65],
1459
        0x1D60D => [0x66],
1460
        0x1D60E => [0x67],
1461
        0x1D60F => [0x68],
1462
        0x1D610 => [0x69],
1463
        0x1D611 => [0x6A],
1464
        0x1D612 => [0x6B],
1465
        0x1D613 => [0x6C],
1466
        0x1D614 => [0x6D],
1467
        0x1D615 => [0x6E],
1468
        0x1D616 => [0x6F],
1469
        0x1D617 => [0x70],
1470
        0x1D618 => [0x71],
1471
        0x1D619 => [0x72],
1472
        0x1D61A => [0x73],
1473
        0x1D61B => [0x74],
1474
        0x1D61C => [0x75],
1475
        0x1D61D => [0x76],
1476
        0x1D61E => [0x77],
1477
        0x1D61F => [0x78],
1478
        0x1D620 => [0x79],
1479
        0x1D621 => [0x7A],
1480
        0x1D63C => [0x61],
1481
        0x1D63D => [0x62],
1482
        0x1D63E => [0x63],
1483
        0x1D63F => [0x64],
1484
        0x1D640 => [0x65],
1485
        0x1D641 => [0x66],
1486
        0x1D642 => [0x67],
1487
        0x1D643 => [0x68],
1488
        0x1D644 => [0x69],
1489
        0x1D645 => [0x6A],
1490
        0x1D646 => [0x6B],
1491
        0x1D647 => [0x6C],
1492
        0x1D648 => [0x6D],
1493
        0x1D649 => [0x6E],
1494
        0x1D64A => [0x6F],
1495
        0x1D64B => [0x70],
1496
        0x1D64C => [0x71],
1497
        0x1D64D => [0x72],
1498
        0x1D64E => [0x73],
1499
        0x1D64F => [0x74],
1500
        0x1D650 => [0x75],
1501
        0x1D651 => [0x76],
1502
        0x1D652 => [0x77],
1503
        0x1D653 => [0x78],
1504
        0x1D654 => [0x79],
1505
        0x1D655 => [0x7A],
1506
        0x1D670 => [0x61],
1507
        0x1D671 => [0x62],
1508
        0x1D672 => [0x63],
1509
        0x1D673 => [0x64],
1510
        0x1D674 => [0x65],
1511
        0x1D675 => [0x66],
1512
        0x1D676 => [0x67],
1513
        0x1D677 => [0x68],
1514
        0x1D678 => [0x69],
1515
        0x1D679 => [0x6A],
1516
        0x1D67A => [0x6B],
1517
        0x1D67B => [0x6C],
1518
        0x1D67C => [0x6D],
1519
        0x1D67D => [0x6E],
1520
        0x1D67E => [0x6F],
1521
        0x1D67F => [0x70],
1522
        0x1D680 => [0x71],
1523
        0x1D681 => [0x72],
1524
        0x1D682 => [0x73],
1525
        0x1D683 => [0x74],
1526
        0x1D684 => [0x75],
1527
        0x1D685 => [0x76],
1528
        0x1D686 => [0x77],
1529
        0x1D687 => [0x78],
1530
        0x1D688 => [0x79],
1531
        0x1D689 => [0x7A],
1532
        0x1D6A8 => [0x3B1],
1533
        0x1D6A9 => [0x3B2],
1534
        0x1D6AA => [0x3B3],
1535
        0x1D6AB => [0x3B4],
1536
        0x1D6AC => [0x3B5],
1537
        0x1D6AD => [0x3B6],
1538
        0x1D6AE => [0x3B7],
1539
        0x1D6AF => [0x3B8],
1540
        0x1D6B0 => [0x3B9],
1541
        0x1D6B1 => [0x3BA],
1542
        0x1D6B2 => [0x3BB],
1543
        0x1D6B3 => [0x3BC],
1544
        0x1D6B4 => [0x3BD],
1545
        0x1D6B5 => [0x3BE],
1546
        0x1D6B6 => [0x3BF],
1547
        0x1D6B7 => [0x3C0],
1548
        0x1D6B8 => [0x3C1],
1549
        0x1D6B9 => [0x3B8],
1550
        0x1D6BA => [0x3C3],
1551
        0x1D6BB => [0x3C4],
1552
        0x1D6BC => [0x3C5],
1553
        0x1D6BD => [0x3C6],
1554
        0x1D6BE => [0x3C7],
1555
        0x1D6BF => [0x3C8],
1556
        0x1D6C0 => [0x3C9],
1557
        0x1D6D3 => [0x3C3],
1558
        0x1D6E2 => [0x3B1],
1559
        0x1D6E3 => [0x3B2],
1560
        0x1D6E4 => [0x3B3],
1561
        0x1D6E5 => [0x3B4],
1562
        0x1D6E6 => [0x3B5],
1563
        0x1D6E7 => [0x3B6],
1564
        0x1D6E8 => [0x3B7],
1565
        0x1D6E9 => [0x3B8],
1566
        0x1D6EA => [0x3B9],
1567
        0x1D6EB => [0x3BA],
1568
        0x1D6EC => [0x3BB],
1569
        0x1D6ED => [0x3BC],
1570
        0x1D6EE => [0x3BD],
1571
        0x1D6EF => [0x3BE],
1572
        0x1D6F0 => [0x3BF],
1573
        0x1D6F1 => [0x3C0],
1574
        0x1D6F2 => [0x3C1],
1575
        0x1D6F3 => [0x3B8],
1576
        0x1D6F4 => [0x3C3],
1577
        0x1D6F5 => [0x3C4],
1578
        0x1D6F6 => [0x3C5],
1579
        0x1D6F7 => [0x3C6],
1580
        0x1D6F8 => [0x3C7],
1581
        0x1D6F9 => [0x3C8],
1582
        0x1D6FA => [0x3C9],
1583
        0x1D70D => [0x3C3],
1584
        0x1D71C => [0x3B1],
1585
        0x1D71D => [0x3B2],
1586
        0x1D71E => [0x3B3],
1587
        0x1D71F => [0x3B4],
1588
        0x1D720 => [0x3B5],
1589
        0x1D721 => [0x3B6],
1590
        0x1D722 => [0x3B7],
1591
        0x1D723 => [0x3B8],
1592
        0x1D724 => [0x3B9],
1593
        0x1D725 => [0x3BA],
1594
        0x1D726 => [0x3BB],
1595
        0x1D727 => [0x3BC],
1596
        0x1D728 => [0x3BD],
1597
        0x1D729 => [0x3BE],
1598
        0x1D72A => [0x3BF],
1599
        0x1D72B => [0x3C0],
1600
        0x1D72C => [0x3C1],
1601
        0x1D72D => [0x3B8],
1602
        0x1D72E => [0x3C3],
1603
        0x1D72F => [0x3C4],
1604
        0x1D730 => [0x3C5],
1605
        0x1D731 => [0x3C6],
1606
        0x1D732 => [0x3C7],
1607
        0x1D733 => [0x3C8],
1608
        0x1D734 => [0x3C9],
1609
        0x1D747 => [0x3C3],
1610
        0x1D756 => [0x3B1],
1611
        0x1D757 => [0x3B2],
1612
        0x1D758 => [0x3B3],
1613
        0x1D759 => [0x3B4],
1614
        0x1D75A => [0x3B5],
1615
        0x1D75B => [0x3B6],
1616
        0x1D75C => [0x3B7],
1617
        0x1D75D => [0x3B8],
1618
        0x1D75E => [0x3B9],
1619
        0x1D75F => [0x3BA],
1620
        0x1D760 => [0x3BB],
1621
        0x1D761 => [0x3BC],
1622
        0x1D762 => [0x3BD],
1623
        0x1D763 => [0x3BE],
1624
        0x1D764 => [0x3BF],
1625
        0x1D765 => [0x3C0],
1626
        0x1D766 => [0x3C1],
1627
        0x1D767 => [0x3B8],
1628
        0x1D768 => [0x3C3],
1629
        0x1D769 => [0x3C4],
1630
        0x1D76A => [0x3C5],
1631
        0x1D76B => [0x3C6],
1632
        0x1D76C => [0x3C7],
1633
        0x1D76D => [0x3C8],
1634
        0x1D76E => [0x3C9],
1635
        0x1D781 => [0x3C3],
1636
        0x1D790 => [0x3B1],
1637
        0x1D791 => [0x3B2],
1638
        0x1D792 => [0x3B3],
1639
        0x1D793 => [0x3B4],
1640
        0x1D794 => [0x3B5],
1641
        0x1D795 => [0x3B6],
1642
        0x1D796 => [0x3B7],
1643
        0x1D797 => [0x3B8],
1644
        0x1D798 => [0x3B9],
1645
        0x1D799 => [0x3BA],
1646
        0x1D79A => [0x3BB],
1647
        0x1D79B => [0x3BC],
1648
        0x1D79C => [0x3BD],
1649
        0x1D79D => [0x3BE],
1650
        0x1D79E => [0x3BF],
1651
        0x1D79F => [0x3C0],
1652
        0x1D7A0 => [0x3C1],
1653
        0x1D7A1 => [0x3B8],
1654
        0x1D7A2 => [0x3C3],
1655
        0x1D7A3 => [0x3C4],
1656
        0x1D7A4 => [0x3C5],
1657
        0x1D7A5 => [0x3C6],
1658
        0x1D7A6 => [0x3C7],
1659
        0x1D7A7 => [0x3C8],
1660
        0x1D7A8 => [0x3C9],
1661
        0x1D7BB => [0x3C3],
1662
        0x3F9 => [0x3C3],
1663
        0x1D2C => [0x61],
1664
        0x1D2D => [0xE6],
1665
        0x1D2E => [0x62],
1666
        0x1D30 => [0x64],
1667
        0x1D31 => [0x65],
1668
        0x1D32 => [0x1DD],
1669
        0x1D33 => [0x67],
1670
        0x1D34 => [0x68],
1671
        0x1D35 => [0x69],
1672
        0x1D36 => [0x6A],
1673
        0x1D37 => [0x6B],
1674
        0x1D38 => [0x6C],
1675
        0x1D39 => [0x6D],
1676
        0x1D3A => [0x6E],
1677
        0x1D3C => [0x6F],
1678
        0x1D3D => [0x223],
1679
        0x1D3E => [0x70],
1680
        0x1D3F => [0x72],
1681
        0x1D40 => [0x74],
1682
        0x1D41 => [0x75],
1683
        0x1D42 => [0x77],
1684
        0x213B => [0x66, 0x61, 0x78],
1685
        0x3250 => [0x70, 0x74, 0x65],
1686
        0x32CC => [0x68, 0x67],
1687
        0x32CE => [0x65, 0x76],
1688
        0x32CF => [0x6C, 0x74, 0x64],
1689
        0x337A => [0x69, 0x75],
1690
        0x33DE => [0x76, 0x2215, 0x6D],
1691
        0x33DF => [0x61, 0x2215, 0x6D],
1692
    ];
1693
1694
    /**
1695
     * Normalization Combining Classes; Code Points not listed
1696
     * got Combining Class 0.
1697
     *
1698
     * @static
1699
     * @var array
1700
     * @access private
1701
     */
1702
    private static $_np_norm_combcls = [
1703
        0x334 => 1,
1704
        0x335 => 1,
1705
        0x336 => 1,
1706
        0x337 => 1,
1707
        0x338 => 1,
1708
        0x93C => 7,
1709
        0x9BC => 7,
1710
        0xA3C => 7,
1711
        0xABC => 7,
1712
        0xB3C => 7,
1713
        0xCBC => 7,
1714
        0x1037 => 7,
1715
        0x3099 => 8,
1716
        0x309A => 8,
1717
        0x94D => 9,
1718
        0x9CD => 9,
1719
        0xA4D => 9,
1720
        0xACD => 9,
1721
        0xB4D => 9,
1722
        0xBCD => 9,
1723
        0xC4D => 9,
1724
        0xCCD => 9,
1725
        0xD4D => 9,
1726
        0xDCA => 9,
1727
        0xE3A => 9,
1728
        0xF84 => 9,
1729
        0x1039 => 9,
1730
        0x1714 => 9,
1731
        0x1734 => 9,
1732
        0x17D2 => 9,
1733
        0x5B0 => 10,
1734
        0x5B1 => 11,
1735
        0x5B2 => 12,
1736
        0x5B3 => 13,
1737
        0x5B4 => 14,
1738
        0x5B5 => 15,
1739
        0x5B6 => 16,
1740
        0x5B7 => 17,
1741
        0x5B8 => 18,
1742
        0x5B9 => 19,
1743
        0x5BB => 20,
1744
        0x5Bc => 21,
1745
        0x5BD => 22,
1746
        0x5BF => 23,
1747
        0x5C1 => 24,
1748
        0x5C2 => 25,
1749
        0xFB1E => 26,
1750
        0x64B => 27,
1751
        0x64C => 28,
1752
        0x64D => 29,
1753
        0x64E => 30,
1754
        0x64F => 31,
1755
        0x650 => 32,
1756
        0x651 => 33,
1757
        0x652 => 34,
1758
        0x670 => 35,
1759
        0x711 => 36,
1760
        0xC55 => 84,
1761
        0xC56 => 91,
1762
        0xE38 => 103,
1763
        0xE39 => 103,
1764
        0xE48 => 107,
1765
        0xE49 => 107,
1766
        0xE4A => 107,
1767
        0xE4B => 107,
1768
        0xEB8 => 118,
1769
        0xEB9 => 118,
1770
        0xEC8 => 122,
1771
        0xEC9 => 122,
1772
        0xECA => 122,
1773
        0xECB => 122,
1774
        0xF71 => 129,
1775
        0xF72 => 130,
1776
        0xF7A => 130,
1777
        0xF7B => 130,
1778
        0xF7C => 130,
1779
        0xF7D => 130,
1780
        0xF80 => 130,
1781
        0xF74 => 132,
1782
        0x321 => 202,
1783
        0x322 => 202,
1784
        0x327 => 202,
1785
        0x328 => 202,
1786
        0x31B => 216,
1787
        0xF39 => 216,
1788
        0x1D165 => 216,
1789
        0x1D166 => 216,
1790
        0x1D16E => 216,
1791
        0x1D16F => 216,
1792
        0x1D170 => 216,
1793
        0x1D171 => 216,
1794
        0x1D172 => 216,
1795
        0x302A => 218,
1796
        0x316 => 220,
1797
        0x317 => 220,
1798
        0x318 => 220,
1799
        0x319 => 220,
1800
        0x31C => 220,
1801
        0x31D => 220,
1802
        0x31E => 220,
1803
        0x31F => 220,
1804
        0x320 => 220,
1805
        0x323 => 220,
1806
        0x324 => 220,
1807
        0x325 => 220,
1808
        0x326 => 220,
1809
        0x329 => 220,
1810
        0x32A => 220,
1811
        0x32B => 220,
1812
        0x32C => 220,
1813
        0x32D => 220,
1814
        0x32E => 220,
1815
        0x32F => 220,
1816
        0x330 => 220,
1817
        0x331 => 220,
1818
        0x332 => 220,
1819
        0x333 => 220,
1820
        0x339 => 220,
1821
        0x33A => 220,
1822
        0x33B => 220,
1823
        0x33C => 220,
1824
        0x347 => 220,
1825
        0x348 => 220,
1826
        0x349 => 220,
1827
        0x34D => 220,
1828
        0x34E => 220,
1829
        0x353 => 220,
1830
        0x354 => 220,
1831
        0x355 => 220,
1832
        0x356 => 220,
1833
        0x591 => 220,
1834
        0x596 => 220,
1835
        0x59B => 220,
1836
        0x5A3 => 220,
1837
        0x5A4 => 220,
1838
        0x5A5 => 220,
1839
        0x5A6 => 220,
1840
        0x5A7 => 220,
1841
        0x5AA => 220,
1842
        0x655 => 220,
1843
        0x656 => 220,
1844
        0x6E3 => 220,
1845
        0x6EA => 220,
1846
        0x6ED => 220,
1847
        0x731 => 220,
1848
        0x734 => 220,
1849
        0x737 => 220,
1850
        0x738 => 220,
1851
        0x739 => 220,
1852
        0x73B => 220,
1853
        0x73C => 220,
1854
        0x73E => 220,
1855
        0x742 => 220,
1856
        0x744 => 220,
1857
        0x746 => 220,
1858
        0x748 => 220,
1859
        0x952 => 220,
1860
        0xF18 => 220,
1861
        0xF19 => 220,
1862
        0xF35 => 220,
1863
        0xF37 => 220,
1864
        0xFC6 => 220,
1865
        0x193B => 220,
1866
        0x20E8 => 220,
1867
        0x1D17B => 220,
1868
        0x1D17C => 220,
1869
        0x1D17D => 220,
1870
        0x1D17E => 220,
1871
        0x1D17F => 220,
1872
        0x1D180 => 220,
1873
        0x1D181 => 220,
1874
        0x1D182 => 220,
1875
        0x1D18A => 220,
1876
        0x1D18B => 220,
1877
        0x59A => 222,
1878
        0x5AD => 222,
1879
        0x1929 => 222,
1880
        0x302D => 222,
1881
        0x302E => 224,
1882
        0x302F => 224,
1883
        0x1D16D => 226,
1884
        0x5AE => 228,
1885
        0x18A9 => 228,
1886
        0x302B => 228,
1887
        0x300 => 230,
1888
        0x301 => 230,
1889
        0x302 => 230,
1890
        0x303 => 230,
1891
        0x304 => 230,
1892
        0x305 => 230,
1893
        0x306 => 230,
1894
        0x307 => 230,
1895
        0x308 => 230,
1896
        0x309 => 230,
1897
        0x30A => 230,
1898
        0x30B => 230,
1899
        0x30C => 230,
1900
        0x30D => 230,
1901
        0x30E => 230,
1902
        0x30F => 230,
1903
        0x310 => 230,
1904
        0x311 => 230,
1905
        0x312 => 230,
1906
        0x313 => 230,
1907
        0x314 => 230,
1908
        0x33D => 230,
1909
        0x33E => 230,
1910
        0x33F => 230,
1911
        0x340 => 230,
1912
        0x341 => 230,
1913
        0x342 => 230,
1914
        0x343 => 230,
1915
        0x344 => 230,
1916
        0x346 => 230,
1917
        0x34A => 230,
1918
        0x34B => 230,
1919
        0x34C => 230,
1920
        0x350 => 230,
1921
        0x351 => 230,
1922
        0x352 => 230,
1923
        0x357 => 230,
1924
        0x363 => 230,
1925
        0x364 => 230,
1926
        0x365 => 230,
1927
        0x366 => 230,
1928
        0x367 => 230,
1929
        0x368 => 230,
1930
        0x369 => 230,
1931
        0x36A => 230,
1932
        0x36B => 230,
1933
        0x36C => 230,
1934
        0x36D => 230,
1935
        0x36E => 230,
1936
        0x36F => 230,
1937
        0x483 => 230,
1938
        0x484 => 230,
1939
        0x485 => 230,
1940
        0x486 => 230,
1941
        0x592 => 230,
1942
        0x593 => 230,
1943
        0x594 => 230,
1944
        0x595 => 230,
1945
        0x597 => 230,
1946
        0x598 => 230,
1947
        0x599 => 230,
1948
        0x59C => 230,
1949
        0x59D => 230,
1950
        0x59E => 230,
1951
        0x59F => 230,
1952
        0x5A0 => 230,
1953
        0x5A1 => 230,
1954
        0x5A8 => 230,
1955
        0x5A9 => 230,
1956
        0x5AB => 230,
1957
        0x5AC => 230,
1958
        0x5AF => 230,
1959
        0x5C4 => 230,
1960
        0x610 => 230,
1961
        0x611 => 230,
1962
        0x612 => 230,
1963
        0x613 => 230,
1964
        0x614 => 230,
1965
        0x615 => 230,
1966
        0x653 => 230,
1967
        0x654 => 230,
1968
        0x657 => 230,
1969
        0x658 => 230,
1970
        0x6D6 => 230,
1971
        0x6D7 => 230,
1972
        0x6D8 => 230,
1973
        0x6D9 => 230,
1974
        0x6DA => 230,
1975
        0x6DB => 230,
1976
        0x6DC => 230,
1977
        0x6DF => 230,
1978
        0x6E0 => 230,
1979
        0x6E1 => 230,
1980
        0x6E2 => 230,
1981
        0x6E4 => 230,
1982
        0x6E7 => 230,
1983
        0x6E8 => 230,
1984
        0x6EB => 230,
1985
        0x6EC => 230,
1986
        0x730 => 230,
1987
        0x732 => 230,
1988
        0x733 => 230,
1989
        0x735 => 230,
1990
        0x736 => 230,
1991
        0x73A => 230,
1992
        0x73D => 230,
1993
        0x73F => 230,
1994
        0x740 => 230,
1995
        0x741 => 230,
1996
        0x743 => 230,
1997
        0x745 => 230,
1998
        0x747 => 230,
1999
        0x749 => 230,
2000
        0x74A => 230,
2001
        0x951 => 230,
2002
        0x953 => 230,
2003
        0x954 => 230,
2004
        0xF82 => 230,
2005
        0xF83 => 230,
2006
        0xF86 => 230,
2007
        0xF87 => 230,
2008
        0x170D => 230,
2009
        0x193A => 230,
2010
        0x20D0 => 230,
2011
        0x20D1 => 230,
2012
        0x20D4 => 230,
2013
        0x20D5 => 230,
2014
        0x20D6 => 230,
2015
        0x20D7 => 230,
2016
        0x20DB => 230,
2017
        0x20DC => 230,
2018
        0x20E1 => 230,
2019
        0x20E7 => 230,
2020
        0x20E9 => 230,
2021
        0xFE20 => 230,
2022
        0xFE21 => 230,
2023
        0xFE22 => 230,
2024
        0xFE23 => 230,
2025
        0x1D185 => 230,
2026
        0x1D186 => 230,
2027
        0x1D187 => 230,
2028
        0x1D189 => 230,
2029
        0x1D188 => 230,
2030
        0x1D1AA => 230,
2031
        0x1D1AB => 230,
2032
        0x1D1AC => 230,
2033
        0x1D1AD => 230,
2034
        0x315 => 232,
2035
        0x31A => 232,
2036
        0x302C => 232,
2037
        0x35F => 233,
2038
        0x362 => 233,
2039
        0x35D => 234,
2040
        0x35E => 234,
2041
        0x360 => 234,
2042
        0x361 => 234,
2043
        0x345 => 240,
2044
    ];
2045
    // }}}
2046
2047
    // {{{ properties
2048
    /**
2049
     * @var string
2050
     * @access private
2051
     */
2052
    private $_punycode_prefix = 'xn--';
2053
2054
    /**
2055
     * @access private
2056
     */
2057
    private $_invalid_ucs = 0x80000000;
2058
2059
    /**
2060
     * @access private
2061
     */
2062
    private $_max_ucs = 0x10FFFF;
2063
2064
    /**
2065
     * @var int
2066
     * @access private
2067
     */
2068
    private $_base = 36;
2069
2070
    /**
2071
     * @var int
2072
     * @access private
2073
     */
2074
    private $_tmin = 1;
2075
2076
    /**
2077
     * @var int
2078
     * @access private
2079
     */
2080
    private $_tmax = 26;
2081
2082
    /**
2083
     * @var int
2084
     * @access private
2085
     */
2086
    private $_skew = 38;
2087
2088
    /**
2089
     * @var int
2090
     * @access private
2091
     */
2092
    private $_damp = 700;
2093
2094
    /**
2095
     * @var int
2096
     * @access private
2097
     */
2098
    private $_initial_bias = 72;
2099
2100
    /**
2101
     * @var int
2102
     * @access private
2103
     */
2104
    private $_initial_n = 0x80;
2105
2106
    /**
2107
     * @var int
2108
     * @access private
2109
     */
2110
    private $_slast;
2111
2112
    /**
2113
     * @access private
2114
     */
2115
    private $_sbase = 0xAC00;
2116
2117
    /**
2118
     * @access private
2119
     */
2120
    private $_lbase = 0x1100;
2121
2122
    /**
2123
     * @access private
2124
     */
2125
    private $_vbase = 0x1161;
2126
2127
    /**
2128
     * @access private
2129
     */
2130
    private $_tbase = 0x11a7;
2131
2132
    /**
2133
     * @var int
2134
     * @access private
2135
     */
2136
    private $_lcount = 19;
2137
2138
    /**
2139
     * @var int
2140
     * @access private
2141
     */
2142
    private $_vcount = 21;
2143
2144
    /**
2145
     * @var int
2146
     * @access private
2147
     */
2148
    private $_tcount = 28;
2149
2150
    /**
2151
     * vcount * tcount
2152
     *
2153
     * @var int
2154
     * @access private
2155
     */
2156
    private $_ncount = 588;
2157
2158
    /**
2159
     * lcount * tcount * vcount
2160
     *
2161
     * @var int
2162
     * @access private
2163
     */
2164
    private $_scount = 11172;
2165
2166
    /**
2167
     * Default encoding for encode()'s input and decode()'s output is UTF-8;
2168
     * Other possible encodings are ucs4_string and ucs4_array
2169
     * See {@link setParams()} for how to select these
2170
     *
2171
     * @var bool
2172
     * @access private
2173
     */
2174
    private $_api_encoding = 'utf8';
2175
2176
    /**
2177
     * Overlong UTF-8 encodings are forbidden
2178
     *
2179
     * @var bool
2180
     * @access private
2181
     */
2182
    private $_allow_overlong = false;
2183
2184
    /**
2185
     * Behave strict or not
2186
     *
2187
     * @var bool
2188
     * @access private
2189
     */
2190
    private $_strict_mode = false;
2191
2192
    /**
2193
     * IDNA-version to use
2194
     * Values are "2003" and "2008".
2195
     * Defaults to "2003", since that was the original version and for
2196
     * compatibility with previous versions of this library.
2197
     * If you need to encode "new" characters like the German "Eszett",
2198
     * please switch to 2008 first before encoding.
2199
     *
2200
     * @var bool
2201
     * @access private
2202
     */
2203
    private $_version = '2003';
2204
2205
    /**
2206
     * Cached value indicating whether or not mbstring function overloading is
2207
     * on for strlen
2208
     * This is cached for optimal performance.
2209
     *
2210
     * @var bool
2211
     * @see Net_IDNA2::_byteLength()
2212
     */
2213
    private static $_mb_string_overload = null;
2214
    // }}}
2215
2216
2217
    // {{{ constructor
2218
2219
    /**
2220
     * Constructor
2221
     *
2222
     * @param array $options Options to initialise the object with
2223
     * @access public
2224
     * @see    setParams()
2225
     */
2226
    public function __construct($options = null)
2227
    {
2228
        $this->_slast = $this->_sbase + $this->_lcount * $this->_vcount * $this->_tcount;
2229
2230
        if (is_array($options)) {
2231
            $this->setParams($options);
2232
        }
2233
2234
        // populate mbstring overloading cache if not set
2235
        if (self::$_mb_string_overload === null) {
2236
            self::$_mb_string_overload = (extension_loaded('mbstring')
2237
                && (ini_get('mbstring.func_overload') & 0x02) === 0x02);
2238
        }
2239
    }
2240
2241
    // }}}
2242
2243
    /**
2244
     * Sets a new option value. Available options and values:
2245
     * [utf8 -     Use either UTF-8 or ISO-8859-1 as input (true for UTF-8, false
2246
     *             otherwise); The output is always UTF-8]
2247
     * [overlong - Unicode does not allow unnecessarily long encodings of chars,
2248
     *             to allow this, set this parameter to true, else to false;
2249
     *             default is false.]
2250
     * [strict -   true: strict mode, good for registration purposes - Causes errors
2251
     *             on failures; false: loose mode, ideal for "wildlife" applications
2252
     *             by silently ignoring errors and returning the original input instead]
2253
     *
2254
     * @param mixed $option Parameter to set (string: single parameter; array of Parameter => Value pairs)
2255
     * @param string $value Value to use (if parameter 1 is a string)
2256
     * @throws \InvalidArgumentException
2257
     * @return bool       true on success, false otherwise
2258
     * @access public
2259
     */
2260
    public function setParams($option, $value = false)
2261
    {
2262
        if (!is_array($option)) {
2263
            $option = [$option => $value];
2264
        }
2265
2266
        foreach ($option as $k => $v) {
2267
            switch ($k) {
2268
                case 'encoding':
2269
                    switch ($v) {
2270
                        case 'utf8':
2271
                        case 'ucs4_string':
2272
                        case 'ucs4_array':
2273
                            $this->_api_encoding = $v;
2274
                            break;
2275
2276
                        default:
2277
                            throw new InvalidArgumentException(
2278
                                'Set Parameter: Unknown parameter ' . $v . ' for option ' . $k
2279
                            );
2280
                    }
2281
2282
                    break;
2283
2284
                case 'overlong':
2285
                    $this->_allow_overlong = ($v) ? true : false;
2286
                    break;
2287
2288
                case 'strict':
2289
                    $this->_strict_mode = ($v) ? true : false;
2290
                    break;
2291
2292
                case 'version':
2293
                    if (in_array($v, ['2003', '2008'])) {
2294
                        $this->_version = $v;
2295
                    } else {
2296
                        throw new InvalidArgumentException(
2297
                            'Set Parameter: Invalid parameter ' . $v . ' for option ' . $k
2298
                        );
2299
                    }
2300
                    break;
2301
2302
                default:
2303
                    return false;
2304
            }
2305
        }
2306
2307
        return true;
2308
    }
2309
2310
    /**
2311
     * Encode a given UTF-8 domain name.
2312
     *
2313
     * @param string $decoded Domain name (UTF-8 or UCS-4)
2314
     * @param string $one_time_encoding Desired input encoding, see {@link set_parameter}
2315
     *                                  If not given will use default-encoding
2316
     * @throws Exception
2317
     * @return string Encoded Domain name (ACE string)
2318
     * @return mixed  processed string
2319
     * @access public
2320
     */
2321
    public function encode($decoded, $one_time_encoding = false)
2322
    {
2323
        // Forcing conversion of input to UCS4 array
2324
        // If one time encoding is given, use this, else the objects property
2325
        switch (($one_time_encoding) ? $one_time_encoding : $this->_api_encoding) {
2326
            case 'utf8':
2327
                $decoded = $this->_utf8_to_ucs4($decoded);
2328
                break;
2329
            case 'ucs4_string':
2330
                $decoded = $this->_ucs4_string_to_ucs4($decoded);
2331
                break;
2332
            case 'ucs4_array':
2333
                break;
2334
            default:
2335
                throw new InvalidArgumentException('Unsupported input format');
2336
        }
2337
2338
        // No input, no output, what else did you expect?
2339
        if (empty($decoded)) {
2340
            return '';
2341
        }
2342
2343
        // Anchors for iteration
2344
        $last_begin = 0;
2345
        // Output string
2346
        $output = '';
2347
2348
        foreach ($decoded as $k => $v) {
0 ignored issues
show
Bug introduced by
The expression $decoded of type array|string is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
2349
            // Make sure to use just the plain dot
2350
            switch ($v) {
2351
                case 0x3002:
2352
                case 0xFF0E:
2353
                case 0xFF61:
2354
                    $decoded[$k] = 0x2E;
2355
                // It's right, no break here
2356
                // The codepoints above have to be converted to dots anyway
2357
2358
                // Stumbling across an anchoring character
2359
                // no break
2360
                case 0x2E:
2361
                case 0x2F:
2362
                case 0x3A:
2363
                case 0x3F:
2364
                case 0x40:
2365
                    // Neither email addresses nor URLs allowed in strict mode
2366
                    if ($this->_strict_mode) {
2367
                        throw new InvalidArgumentException(
2368
                            'Neither email addresses nor URLs are allowed in strict mode.'
2369
                        );
2370
                    }
2371
                    // Skip first char
2372 View Code Duplication
                    if ($k) {
2373
                        $encoded = '';
0 ignored issues
show
Unused Code introduced by
$encoded is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
2374
                        $encoded = $this->_encode(array_slice($decoded, $last_begin, (($k) - $last_begin)));
0 ignored issues
show
Documentation introduced by
array_slice($decoded, $l...egin, $k - $last_begin) is of type array, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
2375
                        if ($encoded) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $encoded of type false|string is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
2376
                            $output .= $encoded;
2377
                        } else {
2378
                            $output .= $this->_ucs4_to_utf8(array_slice($decoded, $last_begin, (($k) - $last_begin)));
2379
                        }
2380
                        $output .= chr($decoded[$k]);
2381
                    }
2382
                    $last_begin = $k + 1;
2383
            }
2384
        }
2385
        // Catch the rest of the string
2386 View Code Duplication
        if ($last_begin) {
2387
            $inp_len = sizeof($decoded);
2388
            $encoded = '';
0 ignored issues
show
Unused Code introduced by
$encoded is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
2389
            $encoded = $this->_encode(array_slice($decoded, $last_begin, (($inp_len) - $last_begin)));
0 ignored issues
show
Documentation introduced by
array_slice($decoded, $l...$inp_len - $last_begin) is of type array, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
2390
            if ($encoded) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $encoded of type false|string is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
2391
                $output .= $encoded;
2392
            } else {
2393
                $output .= $this->_ucs4_to_utf8(array_slice($decoded, $last_begin, (($inp_len) - $last_begin)));
2394
            }
2395
2396
            return $output;
2397
        }
2398
2399
        if ($output = $this->_encode($decoded)) {
0 ignored issues
show
Bug introduced by
It seems like $decoded can also be of type array; however, Net_IDNA2::_encode() does only seem to accept string, 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...
2400
            return $output;
2401
        }
2402
2403
        return $this->_ucs4_to_utf8($decoded);
0 ignored issues
show
Bug introduced by
It seems like $decoded defined by parameter $decoded on line 2321 can also be of type string; however, Net_IDNA2::_ucs4_to_utf8() does only seem to accept array, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
2404
    }
2405
2406
    /**
2407
     * Decode a given ACE domain name.
2408
     *
2409
     * @param string $input Domain name (ACE string)
2410
     * @param string $one_time_encoding Desired output encoding, see {@link set_parameter}
2411
     * @throws Exception
2412
     * @return string                   Decoded Domain name (UTF-8 or UCS-4)
2413
     * @access public
2414
     */
2415
    public function decode($input, $one_time_encoding = false)
2416
    {
2417
        // Optionally set
2418
        if ($one_time_encoding) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $one_time_encoding of type false|string is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
2419
            switch ($one_time_encoding) {
2420
                case 'utf8':
2421
                case 'ucs4_string':
2422
                case 'ucs4_array':
2423
                    break;
2424
                default:
2425
                    throw new InvalidArgumentException('Unknown encoding ' . $one_time_encoding);
2426
            }
2427
        }
2428
        // Make sure to drop any newline characters around
2429
        $input = trim($input);
2430
2431
        // Negotiate input and try to determine, wether it is a plain string,
2432
        // an email address or something like a complete URL
2433
        if (strpos($input, '@')) { // Maybe it is an email address
2434
            // No no in strict mode
2435
            if ($this->_strict_mode) {
2436
                throw new InvalidArgumentException('Only simple domain name parts can be handled in strict mode');
2437
            }
2438
            list($email_pref, $input) = explode('@', $input, 2);
2439
            $arr = explode('.', $input);
2440 View Code Duplication
            foreach ($arr as $k => $v) {
2441
                $conv = $this->_decode($v);
2442
                if ($conv) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $conv of type false|string is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
2443
                    $arr[$k] = $conv;
2444
                }
2445
            }
2446
            $return = $email_pref . '@' . join('.', $arr);
2447
        } elseif (preg_match('![:\./]!', $input)) { // Or a complete domain name (with or without paths / parameters)
2448
            // No no in strict mode
2449
            if ($this->_strict_mode) {
2450
                throw new InvalidArgumentException('Only simple domain name parts can be handled in strict mode');
2451
            }
2452
2453
            $parsed = parse_url($input);
2454
            if (isset($parsed['host'])) {
2455
                $arr = explode('.', $parsed['host']);
2456 View Code Duplication
                foreach ($arr as $k => $v) {
2457
                    $conv = $this->_decode($v);
2458
                    if ($conv) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $conv of type false|string is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
2459
                        $arr[$k] = $conv;
2460
                    }
2461
                }
2462
                $parsed['host'] = join('.', $arr);
2463
                if (isset($parsed['scheme'])) {
2464
                    $parsed['scheme'] .= (strtolower($parsed['scheme']) == 'mailto') ? ':' : '://';
2465
                }
2466
                $return = $this->_unparse_url($parsed);
0 ignored issues
show
Security Bug introduced by
It seems like $parsed defined by parse_url($input) on line 2453 can also be of type false; however, Net_IDNA2::_unparse_url() does only seem to accept array, did you maybe forget to handle an error condition?

This check looks for type mismatches where the missing type is false. This is usually indicative of an error condtion.

Consider the follow example

<?php

function getDate($date)
{
    if ($date !== null) {
        return new DateTime($date);
    }

    return false;
}

This function either returns a new DateTime object or false, if there was an error. This is a typical pattern in PHP programming to show that an error has occurred without raising an exception. The calling code should check for this returned false before passing on the value to another function or method that may not be able to handle a false.

Loading history...
2467
            } else { // parse_url seems to have failed, try without it
2468
                $arr = explode('.', $input);
2469 View Code Duplication
                foreach ($arr as $k => $v) {
2470
                    $conv = $this->_decode($v);
2471
                    if ($conv) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $conv of type false|string is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
2472
                        $arr[$k] = $conv;
2473
                    }
2474
                }
2475
                $return = join('.', $arr);
2476
            }
2477
        } else { // Otherwise we consider it being a pure domain name string
2478
            $return = $this->_decode($input);
2479
        }
2480
        // The output is UTF-8 by default, other output formats need conversion here
2481
        // If one time encoding is given, use this, else the objects property
2482
        switch (($one_time_encoding) ? $one_time_encoding : $this->_api_encoding) {
2483
            case 'utf8':
2484
                return $return;
2485
                break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
2486
            case 'ucs4_string':
2487
                return $this->_ucs4_to_ucs4_string($this->_utf8_to_ucs4($return));
2488
                break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
2489
            case 'ucs4_array':
2490
                return $this->_utf8_to_ucs4($return);
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this->_utf8_to_ucs4($return); (array) is incompatible with the return type documented by Net_IDNA2::decode of type string.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
2491
                break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
2492
            default:
2493
                throw new InvalidArgumentException('Unsupported output format');
2494
        }
2495
    }
2496
2497
    // {{{ private
2498
2499
    /**
2500
     * Opposite function to parse_url()
2501
     * Inspired by code from comments of php.net-documentation for parse_url()
2502
     *
2503
     * @param array $parts_arr parts (strings) as returned by parse_url()
2504
     * @return string
2505
     * @access private
2506
     */
2507
    private function _unparse_url($parts_arr)
2508
    {
2509
        if (!empty($parts_arr['scheme'])) {
2510
            $ret_url = $parts_arr['scheme'];
2511
        }
2512
        if (!empty($parts_arr['user'])) {
2513
            $ret_url .= $parts_arr['user'];
0 ignored issues
show
Bug introduced by
The variable $ret_url does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
2514
            if (!empty($parts_arr['pass'])) {
2515
                $ret_url .= ':' . $parts_arr['pass'];
2516
            }
2517
            $ret_url .= '@';
2518
        }
2519
        $ret_url .= $parts_arr['host'];
2520
        if (!empty($parts_arr['port'])) {
2521
            $ret_url .= ':' . $parts_arr['port'];
2522
        }
2523
        $ret_url .= $parts_arr['path'];
2524
        if (!empty($parts_arr['query'])) {
2525
            $ret_url .= '?' . $parts_arr['query'];
2526
        }
2527
        if (!empty($parts_arr['fragment'])) {
2528
            $ret_url .= '#' . $parts_arr['fragment'];
2529
        }
2530
2531
        return $ret_url;
2532
    }
2533
2534
    /**
2535
     * The actual encoding algorithm.
2536
     *
2537
     * @param string $decoded Decoded string which should be encoded
2538
     * @throws Exception
2539
     * @return string         Encoded string
2540
     * @access private
2541
     */
2542
    private function _encode($decoded)
2543
    {
2544
        // We cannot encode a domain name containing the Punycode prefix
2545
        $extract = self::_byteLength($this->_punycode_prefix);
2546
        $check_pref = $this->_utf8_to_ucs4($this->_punycode_prefix);
2547
        $check_deco = array_slice($decoded, 0, $extract);
2548
2549
        if ($check_pref == $check_deco) {
2550
            throw new InvalidArgumentException('This is already a punycode string');
2551
        }
2552
2553
        // We will not try to encode strings consisting of basic code points only
2554
        $encodable = false;
2555
        foreach ($decoded as $k => $v) {
0 ignored issues
show
Bug introduced by
The expression $decoded of type string is not traversable.
Loading history...
2556
            if ($v > 0x7a) {
2557
                $encodable = true;
2558
                break;
2559
            }
2560
        }
2561
        if (!$encodable) {
2562
            if ($this->_strict_mode) {
2563
                throw new InvalidArgumentException('The given string does not contain encodable chars');
2564
            }
2565
2566
            return false;
2567
        }
2568
2569
        // Do NAMEPREP
2570
        $decoded = $this->_nameprep($decoded);
2571
2572
        $deco_len = count($decoded);
2573
2574
        // Empty array
2575
        if (!$deco_len) {
2576
            return false;
2577
        }
2578
2579
        // How many chars have been consumed
2580
        $codecount = 0;
2581
2582
        // Start with the prefix; copy it to output
2583
        $encoded = $this->_punycode_prefix;
0 ignored issues
show
Unused Code introduced by
$encoded is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
2584
2585
        $encoded = '';
2586
        // Copy all basic code points to output
2587
        for ($i = 0; $i < $deco_len; ++$i) {
2588
            $test = $decoded[$i];
2589
            // Will match [0-9a-zA-Z-]
2590
            if ((0x2F < $test && $test < 0x40)
2591
                || (0x40 < $test && $test < 0x5B)
2592
                || (0x60 < $test && $test <= 0x7B)
2593
                || (0x2D == $test)
2594
            ) {
2595
                $encoded .= chr($decoded[$i]);
2596
                $codecount++;
2597
            }
2598
        }
2599
2600
        // All codepoints were basic ones
2601
        if ($codecount == $deco_len) {
2602
            return $encoded;
2603
        }
2604
2605
        // Start with the prefix; copy it to output
2606
        $encoded = $this->_punycode_prefix . $encoded;
2607
2608
        // If we have basic code points in output, add an hyphen to the end
2609
        if ($codecount) {
2610
            $encoded .= '-';
2611
        }
2612
2613
        // Now find and encode all non-basic code points
2614
        $is_first = true;
2615
        $cur_code = $this->_initial_n;
2616
        $bias = $this->_initial_bias;
2617
        $delta = 0;
2618
2619
        while ($codecount < $deco_len) {
2620
            // Find the smallest code point >= the current code point and
2621
            // remember the last ouccrence of it in the input
2622
            for ($i = 0, $next_code = $this->_max_ucs; $i < $deco_len; $i++) {
2623
                if ($decoded[$i] >= $cur_code && $decoded[$i] <= $next_code) {
2624
                    $next_code = $decoded[$i];
2625
                }
2626
            }
2627
2628
            $delta += ($next_code - $cur_code) * ($codecount + 1);
2629
            $cur_code = $next_code;
2630
2631
            // Scan input again and encode all characters whose code point is $cur_code
2632
            for ($i = 0; $i < $deco_len; $i++) {
2633
                if ($decoded[$i] < $cur_code) {
2634
                    $delta++;
2635
                } else {
2636
                    if ($decoded[$i] == $cur_code) {
2637
                        for ($q = $delta, $k = $this->_base; 1; $k += $this->_base) {
2638
                            $t = ($k <= $bias) ?
2639
                                $this->_tmin : (($k >= $bias + $this->_tmax) ? $this->_tmax : $k - $bias);
2640
2641
                            if ($q < $t) {
2642
                                break;
2643
                            }
2644
2645
                            $encoded .= $this->_encodeDigit(ceil($t + (($q - $t) % ($this->_base - $t))));
2646
                            $q = ($q - $t) / ($this->_base - $t);
2647
                        }
2648
2649
                        $encoded .= $this->_encodeDigit($q);
2650
                        $bias = $this->_adapt($delta, $codecount + 1, $is_first);
2651
                        $codecount++;
2652
                        $delta = 0;
2653
                        $is_first = false;
2654
                    }
2655
                }
2656
            }
2657
2658
            $delta++;
2659
            $cur_code++;
2660
        }
2661
2662
        return $encoded;
2663
    }
2664
2665
    /**
2666
     * The actual decoding algorithm.
2667
     *
2668
     * @param string $encoded Encoded string which should be decoded
2669
     * @throws Exception
2670
     * @return string         Decoded string
2671
     * @access private
2672
     */
2673
    private function _decode($encoded)
2674
    {
2675
        // We do need to find the Punycode prefix
2676
        if (!preg_match('!^' . preg_quote($this->_punycode_prefix, '!') . '!', $encoded)) {
2677
            return false;
2678
        }
2679
2680
        $encode_test = preg_replace('!^' . preg_quote($this->_punycode_prefix, '!') . '!', '', $encoded);
2681
2682
        // If nothing left after removing the prefix, it is hopeless
2683
        if (!$encode_test) {
2684
            return false;
2685
        }
2686
2687
        // Find last occurence of the delimiter
2688
        $delim_pos = strrpos($encoded, '-');
2689
2690
        if ($delim_pos > self::_byteLength($this->_punycode_prefix)) {
2691
            for ($k = self::_byteLength($this->_punycode_prefix); $k < $delim_pos; ++$k) {
2692
                $decoded[] = ord($encoded{$k});
0 ignored issues
show
Coding Style Comprehensibility introduced by
$decoded was never initialized. Although not strictly required by PHP, it is generally a good practice to add $decoded = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
2693
            }
2694
        } else {
2695
            $decoded = [];
2696
        }
2697
2698
        $deco_len = count($decoded);
0 ignored issues
show
Bug introduced by
The variable $decoded does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
2699
        $enco_len = self::_byteLength($encoded);
2700
2701
        // Wandering through the strings; init
2702
        $is_first = true;
2703
        $bias = $this->_initial_bias;
2704
        $idx = 0;
2705
        $char = $this->_initial_n;
2706
2707
        for ($enco_idx = ($delim_pos) ? ($delim_pos + 1) : 0; $enco_idx < $enco_len; ++$deco_len) {
2708
            for ($old_idx = $idx, $w = 1, $k = $this->_base; 1; $k += $this->_base) {
2709
                $digit = $this->_decodeDigit($encoded{$enco_idx++});
2710
                $idx += $digit * $w;
2711
2712
                $t = ($k <= $bias) ?
2713
                    $this->_tmin : (($k >= $bias + $this->_tmax) ? $this->_tmax : ($k - $bias));
2714
2715
                if ($digit < $t) {
2716
                    break;
2717
                }
2718
2719
                $w = (int) ($w * ($this->_base - $t));
2720
            }
2721
2722
            $bias = $this->_adapt($idx - $old_idx, $deco_len + 1, $is_first);
2723
            $is_first = false;
2724
            $char += (int) ($idx / ($deco_len + 1));
2725
            $idx %= ($deco_len + 1);
2726
2727
            if ($deco_len > 0) {
2728
                // Make room for the decoded char
2729
                for ($i = $deco_len; $i > $idx; $i--) {
2730
                    $decoded[$i] = $decoded[($i - 1)];
2731
                }
2732
            }
2733
2734
            $decoded[$idx++] = $char;
2735
        }
2736
2737
        return $this->_ucs4_to_utf8($decoded);
2738
    }
2739
2740
    /**
2741
     * Adapt the bias according to the current code point and position.
2742
     *
2743
     * @param int $delta ...
2744
     * @param int $npoints ...
2745
     * @param bool $is_first ...
2746
     * @return int
2747
     * @access private
2748
     */
2749
    private function _adapt($delta, $npoints, $is_first)
2750
    {
2751
        $delta = (int) ($is_first ? ($delta / $this->_damp) : ($delta / 2));
2752
        $delta += (int) ($delta / $npoints);
2753
2754
        for ($k = 0; $delta > (($this->_base - $this->_tmin) * $this->_tmax) / 2; $k += $this->_base) {
2755
            $delta = (int) ($delta / ($this->_base - $this->_tmin));
2756
        }
2757
2758
        return (int) ($k + ($this->_base - $this->_tmin + 1) * $delta / ($delta + $this->_skew));
2759
    }
2760
2761
    /**
2762
     * Encoding a certain digit.
2763
     *
2764
     * @param int $d One digit to encode
2765
     * @return string  Encoded digit
2766
     * @access private
2767
     */
2768
    private function _encodeDigit($d)
2769
    {
2770
        return chr($d + 22 + 75 * ($d < 26));
2771
    }
2772
2773
    /**
2774
     * Decode a certain digit.
2775
     *
2776
     * @param string $cp One digit (character) to decode
2777
     * @return int     Decoded digit
2778
     * @access private
2779
     */
2780
    private function _decodeDigit($cp)
2781
    {
2782
        $cp = ord($cp);
2783
2784
        return ($cp - 48 < 10) ? $cp - 22 : (($cp - 65 < 26) ? $cp - 65 : (($cp - 97 < 26) ? $cp - 97 : $this->_base));
2785
    }
2786
2787
    /**
2788
     * Do Nameprep according to RFC3491 and RFC3454.
2789
     *
2790
     * @param string $input Unicode Characters
2791
     * @throws Exception
2792
     * @return string      Unicode Characters, Nameprep'd
2793
     * @access private
2794
     */
2795
    private function _nameprep($input)
2796
    {
2797
        $output = [];
2798
2799
        // Walking through the input array, performing the required steps on each of
2800
        // the input chars and putting the result into the output array
2801
        // While mapping required chars we apply the cannonical ordering
2802
2803
        foreach ($input as $v) {
0 ignored issues
show
Bug introduced by
The expression $input of type string is not traversable.
Loading history...
2804
            // Map to nothing == skip that code point
2805
            if (in_array($v, self::$_np_map_nothing)) {
2806
                continue;
2807
            }
2808
2809
            // Try to find prohibited input
2810
            if (in_array($v, self::$_np_prohibit) || in_array($v, self::$_general_prohibited)) {
2811
                throw new Net_IDNA2_Exception_Nameprep('Prohibited input U+' . sprintf('%08X', $v));
2812
            }
2813
2814
            foreach (self::$_np_prohibit_ranges as $range) {
2815
                if ($range[0] <= $v && $v <= $range[1]) {
2816
                    throw new Net_IDNA2_Exception_Nameprep('Prohibited input U+' . sprintf('%08X', $v));
2817
                }
2818
            }
2819
2820
            // Hangul syllable decomposition
2821
            if (0xAC00 <= $v && $v <= 0xD7AF) {
2822
                foreach ($this->_hangulDecompose($v) as $out) {
2823
                    $output[] = $out;
2824
                }
2825
            } else {
2826
                if (($this->_version == '2003') && isset(self::$_np_replacemaps[$v])) {
2827
                    // There's a decomposition mapping for that code point
2828
                    // Decompositions only in version 2003 (original) of IDNA
2829
                    foreach ($this->_applyCannonicalOrdering(self::$_np_replacemaps[$v]) as $out) {
2830
                        $output[] = $out;
2831
                    }
2832
                } else {
2833
                    $output[] = $v;
2834
                }
2835
            }
2836
        }
2837
2838
        // Combine code points
2839
2840
        $last_class = 0;
2841
        $last_starter = 0;
2842
        $out_len = count($output);
2843
2844
        for ($i = 0; $i < $out_len; ++$i) {
2845
            $class = $this->_getCombiningClass($output[$i]);
2846
2847
            if ((!$last_class || $last_class != $class) && $class) {
2848
                // Try to match
2849
                $seq_len = $i - $last_starter;
2850
                $out = $this->_combine(array_slice($output, $last_starter, $seq_len));
2851
2852
                // On match: Replace the last starter with the composed character and remove
2853
                // the now redundant non-starter(s)
2854
                if ($out) {
2855
                    $output[$last_starter] = $out;
2856
2857
                    if (count($out) != $seq_len) {
2858
                        for ($j = $i + 1; $j < $out_len; ++$j) {
2859
                            $output[$j - 1] = $output[$j];
2860
                        }
2861
2862
                        unset($output[$out_len]);
2863
                    }
2864
2865
                    // Rewind the for loop by one, since there can be more possible compositions
2866
                    $i--;
2867
                    $out_len--;
2868
                    $last_class = ($i == $last_starter) ? 0 : $this->_getCombiningClass($output[$i - 1]);
2869
2870
                    continue;
2871
                }
2872
            }
2873
2874
            // The current class is 0
2875
            if (!$class) {
2876
                $last_starter = $i;
2877
            }
2878
2879
            $last_class = $class;
2880
        }
2881
2882
        return $output;
2883
    }
2884
2885
    /**
2886
     * Decomposes a Hangul syllable
2887
     * (see http://www.unicode.org/unicode/reports/tr15/#Hangul).
2888
     *
2889
     * @param int $char 32bit UCS4 code point
2890
     * @return int[]        Either Hangul Syllable decomposed or original 32bit
2891
     *                      value as one value array
2892
     * @access private
2893
     */
2894
    private function _hangulDecompose($char)
2895
    {
2896
        $sindex = $char - $this->_sbase;
2897
2898
        if ($sindex < 0 || $sindex >= $this->_scount) {
2899
            return [$char];
2900
        }
2901
2902
        $result = [];
2903
        $T = $this->_tbase + $sindex % $this->_tcount;
2904
        $result[] = (int) ($this->_lbase + $sindex / $this->_ncount);
2905
        $result[] = (int) ($this->_vbase + ($sindex % $this->_ncount) / $this->_tcount);
2906
2907
        if ($T != $this->_tbase) {
2908
            $result[] = $T;
2909
        }
2910
2911
        return $result;
2912
    }
2913
2914
    /**
2915
     * Ccomposes a Hangul syllable
2916
     * (see http://www.unicode.org/unicode/reports/tr15/#Hangul).
2917
     *
2918
     * @param array $input Decomposed UCS4 sequence
2919
     * @return array       UCS4 sequence with syllables composed
2920
     * @access private
2921
     */
2922
    private function _hangulCompose($input)
2923
    {
2924
        $inp_len = count($input);
2925
2926
        if (!$inp_len) {
2927
            return [];
2928
        }
2929
2930
        $result = [];
2931
        $last = $input[0];
2932
        $result[] = $last; // copy first char from input to output
2933
2934
        for ($i = 1; $i < $inp_len; ++$i) {
2935
            $char = $input[$i];
2936
2937
            // Find out, wether two current characters from L and V
2938
            $lindex = $last - $this->_lbase;
2939
2940
            if (0 <= $lindex && $lindex < $this->_lcount) {
2941
                $vindex = $char - $this->_vbase;
2942
2943
                if (0 <= $vindex && $vindex < $this->_vcount) {
2944
                    // create syllable of form LV
2945
                    $last = ($this->_sbase + ($lindex * $this->_vcount + $vindex) * $this->_tcount);
2946
                    $out_off = count($result) - 1;
2947
                    $result[$out_off] = $last; // reset last
2948
2949
                    // discard char
2950
                    continue;
2951
                }
2952
            }
2953
2954
            // Find out, wether two current characters are LV and T
2955
            $sindex = $last - $this->_sbase;
2956
2957
            if (0 <= $sindex && $sindex < $this->_scount && ($sindex % $this->_tcount) == 0) {
2958
                $tindex = $char - $this->_tbase;
2959
2960
                if (0 <= $tindex && $tindex <= $this->_tcount) {
2961
                    // create syllable of form LVT
2962
                    $last += $tindex;
2963
                    $out_off = count($result) - 1;
2964
                    $result[$out_off] = $last; // reset last
2965
2966
                    // discard char
2967
                    continue;
2968
                }
2969
            }
2970
2971
            // if neither case was true, just add the character
2972
            $last = $char;
2973
            $result[] = $char;
2974
        }
2975
2976
        return $result;
2977
    }
2978
2979
    /**
2980
     * Returns the combining class of a certain wide char.
2981
     *
2982
     * @param int $char Wide char to check (32bit integer)
2983
     * @return int      Combining class if found, else 0
2984
     * @access private
2985
     */
2986
    private function _getCombiningClass($char)
2987
    {
2988
        return isset(self::$_np_norm_combcls[$char]) ? self::$_np_norm_combcls[$char] : 0;
2989
    }
2990
2991
    /**
2992
     * Apllies the cannonical ordering of a decomposed UCS4 sequence.
2993
     *
2994
     * @param array $input Decomposed UCS4 sequence
2995
     * @return array       Ordered USC4 sequence
2996
     * @access private
2997
     */
2998
    private function _applyCannonicalOrdering($input)
2999
    {
3000
        $swap = true;
3001
        $size = count($input);
3002
3003
        while ($swap) {
3004
            $swap = false;
3005
            $last = $this->_getCombiningClass($input[0]);
3006
3007
            for ($i = 0; $i < $size - 1; ++$i) {
3008
                $next = $this->_getCombiningClass($input[$i + 1]);
3009
3010
                if ($next != 0 && $last > $next) {
3011
                    // Move item leftward until it fits
3012
                    for ($j = $i + 1; $j > 0; --$j) {
3013
                        if ($this->_getCombiningClass($input[$j - 1]) <= $next) {
3014
                            break;
3015
                        }
3016
3017
                        $t = $input[$j];
3018
                        $input[$j] = $input[$j - 1];
3019
                        $input[$j - 1] = $t;
3020
                        $swap = 1;
3021
                    }
3022
3023
                    // Reentering the loop looking at the old character again
3024
                    $next = $last;
3025
                }
3026
3027
                $last = $next;
3028
            }
3029
        }
3030
3031
        return $input;
3032
    }
3033
3034
    /**
3035
     * Do composition of a sequence of starter and non-starter.
3036
     *
3037
     * @param array $input UCS4 Decomposed sequence
3038
     * @return array       Ordered USC4 sequence
3039
     * @access private
3040
     */
3041
    private function _combine($input)
3042
    {
3043
        $inp_len = count($input);
3044
3045
        // Is it a Hangul syllable?
3046
        if (1 != $inp_len) {
3047
            $hangul = $this->_hangulCompose($input);
3048
3049
            // This place is probably wrong
3050
            if (count($hangul) != $inp_len) {
3051
                return $hangul;
3052
            }
3053
        }
3054
3055
        foreach (self::$_np_replacemaps as $np_src => $np_target) {
3056
            if ($np_target[0] != $input[0]) {
3057
                continue;
3058
            }
3059
3060
            if (count($np_target) != $inp_len) {
3061
                continue;
3062
            }
3063
3064
            $hit = false;
3065
3066
            foreach ($input as $k2 => $v2) {
3067
                if ($v2 == $np_target[$k2]) {
3068
                    $hit = true;
3069
                } else {
3070
                    $hit = false;
3071
                    break;
3072
                }
3073
            }
3074
3075
            if ($hit) {
3076
                return $np_src;
3077
            }
3078
        }
3079
3080
        return false;
3081
    }
3082
3083
    /**
3084
     * This converts an UTF-8 encoded string to its UCS-4 (array) representation
3085
     * By talking about UCS-4 we mean arrays of 32bit integers representing
3086
     * each of the "chars". This is due to PHP not being able to handle strings with
3087
     * bit depth different from 8. This applies to the reverse method _ucs4_to_utf8(), too.
3088
     * The following UTF-8 encodings are supported:
3089
     * bytes bits  representation
3090
     * 1        7  0xxxxxxx
3091
     * 2       11  110xxxxx 10xxxxxx
3092
     * 3       16  1110xxxx 10xxxxxx 10xxxxxx
3093
     * 4       21  11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
3094
     * 5       26  111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
3095
     * 6       31  1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
3096
     * Each x represents a bit that can be used to store character data.
3097
     *
3098
     * @param string $input utf8-encoded string
3099
     * @throws Exception
3100
     * @return array        ucs4-encoded array
3101
     * @access private
3102
     */
3103
    private function _utf8_to_ucs4($input)
3104
    {
3105
        $output = [];
3106
        $out_len = 0;
3107
        $inp_len = self::_byteLength($input, '8bit');
0 ignored issues
show
Unused Code introduced by
The call to Net_IDNA2::_byteLength() has too many arguments starting with '8bit'.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
3108
        $mode = 'next';
3109
        $test = 'none';
3110
        for ($k = 0; $k < $inp_len; ++$k) {
3111
            $v = ord($input{$k}); // Extract byte from input string
3112
3113
            if ($v < 128) { // We found an ASCII char - put into stirng as is
3114
                $output[$out_len] = $v;
3115
                ++$out_len;
3116
                if ('add' == $mode) {
3117
                    throw new UnexpectedValueException(
3118
                        'Conversion from UTF-8 to UCS-4 failed: malformed input at byte ' . $k
3119
                    );
3120
                }
3121
                continue;
3122
            }
3123
            if ('next' == $mode) { // Try to find the next start byte; determine the width of the Unicode char
3124
                $start_byte = $v;
3125
                $mode = 'add';
3126
                $test = 'range';
3127
                if ($v >> 5 == 6) { // &110xxxxx 10xxxxx
3128
                    $next_byte = 0; // Tells, how many times subsequent bitmasks must rotate 6bits to the left
3129
                    $v = ($v - 192) << 6;
3130 View Code Duplication
                } elseif ($v >> 4 == 14) { // &1110xxxx 10xxxxxx 10xxxxxx
3131
                    $next_byte = 1;
3132
                    $v = ($v - 224) << 12;
3133
                } elseif ($v >> 3 == 30) { // &11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
3134
                    $next_byte = 2;
3135
                    $v = ($v - 240) << 18;
3136 View Code Duplication
                } elseif ($v >> 2 == 62) { // &111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
3137
                    $next_byte = 3;
3138
                    $v = ($v - 248) << 24;
3139
                } elseif ($v >> 1 == 126) { // &1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
3140
                    $next_byte = 4;
3141
                    $v = ($v - 252) << 30;
3142
                } else {
3143
                    throw new UnexpectedValueException('This might be UTF-8, but I don\'t understand it at byte ' . $k);
3144
                }
3145
                if ('add' == $mode) {
3146
                    $output[$out_len] = (int) $v;
3147
                    ++$out_len;
3148
                    continue;
3149
                }
3150
            }
3151
            if ('add' == $mode) {
3152
                if (!$this->_allow_overlong && $test == 'range') {
3153
                    $test = 'none';
3154
                    if (($v < 0xA0 && $start_byte == 0xE0) || ($v < 0x90 && $start_byte == 0xF0) || ($v > 0x8F && $start_byte == 0xF4)) {
0 ignored issues
show
Bug introduced by
The variable $start_byte does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
3155
                        throw new OutOfRangeException(
3156
                            'Bogus UTF-8 character detected (out of legal range) at byte ' . $k
3157
                        );
3158
                    }
3159
                }
3160
                if ($v >> 6 == 2) { // Bit mask must be 10xxxxxx
3161
                    $v = ($v - 128) << ($next_byte * 6);
0 ignored issues
show
Bug introduced by
The variable $next_byte does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
3162
                    $output[($out_len - 1)] += $v;
3163
                    --$next_byte;
3164
                } else {
3165
                    throw new UnexpectedValueException(
3166
                        'Conversion from UTF-8 to UCS-4 failed: malformed input at byte ' . $k
3167
                    );
3168
                }
3169
                if ($next_byte < 0) {
3170
                    $mode = 'next';
3171
                }
3172
            }
3173
        } // for
3174
        return $output;
3175
    }
3176
3177
    /**
3178
     * Convert UCS-4 array into UTF-8 string
3179
     *
3180
     * @param array $input ucs4-encoded array
3181
     * @throws Exception
3182
     * @return string      utf8-encoded string
3183
     * @access private
3184
     */
3185
    private function _ucs4_to_utf8($input)
3186
    {
3187
        $output = '';
3188
3189
        foreach ($input as $v) {
3190
            // $v = ord($v);
3191
3192
            if ($v < 128) {
3193
                // 7bit are transferred literally
3194
                $output .= chr($v);
3195
            } else {
3196
                if ($v < 1 << 11) {
3197
                    // 2 bytes
3198
                    $output .= chr(192 + ($v >> 6))
3199
                        . chr(128 + ($v & 63));
3200
                } else {
3201
                    if ($v < 1 << 16) {
3202
                        // 3 bytes
3203
                        $output .= chr(224 + ($v >> 12))
3204
                            . chr(128 + (($v >> 6) & 63))
3205
                            . chr(128 + ($v & 63));
3206
                    } else {
3207
                        if ($v < 1 << 21) {
3208
                            // 4 bytes
3209
                            $output .= chr(240 + ($v >> 18))
3210
                                . chr(128 + (($v >> 12) & 63))
3211
                                . chr(128 + (($v >> 6) & 63))
3212
                                . chr(128 + ($v & 63));
3213
                        } else {
3214
                            if ($v < 1 << 26) {
3215
                                // 5 bytes
3216
                                $output .= chr(248 + ($v >> 24))
3217
                                    . chr(128 + (($v >> 18) & 63))
3218
                                    . chr(128 + (($v >> 12) & 63))
3219
                                    . chr(128 + (($v >> 6) & 63))
3220
                                    . chr(128 + ($v & 63));
3221
                            } else {
3222
                                if ($v < 1 << 31) {
3223
                                    // 6 bytes
3224
                                    $output .= chr(252 + ($v >> 30))
3225
                                        . chr(128 + (($v >> 24) & 63))
3226
                                        . chr(128 + (($v >> 18) & 63))
3227
                                        . chr(128 + (($v >> 12) & 63))
3228
                                        . chr(128 + (($v >> 6) & 63))
3229
                                        . chr(128 + ($v & 63));
3230
                                } else {
3231
                                    throw new UnexpectedValueException(
3232
                                        'Conversion from UCS-4 to UTF-8 failed: malformed input'
3233
                                    );
3234
                                }
3235
                            }
3236
                        }
3237
                    }
3238
                }
3239
            }
3240
        }
3241
3242
        return $output;
3243
    }
3244
3245
    /**
3246
     * Convert UCS-4 array into UCS-4 string
3247
     *
3248
     * @param array $input ucs4-encoded array
3249
     * @throws Exception
3250
     * @return string      ucs4-encoded string
3251
     * @access private
3252
     */
3253
    private function _ucs4_to_ucs4_string($input)
3254
    {
3255
        $output = '';
3256
        // Take array values and split output to 4 bytes per value
3257
        // The bit mask is 255, which reads &11111111
3258
        foreach ($input as $v) {
3259
            $output .= ($v & (255 << 24) >> 24) . ($v & (255 << 16) >> 16) . ($v & (255 << 8) >> 8) . ($v & 255);
3260
        }
3261
3262
        return $output;
3263
    }
3264
3265
    /**
3266
     * Convert UCS-4 string into UCS-4 array
3267
     *
3268
     * @param string $input ucs4-encoded string
3269
     * @throws InvalidArgumentException
3270
     * @return array        ucs4-encoded array
3271
     * @access private
3272
     */
3273
    private function _ucs4_string_to_ucs4($input)
3274
    {
3275
        $output = [];
3276
3277
        $inp_len = self::_byteLength($input);
3278
        // Input length must be dividable by 4
3279
        if ($inp_len % 4) {
3280
            throw new InvalidArgumentException('Input UCS4 string is broken');
3281
        }
3282
3283
        // Empty input - return empty output
3284
        if (!$inp_len) {
3285
            return $output;
3286
        }
3287
3288
        for ($i = 0, $out_len = -1; $i < $inp_len; ++$i) {
3289
            // Increment output position every 4 input bytes
3290
            if (!$i % 4) {
3291
                $out_len++;
3292
                $output[$out_len] = 0;
3293
            }
3294
            $output[$out_len] += ord($input{$i}) << (8 * (3 - ($i % 4)));
3295
        }
3296
3297
        return $output;
3298
    }
3299
3300
    /**
3301
     * Echo hex representation of UCS4 sequence.
3302
     *
3303
     * @param array $input UCS4 sequence
3304
     * @param bool $include_bit Include bitmask in output
3305
     * @static
3306
     * @access private
3307
     */
3308
    private static function _showHex($input, $include_bit = false): void
0 ignored issues
show
Unused Code introduced by
This method is not used, and could be removed.
Loading history...
3309
    {
3310
        foreach ($input as $k => $v) {
3311
            echo '[', $k, '] => ', sprintf('%X', $v);
3312
3313
            if ($include_bit) {
3314
                echo ' (', self::_showBitmask($v), ')';
3315
            }
3316
3317
            echo "\n";
3318
        }
3319
    }
3320
3321
    /**
3322
     * Gives you a bit representation of given Byte (8 bits), Word (16 bits) or DWord (32 bits)
3323
     * Output width is automagically determined
3324
     *
3325
     * @param int $octet ...
3326
     * @return string    Bitmask-representation
3327
     * @static
3328
     * @access private
3329
     */
3330
    private static function _showBitmask($octet)
3331
    {
3332
        if ($octet >= (1 << 16)) {
3333
            $w = 31;
3334
        } else {
3335
            if ($octet >= (1 << 8)) {
3336
                $w = 15;
3337
            } else {
3338
                $w = 7;
3339
            }
3340
        }
3341
3342
        $return = '';
3343
3344
        for ($i = $w; $i > -1; $i--) {
3345
            $return .= ($octet & (1 << $i)) ? '1' : '0';
3346
        }
3347
3348
        return $return;
3349
    }
3350
3351
    /**
3352
     * Gets the length of a string in bytes even if mbstring function
3353
     * overloading is turned on
3354
     *
3355
     * @param string $string the string for which to get the length.
3356
     * @return int the length of the string in bytes.
3357
     * @see Net_IDNA2::$_mb_string_overload
3358
     */
3359
    private static function _byteLength($string)
3360
    {
3361
        if (self::$_mb_string_overload) {
3362
            return mb_strlen($string, '8bit');
3363
        }
3364
3365
        return strlen((binary) $string);
3366
    }
3367
3368
    // }}}}
3369
3370
    // {{{ factory
3371
3372
    /**
3373
     * Attempts to return a concrete IDNA instance for either php4 or php5.
3374
     *
3375
     * @param array $params Set of paramaters
3376
     * @return Net_IDNA2
3377
     * @access public
3378
     */
3379
    public function getInstance($params = [])
3380
    {
3381
        return new self($params);
3382
    }
3383
3384
    // }}}
3385
3386
    // {{{ singleton
3387
3388
    /**
3389
     * Attempts to return a concrete IDNA instance for either php4 or php5,
3390
     * only creating a new instance if no IDNA instance with the same
3391
     * parameters currently exists.
3392
     *
3393
     * @param array $params Set of paramaters
3394
     * @return object Net_IDNA2
3395
     * @access public
3396
     */
3397
    public function singleton($params = [])
3398
    {
3399
        static $instances;
3400
        if (!isset($instances)) {
3401
            $instances = [];
3402
        }
3403
3404
        $signature = serialize($params);
3405
        if (!isset($instances[$signature])) {
3406
            $instances[$signature] = self::getInstance($params);
3407
        }
3408
3409
        return $instances[$signature];
3410
    }
3411
3412
    // }}}
3413
}
3414