Completed
Push — master ( 6b50ab...b8b3a4 )
by Ronan
01:56
created

Ssn::initialize()   B

Complexity

Conditions 4
Paths 4

Size

Total Lines 25
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 1
Metric Value
c 1
b 0
f 1
dl 0
loc 25
rs 8.5806
cc 4
eloc 12
nc 4
nop 0
1
<?php
2
3
namespace IsoCodes;
4
5
/**
6
 * @example :
7
 * // Generate a SSN for California
8
 * echo "\n" . Ssn::generate('AK');
9
 * echo "\n" . Ssn::generate('AL');
10
 * echo "\n" . Ssn::generate('AR');
11
 * echo "\n" . Ssn::generate('AZ');
12
 * echo "\n" . Ssn::generate('CA');
13
 * echo "\n" . Ssn::generate('CO');
14
 * echo '--';
15
 * // Validate a SSN
16
 * echo Ssn::validate('557-26-9048');
17
 *
18
 * @source  : http://haxorfreek.15.forumer.com/a/us-social-security-number-ssn-generator_post1847.html
19
 */
20
class Ssn implements IsoCodeInterface
21
{
22
    protected static $initialized = false;
23
24
    // Populate this variable with the high group list provided by the Social Security Administration:
25
    // http://www.ssa.gov/employer/ssnvhighgroup.htm
26
27
    // We only want the numbers. Omit the explanatory text at the beginning of the file.
28
    // This list is from September 2007
29
    public static $highgroup = <<<EOT
30
001 06 002 04	003 04	004 08	005 08	006 08
31
        007 06 008 90	009 90 010 90 011 90 012 90
32
        013 90	014 90	015 90	016 90	017 90	018 90
33
        019 90	020 90	021 90	022 90	023 90	024 90
34
        025 90	026 90	027 90*	028 88	029 88	030 88
35
        031 88	032 88 033 88	034 88	035 72	036 72
36
        037 72 038 72	039 70	040 11	041 11 042 11
37
        043 11	044 11	045 11	046 11	047 11	048 08
38
        049 08	050 96	051 96	052 96	053 96	054 96
39
        055 96	056 96	057 96	058 96	059 96 060 96
40
        061 96 062 96 063 96 064 96 065 96	066 96
41
        067 96	068 96	069 96	070 96	071 96	072 96
42
        073 96	074 96	075 96	076 96	077 96	078 96
43
        079 96	080 96	081 96	082 96	083 96	084 96
44
        085 96	086 96	087 96	088 96	089 96	090 96
45
        091 96	092 96	093 96	094 96	095 96	096 96
46
        097 96	098 96	099 96	100 96	101 96	102 96
47
        103 96	104 96	105 96	106 96	107 96	108 96
48
        109 96	110 96*	111 96*	112 96*	113 96* 114 94
49
        115 94 116 94 117 94 118 94 119 94	120 94
50
        121 94	122 94	123 94	124 94 125 94 126 94
51
        127 94 128 94	129 94	130 94	131 94	132 94
52
        133 94	134 94	135 19	136 19	137 19	138 19
53
        139 19	140 19	141 19	142 19* 143 19*	144 17
54
        145 17	146 17	147 17	148 17	149 17	150 17
55
        151 17	152 17	153 17	154 17 155 17	156 17
56
        157 17	158 17	159 84	160 84	161 84	162 84
57
        163 84	164 84	165 84	166 84	167 84	168 84
58
        169 84	170 84	171 84	172 84	173 84	174 84
59
        175 84	176 84	177 84	178 84*	179 84*	180 82
60
        181 82	182 82	183 82	184 82	185 82	186 82
61
        187 82	188 82	189 82	190 82	191 82	192 82
62
        193 82	194 82	195 82 196 82	197 82	198 82
63
        199 82 200 82	201 82	202 82	203 82	204 82
64
        205 82	206 82	207 82	208 82	209 82	210 82
65
        211 82	212 79	213 79	214 79	215 79	216 79
66
        217 79*	218 77	219 77	220 77	221 06	222 04
67
        223 99	224 99 225 99 226 99 227 99	228 99
68
        229 99	230 99	231 99	232 53	233 53	234 53
69
        235 53	236 53	237 99	238 99	239 99	240 99
70
        241 99	242 99	243 99 244 99 245 99	246 99
71
        247 99	248 99	249 99	250 99	251 99	252 99
72
        253 99	254 99	255 99	256 99	257 99	258 99
73
        259 99	260 99	261 99	262 99	263 99	264 99
74
        265 99	266 99	267 99	268 13	269 13	270 13
75
        271 13 272 13 273 13	274 13	275 13	276 13
76
        277 13	278 13	279 13	280 13	281 13	282 13
77
        283 13	284 13*	285 13*	286 11 287 11	288 11
78
        289 11 290 11	291 11	292 11	293 11	294 11
79
        295 11	296 11	297 11	298 11	299 11	300 11
80
        301 11	302 11	303 33*	304 31	305 31	306 31
81
        307 31	308 31	309 31	310 31 311 31	312 31
82
        313 31	314 31 315 31	316 31	317 31	318 06
83
        319 06	320 06	321 06	322 06	323 06	324 06
84
        325 06	326 06	327 06	328 06	329 06	330 06
85
        331 06 332 06	333 06	334 06 335 06 336 06
86
        337 06	338 06	339 06	340 06	341 06	342 06
87
        343 06	344 06	345 06	346 06	347 06*	348 06*
88
        349 04	350 04	351 04 352 04 353 04	354 04
89
        355 04	356 04	357 04	358 04	359 04	360 04
90
        361 04	362 35	363 35*	364 35*	365 33	366 33
91
        367 33	368 33	369 33	370 33	371 33	372 33
92
        373 33	374 33	375 33	376 33	377 33 378 33
93
        379 33	380 33	381 33	382 33	383 33	384 33
94
        385 33	386 33 387 29	388 29	389 29	390 29
95
        391 29	392 29	393 27 394 27	395 27	396 27
96
        397 27	398 27	399 27	400 67	401 67 402 67
97
        403 67	404 67	405 67	406 67*	407 65	408 99
98
        409 99	410 99	411 99 412 99	413 99	414 99
99
        415 99	416 61	417 61	418 61	419 61 420 61
100
        421 61	422 61	423 61	424 61	425 99	426 99
101
        427 99	428 99	429 99	430 99	431 99	432 99
102
        433 99	434 99	435 99	436 99	437 99	438 99
103
        439 99 440 23	441 23 442 23	443 23	444 23
104
        445 23	446 23*	447 21	448 21	449 99	450 99
105
        451 99	452 99	453 99	454 99	455 99	456 99
106
        457 99	458 99	459 99	460 99	461 99	462 99
107
        463 99	464 99	465 99	466 99	467 99	468 51
108
        469 51*	470 49	471 49	472 49	473 49	474 49
109
        475 49	476 49	477 49	478 37	479 37	480 37
110
        481 37 482 37	483 37	484 37	485 35	486 25
111
        487 25	488 25 489 25	490 25	491 25	492 25
112
        493 25	494 25	495 25*	496 23	497 23	498 23
113
        499 23 500 23	501 33	502 33*	503 41 504 39
114
        505 53	506 51	507 51 508 51	509 27	510 27
115
        511 27	512 27	513 27 514 27	515 27*	516 45
116
        517 43 518 77	519 77 520 53 521 99	522 99
117
        523 99	524 99	525 99	526 99	527 99	528 99
118
        529 99	530 99	531 63*	532 61	533 61	534 61
119
        535 61 536 61	537 61	538 61 539 61*	540 73
120
        541 73	542 73	543 73	544 73	545 99	546 99
121
        547 99	548 99	549 99	550 99	551 99	552 99
122
        553 99	554 99	555 99	556 99	557 99	558 99
123
        559 99	560 99	561 99	562 99	563 99	564 99
124
        565 99	566 99	567 99	568 99	569 99	570 99
125
        571 99	572 99	573 99	574 49	575 99	576 99
126
        577 45	578 45* 579 43	580 37	581 99	582 99
127
        583 99	584 99	585 99	586 61	587 99 588 03
128
        589 99 590 99	591 99	592 99	593 99 594 99
129
        595 99 596 84	597 84	598 84*	599 82	600 99
130
        601 99 602 65	603 65	604 65	605 65	606 65
131
        607 65* 608 65*	609 65*	610 65*	611 65*	612 65*
132
        613 65* 614 65*	615 63	616 63	617 63	618 63
133
        619 63 620 63	621 63	622 63	623 63	624 63
134
        625 63 626 63	627 11	628 11	629 11	630 11
135
        631 11* 632 11*	633 11*	634 11*	635 11* 636 11*
136
        637 08 638 08	639 08	640 08 641 08	642 08
137
        643 08 644 08	645 08	646 96 647 94	648 44
138
        649 44 650 46 651 46*	652 44 653 44	654 26
139
        655 26 656 26	657 26	658 24	659 16 660 16*
140
        661 14 662 14 663 14 664 14	665 14	667 34
141
        668 34 669 34 670 34	671 34	672 34*	673 34*
142
        674 32 675 32 676 14	677 14* 678 12 679 12
143
        680 90* 681 14*	682 12 683 12 684 12	685 12
144
        686 12 687 12	688 12	689 12	690 12	691 07
145
        692 07 693 07 694 07	695 07 696 07 697 07
146
        698 07 699 07* 700 18	701 18 702 18 703 18
147
        704 18 705 18 706 18	707 18 708 18 709 18
148
        710 18 711 18	712 18	713 18 714 18 715 18
149
        716 18 717 18	718 18	719 18 720 18 721 18
150
        722 18 723 18	724 28	725 18 726 18 727 10
151
        728 14 729 10	730 10	731 10 732 09	733 09
152
        750 09 751 07	752 01 753 01 756 05 757 05
153
        758 05 759 05 760 05* 761 03 762 03 763 03
154
        764 80 765 80*	766 64* 767 64*	768 62 769 62
155
        770 62 771 62 772 62*
156
EOT;
157
158
    // This information is obtained from:
159
    // http://www.ssa.gov/employer/stateweb.htm
160
    public static $statePrefixes = array(
161
        'AK' => array(574),
162
        'AL' => array(416, 417, 418, 419, 420, 421, 422, 423, 424),
163
        'AR' => array(429, 430, 431, 432, 676, 677, 678, 679),
164
        'AZ' => array(526, 527, 600, 601, 764, 765),
165
        'CA' => array(545, 546, 547, 548, 549, 550, 551, 552, 553, 554, 555, 556, 557, 558, 559, 560, 561, 562, 563, 564, 565, 566, 567, 568, 569, 570, 571, 572, 573, 602, 603, 604, 605, 606, 607, 608, 609, 610, 611, 612, 613, 614, 615, 616, 617, 618, 619, 620, 621, 622, 623, 624, 625, 626),
166
        'CO' => array(521, 522, 523, 524, 650, 651, 652, 653),
167
        'CT' => array(40, 41, 42, 43, 44, 45, 46, 47, 48, 49),
168
        'DC' => array(577, 578, 579),
169
        'DE' => array(221, 222),
170
        'FL' => array(261, 262, 263, 264, 265, 266, 267, 589, 590, 591, 592, 593, 594, 595, 766, 767, 768, 769, 770, 771, 772),
171
        'GA' => array(252, 253, 254, 255, 256, 257, 258, 259, 260, 667, 668, 669, 670, 671, 672, 673, 674, 675),
172
        'HI' => array(575, 576, 750, 751),
173
        'IA' => array(478, 479, 480, 481, 482, 483, 484, 485),
174
        'ID' => array(518, 519),
175
        'IL' => array(318, 319, 320, 321, 322, 323, 324, 325, 326, 327, 328, 329, 330, 331, 332, 333, 334, 335, 336, 337, 338, 339, 340, 341, 342, 343, 344, 345, 346, 347, 348, 349, 350, 351, 352, 353, 354, 355, 356, 357, 358, 359, 360, 361),
176
        'IN' => array(303, 304, 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, 315, 316, 317),
177
        'KS' => array(509, 510, 511, 512, 513, 514, 515),
178
        'KY' => array(400, 401, 402, 403, 404, 405, 406, 407),
179
        'LA' => array(433, 434, 435, 436, 437, 438, 439, 659, 660, 661, 662, 663, 664, 665),
180
        'MA' => array(10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34),
181
        'MD' => array(212, 213, 214, 215, 216, 217, 218, 219, 220),
182
        'ME' => array(4, 5, 6, 7),
183
        'MI' => array(362, 363, 364, 365, 366, 367, 368, 369, 370, 371, 372, 373, 374, 375, 376, 377, 378, 379, 380, 381, 382, 383, 384, 385, 386),
184
        'MN' => array(468, 469, 470, 471, 472, 473, 474, 475, 476, 477),
185
        'MO' => array(486, 487, 488, 489, 490, 491, 492, 493, 494, 495, 496, 497, 498, 499, 500),
186
        'MS' => array(425, 426, 427, 428, 587),
187
        'MT' => array(516, 517),
188
        'NC' => array(237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 681, 682, 683, 684, 685, 686, 687, 688, 689, 690),
189
        'ND' => array(501, 502),
190
        'NE' => array(505, 506, 507, 508),
191
        'NH' => array(1, 2, 3),
192
        'NJ' => array(135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158),
193
        'NM' => array(525, 585, 648, 649),
194
        'NV' => array(530, 680),
195
        'NY' => array(50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134),
196
        'OH' => array(268, 269, 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, 295, 296, 297, 298, 299, 300, 301, 302),
197
        'OK' => array(440, 441, 442, 443, 444, 445, 446, 447, 448),
198
        'OR' => array(540, 541, 542, 543, 544),
199
        'PA' => array(159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211),
200
        'RI' => array(35, 36, 37, 38, 39),
201
        'SC' => array(247, 248, 249, 250, 251, 654, 655, 656, 657, 658),
202
        'SD' => array(503, 504),
203
        'TN' => array(408, 409, 410, 411, 412, 413, 414, 415, 756, 757, 758, 759, 760, 761, 762, 763),
204
        'TX' => array(449, 450, 451, 452, 453, 454, 455, 456, 457, 458, 459, 460, 461, 462, 463, 464, 465, 466, 467, 627, 628, 629, 630, 631, 632, 633, 634, 635, 636, 637, 638, 639, 640, 641, 642, 643, 644, 645),
205
        'UT' => array(528, 529, 646, 647),
206
        'VA' => array(223, 224, 225, 226, 227, 228, 229, 230, 231, 691, 692, 693, 694, 695, 696, 697, 698, 699),
207
        'VT' => array(8, 9),
208
        'WA' => array(531, 532, 533, 534, 535, 536, 537, 538, 539),
209
        'WI' => array(387, 388, 389, 390, 391, 392, 393, 394, 395, 396, 397, 398, 399),
210
        'WV' => array(232, 233, 234, 235, 236),
211
        'WY' => array(520),
212
    );
213
214
    public static $states = array('AK', 'AL', 'AR', 'AZ', 'CA', 'CO', 'CT', 'DC', 'DE', 'FL', 'GA', 'HI', 'IA', 'ID', 'IL', 'IN', 'KS', 'KY', 'LA', 'MA', 'MD', 'ME', 'MI', 'MN', 'MO', 'MS', 'MT', 'NC', 'ND', 'NE', 'NH', 'NJ', 'NM', 'NV', 'NY', 'OH', 'OK', 'OR', 'PA', 'RI', 'SC', 'SD', 'TN', 'TX', 'UT', 'VA', 'VT', 'WA', 'WI', 'WV', 'WY');
215
216
    // The SSA uses a funky method of figuring out what group number to use next. This area has them in the proper order and makes it easier to generate a SSN.
217
    public static $possibleGroups = array(1, 3, 5, 7, 9, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62, 64, 66, 68, 70, 72, 74, 76, 78, 80, 82, 84, 86, 88, 90, 92, 94, 96, 98, 2, 4, 6, 8, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29, 31, 33, 35, 37, 39, 41, 43, 45, 47, 49, 51, 53, 55, 57, 59, 61, 63, 65, 67, 69, 71, 73, 75, 77, 79, 81, 83, 85, 87, 89, 91, 93, 95, 97, 99);
218
219
    public function __construct()
220
    {
221
        @trigger_error('Instantiating Ssn validator is deprecated since version 1.3. Use Ssn::validate($ssn) instead.', E_USER_DEPRECATED);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
222
    }
223
224
    /**
225
     * Cleans the high group number list so it is useful.
226
     */
227
    protected static function initialize()
228
    {
229
        $highgroup = static::$highgroup;
230
231
        // Trim the high group list and remove asterisks, fix space/tabs, and replace new lines with tabs.
232
        // The data isn't formatted well so we have to do quite a bit of random replacing.
233
        $highgroup = trim((string) $highgroup);
234
        $highgroup = str_replace(array('*', " \t", "\n", ' '), array('', "\t", "\t", "\t"), $highgroup);
235
236
        // Explode on tab. This should give us an array of prefixes and group numbers, IE '203 82', '204 82', etc
237
        $highgroup = explode("\t", $highgroup);
238
239
        // Make array useful by splitting the prefix and group number
240
        // We also convert the string to an int for easier handling later down the road
241
        $cleangroup = array();
242
        foreach ($highgroup as $value) {
243
            if (trim($value) != '') {
244
                $temp = explode(' ', $value);
245
                if (isset($temp[1])) {
246
                    $cleangroup[(int) trim($temp[0])] = (int) trim($temp[1]);
247
                    static::$highgroup                = (string) $cleangroup;
248
                }
249
            }
250
        }
251
    }
252
253
    /**
254
     * Generate an SSN based on state.
255
     *
256
     * @param mixed  $state
257
     * @param string $separator
258
     *
259
     * @return false|string (false: bad state found)
260
     */
261
    public static function generate($state = false, $separator = '-')
262
    {
263
        if (!static::$initialized) {
264
            static::initialize();
265
            static::$initialized = true;
266
        }
267
268
        $states         = static::$states;
269
        $statePrefixes  = static::$statePrefixes;
270
        $highgroup      = static::$highgroup;
271
        $possibleGroups = static::$possibleGroups;
272
273
        if ($state === false) {
274
            $state = $states[mt_rand(0, count($states) - 1)];
275
        }
276
277
        $state = strtoupper($state);
278
279
        // Sanity check: is this a valid state?
280
        if (!isset($statePrefixes[$state])) {
281
            return false;
282
        }
283
284
        // Generate area number
285
        $area = $statePrefixes[$state][array_rand($statePrefixes[$state])];
286
287
        // Generate group number
288
        $group = $possibleGroups[mt_rand(0, array_search($highgroup[$area], $possibleGroups))]; // Generate valid group number
289
290
        // Generate last four
291
        $lastfour = sprintf('%04s', trim(mt_rand(0, 9999)));
292
293
        return sprintf('%03s', $area).$separator.sprintf('%02s', $group).$separator.$lastfour;
294
    }
295
296
    /**
297
     * Validate a SSN.
298
     *
299
     * @param mixed $ssn
300
     *
301
     * @return bool : false, or two letter state abbreviation if it is valid
302
     */
303
    public static function validate($ssn)
304
    {
305
        if (!static::$initialized) {
306
            static::initialize();
307
            static::$initialized = true;
308
        }
309
310
        if (!is_string($ssn)) {
311
            return false;
312
        }
313
        if (trim($ssn) === '') {
314
            return false;
315
        }
316
        $statePrefixes  = static::$statePrefixes;
317
        $highgroup      = static::$highgroup;
318
        $possibleGroups = static::$possibleGroups;
319
320
        // Split up the SSN
321
        // If not 9 or 11 long, then return false
322
        $length = strlen($ssn);
323
        if ($length == 9) {
324
            $areaNumber  = substr($ssn, 0, 3);
325
            $groupNumber = substr($ssn, 3, 2);
326
            $lastFour    = substr($ssn, 5);
327
        } elseif ($length == 11) {
328
            $areaNumber  = substr($ssn, 0, 3);
329
            $groupNumber = substr($ssn, 4, 2);
330
            $lastFour    = substr($ssn, 7);
331
        } else {
332
            return false;
333
        }
334
335
        // Strip leading zeros
336
        $areaNumber  = ltrim($areaNumber, 0);
337
        $groupNumber = ltrim($groupNumber, 0);
338
339
        // Check if parts are numeric
340
        if (!is_numeric($areaNumber) || !is_numeric($groupNumber) || !is_numeric($lastFour)) {
341
            return false;
342
        }
343
344
        foreach ($statePrefixes as $numbers) {
345
            // Search for the area number in the state list
346
            if (in_array($areaNumber, $numbers)) {
347
                // Make sure the group number is valid
348
                if (array_search($highgroup[$areaNumber], $possibleGroups) >= array_search($groupNumber, $possibleGroups)) {
349
                    //return $state => must use "as $state => numbers" in the foreach loop;
350
                    return true;
351
                } else {
352
                    return false;
353
                }
354
            }
355
        }
356
357
        return false;
358
    }
359
}
360