1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace libphonenumber\Tests\core; |
4
|
|
|
|
5
|
|
|
use libphonenumber\CountryCodeSource; |
6
|
|
|
use libphonenumber\CountryCodeToRegionCodeMapForTesting; |
7
|
|
|
use libphonenumber\Leniency; |
8
|
|
|
use libphonenumber\PhoneNumber; |
9
|
|
|
use libphonenumber\PhoneNumberMatch; |
10
|
|
|
use libphonenumber\PhoneNumberMatcher; |
11
|
|
|
use libphonenumber\PhoneNumberUtil; |
12
|
|
|
use libphonenumber\RegionCode; |
13
|
|
|
|
14
|
|
|
class PhoneNumberMatcherTest extends \PHPUnit_Framework_TestCase |
15
|
|
|
{ |
16
|
|
|
/** |
17
|
|
|
* @var PhoneNumberUtil |
18
|
|
|
*/ |
19
|
|
|
protected $phoneUtil; |
20
|
|
|
|
21
|
|
|
public function setUp() |
22
|
|
|
{ |
23
|
|
|
PhoneNumberUtil::resetInstance(); |
24
|
|
|
$this->phoneUtil = PhoneNumberUtil::getInstance( |
25
|
|
|
PhoneNumberUtilTest::TEST_META_DATA_FILE_PREFIX, |
26
|
|
|
CountryCodeToRegionCodeMapForTesting::$countryCodeToRegionCodeMapForTesting |
27
|
|
|
); |
28
|
|
|
} |
29
|
|
|
|
30
|
|
|
public function testContainsMoreThanOneSlashInNationalNumber() |
31
|
|
|
{ |
32
|
|
|
// A date should return true. |
33
|
|
|
$number = new PhoneNumber(); |
34
|
|
|
$number->setCountryCode(1); |
35
|
|
|
$number->setCountryCodeSource(CountryCodeSource::FROM_DEFAULT_COUNTRY); |
36
|
|
|
$candidate = "1/05/2013"; |
37
|
|
|
$this->assertTrue(PhoneNumberMatcher::containsMoreThanOneSlashInNationalNumber($number, $candidate)); |
38
|
|
|
|
39
|
|
|
// Here, the country code source thinks it started with a country calling code, but this is not |
40
|
|
|
// the same as the part before the slash, so it's still true. |
41
|
|
|
$number = new PhoneNumber(); |
42
|
|
|
$number->setCountryCodeSource(274); |
43
|
|
|
$number->setCountryCodeSource(CountryCodeSource::FROM_NUMBER_WITHOUT_PLUS_SIGN); |
44
|
|
|
$candidate = "27/4/2013"; |
45
|
|
|
$this->assertTrue(PhoneNumberMatcher::containsMoreThanOneSlashInNationalNumber($number, $candidate)); |
46
|
|
|
|
47
|
|
|
// Now it should be false, because the first slash is after the country calling code. |
48
|
|
|
$number = new PhoneNumber(); |
49
|
|
|
$number->setCountryCode(49); |
50
|
|
|
$number->setCountryCodeSource(CountryCodeSource::FROM_NUMBER_WITH_PLUS_SIGN); |
51
|
|
|
$candidate = "49/69/2013"; |
52
|
|
|
$this->assertFalse(PhoneNumberMatcher::containsMoreThanOneSlashInNationalNumber($number, $candidate)); |
53
|
|
|
|
54
|
|
|
$number = new PhoneNumber(); |
55
|
|
|
$number->setCountryCode(49); |
56
|
|
|
$number->setCountryCodeSource(CountryCodeSource::FROM_NUMBER_WITHOUT_PLUS_SIGN); |
57
|
|
|
$candidate = "+49/69/2013"; |
58
|
|
|
$this->assertFalse(PhoneNumberMatcher::containsMoreThanOneSlashInNationalNumber($number, $candidate)); |
59
|
|
|
|
60
|
|
|
$candidate = "+ 49/69/2013"; |
61
|
|
|
$this->assertFalse(PhoneNumberMatcher::containsMoreThanOneSlashInNationalNumber($number, $candidate)); |
62
|
|
|
|
63
|
|
|
$candidate = "+ 49/69/20/13"; |
64
|
|
|
$this->assertTrue(PhoneNumberMatcher::containsMoreThanOneSlashInNationalNumber($number, $candidate)); |
65
|
|
|
|
66
|
|
|
// Here, the first group is not assumed to be the country calling code, even though it is the |
67
|
|
|
// same as it, so this should return true. |
68
|
|
|
$number = new PhoneNumber(); |
69
|
|
|
$number->setCountryCode(49); |
70
|
|
|
$number->setCountryCodeSource(CountryCodeSource::FROM_DEFAULT_COUNTRY); |
71
|
|
|
$candidate = "49/69/2013"; |
72
|
|
|
$this->assertTrue(PhoneNumberMatcher::containsMoreThanOneSlashInNationalNumber($number, $candidate)); |
73
|
|
|
} |
74
|
|
|
|
75
|
|
|
public function testFindNationalNumber() |
76
|
|
|
{ |
77
|
|
|
// same cases as in testParseNationalNumber |
78
|
|
|
$this->doTestFindInContext("033316005", RegionCode::NZ); |
79
|
|
|
// ("33316005", RegionCode.NZ) is omitted since the national prefix is obligatory for these |
80
|
|
|
// types of numbers in New Zealand. |
81
|
|
|
// National prefix attached and some formatting present. |
82
|
|
|
$this->doTestFindInContext("03-331 6005", RegionCode::NZ); |
83
|
|
|
$this->doTestFindInContext("03 331 6005", RegionCode::NZ); |
84
|
|
|
// Testing international prefixes. |
85
|
|
|
// Should strip country code. |
86
|
|
|
$this->doTestFindInContext("0064 3 331 6005", RegionCode::NZ); |
87
|
|
|
// Try again, but this time we have an international number with Region Code US. It should |
88
|
|
|
// recognize the country code and parse accordingly. |
89
|
|
|
$this->doTestFindInContext("01164 3 331 6005", RegionCode::US); |
90
|
|
|
$this->doTestFindInContext("+64 3 331 6005", RegionCode::US); |
91
|
|
|
|
92
|
|
|
$this->doTestFindInContext("64(0)64123456", RegionCode::NZ); |
93
|
|
|
// Check that using a "/" is fine in a phone number. |
94
|
|
|
// Note that real Polish numbers do *not* start with a 0. |
95
|
|
|
$this->doTestFindInContext("0123/456789", RegionCode::PL); |
96
|
|
|
$this->doTestFindInContext("123-456-7890", RegionCode::US); |
97
|
|
|
} |
98
|
|
|
|
99
|
|
|
public function testFindWithInternationalPrefixes() |
100
|
|
|
{ |
101
|
|
|
$this->doTestFindInContext("+1 (650) 333-6000", RegionCode::NZ); |
102
|
|
|
$this->doTestFindInContext("1-650-333-6000", RegionCode::US); |
103
|
|
|
// Calling the US number from Singapore by using different service providers |
104
|
|
|
// 1st test: calling using SingTel IDD service (IDD is 001) |
105
|
|
|
$this->doTestFindInContext("0011-650-333-6000", RegionCode::SG); |
106
|
|
|
// 2nd test: calling using StarHub IDD service (IDD is 008) |
107
|
|
|
$this->doTestFindInContext("0081-650-333-6000", RegionCode::SG); |
108
|
|
|
// 3rd test: calling using SingTel V019 service (IDD is 019) |
109
|
|
|
$this->doTestFindInContext("0191-650-333-6000", RegionCode::SG); |
110
|
|
|
// Calling the US number from Poland |
111
|
|
|
$this->doTestFindInContext("0~01-650-333-6000", RegionCode::PL); |
112
|
|
|
// Using "++" at the start. |
113
|
|
|
$this->doTestFindInContext("++1 (650) 333-6000", RegionCode::PL); |
114
|
|
|
// Using a full-width plus sign. |
115
|
|
|
$this->doTestFindInContext("\xEF\xBC\x8B1 (650) 333-6000", RegionCode::SG); |
116
|
|
|
// The whole number, including punctuation, is here represented in full-width form. |
117
|
|
|
$this->doTestFindInContext("+1 (650) 333-6000", RegionCode::SG); |
118
|
|
|
} |
119
|
|
|
|
120
|
|
|
public function testFindWithLeadingZero() |
121
|
|
|
{ |
122
|
|
|
$this->doTestFindInContext("+39 02-36618 300", RegionCode::NZ); |
123
|
|
|
$this->doTestFindInContext("02-36618 300", RegionCode::IT); |
124
|
|
|
$this->doTestFindInContext("312 345 678", RegionCode::IT); |
125
|
|
|
} |
126
|
|
|
|
127
|
|
|
public function testFindNationalNumberArgentina() |
128
|
|
|
{ |
129
|
|
|
// Test parsing mobile numbers of Argentina. |
130
|
|
|
$this->doTestFindInContext("+54 9 343 555 1212", RegionCode::AR); |
131
|
|
|
$this->doTestFindInContext("0343 15 555 1212", RegionCode::AR); |
132
|
|
|
|
133
|
|
|
$this->doTestFindInContext("+54 9 3715 65 4320", RegionCode::AR); |
134
|
|
|
$this->doTestFindInContext("03715 15 65 4320", RegionCode::AR); |
135
|
|
|
|
136
|
|
|
// Test parsing fixed-line numbers of Argentina. |
137
|
|
|
$this->doTestFindInContext("+54 11 3797 0000", RegionCode::AR); |
138
|
|
|
$this->doTestFindInContext("011 3797 0000", RegionCode::AR); |
139
|
|
|
|
140
|
|
|
$this->doTestFindInContext("+54 3715 65 4321", RegionCode::AR); |
141
|
|
|
$this->doTestFindInContext("03715 65 4321", RegionCode::AR); |
142
|
|
|
|
143
|
|
|
$this->doTestFindInContext("+54 23 1234 0000", RegionCode::AR); |
144
|
|
|
$this->doTestFindInContext("023 1234 0000", RegionCode::AR); |
145
|
|
|
} |
146
|
|
|
|
147
|
|
|
public function testFindWithXInNumber() |
148
|
|
|
{ |
149
|
|
|
$this->doTestFindInContext("(0xx) 123456789", RegionCode::AR); |
150
|
|
|
// A case where x denotes both carrier codes and extension symbol. |
151
|
|
|
$this->doTestFindInContext("(0xx) 123456789 x 1234", RegionCode::AR); |
152
|
|
|
|
153
|
|
|
// This test is intentionally constructed such that the number of digit after xx is larger than |
154
|
|
|
// 7, so that the number won't be mistakenly treated as an extension, as we allow extensions up |
155
|
|
|
// to 7 digits. This assumption is okay for now as all the countries where a carrier selection |
156
|
|
|
// code is written in the form of xx have a national significant number of length larger than 7. |
157
|
|
|
$this->doTestFindInContext("011xx5481429712", RegionCode::US); |
158
|
|
|
} |
159
|
|
|
|
160
|
|
|
public function testFindNumbersMexico() |
161
|
|
|
{ |
162
|
|
|
// Test parsing fixed-line numbers of Mexico. |
163
|
|
|
$this->doTestFindInContext("+52 (449)978-0001", RegionCode::MX); |
164
|
|
|
$this->doTestFindInContext("01 (449)978-0001", RegionCode::MX); |
165
|
|
|
$this->doTestFindInContext("(449)978-0001", RegionCode::MX); |
166
|
|
|
|
167
|
|
|
// Test parsing mobile numbers of Mexico. |
168
|
|
|
$this->doTestFindInContext("+52 1 33 1234-5678", RegionCode::MX); |
169
|
|
|
$this->doTestFindInContext("044 (33) 1234-5678", RegionCode::MX); |
170
|
|
|
$this->doTestFindInContext("045 33 1234-5678", RegionCode::MX); |
171
|
|
|
} |
172
|
|
|
|
173
|
|
|
public function testFindNumbersWithPlusWithNoRegion() |
174
|
|
|
{ |
175
|
|
|
// RegionCode.ZZ is allowed only if the number starts with a '+' - then the country code can be |
176
|
|
|
// calculated. |
177
|
|
|
$this->doTestFindInContext("+64 3 331 6005", RegionCode::ZZ); |
178
|
|
|
// Null is also allowed for the region code in these cases. |
179
|
|
|
$this->doTestFindInContext("+64 3 331 6005", null); |
180
|
|
|
} |
181
|
|
|
|
182
|
|
|
public function testFindExtensions() |
183
|
|
|
{ |
184
|
|
|
$this->doTestFindInContext("03 331 6005 ext 3456", RegionCode::NZ); |
185
|
|
|
$this->doTestFindInContext("03-3316005x3456", RegionCode::NZ); |
186
|
|
|
$this->doTestFindInContext("03-3316005 int.3456", RegionCode::NZ); |
187
|
|
|
$this->doTestFindInContext("03 3316005 #3456", RegionCode::NZ); |
188
|
|
|
$this->doTestFindInContext("0~0 1800 7493 524", RegionCode::PL); |
189
|
|
|
$this->doTestFindInContext("(1800) 7493.524", RegionCode::US); |
190
|
|
|
// Check that the last instance of an extension token is matched. |
191
|
|
|
$this->doTestFindInContext("0~0 1800 7493 524 ~1234", RegionCode::PL); |
192
|
|
|
// Verifying bug-fix where the last digit of a number was previously omitted if it was a 0 when |
193
|
|
|
// extracting the extension. Also verifying a few different cases of extensions. |
194
|
|
|
$this->doTestFindInContext("+44 2034567890x456", RegionCode::NZ); |
195
|
|
|
$this->doTestFindInContext("+44 2034567890x456", RegionCode::GB); |
196
|
|
|
$this->doTestFindInContext("+44 2034567890 x456", RegionCode::GB); |
197
|
|
|
$this->doTestFindInContext("+44 2034567890 X456", RegionCode::GB); |
198
|
|
|
$this->doTestFindInContext("+44 2034567890 X 456", RegionCode::GB); |
199
|
|
|
$this->doTestFindInContext("+44 2034567890 X 456", RegionCode::GB); |
200
|
|
|
$this->doTestFindInContext("+44 2034567890 X 456", RegionCode::GB); |
201
|
|
|
|
202
|
|
|
$this->doTestFindInContext("(800) 901-3355 x 7246433", RegionCode::US); |
203
|
|
|
$this->doTestFindInContext("(800) 901-3355 , ext 7246433", RegionCode::US); |
204
|
|
|
$this->doTestFindInContext("(800) 901-3355 ,extension 7246433", RegionCode::US); |
205
|
|
|
// The next test differs from PhoneNumberUtil -> when matching we don't consider a lone comma to |
206
|
|
|
// indicate an extension, although we accept it when parsing. |
207
|
|
|
$this->doTestFindInContext("(800) 901-3355 ,x 7246433", RegionCode::US); |
208
|
|
|
$this->doTestFindInContext("(800) 901-3355 ext: 7246433", RegionCode::US); |
209
|
|
|
} |
210
|
|
|
|
211
|
|
|
public function testFindInterspersedWithSpace() |
212
|
|
|
{ |
213
|
|
|
$this->doTestFindInContext("0 3 3 3 1 6 0 0 5", RegionCode::NZ); |
214
|
|
|
} |
215
|
|
|
|
216
|
|
|
/** |
217
|
|
|
* Test matching behaviour when starting in the middle of a phone number. |
218
|
|
|
*/ |
219
|
|
|
public function testIntermediateParsePositions() |
220
|
|
|
{ |
221
|
|
|
$text = "Call 033316005 or 032316005!"; |
222
|
|
|
// | | | | | | |
|
|
|
|
223
|
|
|
// 0 5 10 15 20 25 |
|
|
|
|
224
|
|
|
|
225
|
|
|
// Iterate over all possible indices. |
226
|
|
|
for ($i = 0; $i <= 5; $i++) { |
227
|
|
|
$this->assertEqualRange($text, $i, 5, 14); |
228
|
|
|
} |
229
|
|
|
// 7 and 8 digits in a row are still parsed as number. |
230
|
|
|
$this->assertEqualRange($text, 6, 6, 14); |
231
|
|
|
$this->assertEqualRange($text, 7, 7, 14); |
232
|
|
|
// Anything smaller is skipped to the second instance. |
233
|
|
|
for ($i = 8; $i <= 19; $i++) { |
234
|
|
|
$this->assertEqualRange($text, $i, 19, 28); |
235
|
|
|
} |
236
|
|
|
} |
237
|
|
|
|
238
|
|
|
public function testFourMatchesInARow() |
239
|
|
|
{ |
240
|
|
|
$number1 = "415-666-7777"; |
241
|
|
|
$number2 = "800-443-1223"; |
242
|
|
|
$number3 = "212-443-1223"; |
243
|
|
|
$number4 = "650-443-1223"; |
244
|
|
|
$text = $number1 . " - " . $number2 . " - " . $number3 . " - " . $number4; |
245
|
|
|
|
246
|
|
|
$iterator = $this->phoneUtil->findNumbers($text, RegionCode::US); |
247
|
|
|
|
248
|
|
|
$iterator->next(); |
249
|
|
|
$match = $iterator->current(); |
250
|
|
|
$this->assertMatchProperties($match, $text, $number1, RegionCode::US); |
251
|
|
|
|
252
|
|
|
$iterator->next(); |
253
|
|
|
$match = $iterator->current(); |
254
|
|
|
$this->assertMatchProperties($match, $text, $number2, RegionCode::US); |
255
|
|
|
|
256
|
|
|
$iterator->next(); |
257
|
|
|
$match = $iterator->current(); |
258
|
|
|
$this->assertMatchProperties($match, $text, $number3, RegionCode::US); |
259
|
|
|
|
260
|
|
|
$iterator->next(); |
261
|
|
|
$match = $iterator->current(); |
262
|
|
|
$this->assertMatchProperties($match, $text, $number4, RegionCode::US); |
263
|
|
|
} |
264
|
|
|
|
265
|
|
|
public function testMatchesFoundWithMultipleSpaces() |
266
|
|
|
{ |
267
|
|
|
$number1 = "(415) 666-7777"; |
268
|
|
|
$number2 = "(800) 443-1223"; |
269
|
|
|
$text = $number1 . " " . $number2; |
270
|
|
|
|
271
|
|
|
$iterator = $this->phoneUtil->findNumbers($text, RegionCode::US); |
272
|
|
|
|
273
|
|
|
$iterator->next(); |
274
|
|
|
$match = $iterator->current(); |
275
|
|
|
$this->assertMatchProperties($match, $text, $number1, RegionCode::US); |
276
|
|
|
|
277
|
|
|
$iterator->next(); |
278
|
|
|
$match = $iterator->current(); |
279
|
|
|
$this->assertMatchProperties($match, $text, $number2, RegionCode::US); |
280
|
|
|
} |
281
|
|
|
|
282
|
|
|
public function testMatchWithSurroundingZipcodes() |
283
|
|
|
{ |
284
|
|
|
$number = "415-666-7777"; |
285
|
|
|
$zipPreceding = "My address is CA 34215 - " . $number . " is my number."; |
286
|
|
|
|
287
|
|
|
$iterator = $this->phoneUtil->findNumbers($zipPreceding, RegionCode::US); |
288
|
|
|
|
289
|
|
|
$iterator->next(); |
290
|
|
|
$match = $iterator->current(); |
291
|
|
|
$this->assertMatchProperties($match, $zipPreceding, $number, RegionCode::US); |
292
|
|
|
|
293
|
|
|
// Now repeat, but this time the phone number has spaces in it. It should still be found. |
294
|
|
|
$number = "(415) 666 7777"; |
295
|
|
|
|
296
|
|
|
$zipFollowing = "My number is " . $number . ". 34215 is my zip-code."; |
297
|
|
|
|
298
|
|
|
$iterator = $this->phoneUtil->findNumbers($zipFollowing, RegionCode::US); |
299
|
|
|
|
300
|
|
|
$iterator->next(); |
301
|
|
|
$match = $iterator->current(); |
302
|
|
|
$this->assertMatchProperties($match, $zipFollowing, $number, RegionCode::US); |
303
|
|
|
} |
304
|
|
|
|
305
|
|
|
public function dataLatinLetters() |
306
|
|
|
{ |
307
|
|
|
return array( |
308
|
|
|
array('c', true), |
309
|
|
|
array('C', true), |
310
|
|
|
array("\xC3\x89", true), |
311
|
|
|
array("\xCC\x81", true), // Combining acute accent |
312
|
|
|
// Punctuation, digits and white-space are not considered "latin letters". |
313
|
|
|
array(':', false), |
314
|
|
|
array('5', false), |
315
|
|
|
array('-', false), |
316
|
|
|
array('.', false), |
317
|
|
|
array(' ', false), |
318
|
|
|
array("\xE6\x88\x91", false), // Chinese character |
319
|
|
|
array("\xE3\x81\xAE", false), // Hiragana letter no |
320
|
|
|
); |
321
|
|
|
} |
322
|
|
|
|
323
|
|
|
/** |
324
|
|
|
* @dataProvider dataLatinLetters |
325
|
|
|
* @param string $letter |
326
|
|
|
* @param string $expectedResult |
327
|
|
|
*/ |
328
|
|
|
public function testIsLatinLetter($letter, $expectedResult) |
329
|
|
|
{ |
330
|
|
|
$this->assertEquals($expectedResult, PhoneNumberMatcher::isLatinLetter($letter), |
331
|
|
|
"{$letter} should return {$expectedResult}"); |
332
|
|
|
} |
333
|
|
|
|
334
|
|
|
public function testMatchesWithSurroundingLatinChars() |
335
|
|
|
{ |
336
|
|
|
$possibleOnlyContexts = array(); |
337
|
|
|
$possibleOnlyContexts[] = array("abc", "def"); |
338
|
|
|
$possibleOnlyContexts[] = array("abc", ""); |
339
|
|
|
$possibleOnlyContexts[] = array("", "def"); |
340
|
|
|
// Latin capital letter e with an acute accent. |
341
|
|
|
$possibleOnlyContexts[] = array("\xC3\x89", ""); |
342
|
|
|
// e with an acute accent decomposed (with combining mark). |
343
|
|
|
$possibleOnlyContexts[] = array("e\xCC\x81", ""); |
344
|
|
|
|
345
|
|
|
// Numbers should not be considered valid, if they are surrounded by Latin characters, but |
346
|
|
|
// should be considered possible. |
347
|
|
|
$this->findMatchesInContexts($possibleOnlyContexts, false, true); |
348
|
|
|
} |
349
|
|
|
|
350
|
|
|
public function testMoneyNotSeenAsPhoneNumber() |
351
|
|
|
{ |
352
|
|
|
$possibleOnlyContexts = array(); |
353
|
|
|
$possibleOnlyContexts[] = array("$", ""); |
354
|
|
|
$possibleOnlyContexts[] = array("", "$"); |
355
|
|
|
$possibleOnlyContexts[] = array("\xC2\xA3", ""); // Pound sign |
356
|
|
|
$possibleOnlyContexts[] = array("\xC2\xA5", ""); // Yen sign |
357
|
|
|
|
358
|
|
|
$this->findMatchesInContexts($possibleOnlyContexts, false, true); |
359
|
|
|
} |
360
|
|
|
|
361
|
|
|
public function testPercentageNotSeenAsPhoneNumber() |
362
|
|
|
{ |
363
|
|
|
$possibleOnlyContexts = array(); |
364
|
|
|
$possibleOnlyContexts[] = array("", "%"); |
365
|
|
|
// Numbers followed by % should be dropped |
366
|
|
|
$this->findMatchesInContexts($possibleOnlyContexts, false, true); |
367
|
|
|
} |
368
|
|
|
|
369
|
|
|
public function testPhoneNumberWithLeadingOrTrailingMoneyMatches() |
370
|
|
|
{ |
371
|
|
|
// Because of the space after the 20 (or before the 100) these dollar amounts should not stop |
372
|
|
|
// the actual number from being found. |
373
|
|
|
$contexts = array(); |
374
|
|
|
$contexts[] = array('$20 ', ''); |
375
|
|
|
$contexts[] = array('', ' 100$'); |
376
|
|
|
|
377
|
|
|
$this->findMatchesInContexts($contexts, true, true); |
378
|
|
|
} |
379
|
|
|
|
380
|
|
|
public function testMatchesWithSurroundingLatinCharsAndLeadingPunctuation() |
381
|
|
|
{ |
382
|
|
|
// Contexts with trailing characters. Leading characters are okay here since the numbers we will |
383
|
|
|
// insert start with punctuation, but trailing characters are still not allowed. |
384
|
|
|
$possibleOnlyContexts = array(); |
385
|
|
|
$possibleOnlyContexts[] = array("abc", "def"); |
386
|
|
|
$possibleOnlyContexts[] = array("", "def"); |
387
|
|
|
$possibleOnlyContexts[] = array("", "\xC3\x89"); |
388
|
|
|
|
389
|
|
|
// Numbers should not be considered valid, if they have trailing Latin characters, but should be |
390
|
|
|
// considered possible. |
391
|
|
|
$numberWithPlus = "+14156667777"; |
392
|
|
|
$numberWithBrackets = "(415)6667777"; |
393
|
|
|
$this->findMatchesInContexts($possibleOnlyContexts, false, true, RegionCode::US, $numberWithPlus); |
394
|
|
|
$this->findMatchesInContexts($possibleOnlyContexts, false, true, RegionCode::US, $numberWithBrackets); |
395
|
|
|
|
396
|
|
|
$validContexts = array(); |
397
|
|
|
$validContexts[] = array("abc", ""); |
398
|
|
|
$validContexts[] = array("\xC3\x89", ""); |
399
|
|
|
$validContexts[] = array("\xC3\x89", "."); // Trailing punctuation. |
400
|
|
|
$validContexts[] = array("\xC3\x89", " def"); // Trailing white space. |
401
|
|
|
|
402
|
|
|
// Numbers should be considered valid, since they start with punctuation. |
403
|
|
|
$this->findMatchesInContexts($validContexts, true, true, RegionCode::US, $numberWithPlus); |
404
|
|
|
$this->findMatchesInContexts($validContexts, true, true, RegionCode::US, $numberWithBrackets); |
405
|
|
|
} |
406
|
|
|
|
407
|
|
|
public function testMatchesWithSurroundingChineseChars() |
408
|
|
|
{ |
409
|
|
|
$validContexts = array(); |
410
|
|
|
$validContexts[] = array("我的电话号码是", ""); |
411
|
|
|
$validContexts[] = array("", "是我的电话号码"); |
412
|
|
|
$validContexts[] = array("请拨打", "我在明天"); |
413
|
|
|
|
414
|
|
|
// Numbers should be considered valid, since they are surrounded by Chinese. |
415
|
|
|
$this->findMatchesInContexts($validContexts, true, true); |
416
|
|
|
} |
417
|
|
|
|
418
|
|
|
public function testMatchesWithSurroundingPunctuation() |
419
|
|
|
{ |
420
|
|
|
$validContexts = array(); |
421
|
|
|
$validContexts[] = array("My number-", ""); // At end of text |
422
|
|
|
$validContexts[] = array("", ".Nice day."); // At start of text |
423
|
|
|
$validContexts[] = array("Tel:", "."); // Punctuation surrounds number. |
424
|
|
|
$validContexts[] = array("Tel: ", " on Saturdays."); // White-space is also fine. |
425
|
|
|
|
426
|
|
|
// Numbers should be considered valid, since they are surrounded by punctuation. |
427
|
|
|
$this->findMatchesInContexts($validContexts, true, true); |
428
|
|
|
} |
429
|
|
|
|
430
|
|
|
public function testMatchesMultiplePhoneNumbersSeparatedByPhoneNumberPunctuation() |
431
|
|
|
{ |
432
|
|
|
$text = "Call 650-253-4561 -- 455-234-3451"; |
433
|
|
|
$region = RegionCode::US; |
434
|
|
|
|
435
|
|
|
$number1 = new PhoneNumber(); |
436
|
|
|
$number1->setCountryCode($this->phoneUtil->getCountryCodeForRegion($region)); |
437
|
|
|
$number1->setNationalNumber(6502534561); |
438
|
|
|
$match1 = new PhoneNumberMatch(5, "650-253-4561", $number1); |
439
|
|
|
|
440
|
|
|
$number2 = new PhoneNumber(); |
441
|
|
|
$number2->setCountryCode($this->phoneUtil->getCountryCodeForRegion($region)); |
442
|
|
|
$number2->setNationalNumber(4552343451); |
443
|
|
|
$match2 = new PhoneNumberMatch(21, "455-234-3451", $number2); |
444
|
|
|
|
445
|
|
|
$matches = $this->phoneUtil->findNumbers($text, $region); |
446
|
|
|
|
447
|
|
|
$matches->next(); |
448
|
|
|
$this->assertEquals($match1, $matches->current()); |
449
|
|
|
|
450
|
|
|
$matches->next(); |
451
|
|
|
$this->assertEquals($match2, $matches->current()); |
452
|
|
|
} |
453
|
|
|
|
454
|
|
|
public function testDoesNotMatchMultiplePhoneNumbersSeparatedWithNoWhiteSpace() |
455
|
|
|
{ |
456
|
|
|
// No white-space found between numbers - neither is found. |
457
|
|
|
$text = "Call 650-253-4561--455-234-3451"; |
458
|
|
|
$region = RegionCode::US; |
459
|
|
|
|
460
|
|
|
$this->assertTrue($this->hasNoMatches($this->phoneUtil->findNumbers($text, $region))); |
461
|
|
|
} |
462
|
|
|
|
463
|
|
|
/** |
464
|
|
|
* Strings with number-like things that shouldn't be found under any level. |
465
|
|
|
* @return array |
466
|
|
|
*/ |
467
|
|
|
public function dataImpossibleCases() |
468
|
|
|
{ |
469
|
|
|
return array( |
470
|
|
|
array("12345", RegionCode::US), |
471
|
|
|
array("23456789", RegionCode::US), |
472
|
|
|
array("234567890112", RegionCode::US), |
473
|
|
|
array("650+253+1234", RegionCode::US), |
474
|
|
|
array("3/10/1984", RegionCode::CA), |
475
|
|
|
array("03/27/2011", RegionCode::US), |
476
|
|
|
array("31/8/2011", RegionCode::US), |
477
|
|
|
array("1/12/2011", RegionCode::US), |
478
|
|
|
array("10/12/82", RegionCode::DE), |
479
|
|
|
array("650x2531234", RegionCode::US), |
480
|
|
|
array("2012-01-02 08:00", RegionCode::US), |
481
|
|
|
array("2012/01/02 08:00", RegionCode::US), |
482
|
|
|
array("20120102 08:00", RegionCode::US), |
483
|
|
|
array("2014-04-12 04:04 PM", RegionCode::US), |
484
|
|
|
array("2014-04-12 04:04 PM", RegionCode::US), |
485
|
|
|
array("2014-04-12 04:04 PM", RegionCode::US), |
486
|
|
|
array("2014-04-12 04:04 PM", RegionCode::US), |
487
|
|
|
); |
488
|
|
|
} |
489
|
|
|
|
490
|
|
|
/** |
491
|
|
|
* Strings with number-like things that should only be found under "possible". |
492
|
|
|
* @return array |
493
|
|
|
*/ |
494
|
|
|
public function dataPossibleOnlyCases() |
495
|
|
|
{ |
496
|
|
|
return array( |
497
|
|
|
// US numbers cannot start with 7 in the test metadata to be valid. |
498
|
|
|
array("7121115678", RegionCode::US), |
499
|
|
|
// 'X' should not be found in numbers at leniencies stricter than POSSIBLE, unless it represents |
500
|
|
|
// a carrier code or extension. |
501
|
|
|
array("1650 x 253 - 1234", RegionCode::US), |
502
|
|
|
array("650 x 253 - 1234", RegionCode::US), |
503
|
|
|
array("6502531x234", RegionCode::US), |
504
|
|
|
array("(20) 3346 1234", RegionCode::GB), // Non-optional NP omitted |
505
|
|
|
); |
506
|
|
|
} |
507
|
|
|
|
508
|
|
|
/** |
509
|
|
|
* Strings with number-like things that should only be found up to and including the "valid" |
510
|
|
|
* leniency level. |
511
|
|
|
* @return array |
512
|
|
|
*/ |
513
|
|
|
public function dataValidCases() |
514
|
|
|
{ |
515
|
|
|
return array( |
516
|
|
|
array("65 02 53 00 00", RegionCode::US), |
517
|
|
|
array("6502 538365", RegionCode::US), |
518
|
|
|
array("650//253-1234", RegionCode::US), // 2 slashes are illegal at higher levels |
519
|
|
|
array("650/253/1234", RegionCode::US), |
520
|
|
|
array("9002309. 158", RegionCode::US), |
521
|
|
|
array("12 7/8 - 14 12/34 - 5", RegionCode::US), |
522
|
|
|
array("12.1 - 23.71 - 23.45", RegionCode::US), |
523
|
|
|
array("800 234 1 111x1111", RegionCode::US), |
524
|
|
|
array("1979-2011 100", RegionCode::US), |
525
|
|
|
array("+494949-4-94", RegionCode::DE), // National number in wrong format |
526
|
|
|
array("4156666-777", RegionCode::US), |
527
|
|
|
array("2012-0102 08", RegionCode::US), // Very strange formatting. |
528
|
|
|
array("2012-01-02 08", RegionCode::US), |
529
|
|
|
// Breakdown assistance number with unexpected formatting. |
530
|
|
|
array("1800-1-0-10 22", RegionCode::AU), |
531
|
|
|
array("030-3-2 23 12 34", RegionCode::DE), |
532
|
|
|
array("03 0 -3 2 23 12 34", RegionCode::DE), |
533
|
|
|
array("(0)3 0 -3 2 23 12 34", RegionCode::DE), |
534
|
|
|
array("0 3 0 -3 2 23 12 34", RegionCode::DE), |
535
|
|
|
); |
536
|
|
|
} |
537
|
|
|
|
538
|
|
|
/** |
539
|
|
|
* Strings with number-like things that should only be found up to and including the |
540
|
|
|
* "strict_grouping" leniency level. |
541
|
|
|
* @return array |
542
|
|
|
*/ |
543
|
|
|
public function dataStrictGroupingCases() |
544
|
|
|
{ |
545
|
|
|
return array( |
546
|
|
|
array("(415) 6667777", RegionCode::US), |
547
|
|
|
array("415-6667777", RegionCode::US), |
548
|
|
|
// Should be found by strict grouping but not exact grouping, as the last two groups are |
549
|
|
|
// formatted together as a block. |
550
|
|
|
array("0800-2491234", RegionCode::DE), |
551
|
|
|
// Doesn't match any formatting in the test file, but almost matches an alternate format (the |
552
|
|
|
// last two groups have been squashed together here). |
553
|
|
|
array("0900-1 123123", RegionCode::DE), |
554
|
|
|
array("(0)900-1 123123", RegionCode::DE), |
555
|
|
|
array("0 900-1 123123", RegionCode::DE), |
556
|
|
|
// NDC also found as part of the country calling code; this shouldn't ruin the grouping |
557
|
|
|
// expectations. |
558
|
|
|
array("+33 3 34 2312", RegionCode::FR), |
559
|
|
|
); |
560
|
|
|
} |
561
|
|
|
|
562
|
|
|
/** |
563
|
|
|
* Strings with number-like things that should be found at all levels. |
564
|
|
|
* @return array |
565
|
|
|
*/ |
566
|
|
|
public function dataExactGroupingCases() |
567
|
|
|
{ |
568
|
|
|
return array( |
569
|
|
|
array("4156667777", RegionCode::US), |
570
|
|
|
array("415-666-7777", RegionCode::US), |
571
|
|
|
array("4156667777", RegionCode::US), |
572
|
|
|
array("4156667777 x 123", RegionCode::US), |
573
|
|
|
array("415-666-7777", RegionCode::US), |
574
|
|
|
array("415/666-7777", RegionCode::US), |
575
|
|
|
array("415-666-7777 ext. 503", RegionCode::US), |
576
|
|
|
array("1 415 666 7777 x 123", RegionCode::US), |
577
|
|
|
array("+1 415-666-7777", RegionCode::US), |
578
|
|
|
array("+494949 49", RegionCode::DE), |
579
|
|
|
array("+49-49-34", RegionCode::DE), |
580
|
|
|
array("+49-4931-49", RegionCode::DE), |
581
|
|
|
array("04931-49", RegionCode::DE), // With National Prefix |
582
|
|
|
array("+49-494949", RegionCode::DE), // One group with country code |
583
|
|
|
array("+49-494949 ext. 49", RegionCode::DE), |
584
|
|
|
array("+49494949 ext. 49", RegionCode::DE), |
585
|
|
|
array("0494949", RegionCode::DE), |
586
|
|
|
array("0494949 ext. 49", RegionCode::DE), |
587
|
|
|
array("01 (33) 3461 2234", RegionCode::MX), // Optional NP present |
588
|
|
|
array("(33) 3461 2234", RegionCode::MX), // Optional NP omitted |
589
|
|
|
array("1800-10-10 22", RegionCode::AU), // Breakdown assistance number. |
590
|
|
|
// Doesn't match any formatting in the test file, but matches an alternate format exactly. |
591
|
|
|
array("0900-1 123 123", RegionCode::DE), |
592
|
|
|
array("(0)900-1 123 123", RegionCode::DE), |
593
|
|
|
array("0 900-1 123 123", RegionCode::DE), |
594
|
|
|
array("+33 3 34 23 12", RegionCode::FR), |
595
|
|
|
); |
596
|
|
|
} |
597
|
|
|
|
598
|
|
|
public function data_testMatchesWithPossibleLeniency() |
599
|
|
|
{ |
600
|
|
|
return $this->dataStrictGroupingCases() |
601
|
|
|
+ $this->dataExactGroupingCases() |
602
|
|
|
+ $this->dataValidCases() |
603
|
|
|
+ $this->dataPossibleOnlyCases(); |
604
|
|
|
} |
605
|
|
|
|
606
|
|
|
/** |
607
|
|
|
* @param string $rawString |
608
|
|
|
* @param string $region |
609
|
|
|
* @dataProvider data_testMatchesWithPossibleLeniency |
610
|
|
|
*/ |
611
|
|
|
public function testMatchesWithPossibleLeniency($rawString, $region) |
612
|
|
|
{ |
613
|
|
|
$this->doTestNumberMatchesForLeniency($rawString, $region, Leniency::POSSIBLE()); |
614
|
|
|
} |
615
|
|
|
|
616
|
|
|
/** |
617
|
|
|
* @param string $rawString |
618
|
|
|
* @param string $region |
619
|
|
|
* @dataProvider dataImpossibleCases |
620
|
|
|
*/ |
621
|
|
|
public function testNonMatchesWithPossibleLeniency($rawString, $region) |
622
|
|
|
{ |
623
|
|
|
$this->doTestNumberNonMatchesForLeniency($rawString, $region, Leniency::POSSIBLE()); |
624
|
|
|
} |
625
|
|
|
|
626
|
|
|
public function data_testMatchesWithValidLeniency() |
627
|
|
|
{ |
628
|
|
|
return $this->dataStrictGroupingCases() |
629
|
|
|
+ $this->dataExactGroupingCases() |
630
|
|
|
+ $this->dataValidCases(); |
631
|
|
|
} |
632
|
|
|
|
633
|
|
|
/** |
634
|
|
|
* @param string $rawString |
635
|
|
|
* @param string $region |
636
|
|
|
* @dataProvider data_testMatchesWithValidLeniency |
637
|
|
|
*/ |
638
|
|
|
public function testMatchesWithValidLeniency($rawString, $region) |
639
|
|
|
{ |
640
|
|
|
$this->doTestNumberMatchesForLeniency($rawString, $region, Leniency::VALID()); |
641
|
|
|
} |
642
|
|
|
|
643
|
|
|
public function data_testNonMatchesWithValidLeniency() |
644
|
|
|
{ |
645
|
|
|
return $this->dataImpossibleCases() |
646
|
|
|
+ $this->dataPossibleOnlyCases(); |
647
|
|
|
} |
648
|
|
|
|
649
|
|
|
/** |
650
|
|
|
* @param $rawString |
651
|
|
|
* @param $region |
652
|
|
|
* @dataProvider data_testNonMatchesWithValidLeniency |
653
|
|
|
*/ |
654
|
|
|
public function testNonMatchesWithValidLeniency($rawString, $region) |
655
|
|
|
{ |
656
|
|
|
$this->doTestNumberNonMatchesForLeniency($rawString, $region, Leniency::VALID()); |
657
|
|
|
} |
658
|
|
|
|
659
|
|
|
public function data_testMatchesWithStrictGroupingLeniency() |
660
|
|
|
{ |
661
|
|
|
return $this->dataStrictGroupingCases() |
662
|
|
|
+ $this->dataExactGroupingCases(); |
663
|
|
|
} |
664
|
|
|
|
665
|
|
|
/** |
666
|
|
|
* @param string $rawString |
667
|
|
|
* @param string $region |
668
|
|
|
* @dataProvider data_testMatchesWithStrictGroupingLeniency |
669
|
|
|
*/ |
670
|
|
|
public function testMatchesWithStrictGroupingLeniency($rawString, $region) |
671
|
|
|
{ |
672
|
|
|
$this->doTestNumberMatchesForLeniency($rawString, $region, Leniency::STRICT_GROUPING()); |
673
|
|
|
} |
674
|
|
|
|
675
|
|
|
public function data_testNonMatchesWithStrictGroupLeniency() |
676
|
|
|
{ |
677
|
|
|
return $this->dataImpossibleCases() |
678
|
|
|
+ $this->dataPossibleOnlyCases() |
679
|
|
|
+ $this->dataValidCases(); |
680
|
|
|
} |
681
|
|
|
|
682
|
|
|
/** |
683
|
|
|
* @param string $rawString |
684
|
|
|
* @param string $region |
685
|
|
|
* @dataProvider data_testNonMatchesWithStrictGroupLeniency |
686
|
|
|
*/ |
687
|
|
|
public function testNonMatchesWithStrictGroupLeniency($rawString, $region) |
688
|
|
|
{ |
689
|
|
|
$this->doTestNumberNonMatchesForLeniency($rawString, $region, Leniency::STRICT_GROUPING()); |
690
|
|
|
} |
691
|
|
|
|
692
|
|
|
/** |
693
|
|
|
* @param string $rawString |
694
|
|
|
* @param string $region |
695
|
|
|
* @dataProvider dataExactGroupingCases |
696
|
|
|
*/ |
697
|
|
|
public function testMatchesWithExactGroupingLeniency($rawString, $region) |
698
|
|
|
{ |
699
|
|
|
$this->doTestNumberMatchesForLeniency($rawString, $region, Leniency::EXACT_GROUPING()); |
700
|
|
|
} |
701
|
|
|
|
702
|
|
|
public function data_testNonMatchesExactGroupLeniency() |
703
|
|
|
{ |
704
|
|
|
return $this->dataImpossibleCases() |
705
|
|
|
+ $this->dataPossibleOnlyCases() |
706
|
|
|
+ $this->dataValidCases() |
707
|
|
|
+ $this->dataStrictGroupingCases(); |
708
|
|
|
} |
709
|
|
|
|
710
|
|
|
/** |
711
|
|
|
* @param string $rawString |
712
|
|
|
* @param string $region |
713
|
|
|
* @dataProvider data_testNonMatchesExactGroupLeniency |
714
|
|
|
*/ |
715
|
|
|
public function testNonMatchesExactGroupLeniency($rawString, $region) |
716
|
|
|
{ |
717
|
|
|
$this->doTestNumberNonMatchesForLeniency($rawString, $region, Leniency::EXACT_GROUPING()); |
718
|
|
|
} |
719
|
|
|
|
720
|
|
|
private function doTestNumberMatchesForLeniency($string, $region, Leniency\AbstractLeniency $leniency) |
721
|
|
|
{ |
722
|
|
|
$iterator = $this->findNumbersForLeniency($string, $region, $leniency); |
723
|
|
|
|
724
|
|
|
$iterator->next(); |
725
|
|
|
$match = $iterator->current(); |
726
|
|
|
|
727
|
|
|
$this->assertNotNull($match, "No match found in {$string} ({$region}) for leniency {$leniency}"); |
728
|
|
|
$this->assertEquals($string, $match->rawString(), "Found wrong match in test {$string} ({$region}). Found {$match->rawString()}"); |
729
|
|
|
} |
730
|
|
|
|
731
|
|
|
private function doTestNumberNonMatchesForLeniency($string, $region, Leniency\AbstractLeniency $leniency) |
732
|
|
|
{ |
733
|
|
|
$iterator = $this->findNumbersForLeniency($string, $region, $leniency); |
734
|
|
|
|
735
|
|
|
$iterator->next(); |
736
|
|
|
$match = $iterator->current(); |
737
|
|
|
|
738
|
|
|
$this->assertNull($match, "Match found in {$string} ({$region}) for leniency {$leniency}"); |
739
|
|
|
} |
740
|
|
|
|
741
|
|
|
/** |
742
|
|
|
* Helper method which tests the contexts provided and ensures that: |
743
|
|
|
* -- if isValid is true, they all find a test number inserted in the middle when leniency of |
744
|
|
|
* matching is set to VALID; else no test number should be extracted at that leniency level |
745
|
|
|
* -- if isPossible is true, they all find a test number inserted in the middle when leniency of |
746
|
|
|
* matching is set to POSSIBLE; else no test number should be extracted at that leniency level |
747
|
|
|
* |
748
|
|
|
* |
749
|
|
|
* @param array $contexts |
750
|
|
|
* @param bool $isValid |
751
|
|
|
* @param bool $isPossible |
752
|
|
|
* @param string $region |
753
|
|
|
* @param string $number |
754
|
|
|
*/ |
755
|
|
|
private function findMatchesInContexts($contexts, $isValid, $isPossible, $region = RegionCode::US, $number = "415-666-7777") |
756
|
|
|
{ |
757
|
|
|
if ($isValid) { |
758
|
|
|
$this->doTestInContext($number, $region, $contexts, Leniency::VALID()); |
759
|
|
|
} else { |
760
|
|
|
foreach ($contexts as $context) { |
761
|
|
|
$text = $context[0] . $number . $context[1]; |
762
|
|
|
$this->assertTrue($this->hasNoMatches($this->phoneUtil->findNumbers($text, $region)), |
763
|
|
|
"Should not have found a number in {$text}"); |
764
|
|
|
} |
765
|
|
|
} |
766
|
|
|
|
767
|
|
|
if ($isPossible) { |
768
|
|
|
$this->doTestInContext($number, $region, $contexts, Leniency::POSSIBLE()); |
769
|
|
|
} else { |
770
|
|
|
foreach ($contexts as $context) { |
771
|
|
|
$text = $context[0] . $number . $context[1]; |
772
|
|
|
$this->assertTrue($this->hasNoMatches($this->phoneUtil->findNumbers($text, $region)), |
773
|
|
|
"Should not have found a number in {$text}"); |
774
|
|
|
} |
775
|
|
|
} |
776
|
|
|
} |
777
|
|
|
|
778
|
|
|
public function testNonMatchingBracketsAreInvalid() |
779
|
|
|
{ |
780
|
|
|
// The digits up to the ", " form a valid US number, but it shouldn't be matched as one since |
781
|
|
|
// there was a non-matching bracket present. |
782
|
|
|
$this->assertTrue($this->hasNoMatches($this->phoneUtil->findNumbers( |
783
|
|
|
"80.585 [79.964, 81.191]", RegionCode::US))); |
784
|
|
|
|
785
|
|
|
// The trailing "]" is thrown away before parsing, so the resultant number, while a valid US |
786
|
|
|
// number, does not have matching brackets. |
787
|
|
|
$this->assertTrue($this->hasNoMatches($this->phoneUtil->findNumbers( |
788
|
|
|
"80.585 [79.964]", RegionCode::US))); |
789
|
|
|
|
790
|
|
|
$this->assertTrue($this->hasNoMatches($this->phoneUtil->findNumbers( |
791
|
|
|
"80.585 ((79.964)", RegionCode::US))); |
792
|
|
|
|
793
|
|
|
// This case has too many sets of brackets to be valid. |
794
|
|
|
$this->assertTrue($this->hasNoMatches($this->phoneUtil->findNumbers( |
795
|
|
|
"(80).(585) (79).(9)64", RegionCode::US))); |
796
|
|
|
} |
797
|
|
|
|
798
|
|
|
public function testNoMatchIfRegionIsNull() |
799
|
|
|
{ |
800
|
|
|
// Fail on non-international prefix if region code is null. |
801
|
|
|
$this->assertTrue($this->hasNoMatches($this->phoneUtil->findNumbers( |
802
|
|
|
"Random text body - number is 0331 6005, see you there", null))); |
803
|
|
|
} |
804
|
|
|
|
805
|
|
|
public function testNoMatchInEmptyString() |
806
|
|
|
{ |
807
|
|
|
$this->assertTrue($this->hasNoMatches($this->phoneUtil->findNumbers("", RegionCode::US))); |
808
|
|
|
$this->assertTrue($this->hasNoMatches($this->phoneUtil->findNumbers(" ", RegionCode::US))); |
809
|
|
|
} |
810
|
|
|
|
811
|
|
|
public function testNoMatchIfNoNumber() |
812
|
|
|
{ |
813
|
|
|
$this->assertTrue($this->hasNoMatches($this->phoneUtil->findNumbers( |
814
|
|
|
"Random text body - number is foobar, see you there", RegionCode::US))); |
815
|
|
|
} |
816
|
|
|
|
817
|
|
|
public function testSequences() |
818
|
|
|
{ |
819
|
|
|
// Test multiple occurrences. |
820
|
|
|
$text = "Call 033316005 or 032316005!"; |
821
|
|
|
$region = RegionCode::NZ; |
822
|
|
|
|
823
|
|
|
$number1 = new PhoneNumber(); |
824
|
|
|
$number1->setCountryCode($this->phoneUtil->getCountryCodeForRegion($region)); |
825
|
|
|
$number1->setNationalNumber(33316005); |
826
|
|
|
$match1 = new PhoneNumberMatch(5, "033316005", $number1); |
827
|
|
|
|
828
|
|
|
$number2 = new PhoneNumber(); |
829
|
|
|
$number2->setCountryCode($this->phoneUtil->getCountryCodeForRegion($region)); |
830
|
|
|
$number2->setNationalNumber(32316005); |
831
|
|
|
$match2 = new PhoneNumberMatch(19, "032316005", $number2); |
832
|
|
|
|
833
|
|
|
$matches = $this->phoneUtil->findNumbers($text, $region, Leniency::POSSIBLE()); |
834
|
|
|
|
835
|
|
|
$matches->next(); |
836
|
|
|
$this->assertEquals($match1, $matches->current()); |
837
|
|
|
|
838
|
|
|
$matches->next(); |
839
|
|
|
$this->assertEquals($match2, $matches->current()); |
840
|
|
|
} |
841
|
|
|
|
842
|
|
|
public function testNullInput() |
843
|
|
|
{ |
844
|
|
|
$this->assertTrue($this->hasNoMatches($this->phoneUtil->findNumbers(null, RegionCode::US))); |
845
|
|
|
$this->assertTrue($this->hasNoMatches($this->phoneUtil->findNumbers(null, null))); |
846
|
|
|
} |
847
|
|
|
|
848
|
|
|
public function testMaxMatches() |
849
|
|
|
{ |
850
|
|
|
// Set up text with 100 valid phone numbers. |
851
|
|
|
$numbers = str_repeat("My info: 415-666-7777,", 100); |
852
|
|
|
|
853
|
|
|
// Matches all 100. Max only applies to failed cases. |
854
|
|
|
$number = $this->phoneUtil->parse("+14156667777", null); |
855
|
|
|
$expected = array_fill(0, 100, $number); |
856
|
|
|
|
857
|
|
|
$iterable = $this->phoneUtil->findNumbers($numbers, RegionCode::US, Leniency::VALID(), 10); |
858
|
|
|
$actual = array(); |
859
|
|
|
foreach ($iterable as $match) { |
860
|
|
|
$actual[] = $match->number(); |
861
|
|
|
} |
862
|
|
|
|
863
|
|
|
$this->assertEquals($expected, $actual); |
864
|
|
|
} |
865
|
|
|
|
866
|
|
|
public function testMaxMatchesInvalid() |
867
|
|
|
{ |
868
|
|
|
// Set up text with 10 invalid phone numbers followed by 100 valid. |
869
|
|
|
$numbers = ""; |
870
|
|
|
for ($i = 0; $i < 10; $i++) { |
871
|
|
|
$numbers .= "My address is 949-8945-0"; |
872
|
|
|
} |
873
|
|
|
for ($i = 0; $i < 100; $i++) { |
874
|
|
|
$numbers .= "My info: 415-666-7777,"; |
875
|
|
|
} |
876
|
|
|
|
877
|
|
|
$iterable = $this->phoneUtil->findNumbers($numbers, RegionCode::US, Leniency::VALID(), 10); |
878
|
|
|
$iterable->next(); |
879
|
|
|
$this->assertNull($iterable->current()); |
880
|
|
|
} |
881
|
|
|
|
882
|
|
|
public function testMaxMatchesMixed() |
883
|
|
|
{ |
884
|
|
|
// Set up text with 100 valid numbers inside an invalid number. |
885
|
|
|
$numbers = ""; |
886
|
|
|
for ($i = 0; $i < 100; $i++) { |
887
|
|
|
$numbers .= "My info: 415-666-7777 123 fake street"; |
888
|
|
|
} |
889
|
|
|
|
890
|
|
|
// Only matches the first 10 despite there being 100 numbers due to max matches |
891
|
|
|
$number = $this->phoneUtil->parse("+14156667777", null); |
892
|
|
|
$expected = array_fill(0, 10, $number); |
893
|
|
|
|
894
|
|
|
$iterable = $this->phoneUtil->findNumbers($numbers, RegionCode::US, Leniency::VALID(), 10); |
895
|
|
|
|
896
|
|
|
$actual = array(); |
897
|
|
|
foreach ($iterable as $match) { |
898
|
|
|
$actual[] = $match->number(); |
899
|
|
|
} |
900
|
|
|
|
901
|
|
|
$this->assertEquals($expected, $actual); |
902
|
|
|
} |
903
|
|
|
|
904
|
|
|
public function testNonPlusPrefixedNumbersNotFoundForInvalidRegion() |
905
|
|
|
{ |
906
|
|
|
// Does not start with a "+", we won't match it. |
907
|
|
|
$iterable = $this->phoneUtil->findNumbers("1 456 764 156", RegionCode::ZZ); |
908
|
|
|
|
909
|
|
|
$iterable->next(); |
910
|
|
|
$this->assertFalse($iterable->valid()); |
911
|
|
|
} |
912
|
|
|
|
913
|
|
|
public function testEmptyIteration() |
914
|
|
|
{ |
915
|
|
|
$iterable = $this->phoneUtil->findNumbers("", RegionCode::ZZ); |
916
|
|
|
|
917
|
|
|
$iterable->next(); |
918
|
|
|
$this->assertFalse($iterable->valid()); |
919
|
|
|
} |
920
|
|
|
|
921
|
|
|
public function testSingleIteration() |
922
|
|
|
{ |
923
|
|
|
$iterable = $this->phoneUtil->findNumbers("+14156667777", RegionCode::ZZ); |
924
|
|
|
|
925
|
|
|
$iterable->next(); |
926
|
|
|
$this->assertTrue($iterable->valid()); |
927
|
|
|
$this->assertNotNull($iterable->current()); |
928
|
|
|
|
929
|
|
|
$iterable->next(); |
930
|
|
|
$this->assertFalse($iterable->valid()); |
931
|
|
|
$this->assertNull($iterable->current()); |
932
|
|
|
} |
933
|
|
|
|
934
|
|
|
public function testDoubleIteration() |
935
|
|
|
{ |
936
|
|
|
$iterable = $this->phoneUtil->findNumbers("+14156667777 foobar +14156667777 ", RegionCode::ZZ); |
937
|
|
|
|
938
|
|
|
$iterable->next(); |
939
|
|
|
$this->assertTrue($iterable->valid()); |
940
|
|
|
$this->assertNotNull($iterable->current()); |
941
|
|
|
|
942
|
|
|
$iterable->next(); |
943
|
|
|
$this->assertTrue($iterable->valid()); |
944
|
|
|
$this->assertNotNull($iterable->current()); |
945
|
|
|
|
946
|
|
|
$iterable->next(); |
947
|
|
|
$this->assertFalse($iterable->valid()); |
948
|
|
|
$this->assertNull($iterable->current()); |
949
|
|
|
} |
950
|
|
|
|
951
|
|
|
/** |
952
|
|
|
* Asserts that the expected match is non-null, and that the raw string and expected |
953
|
|
|
* proto buffer are set appropriately |
954
|
|
|
* |
955
|
|
|
* @param PhoneNumberMatch $match |
956
|
|
|
* @param string $text |
957
|
|
|
* @param string $number |
958
|
|
|
* @param string $region |
959
|
|
|
*/ |
960
|
|
|
private function assertMatchProperties(PhoneNumberMatch $match, $text, $number, $region) |
961
|
|
|
{ |
962
|
|
|
$expectedResult = $this->phoneUtil->parse($number, $region); |
963
|
|
|
$this->assertNotNull($match, "Did not find a number in {$text}; expected {$number}"); |
964
|
|
|
$this->assertEquals($expectedResult, $match->number()); |
965
|
|
|
$this->assertEquals($number, $match->rawString()); |
966
|
|
|
} |
967
|
|
|
|
968
|
|
|
/** |
969
|
|
|
* Asserts that another number can be found in $text starting at $index, and that its |
970
|
|
|
* corresponding range in $start to $end |
971
|
|
|
* |
972
|
|
|
* @param string $text |
973
|
|
|
* @param int $index |
974
|
|
|
* @param int $start |
975
|
|
|
* @param int $end |
976
|
|
|
*/ |
977
|
|
|
private function assertEqualRange($text, $index, $start, $end) |
978
|
|
|
{ |
979
|
|
|
$sub = mb_substr($text, $index, mb_strlen($text) - $index); |
980
|
|
|
$matches = $this->phoneUtil->findNumbers($sub, RegionCode::NZ, Leniency::POSSIBLE(), PHP_INT_MAX); |
981
|
|
|
|
982
|
|
|
$matches->next(); |
983
|
|
|
$this->assertTrue($matches->valid()); |
984
|
|
|
|
985
|
|
|
$match = $matches->current(); |
986
|
|
|
|
987
|
|
|
$this->assertEquals($start - $index, $match->start()); |
988
|
|
|
$this->assertEquals($end - $index, $match->end()); |
989
|
|
|
$this->assertEquals(mb_substr($sub, $match->start(), $match->end() - $match->start()), $match->rawString()); |
990
|
|
|
} |
991
|
|
|
|
992
|
|
|
/** |
993
|
|
|
* Tests numbers found by PhoneNumberUtil->findNumbers in various |
994
|
|
|
* textual contexts |
995
|
|
|
* |
996
|
|
|
* @param string $number The number to test |
997
|
|
|
* @param string $defaultCountry The corresponding region code |
998
|
|
|
*/ |
999
|
|
|
private function doTestFindInContext($number, $defaultCountry) |
1000
|
|
|
{ |
1001
|
|
|
$this->findPossibleInContext($number, $defaultCountry); |
1002
|
|
|
|
1003
|
|
|
$parsed = $this->phoneUtil->parse($number, $defaultCountry); |
1004
|
|
|
if ($this->phoneUtil->isValidNumber($parsed)) { |
1005
|
|
|
$this->findValidInContext($number, $defaultCountry); |
1006
|
|
|
} |
1007
|
|
|
} |
1008
|
|
|
|
1009
|
|
|
/** |
1010
|
|
|
* Tests valid numbers in contexts that should pass for Leniency::POSSIBLE() |
1011
|
|
|
* @param $number |
1012
|
|
|
* @param $defaultCountry |
1013
|
|
|
*/ |
1014
|
|
|
private function findPossibleInContext($number, $defaultCountry) |
1015
|
|
|
{ |
1016
|
|
|
$contextPairs = array(); |
1017
|
|
|
$contextPairs[] = array("", ""); // no content |
1018
|
|
|
$contextPairs[] = array(" ", "\t"); // whitespace only |
1019
|
|
|
$contextPairs[] = array("Hello ", ""); // no context at end |
1020
|
|
|
$contextPairs[] = array("", " to call me!"); // no context at start |
1021
|
|
|
$contextPairs[] = array("Hi there, call ", " to reach me!"); // no context at start |
1022
|
|
|
$contextPairs[] = array("Hi here, call ", " , or don't"); // with commas |
1023
|
|
|
// Three examples without whitespace around the number |
1024
|
|
|
$contextPairs[] = array("Hi call", ""); |
1025
|
|
|
$contextPairs[] = array("", "forme"); |
1026
|
|
|
$contextPairs[] = array("Hi call", "forme"); |
1027
|
|
|
// With other small numbers. |
1028
|
|
|
$contextPairs[] = array("It's cheap! Call ", " before 6:30"); |
1029
|
|
|
// With a second number later. |
1030
|
|
|
$contextPairs[] = array("Call ", " or +1800-123-4567!"); |
1031
|
|
|
$contextPairs[] = array("Call me on June 2 at", ""); // with a Month-Day date |
1032
|
|
|
// With publication pages |
1033
|
|
|
$contextPairs[] = array("As quoted by Alfonso 12-15 (2009), you may call me at ", ""); |
1034
|
|
|
$contextPairs[] = array("As quoted by Alfonso et al. 12-15 (2009), you may call me at ", ""); |
1035
|
|
|
// With dates, written in the American style. |
1036
|
|
|
$contextPairs[] = array("As I said on 03/10/2011, you may call be at ", ""); |
1037
|
|
|
// With trailing numbers after a comma. The 45 should not be considered an extension |
1038
|
|
|
$contextPairs[] = array("", ", 45 days a year"); |
1039
|
|
|
// When matching we don't consider semicolon along with legitimate extension symbol to indicate |
1040
|
|
|
// an extension. The 7246433 should not be considered an extension. |
1041
|
|
|
$contextPairs[] = array("", ";x 7246433"); |
1042
|
|
|
// With a postfix stripped off as it looks like the start of another number. |
1043
|
|
|
$contextPairs[] = array("Call ", "/x12 more"); |
1044
|
|
|
|
1045
|
|
|
$this->doTestInContext($number, $defaultCountry, $contextPairs, Leniency::POSSIBLE()); |
1046
|
|
|
} |
1047
|
|
|
|
1048
|
|
|
/** |
1049
|
|
|
* Tests valid numbers in contexts that fail for Leniency::POSSIBLE() but are valid for |
1050
|
|
|
* Leniency::VALID() |
1051
|
|
|
* |
1052
|
|
|
* @param string $number |
1053
|
|
|
* @param string $defaultCountry |
1054
|
|
|
*/ |
1055
|
|
|
private function findValidInContext($number, $defaultCountry) |
1056
|
|
|
{ |
1057
|
|
|
$contextPairs = array(); |
1058
|
|
|
// With other small numbers |
1059
|
|
|
$contextPairs[] = array("It's only 9.99! Call ", " to buy"); |
1060
|
|
|
// with a number Day.Month.Year date. |
1061
|
|
|
$contextPairs[] = array("Call me on 21.6.1984 at ", ""); |
1062
|
|
|
// With a number Month/Day date. |
1063
|
|
|
$contextPairs[] = array("Call me on 06/21 at ", ""); |
1064
|
|
|
// With a number Day.Month date. |
1065
|
|
|
$contextPairs[] = array("Call me on 21.6. at ", ""); |
1066
|
|
|
// With a number Month/Day/Year date. |
1067
|
|
|
$contextPairs[] = array("Call me on 06/21/84 at ", ""); |
1068
|
|
|
|
1069
|
|
|
$this->doTestInContext($number, $defaultCountry, $contextPairs, Leniency::VALID()); |
1070
|
|
|
} |
1071
|
|
|
|
1072
|
|
|
private function doTestInContext($number, $defaultCountry, $contextPairs, Leniency\AbstractLeniency $leniency) |
1073
|
|
|
{ |
1074
|
|
|
foreach ($contextPairs as $context) { |
1075
|
|
|
$prefix = $context[0]; |
1076
|
|
|
$text = $prefix . $number . $context[1]; |
1077
|
|
|
|
1078
|
|
|
$start = mb_strlen($prefix); |
1079
|
|
|
$end = $start + mb_strlen($number); |
1080
|
|
|
|
1081
|
|
|
$iterator = $this->phoneUtil->findNumbers($text, $defaultCountry, $leniency, PHP_INT_MAX); |
1082
|
|
|
|
1083
|
|
|
$iterator->next(); |
1084
|
|
|
$match = $iterator->current(); |
1085
|
|
|
$this->assertNotNull($match, "Did not find number in '{$text}'; expected '{$number}'"); |
1086
|
|
|
|
1087
|
|
|
$extracted = mb_substr($text, $match->start(), $match->end() - $match->start()); |
1088
|
|
|
$this->assertEquals($start, $match->start(), "Unexpected phone region in '{$text}'; extracted '{$extracted}'"); |
1089
|
|
|
$this->assertEquals($end, $match->end(), "Unexpected phone region in '{$text}'; extracted '{$extracted}'"); |
1090
|
|
|
$this->assertEquals($number, $extracted); |
1091
|
|
|
$this->assertEquals($extracted, $match->rawString()); |
1092
|
|
|
|
1093
|
|
|
$this->ensureTermination($text, $defaultCountry, $leniency); |
1094
|
|
|
} |
1095
|
|
|
} |
1096
|
|
|
|
1097
|
|
|
private function ensureTermination($text, $defaultCountry, Leniency\AbstractLeniency $leniency) |
1098
|
|
|
{ |
1099
|
|
|
|
1100
|
|
|
$textLength = mb_strlen($text); |
1101
|
|
|
for ($index = 0; $index <= $textLength; $index++) { |
1102
|
|
|
$sub = mb_substr($text, $index); |
1103
|
|
|
$matches = ""; |
1104
|
|
|
// Iterates over all matches. |
1105
|
|
|
foreach ($this->phoneUtil->findNumbers($sub, $defaultCountry, $leniency, PHP_INT_MAX) as $match) |
1106
|
|
|
{ |
1107
|
|
|
$matches .= ", " . $match; |
1108
|
|
|
} |
1109
|
|
|
} |
1110
|
|
|
} |
1111
|
|
|
|
1112
|
|
|
private function findNumbersForLeniency($text, $defaultCountry, Leniency\AbstractLeniency $leniency) |
1113
|
|
|
{ |
1114
|
|
|
return $this->phoneUtil->findNumbers($text, $defaultCountry, $leniency, PHP_INT_MAX); |
1115
|
|
|
} |
1116
|
|
|
|
1117
|
|
|
private function hasNoMatches(PhoneNumberMatcher $match) |
1118
|
|
|
{ |
1119
|
|
|
return !$match->valid(); |
1120
|
|
|
} |
1121
|
|
|
} |
1122
|
|
|
|
Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.
The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.
This check looks for comments that seem to be mostly valid code and reports them.