Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1441 ariadna 1
<?php
2
 
3
/**
4
 * Methods for getting information about short phone numbers, such as short codes and emergency
5
 * numbers. Note that most commercial short numbers are not handled here, but by the
6
 * {@link PhoneNumberUtil}.
7
 *
8
 * @author Shaopeng Jia
9
 * @author David Yonge-Mallo
10
 * @since 5.8
11
 */
12
 
13
namespace libphonenumber;
14
 
15
class ShortNumberInfo
16
{
17
    protected static ?ShortNumberInfo $instance;
18
    protected MatcherAPIInterface $matcherAPI;
19
    protected string $currentFilePrefix;
20
    /**
21
     * @var array<string,PhoneMetadata>
22
     */
23
    protected array $regionToMetadataMap = [];
24
    /**
25
     * @var array<int,string[]>
26
     */
27
    protected array $countryCallingCodeToRegionCodeMap = [];
28
    /**
29
     * @var array<int,PhoneMetadata>
30
     */
31
    protected array $countryCodeToNonGeographicalMetadataMap = [];
32
    protected const REGIONS_WHERE_EMERGENCY_NUMBERS_MUST_BE_EXACT = [
33
        'BR',
34
        'CL',
35
        'NI',
36
    ];
37
 
38
    protected function __construct(MatcherAPIInterface $matcherAPI)
39
    {
40
        $this->matcherAPI = $matcherAPI;
41
 
42
        // TODO: Create ShortNumberInfo for a given map
43
        $this->countryCallingCodeToRegionCodeMap = CountryCodeToRegionCodeMap::COUNTRY_CODE_TO_REGION_CODE_MAP;
44
 
45
        $this->currentFilePrefix = __DIR__ . '/data/ShortNumberMetadata';
46
 
47
        // Initialise PhoneNumberUtil to make sure regex's are setup correctly
48
        PhoneNumberUtil::getInstance();
49
    }
50
 
51
    public static function getInstance(): ShortNumberInfo
52
    {
53
        if (!isset(static::$instance)) {
54
            static::$instance = new self(RegexBasedMatcher::create());
55
        }
56
 
57
        return static::$instance;
58
    }
59
 
60
    public static function resetInstance(): void
61
    {
62
        static::$instance = null;
63
    }
64
 
65
    /**
66
     * Returns a list with the region codes that match the specific country calling code. For
67
     * non-geographical country calling codes, the region code 001 is returned. Also, in the case
68
     * of no region code being found, an empty list is returned.
69
     *
70
     * @return string[]
71
     */
72
    protected function getRegionCodesForCountryCode(int $countryCallingCode): array
73
    {
74
        return $this->countryCallingCodeToRegionCodeMap[$countryCallingCode] ?? [];
75
    }
76
 
77
    /**
78
     * Helper method to check that the country calling code of the number matches the region it's
79
     * being dialed from.
80
     */
81
    protected function regionDialingFromMatchesNumber(PhoneNumber $number, string $regionDialingFrom): bool
82
    {
83
        if (trim($regionDialingFrom) === '') {
84
            return false;
85
        }
86
 
87
        $regionCodes = $this->getRegionCodesForCountryCode($number->getCountryCode());
88
 
89
        return in_array(strtoupper($regionDialingFrom), $regionCodes);
90
    }
91
 
92
    /**
93
     * @return string[]
94
     */
95
    public function getSupportedRegions(): array
96
    {
97
        return ShortNumbersRegionCodeSet::SHORT_NUMBERS_REGION_CODE_SET;
98
    }
99
 
100
    /**
101
     * Gets a valid short number for the specified region.
102
     *
103
     * @param $regionCode string the region for which an example short number is needed
104
     * @return string a valid short number for the specified region. Returns an empty string when the
105
     *      metadata does not contain such information.
106
     */
107
    public function getExampleShortNumber(string $regionCode): string
108
    {
109
        $phoneMetadata = $this->getMetadataForRegion($regionCode);
110
        if ($phoneMetadata === null) {
111
            return '';
112
        }
113
 
114
        /** @var PhoneNumberDesc $desc */
115
        $desc = $phoneMetadata->getShortCode();
116
        if ($desc !== null && $desc->hasExampleNumber()) {
117
            return $desc->getExampleNumber();
118
        }
119
        return '';
120
    }
121
 
122
    public function getMetadataForRegion(string $regionCode): ?PhoneMetadata
123
    {
124
        $regionCode = strtoupper($regionCode);
125
 
126
        if (!in_array($regionCode, ShortNumbersRegionCodeSet::SHORT_NUMBERS_REGION_CODE_SET)) {
127
            return null;
128
        }
129
 
130
        if (!isset($this->regionToMetadataMap[$regionCode])) {
131
            // The regionCode here will be valid and won't be '001', so we don't need to worry about
132
            // what to pass in for the country calling code.
133
            $this->loadMetadataFromFile($this->currentFilePrefix, $regionCode, 0);
134
        }
135
 
136
        return $this->regionToMetadataMap[$regionCode] ?? null;
137
    }
138
 
139
    protected function loadMetadataFromFile(string $filePrefix, string $regionCode, int $countryCallingCode): void
140
    {
141
        $isNonGeoRegion = PhoneNumberUtil::REGION_CODE_FOR_NON_GEO_ENTITY === $regionCode;
142
        $fileName = $filePrefix . '_' . ($isNonGeoRegion ? $countryCallingCode : $regionCode) . '.php';
143
        if (!is_readable($fileName)) {
144
            throw new \Exception('missing metadata: ' . $fileName);
145
        }
146
 
147
        $metadataLoader = new DefaultMetadataLoader();
148
        $data = $metadataLoader->loadMetadata($fileName);
149
 
150
        $metadata = new PhoneMetadata();
151
        $metadata->fromArray($data);
152
        if ($isNonGeoRegion) {
153
            $this->countryCodeToNonGeographicalMetadataMap[$countryCallingCode] = $metadata;
154
        } else {
155
            $this->regionToMetadataMap[$regionCode] = $metadata;
156
        }
157
    }
158
 
159
    /**
160
     *  Gets a valid short number for the specified cost category.
161
     *
162
     * @param string $regionCode the region for which an example short number is needed
163
     * @param int $cost the ShortNumberCost category of number that is needed
164
     * @return string a valid short number for the specified region and cost category. Returns an empty string
165
     * when the metadata does not contain such information, or the cost is UNKNOWN_COST.
166
     */
167
    public function getExampleShortNumberForCost(string $regionCode, int $cost): string
168
    {
169
        $phoneMetadata = $this->getMetadataForRegion($regionCode);
170
        if ($phoneMetadata === null) {
171
            return '';
172
        }
173
 
174
        /** @var PhoneNumberDesc $desc */
175
        $desc = null;
176
        switch ($cost) {
177
            case ShortNumberCost::TOLL_FREE:
178
                $desc = $phoneMetadata->getTollFree();
179
                break;
180
            case ShortNumberCost::STANDARD_RATE:
181
                $desc = $phoneMetadata->getStandardRate();
182
                break;
183
            case ShortNumberCost::PREMIUM_RATE:
184
                $desc = $phoneMetadata->getPremiumRate();
185
                break;
186
            default:
187
                // UNKNOWN_COST numbers are computed by the process of elimination from the other cost categories
188
                break;
189
        }
190
 
191
        if ($desc !== null && $desc->hasExampleNumber()) {
192
            return $desc->getExampleNumber();
193
        }
194
 
195
        return '';
196
    }
197
 
198
    /**
199
     * Returns true if the given number, exactly as dialed, might be used to connect to an emergency
200
     * service in the given region.
201
     * <p>
202
     * This method accepts a string, rather than a PhoneNumber, because it needs to distinguish
203
     * cases such as "+1 911" and "911", where the former may not connect to an emergency service in
204
     * all cases but the latter would. This method takes into account cases where the number might
205
     * contain formatting, or might have additional digits appended (when it is okay to do that in
206
     * the specified region).
207
     *
208
     * @param string $number the phone number to test
209
     * @param string $regionCode the region where the phone number if being dialled
210
     * @return boolean whether the number might be used to connect to an emergency service in the given region
211
     */
212
    public function connectsToEmergencyNumber(string $number, string $regionCode): bool
213
    {
214
        return $this->matchesEmergencyNumberHelper($number, $regionCode, true /* allows prefix match */);
215
    }
216
 
217
    /**
218
     */
219
    protected function matchesEmergencyNumberHelper(string $number, string $regionCode, bool $allowPrefixMatch): bool
220
    {
221
        $number = PhoneNumberUtil::extractPossibleNumber($number);
222
        $matcher = new Matcher(PhoneNumberUtil::PLUS_CHARS_PATTERN, $number);
223
        if ($matcher->lookingAt()) {
224
            // Returns false if the number starts with a plus sign. We don't believe dialing the country
225
            // code before emergency numbers (e.g. +1911) works, but later, if that proves to work, we can
226
            // add additional logic here to handle it.
227
            return false;
228
        }
229
 
230
        $metadata = $this->getMetadataForRegion($regionCode);
231
        if ($metadata === null || !$metadata->hasEmergency()) {
232
            return false;
233
        }
234
 
235
        $normalizedNumber = PhoneNumberUtil::normalizeDigitsOnly($number);
236
        $emergencyDesc = $metadata->getEmergency();
237
 
238
        $allowPrefixMatchForRegion = (
239
            $allowPrefixMatch
240
            && !in_array(strtoupper($regionCode), static::REGIONS_WHERE_EMERGENCY_NUMBERS_MUST_BE_EXACT)
241
        );
242
 
243
        return $this->matcherAPI->matchNationalNumber($normalizedNumber, $emergencyDesc, $allowPrefixMatchForRegion);
244
    }
245
 
246
    /**
247
     * Given a valid short number, determines whether it is carrier-specific (however, nothing is
248
     * implied about its validity). Carrier-specific numbers may connect to a different end-point, or
249
     * not connect at all, depending on the user's carrier. If it is important that the number is
250
     * valid, then its validity must first be checked using {@link #isValidShortNumber} or
251
     * {@link #isValidShortNumberForRegion}.
252
     *
253
     * @param PhoneNumber $number the valid short number to check
254
     * @return boolean whether the short number is carrier-specific, assuming the input was a valid short
255
     *     number
256
     */
257
    public function isCarrierSpecific(PhoneNumber $number): bool
258
    {
259
        $regionCodes = $this->getRegionCodesForCountryCode($number->getCountryCode());
260
        $regionCode = $this->getRegionCodeForShortNumberFromRegionList($number, $regionCodes);
261
        $nationalNumber = $this->getNationalSignificantNumber($number);
262
        $phoneMetadata = $this->getMetadataForRegion($regionCode);
263
 
264
        return ($phoneMetadata !== null) && $this->matchesPossibleNumberAndNationalNumber(
265
            $nationalNumber,
266
            $phoneMetadata->getCarrierSpecific()
267
        );
268
    }
269
 
270
    /**
271
     * Given a valid short number, determines whether it is carrier-specific when dialed from the
272
     * given region (however, nothing is implied about its validity). Carrier-specific numbers may
273
     * connect to a different end-point, or not connect at all, depending on the user's carrier. If
274
     * it is important that the number is valid, then its validity must first be checked using
275
     * {@link #isValidShortNumber} or {@link #isValidShortNumberForRegion}. Returns false if the
276
     * number doesn't match the region provided.
277
     * @param PhoneNumber $number The valid short number to check
278
     * @param string $regionDialingFrom The region from which the number is dialed
279
     * @return bool Whether the short number is carrier-specific in the provided region, assuming the
280
     *      input was a valid short number
281
     */
282
    public function isCarrierSpecificForRegion(PhoneNumber $number, string $regionDialingFrom): bool
283
    {
284
        if (!$this->regionDialingFromMatchesNumber($number, $regionDialingFrom)) {
285
            return false;
286
        }
287
 
288
        $nationalNumber = $this->getNationalSignificantNumber($number);
289
        $phoneMetadata = $this->getMetadataForRegion($regionDialingFrom);
290
 
291
        return ($phoneMetadata !== null)
292
            && $this->matchesPossibleNumberAndNationalNumber($nationalNumber, $phoneMetadata->getCarrierSpecific());
293
    }
294
 
295
    /**
296
     * Given a valid short number, determines whether it is an SMS service (however, nothing is
297
     * implied about its validity). An SMS service is where the primary or only intended usage is to
298
     * receive and/or send text messages (SMSs). This includes MMS as MMS numbers downgrade to SMS if
299
     * the other party isn't MMS-capable. If it is important that the number is valid, then its
300
     * validity must first be checked using {@link #isValidShortNumber} or {@link
301
     * #isValidShortNumberForRegion}. Returns false if the number doesn't match the region provided.
302
     *
303
     * @param PhoneNumber $number The valid short number to check
304
     * @param string $regionDialingFrom The region from which the number is dialed
305
     * @return bool Whether the short number is an SMS service in the provided region, assuming the input
306
     *  was a valid short number.
307
     */
308
    public function isSmsServiceForRegion(PhoneNumber $number, string $regionDialingFrom): bool
309
    {
310
        if (!$this->regionDialingFromMatchesNumber($number, $regionDialingFrom)) {
311
            return false;
312
        }
313
 
314
        $phoneMetadata = $this->getMetadataForRegion($regionDialingFrom);
315
 
316
        return ($phoneMetadata !== null)
317
            && $this->matchesPossibleNumberAndNationalNumber(
318
                $this->getNationalSignificantNumber($number),
319
                $phoneMetadata->getSmsServices()
320
            );
321
    }
322
 
323
    /**
324
     * Helper method to get the region code for a given phone number, from a list of possible region
325
     * codes. If the list contains more than one region, the first region for which the number is
326
     * valid is returned.
327
     *
328
     * @param string[] $regionCodes
329
     * @return string|null Region Code (or null if none are found)
330
     */
331
    protected function getRegionCodeForShortNumberFromRegionList(PhoneNumber $number, array $regionCodes): ?string
332
    {
333
        if ($regionCodes === []) {
334
            return null;
335
        }
336
 
337
        if (count($regionCodes) === 1) {
338
            return $regionCodes[0];
339
        }
340
 
341
        $nationalNumber = $this->getNationalSignificantNumber($number);
342
 
343
        foreach ($regionCodes as $regionCode) {
344
            $phoneMetadata = $this->getMetadataForRegion($regionCode);
345
            if ($phoneMetadata !== null
346
                && $this->matchesPossibleNumberAndNationalNumber($nationalNumber, $phoneMetadata->getShortCode())
347
            ) {
348
                // The number is valid for this region.
349
                return $regionCode;
350
            }
351
        }
352
        return null;
353
    }
354
 
355
    /**
356
     * Check whether a short number is a possible number. If a country calling code is shared by
357
     * multiple regions, this returns true if it's possible in any of them. This provides a more
358
     * lenient check than {@link #isValidShortNumber}. See {@link
359
     * #IsPossibleShortNumberForRegion(PhoneNumber, String)} for details.
360
     *
361
     * @param $number PhoneNumber the short number to check
362
     * @return boolean whether the number is a possible short number
363
     */
364
    public function isPossibleShortNumber(PhoneNumber $number): bool
365
    {
366
        $regionCodes = $this->getRegionCodesForCountryCode($number->getCountryCode());
367
        $shortNumberLength = strlen($this->getNationalSignificantNumber($number));
368
 
369
        foreach ($regionCodes as $region) {
370
            $phoneMetadata = $this->getMetadataForRegion($region);
371
 
372
            if ($phoneMetadata === null) {
373
                continue;
374
            }
375
 
376
            if (in_array($shortNumberLength, $phoneMetadata->getGeneralDesc()->getPossibleLength())) {
377
                return true;
378
            }
379
        }
380
 
381
        return false;
382
    }
383
 
384
    /**
385
     * Check whether a short number is a possible number when dialled from a region, given the number
386
     * in the form of a string, and the region where the number is dialled from. This provides a more
387
     * lenient check than {@link #isValidShortNumber}.
388
     *
389
     * @param PhoneNumber $shortNumber The short number to check
390
     * @param string $regionDialingFrom Region dialing From
391
     * @return boolean whether the number is a possible short number
392
     */
393
    public function isPossibleShortNumberForRegion(PhoneNumber $shortNumber, string $regionDialingFrom): bool
394
    {
395
        if (!$this->regionDialingFromMatchesNumber($shortNumber, $regionDialingFrom)) {
396
            return false;
397
        }
398
 
399
        $phoneMetadata = $this->getMetadataForRegion($regionDialingFrom);
400
 
401
        if ($phoneMetadata === null) {
402
            return false;
403
        }
404
 
405
        $numberLength = strlen($this->getNationalSignificantNumber($shortNumber));
406
        return in_array($numberLength, $phoneMetadata->getGeneralDesc()->getPossibleLength());
407
    }
408
 
409
    /**
410
     * Tests whether a short number matches a valid pattern. If a country calling code is shared by
411
     * multiple regions, this returns true if it's valid in any of them. Note that this doesn't verify
412
     * the number is actually in use, which is impossible to tell by just looking at the number
413
     * itself. See {@link #isValidShortNumberForRegion(PhoneNumber, String)} for details.
414
     *
415
     * @param $number PhoneNumber the short number for which we want to test the validity
416
     * @return boolean whether the short number matches a valid pattern
417
     */
418
    public function isValidShortNumber(PhoneNumber $number): bool
419
    {
420
        $regionCodes = $this->getRegionCodesForCountryCode($number->getCountryCode());
421
        $regionCode = $this->getRegionCodeForShortNumberFromRegionList($number, $regionCodes);
422
 
423
        if ($regionCode === null) {
424
            return false;
425
        }
426
 
427
        if (count($regionCodes) > 1) {
428
            // If a matching region had been found for the phone number from among two or more regions,
429
            // then we have already implicitly verified its validity for that region.
430
            return true;
431
        }
432
 
433
        return $this->isValidShortNumberForRegion($number, $regionCode);
434
    }
435
 
436
    /**
437
     * Tests whether a short number matches a valid pattern in a region. Note that this doesn't verify
438
     * the number is actually in use, which is impossible to tell by just looking at the number
439
     * itself.
440
     *
441
     * @param PhoneNumber $number The Short number for which we want to test the validity
442
     * @param string $regionDialingFrom the region from which the number is dialed
443
     * @return boolean whether the short number matches a valid pattern
444
     */
445
    public function isValidShortNumberForRegion(PhoneNumber $number, string $regionDialingFrom): bool
446
    {
447
        if (!$this->regionDialingFromMatchesNumber($number, $regionDialingFrom)) {
448
            return false;
449
        }
450
        $phoneMetadata = $this->getMetadataForRegion($regionDialingFrom);
451
 
452
        if ($phoneMetadata === null) {
453
            return false;
454
        }
455
 
456
        $shortNumber = $this->getNationalSignificantNumber($number);
457
 
458
        $generalDesc = $phoneMetadata->getGeneralDesc();
459
 
460
        if (!$this->matchesPossibleNumberAndNationalNumber($shortNumber, $generalDesc)) {
461
            return false;
462
        }
463
 
464
        $shortNumberDesc = $phoneMetadata->getShortCode();
465
 
466
        return $this->matchesPossibleNumberAndNationalNumber($shortNumber, $shortNumberDesc);
467
    }
468
 
469
    /**
470
     * Gets the expected cost category of a short number  when dialled from a region (however, nothing is
471
     * implied about its validity). If it is important that the number is valid, then its validity
472
     * must first be checked using {@link isValidShortNumberForRegion}. Note that emergency numbers
473
     * are always considered toll-free.
474
     * Example usage:
475
     * <pre>{@code
476
     * $shortInfo = ShortNumberInfo::getInstance();
477
     * $shortNumber = PhoneNumberUtil::parse("110", "US);
478
     * $regionCode = "FR";
479
     * if ($shortInfo->isValidShortNumberForRegion($shortNumber, $regionCode)) {
480
     *     $cost = $shortInfo->getExpectedCostForRegion($shortNumber, $regionCode);
481
     *    // Do something with the cost information here.
482
     * }}</pre>
483
     *
484
     * @param PhoneNumber $number the short number for which we want to know the expected cost category,
485
     *     as a string
486
     * @param string $regionDialingFrom the region from which the number is dialed
487
     * @return int the expected cost category for that region of the short number. Returns UNKNOWN_COST if
488
     *     the number does not match a cost category. Note that an invalid number may match any cost
489
     *     category.
490
     * @see ShortNumberCost
491
     */
492
    public function getExpectedCostForRegion(PhoneNumber $number, string $regionDialingFrom): int
493
    {
494
        if (!$this->regionDialingFromMatchesNumber($number, $regionDialingFrom)) {
495
            return ShortNumberCost::UNKNOWN_COST;
496
        }
497
        // Note that regionDialingFrom may be null, in which case phoneMetadata will also be null.
498
        $phoneMetadata = $this->getMetadataForRegion($regionDialingFrom);
499
        if ($phoneMetadata === null) {
500
            return ShortNumberCost::UNKNOWN_COST;
501
        }
502
 
503
        $shortNumber = $this->getNationalSignificantNumber($number);
504
 
505
        // The possible lengths are not present for a particular sub-type if they match the general
506
        // description; for this reason, we check the possible lengths against the general description
507
        // first to allow an early exit if possible.
508
        if (!in_array(strlen($shortNumber), $phoneMetadata->getGeneralDesc()->getPossibleLength())) {
509
            return ShortNumberCost::UNKNOWN_COST;
510
        }
511
 
512
        // The cost categories are tested in order of decreasing expense, since if for some reason the
513
        // patterns overlap the most expensive matching cost category should be returned.
514
        if ($this->matchesPossibleNumberAndNationalNumber($shortNumber, $phoneMetadata->getPremiumRate())) {
515
            return ShortNumberCost::PREMIUM_RATE;
516
        }
517
 
518
        if ($this->matchesPossibleNumberAndNationalNumber($shortNumber, $phoneMetadata->getStandardRate())) {
519
            return ShortNumberCost::STANDARD_RATE;
520
        }
521
 
522
        if ($this->matchesPossibleNumberAndNationalNumber($shortNumber, $phoneMetadata->getTollFree())) {
523
            return ShortNumberCost::TOLL_FREE;
524
        }
525
 
526
        if ($this->isEmergencyNumber($shortNumber, $regionDialingFrom)) {
527
            // Emergency numbers are implicitly toll-free.
528
            return ShortNumberCost::TOLL_FREE;
529
        }
530
 
531
        return ShortNumberCost::UNKNOWN_COST;
532
    }
533
 
534
    /**
535
     * Gets the expected cost category of a short number (however, nothing is implied about its
536
     * validity). If the country calling code is unique to a region, this method behaves exactly the
537
     * same as {@link #getExpectedCostForRegion(PhoneNumber, String)}. However, if the country calling
538
     * code is shared by multiple regions, then it returns the highest cost in the sequence
539
     * PREMIUM_RATE, UNKNOWN_COST, STANDARD_RATE, TOLL_FREE. The reason for the position of
540
     * UNKNOWN_COST in this order is that if a number is UNKNOWN_COST in one region but STANDARD_RATE
541
     * or TOLL_FREE in another, its expected cost cannot be estimated as one of the latter since it
542
     * might be a PREMIUM_RATE number.
543
     *
544
     * <p>
545
     * For example, if a number is STANDARD_RATE in the US, but TOLL_FREE in Canada, the expected
546
     * cost returned by this method will be STANDARD_RATE, since the NANPA countries share the same
547
     * country calling code.
548
     * </p>
549
     *
550
     * Note: If the region from which the number is dialed is known, it is highly preferable to call
551
     * {@link #getExpectedCostForRegion(PhoneNumber, String)} instead.
552
     *
553
     * @param PhoneNumber $number the short number for which we want to know the expected cost category
554
     * @return int the highest expected cost category of the short number in the region(s) with the given
555
     *     country calling code
556
     * @see ShortNumberCost
557
     */
558
    public function getExpectedCost(PhoneNumber $number): int
559
    {
560
        $regionCodes = $this->getRegionCodesForCountryCode($number->getCountryCode());
561
        if ($regionCodes === []) {
562
            return ShortNumberCost::UNKNOWN_COST;
563
        }
564
        if (count($regionCodes) === 1) {
565
            return $this->getExpectedCostForRegion($number, $regionCodes[0]);
566
        }
567
        $cost = ShortNumberCost::TOLL_FREE;
568
        foreach ($regionCodes as $regionCode) {
569
            $costForRegion = $this->getExpectedCostForRegion($number, $regionCode);
570
            switch ($costForRegion) {
571
                case ShortNumberCost::PREMIUM_RATE:
572
                    return ShortNumberCost::PREMIUM_RATE;
573
 
574
                case ShortNumberCost::UNKNOWN_COST:
575
                    $cost = ShortNumberCost::UNKNOWN_COST;
576
                    break;
577
 
578
                case ShortNumberCost::STANDARD_RATE:
579
                    if ($cost !== ShortNumberCost::UNKNOWN_COST) {
580
                        $cost = ShortNumberCost::STANDARD_RATE;
581
                    }
582
                    break;
583
                case ShortNumberCost::TOLL_FREE:
584
                    // Do nothing
585
                    break;
586
            }
587
        }
588
        return $cost;
589
    }
590
 
591
    /**
592
     * Returns true if the given number exactly matches an emergency service number in the given
593
     * region.
594
     * <p>
595
     * This method takes into account cases where the number might contain formatting, but doesn't
596
     * allow additional digits to be appended. Note that {@code isEmergencyNumber(number, region)}
597
     * implies {@code connectsToEmergencyNumber(number, region)}.
598
     *
599
     * @param string $number the phone number to test
600
     * @param string $regionCode the region where the phone number is being dialled
601
     * @return boolean whether the number exactly matches an emergency services number in the given region
602
     */
603
    public function isEmergencyNumber(string $number, string $regionCode): bool
604
    {
605
        return $this->matchesEmergencyNumberHelper($number, $regionCode, false /* doesn't allow prefix match */);
606
    }
607
 
608
    /**
609
     * Gets the national significant number of the a phone number. Note a national significant number
610
     * doesn't contain a national prefix or any formatting.
611
     * <p>
612
     * This is a temporary duplicate of the {@code getNationalSignificantNumber} method from
613
     * {@code PhoneNumberUtil}. Ultimately a canonical static version should exist in a separate
614
     * utility class (to prevent {@code ShortNumberInfo} needing to depend on PhoneNumberUtil).
615
     *
616
     * @param PhoneNumber $number the phone number for which the national significant number is needed
617
     * @return string the national significant number of the PhoneNumber object passed in
618
     */
619
    protected function getNationalSignificantNumber(PhoneNumber $number): string
620
    {
621
        // If leading zero(s) have been set, we prefix this now. Note this is not a national prefix.
622
        $nationalNumber = '';
623
        if ($number->isItalianLeadingZero()) {
624
            $zeros = str_repeat('0', $number->getNumberOfLeadingZeros());
625
            $nationalNumber .= $zeros;
626
        }
627
 
628
        $nationalNumber .= $number->getNationalNumber();
629
 
630
        return $nationalNumber;
631
    }
632
 
633
    /**
634
     * TODO: Once we have benchmarked ShortnumberInfo, consider if it is worth keeping
635
     * this performance optimization.
636
     */
637
    protected function matchesPossibleNumberAndNationalNumber(string $number, PhoneNumberDesc $numberDesc): bool
638
    {
639
        if (count($numberDesc->getPossibleLength()) > 0 && !in_array(strlen($number), $numberDesc->getPossibleLength())) {
640
            return false;
641
        }
642
 
643
        return $this->matcherAPI->matchNationalNumber($number, $numberDesc, false);
644
    }
645
}