Обновление клиента (apps, 3rdparty, install)

This commit is contained in:
root
2026-03-16 08:42:57 +00:00
parent b8905de237
commit f390426546
3354 changed files with 505213 additions and 3 deletions
+177
View File
@@ -0,0 +1,177 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
@@ -0,0 +1,11 @@
<?php
declare(strict_types=1);
/**
* This file specifies the revision of the metadata to build from.
* It can be a commit, branch or tag of the https://github.com/google/libphonenumber project
* For more information, look at the phing tasks in build.xml
* @internal
*/
return 'v9.0.17';
@@ -0,0 +1,36 @@
<?php
declare(strict_types=1);
namespace libphonenumber;
/**
* Country code source from number
*/
enum CountryCodeSource: int
{
/**
* The country_code is derived based on a phone number with a leading "+", e.g. the French
* number "+33 1 42 68 53 00".
*/
case FROM_NUMBER_WITH_PLUS_SIGN = 0;
/**
* The country_code is derived based on a phone number with a leading IDD, e.g. the French
* number "011 33 1 42 68 53 00", as it is dialled from US.
*/
case FROM_NUMBER_WITH_IDD = 1;
/**
* The country_code is derived based on a phone number without a leading "+", e.g. the French
* number "33 1 42 68 53 00" when defaultCountry is supplied as France.
*/
case FROM_NUMBER_WITHOUT_PLUS_SIGN = 2;
/**
* The country_code is derived NOT based on the phone number itself, but from the defaultCountry
* parameter provided in the parsing function by the clients. This happens mostly for numbers
* written in the national format (without country code). For example, this would be set when
* parsing the French number "01 42 68 53 00", when defaultCountry is supplied as France.
*/
case FROM_DEFAULT_COUNTRY = 3;
case UNSPECIFIED = 4;
}
@@ -0,0 +1,269 @@
<?php
/**
* libphonenumber-for-php-lite data file
* This file has been @generated from libphonenumber data
* Do not modify!
* @internal
*/
declare(strict_types=1);
namespace libphonenumber;
/**
* @internal
*/
class CountryCodeToRegionCodeMap
{
/**
* A mapping from a country code to the region codes which denote the
* country/region represented by that country code. In the case of multiple
* countries sharing a calling code, such as the NANPA countries, the one
* indicated with "isMainCountryForCode" in the metadata should be first.
* @var array<int,string[]>
*/
public const COUNTRY_CODE_TO_REGION_CODE_MAP = [
1 => [
'US',
'AG',
'AI',
'AS',
'BB',
'BM',
'BS',
'CA',
'DM',
'DO',
'GD',
'GU',
'JM',
'KN',
'KY',
'LC',
'MP',
'MS',
'PR',
'SX',
'TC',
'TT',
'VC',
'VG',
'VI',
],
7 => ['RU', 'KZ'],
20 => ['EG'],
27 => ['ZA'],
30 => ['GR'],
31 => ['NL'],
32 => ['BE'],
33 => ['FR'],
34 => ['ES'],
36 => ['HU'],
39 => ['IT', 'VA'],
40 => ['RO'],
41 => ['CH'],
43 => ['AT'],
44 => ['GB', 'GG', 'IM', 'JE'],
45 => ['DK'],
46 => ['SE'],
47 => ['NO', 'SJ'],
48 => ['PL'],
49 => ['DE'],
51 => ['PE'],
52 => ['MX'],
53 => ['CU'],
54 => ['AR'],
55 => ['BR'],
56 => ['CL'],
57 => ['CO'],
58 => ['VE'],
60 => ['MY'],
61 => ['AU', 'CC', 'CX'],
62 => ['ID'],
63 => ['PH'],
64 => ['NZ'],
65 => ['SG'],
66 => ['TH'],
81 => ['JP'],
82 => ['KR'],
84 => ['VN'],
86 => ['CN'],
90 => ['TR'],
91 => ['IN'],
92 => ['PK'],
93 => ['AF'],
94 => ['LK'],
95 => ['MM'],
98 => ['IR'],
211 => ['SS'],
212 => ['MA', 'EH'],
213 => ['DZ'],
216 => ['TN'],
218 => ['LY'],
220 => ['GM'],
221 => ['SN'],
222 => ['MR'],
223 => ['ML'],
224 => ['GN'],
225 => ['CI'],
226 => ['BF'],
227 => ['NE'],
228 => ['TG'],
229 => ['BJ'],
230 => ['MU'],
231 => ['LR'],
232 => ['SL'],
233 => ['GH'],
234 => ['NG'],
235 => ['TD'],
236 => ['CF'],
237 => ['CM'],
238 => ['CV'],
239 => ['ST'],
240 => ['GQ'],
241 => ['GA'],
242 => ['CG'],
243 => ['CD'],
244 => ['AO'],
245 => ['GW'],
246 => ['IO'],
247 => ['AC'],
248 => ['SC'],
249 => ['SD'],
250 => ['RW'],
251 => ['ET'],
252 => ['SO'],
253 => ['DJ'],
254 => ['KE'],
255 => ['TZ'],
256 => ['UG'],
257 => ['BI'],
258 => ['MZ'],
260 => ['ZM'],
261 => ['MG'],
262 => ['RE', 'YT'],
263 => ['ZW'],
264 => ['NA'],
265 => ['MW'],
266 => ['LS'],
267 => ['BW'],
268 => ['SZ'],
269 => ['KM'],
290 => ['SH', 'TA'],
291 => ['ER'],
297 => ['AW'],
298 => ['FO'],
299 => ['GL'],
350 => ['GI'],
351 => ['PT'],
352 => ['LU'],
353 => ['IE'],
354 => ['IS'],
355 => ['AL'],
356 => ['MT'],
357 => ['CY'],
358 => ['FI', 'AX'],
359 => ['BG'],
370 => ['LT'],
371 => ['LV'],
372 => ['EE'],
373 => ['MD'],
374 => ['AM'],
375 => ['BY'],
376 => ['AD'],
377 => ['MC'],
378 => ['SM'],
380 => ['UA'],
381 => ['RS'],
382 => ['ME'],
383 => ['XK'],
385 => ['HR'],
386 => ['SI'],
387 => ['BA'],
389 => ['MK'],
420 => ['CZ'],
421 => ['SK'],
423 => ['LI'],
500 => ['FK'],
501 => ['BZ'],
502 => ['GT'],
503 => ['SV'],
504 => ['HN'],
505 => ['NI'],
506 => ['CR'],
507 => ['PA'],
508 => ['PM'],
509 => ['HT'],
590 => ['GP', 'BL', 'MF'],
591 => ['BO'],
592 => ['GY'],
593 => ['EC'],
594 => ['GF'],
595 => ['PY'],
596 => ['MQ'],
597 => ['SR'],
598 => ['UY'],
599 => ['CW', 'BQ'],
670 => ['TL'],
672 => ['NF'],
673 => ['BN'],
674 => ['NR'],
675 => ['PG'],
676 => ['TO'],
677 => ['SB'],
678 => ['VU'],
679 => ['FJ'],
680 => ['PW'],
681 => ['WF'],
682 => ['CK'],
683 => ['NU'],
685 => ['WS'],
686 => ['KI'],
687 => ['NC'],
688 => ['TV'],
689 => ['PF'],
690 => ['TK'],
691 => ['FM'],
692 => ['MH'],
800 => ['001'],
808 => ['001'],
850 => ['KP'],
852 => ['HK'],
853 => ['MO'],
855 => ['KH'],
856 => ['LA'],
870 => ['001'],
878 => ['001'],
880 => ['BD'],
881 => ['001'],
882 => ['001'],
883 => ['001'],
886 => ['TW'],
888 => ['001'],
960 => ['MV'],
961 => ['LB'],
962 => ['JO'],
963 => ['SY'],
964 => ['IQ'],
965 => ['KW'],
966 => ['SA'],
967 => ['YE'],
968 => ['OM'],
970 => ['PS'],
971 => ['AE'],
972 => ['IL'],
973 => ['BH'],
974 => ['QA'],
975 => ['BT'],
976 => ['MN'],
977 => ['NP'],
979 => ['001'],
992 => ['TJ'],
993 => ['TM'],
994 => ['AZ'],
995 => ['GE'],
996 => ['KG'],
998 => ['UZ'],
];
}
@@ -0,0 +1,18 @@
<?php
declare(strict_types=1);
namespace libphonenumber;
/**
* Types of phone number matches
* See detailed description beside the isNumberMatch() method
*/
enum MatchType: int
{
case NOT_A_NUMBER = 0;
case NO_MATCH = 1;
case SHORT_NSN_MATCH = 2;
case NSN_MATCH = 3;
case EXACT_MATCH = 4;
}
@@ -0,0 +1,151 @@
<?php
declare(strict_types=1);
namespace libphonenumber;
/**
* Matcher for various regex matching
*
* Note that this is NOT the same as google's java PhoneNumberMatcher class.
* This class is a minimal port of java's built-in matcher class, whereas PhoneNumberMatcher
* is designed to recognize phone numbers embedded in any text.
*
* @internal
*/
class Matcher
{
protected string $pattern;
protected string $subject = '';
/**
* @var array<int,mixed>
*/
protected array $groups = [];
private int $searchIndex = 0;
public function __construct(string $pattern, string $subject)
{
$this->pattern = str_replace('/', '\/', $pattern);
$this->subject = $subject;
}
protected function doMatch(string $type = 'find', int $offset = 0): bool
{
$final_pattern = '(?:' . $this->pattern . ')';
switch ($type) {
case 'matches':
$final_pattern = '^' . $final_pattern . '$';
break;
case 'lookingAt':
$final_pattern = '^' . $final_pattern;
break;
case 'find':
default:
// no changes
break;
}
$final_pattern = '/' . $final_pattern . '/ui';
$search = mb_substr($this->subject, $offset);
$result = preg_match($final_pattern, $search, $groups, PREG_OFFSET_CAPTURE);
if ($result === 1) {
// Expand $groups into $this->groups, but being multi-byte aware
$positions = [];
foreach ($groups as $group) {
$positions[] = [
$group[0],
$offset + mb_strlen(substr($search, 0, $group[1])),
];
}
$this->groups = $positions;
}
return ($result === 1);
}
public function matches(): bool
{
return $this->doMatch('matches');
}
public function lookingAt(): bool
{
return $this->doMatch('lookingAt');
}
public function find(?int $offset = null): bool
{
if ($offset === null) {
$offset = $this->searchIndex;
}
// Increment search index for the next time we call this
$this->searchIndex++;
return $this->doMatch('find', $offset);
}
public function groupCount(): ?int
{
if ($this->groups === []) {
return null;
}
return count($this->groups) - 1;
}
public function group(?int $group = null): ?string
{
if ($group === null) {
$group = 0;
}
return $this->groups[$group][0] ?? null;
}
public function end(?int $group = null): ?int
{
if ($group === null) {
$group = 0;
}
if (!isset($this->groups[$group])) {
return null;
}
return $this->groups[$group][1] + mb_strlen($this->groups[$group][0]);
}
public function start(?int $group = null): mixed
{
if ($group === null) {
$group = 0;
}
if (!isset($this->groups[$group])) {
return null;
}
return $this->groups[$group][1];
}
public function replaceFirst(string $replacement): string
{
return preg_replace('/' . $this->pattern . '/x', $replacement, $this->subject, 1);
}
public function replaceAll(string $replacement): string
{
return preg_replace('/' . $this->pattern . '/x', $replacement, $this->subject);
}
public function reset(string $input = ''): static
{
$this->subject = $input;
return $this;
}
}
@@ -0,0 +1,23 @@
<?php
declare(strict_types=1);
namespace libphonenumber;
/**
* Interface MatcherAPIInterface
*
* Internal phonenumber matching API used to isolate the underlying implementation of the
* matcher and allow different implementations to be swapped in easily.
*
* @package libphonenumber
* @internal
*/
interface MatcherAPIInterface
{
/**
* Returns whether the given national number (a string containing only decimal digits) matches
* the national number pattern defined in the given {@code PhoneNumberDesc} message.
*/
public function matchNationalNumber(string $number, PhoneNumberDesc $numberDesc, bool $allowPrefixMatch): bool;
}
@@ -0,0 +1,18 @@
<?php
declare(strict_types=1);
namespace libphonenumber;
interface MetadataSourceInterface
{
/**
* Gets phone metadata for a region.
*/
public function getMetadataForRegion(string $regionCode): PhoneMetadata;
/**
* Gets phone metadata for a non-geographical region.
*/
public function getMetadataForNonGeographicalRegion(int $countryCallingCode): PhoneMetadata;
}
@@ -0,0 +1,84 @@
<?php
declare(strict_types=1);
namespace libphonenumber;
use RuntimeException;
/**
* @internal
*/
class MultiFileMetadataSourceImpl implements MetadataSourceInterface
{
/**
* A mapping from a region code to the PhoneMetadata for that region.
* @var PhoneMetadata[]
*/
protected array $regionToMetadataMap = [];
/**
* A mapping from a country calling code for a non-geographical entity to the PhoneMetadata for
* that country calling code. Examples of the country calling codes include 800 (International
* Toll Free Service) and 808 (International Shared Cost Service).
* @var PhoneMetadata[]
*/
protected array $countryCodeToNonGeographicalMetadataMap = [];
/**
* @param string $currentFilePrefix The prefix of the metadata class names from which region data is loaded
*/
public function __construct(
protected readonly string $currentFilePrefix = __NAMESPACE__ . '\data\PhoneNumberMetadata_'
) {}
public function getMetadataForRegion(string $regionCode): PhoneMetadata
{
$regionCode = strtoupper($regionCode);
if (!isset($this->regionToMetadataMap[$regionCode])) {
// The regionCode here will be valid and won't be '001', so we don't need to worry about
// what to pass in for the country calling code.
$this->loadMetadataFromFile($this->currentFilePrefix, $regionCode, 0);
}
return $this->regionToMetadataMap[$regionCode];
}
public function getMetadataForNonGeographicalRegion(int $countryCallingCode): PhoneMetadata
{
if (!isset($this->countryCodeToNonGeographicalMetadataMap[$countryCallingCode])) {
$this->loadMetadataFromFile($this->currentFilePrefix, PhoneNumberUtil::REGION_CODE_FOR_NON_GEO_ENTITY, $countryCallingCode);
}
return $this->countryCodeToNonGeographicalMetadataMap[$countryCallingCode];
}
/**
* @throws RuntimeException
*/
public function loadMetadataFromFile(string $filePrefix, string $regionCode, int $countryCallingCode): void
{
$regionCode = strtoupper($regionCode);
$isNonGeoRegion = PhoneNumberUtil::REGION_CODE_FOR_NON_GEO_ENTITY === $regionCode;
$class = $filePrefix . ($isNonGeoRegion ? $countryCallingCode : ucfirst($regionCode));
if (!class_exists($class)) {
throw new RuntimeException('missing metadata: ' . $class);
}
$metadata = new $class();
if (!$metadata instanceof PhoneMetadata) {
throw new RuntimeException('invalid metadata: ' . $class);
}
if ($isNonGeoRegion) {
$this->countryCodeToNonGeographicalMetadataMap[$countryCallingCode] = $metadata;
} else {
$this->regionToMetadataMap[$regionCode] = $metadata;
}
}
}
@@ -0,0 +1,183 @@
<?php
declare(strict_types=1);
namespace libphonenumber;
/**
* Number Format
* @internal
*/
class NumberFormat
{
protected string $pattern = '';
protected bool $hasPattern = false;
protected string $format = '';
protected bool $hasFormat = false;
/**
* @var array<int,string>
*/
protected array $leadingDigitsPattern = [];
protected string $nationalPrefixFormattingRule = '';
protected bool $hasNationalPrefixFormattingRule = false;
protected bool $nationalPrefixOptionalWhenFormatting = false;
protected bool $hasNationalPrefixOptionalWhenFormatting = false;
protected string $domesticCarrierCodeFormattingRule = '';
protected bool $hasDomesticCarrierCodeFormattingRule = false;
public function hasPattern(): bool
{
return $this->hasPattern;
}
public function getPattern(): string
{
return $this->pattern;
}
public function setPattern(string $value): static
{
$this->hasPattern = true;
$this->pattern = $value;
return $this;
}
public function hasNationalPrefixOptionalWhenFormatting(): bool
{
return $this->hasNationalPrefixOptionalWhenFormatting;
}
public function getNationalPrefixOptionalWhenFormatting(): bool
{
return $this->nationalPrefixOptionalWhenFormatting;
}
public function setNationalPrefixOptionalWhenFormatting(bool $nationalPrefixOptionalWhenFormatting): static
{
$this->hasNationalPrefixOptionalWhenFormatting = true;
$this->nationalPrefixOptionalWhenFormatting = $nationalPrefixOptionalWhenFormatting;
return $this;
}
public function hasFormat(): bool
{
return $this->hasFormat;
}
public function getFormat(): string
{
return $this->format;
}
public function setFormat(string $value): static
{
$this->hasFormat = true;
$this->format = $value;
return $this;
}
/**
* @return array<int,string>
*/
public function leadingDigitPatterns(): array
{
return $this->leadingDigitsPattern;
}
public function leadingDigitsPatternSize(): int
{
return count($this->leadingDigitsPattern);
}
public function getLeadingDigitsPattern(int $index): string
{
return $this->leadingDigitsPattern[$index];
}
/**
* @param array<int,string> $patterns
*/
public function setLeadingDigitsPattern(array $patterns): static
{
$this->leadingDigitsPattern = $patterns;
return $this;
}
public function addLeadingDigitsPattern(string $value): static
{
$this->leadingDigitsPattern[] = $value;
return $this;
}
public function hasNationalPrefixFormattingRule(): bool
{
return $this->hasNationalPrefixFormattingRule;
}
public function getNationalPrefixFormattingRule(): string
{
return $this->nationalPrefixFormattingRule;
}
public function setNationalPrefixFormattingRule(string $value): static
{
$this->hasNationalPrefixFormattingRule = true;
$this->nationalPrefixFormattingRule = $value;
return $this;
}
public function clearNationalPrefixFormattingRule(): static
{
$this->nationalPrefixFormattingRule = '';
return $this;
}
public function hasDomesticCarrierCodeFormattingRule(): bool
{
return $this->hasDomesticCarrierCodeFormattingRule;
}
public function getDomesticCarrierCodeFormattingRule(): string
{
return $this->domesticCarrierCodeFormattingRule;
}
public function setDomesticCarrierCodeFormattingRule(string $value): static
{
$this->hasDomesticCarrierCodeFormattingRule = true;
$this->domesticCarrierCodeFormattingRule = $value;
return $this;
}
public function mergeFrom(NumberFormat $other): static
{
if ($other->hasPattern()) {
$this->setPattern($other->getPattern());
}
if ($other->hasFormat()) {
$this->setFormat($other->getFormat());
}
$leadingDigitsPatternSize = $other->leadingDigitsPatternSize();
for ($i = 0; $i < $leadingDigitsPatternSize; $i++) {
$this->addLeadingDigitsPattern($other->getLeadingDigitsPattern($i));
}
if ($other->hasNationalPrefixFormattingRule()) {
$this->setNationalPrefixFormattingRule($other->getNationalPrefixFormattingRule());
}
if ($other->hasDomesticCarrierCodeFormattingRule()) {
$this->setDomesticCarrierCodeFormattingRule($other->getDomesticCarrierCodeFormattingRule());
}
if ($other->hasNationalPrefixOptionalWhenFormatting()) {
$this->setNationalPrefixOptionalWhenFormatting($other->getNationalPrefixOptionalWhenFormatting());
}
return $this;
}
}
@@ -0,0 +1,64 @@
<?php
declare(strict_types=1);
namespace libphonenumber;
use Exception;
use Stringable;
use Throwable;
/**
* Generic exception class for errors encountered when parsing phone numbers.
*/
class NumberParseException extends Exception implements Stringable
{
/**
* The country code supplied did not belong to a supported country or non-geographical entity.
*/
public const INVALID_COUNTRY_CODE = 0;
/**
* This indicates the string passed is not a valid number. Either the string had less than 3
* digits in it or had an invalid phone-context parameter. More specifically, the number failed
* to match the regular expression VALID_PHONE_NUMBER, RFC3966_GLOBAL_NUMBER_DIGITS, or
* RFC3966_DOMAINNAME in PhoneNumberUtil
*/
public const NOT_A_NUMBER = 1;
/**
* This indicates the string started with an international dialing prefix, but after this was
* stripped from the number, had less digits than any valid phone number (including country
* code) could have.
*/
public const TOO_SHORT_AFTER_IDD = 2;
/**
* This indicates the string, after any country code has been stripped, had less digits than any
* valid phone number could have.
*/
public const TOO_SHORT_NSN = 3;
/**
* This indicates the string had more digits than any valid phone number could have.
*/
public const TOO_LONG = 4;
protected int $errorType;
public function __construct(int $errorType, string $message, ?Throwable $previous = null)
{
parent::__construct($message, $errorType, $previous);
$this->message = $message;
$this->errorType = $errorType;
}
/**
* Returns the error type of the exception that has been thrown.
*/
public function getErrorType(): int
{
return $this->errorType;
}
public function __toString(): string
{
return 'Error type: ' . $this->errorType . '. ' . $this->message;
}
}
@@ -0,0 +1,305 @@
<?php
declare(strict_types=1);
namespace libphonenumber;
use function count;
/**
* Class PhoneMetadata
* @package libphonenumber
* @internal Used internally, and can change at any time
*/
class PhoneMetadata
{
/**
* @var string|null
*/
protected const ID = null;
/**
* @var int|null
*/
protected const COUNTRY_CODE = null;
/**
* @var string|null
*/
protected const LEADING_DIGITS = null;
/**
* @var string|null
*/
protected const NATIONAL_PREFIX = null;
protected ?string $nationalPrefixForParsing = null;
protected ?string $internationalPrefix = null;
protected ?string $preferredInternationalPrefix = null;
protected ?string $nationalPrefixTransformRule = null;
protected ?string $preferredExtnPrefix = null;
protected bool $mainCountryForCode = false;
protected bool $mobileNumberPortableRegion = false;
protected ?PhoneNumberDesc $generalDesc = null;
protected ?PhoneNumberDesc $mobile = null;
protected ?PhoneNumberDesc $premiumRate = null;
protected ?PhoneNumberDesc $fixedLine = null;
protected bool $sameMobileAndFixedLinePattern = false;
/**
* @var NumberFormat[]
*/
protected array $numberFormat = [];
protected ?PhoneNumberDesc $tollFree = null;
protected ?PhoneNumberDesc $sharedCost = null;
protected ?PhoneNumberDesc $personalNumber = null;
protected ?PhoneNumberDesc $voip = null;
protected ?PhoneNumberDesc $pager = null;
protected ?PhoneNumberDesc $uan = null;
protected ?PhoneNumberDesc $emergency = null;
protected ?PhoneNumberDesc $voicemail = null;
protected ?PhoneNumberDesc $short_code = null;
protected ?PhoneNumberDesc $standard_rate = null;
protected ?PhoneNumberDesc $carrierSpecific = null;
protected ?PhoneNumberDesc $smsServices = null;
protected ?PhoneNumberDesc $noInternationalDialling = null;
/**
* @var NumberFormat[]
*/
protected array $intlNumberFormat = [];
public function isMainCountryForCode(): bool
{
return $this->mainCountryForCode;
}
public function getMainCountryForCode(): bool
{
return $this->mainCountryForCode;
}
public function numberFormatSize(): int
{
return count($this->numberFormat);
}
public function getNumberFormat(int $index): NumberFormat
{
return $this->numberFormat[$index];
}
public function intlNumberFormatSize(): int
{
return count($this->intlNumberFormat);
}
public function getIntlNumberFormat(int $index): NumberFormat
{
return $this->intlNumberFormat[$index];
}
public function hasGeneralDesc(): bool
{
return $this->generalDesc !== null;
}
public function getGeneralDesc(): ?PhoneNumberDesc
{
return $this->generalDesc;
}
public function hasFixedLine(): bool
{
return $this->fixedLine !== null;
}
public function getFixedLine(): ?PhoneNumberDesc
{
return $this->fixedLine;
}
public function hasMobile(): bool
{
return $this->mobile !== null;
}
public function getMobile(): ?PhoneNumberDesc
{
return $this->mobile;
}
public function getTollFree(): ?PhoneNumberDesc
{
return $this->tollFree;
}
public function getPremiumRate(): ?PhoneNumberDesc
{
return $this->premiumRate;
}
public function getSharedCost(): ?PhoneNumberDesc
{
return $this->sharedCost;
}
public function getPersonalNumber(): ?PhoneNumberDesc
{
return $this->personalNumber;
}
public function getVoip(): ?PhoneNumberDesc
{
return $this->voip;
}
public function getPager(): ?PhoneNumberDesc
{
return $this->pager;
}
public function getUan(): ?PhoneNumberDesc
{
return $this->uan;
}
public function hasEmergency(): bool
{
return $this->emergency !== null;
}
public function getEmergency(): ?PhoneNumberDesc
{
return $this->emergency;
}
public function getVoicemail(): ?PhoneNumberDesc
{
return $this->voicemail;
}
public function getShortCode(): ?PhoneNumberDesc
{
return $this->short_code;
}
public function getStandardRate(): ?PhoneNumberDesc
{
return $this->standard_rate;
}
public function getCarrierSpecific(): ?PhoneNumberDesc
{
return $this->carrierSpecific;
}
public function getSmsServices(): ?PhoneNumberDesc
{
return $this->smsServices;
}
public function getNoInternationalDialling(): ?PhoneNumberDesc
{
return $this->noInternationalDialling;
}
public function getId(): ?string
{
return static::ID;
}
public function getCountryCode(): ?int
{
return static::COUNTRY_CODE;
}
public function getInternationalPrefix(): ?string
{
return $this->internationalPrefix;
}
public function hasPreferredInternationalPrefix(): bool
{
return ($this->preferredInternationalPrefix !== null);
}
public function getPreferredInternationalPrefix(): ?string
{
return $this->preferredInternationalPrefix;
}
public function hasNationalPrefix(): bool
{
return static::NATIONAL_PREFIX !== null;
}
public function getNationalPrefix(): ?string
{
return static::NATIONAL_PREFIX;
}
public function hasPreferredExtnPrefix(): bool
{
return $this->preferredExtnPrefix !== null;
}
public function getPreferredExtnPrefix(): ?string
{
return $this->preferredExtnPrefix;
}
public function hasNationalPrefixForParsing(): bool
{
return $this->nationalPrefixForParsing !== null;
}
public function getNationalPrefixForParsing(): ?string
{
return $this->nationalPrefixForParsing;
}
public function getNationalPrefixTransformRule(): ?string
{
return $this->nationalPrefixTransformRule;
}
public function getSameMobileAndFixedLinePattern(): bool
{
return $this->sameMobileAndFixedLinePattern;
}
/**
* @return NumberFormat[]
*/
public function numberFormats(): array
{
return $this->numberFormat;
}
/**
* @return NumberFormat[]
*/
public function intlNumberFormats(): array
{
return $this->intlNumberFormat;
}
public function hasLeadingDigits(): bool
{
return static::LEADING_DIGITS !== null;
}
public function getLeadingDigits(): ?string
{
return static::LEADING_DIGITS;
}
public function isMobileNumberPortableRegion(): bool
{
return $this->mobileNumberPortableRegion;
}
public function setInternationalPrefix(string $value): static
{
$this->internationalPrefix = $value;
return $this;
}
}
@@ -0,0 +1,409 @@
<?php
declare(strict_types=1);
namespace libphonenumber;
use Serializable;
/**
* It is not recommended to create PhoneNumber objects directly, instead you should
* use PhoneNumberUtil::parse() to parse the number and return a PhoneNumber object
* @no-named-arguments
*/
class PhoneNumber implements Serializable
{
/**
* The country calling code for this number, as defined by the International Telecommunication Union
* (ITU). For example, this would be 1 for NANPA countries, and 33 for France.
*/
protected ?int $countryCode = null;
/**
* National (significant) Number is defined in International Telecommunication Union (ITU)
* Recommendation E.164. It is a language/country-neutral representation of a phone number at a
* country level. For countries which have the concept of an "area code" or "national destination
* code", this is included in the National (significant) Number. Although the ITU says the maximum
* length should be 15, we have found longer numbers in some countries e.g. Germany.
*
* Note that the National (significant) Number does not contain the National(trunk) prefix.
*/
protected ?string $nationalNumber = null;
/**
* Extension is not standardized in ITU recommendations, except for being defined as a series of
* numbers with a maximum length of 40 digits. It is defined as a string here to accommodate for the
* possible use of a leading zero in the extension (organizations have complete freedom to do so,
* as there is no standard defined). However, only ASCII digits should be stored here.
*/
protected ?string $extension = null;
/**
* In some countries, the national (significant) number starts with one or more "0"s without this
* being a national prefix or trunk code of some kind. For example, the leading zero in the national
* (significant) number of an Italian phone number indicates the number is a fixed-line number.
* There have been plans to migrate fixed-line numbers to start with the digit two since December
* 2000, but it has not happened yet. See http://en.wikipedia.org/wiki/%2B39 for more details.
*
* These fields can be safely ignored (there is no need to set them) for most countries. Some
* limited number of countries behave like Italy - for these cases, if the leading zero(s) of a
* number would be retained even when dialling internationally, set this flag to true, and also
* set the number of leading zeros.
*
* Clients who use the parsing functionality of the i18n phone number libraries
* will have these fields set if necessary automatically.
*/
protected ?bool $italianLeadingZero = null;
/**
* This field is used to store the raw input string containing phone numbers before it was
* canonicalized by the library. For example, it could be used to store alphanumerical numbers
* such as "1-800-GOOG-411".
*/
protected ?string $rawInput = null;
/**
* The source from which the country_code is derived. This is not set in the general parsing method,
* but in the method that parses and keeps raw_input. New fields could be added upon request.
*/
protected ?CountryCodeSource $countryCodeSource = CountryCodeSource::UNSPECIFIED;
/**
* The carrier selection code that is preferred when calling this phone number domestically. This
* also includes codes that need to be dialed in some countries when calling from landlines to
* mobiles or vice versa. For example, in Columbia, a "3" needs to be dialed before the phone number
* itself when calling from a mobile phone to a domestic landline phone and vice versa.
*
* Note this is the "preferred" code, which means other codes may work as well.
*/
protected ?string $preferredDomesticCarrierCode = null;
/**
* Whether this phone number has a number of leading zeros set.
*/
protected bool $hasNumberOfLeadingZeros = false;
/**
* The number of leading zeros of this phone number.
*/
protected int $numberOfLeadingZeros = 1;
public function clear(): static
{
$this->clearCountryCode();
$this->clearNationalNumber();
$this->clearExtension();
$this->clearItalianLeadingZero();
$this->clearNumberOfLeadingZeros();
$this->clearRawInput();
$this->clearCountryCodeSource();
$this->clearPreferredDomesticCarrierCode();
return $this;
}
public function clearCountryCode(): static
{
$this->countryCode = null;
return $this;
}
public function clearNationalNumber(): static
{
$this->nationalNumber = null;
return $this;
}
public function clearExtension(): static
{
$this->extension = null;
return $this;
}
public function clearItalianLeadingZero(): static
{
$this->italianLeadingZero = null;
return $this;
}
public function clearNumberOfLeadingZeros(): static
{
$this->hasNumberOfLeadingZeros = false;
$this->numberOfLeadingZeros = 1;
return $this;
}
public function clearRawInput(): static
{
$this->rawInput = null;
return $this;
}
public function clearCountryCodeSource(): static
{
$this->countryCodeSource = CountryCodeSource::UNSPECIFIED;
return $this;
}
public function clearPreferredDomesticCarrierCode(): static
{
$this->preferredDomesticCarrierCode = null;
return $this;
}
/**
* Merges the information from another phone number into this phone number.
*/
public function mergeFrom(PhoneNumber $other): static
{
if ($other->hasCountryCode()) {
$this->setCountryCode($other->getCountryCode());
}
if ($other->hasNationalNumber()) {
$this->setNationalNumber($other->getNationalNumber());
}
if ($other->hasExtension()) {
$this->setExtension($other->getExtension());
}
if ($other->hasItalianLeadingZero()) {
$this->setItalianLeadingZero($other->isItalianLeadingZero());
}
if ($other->hasNumberOfLeadingZeros()) {
$this->setNumberOfLeadingZeros($other->getNumberOfLeadingZeros());
}
if ($other->hasRawInput()) {
$this->setRawInput($other->getRawInput());
}
if ($other->hasCountryCodeSource()) {
$this->setCountryCodeSource($other->getCountryCodeSource());
}
if ($other->hasPreferredDomesticCarrierCode()) {
$this->setPreferredDomesticCarrierCode($other->getPreferredDomesticCarrierCode());
}
return $this;
}
public function hasCountryCode(): bool
{
return $this->countryCode !== null;
}
public function getCountryCode(): ?int
{
return $this->countryCode;
}
public function setCountryCode(int $value): static
{
$this->countryCode = $value;
return $this;
}
public function hasNationalNumber(): bool
{
return $this->nationalNumber !== null;
}
public function getNationalNumber(): ?string
{
return $this->nationalNumber;
}
public function setNationalNumber(string $value): static
{
$this->nationalNumber = $value;
return $this;
}
public function hasExtension(): bool
{
return isset($this->extension) && $this->extension !== '';
}
public function getExtension(): ?string
{
return $this->extension;
}
public function setExtension(string $value): static
{
$this->extension = $value;
return $this;
}
public function hasItalianLeadingZero(): bool
{
return isset($this->italianLeadingZero);
}
public function setItalianLeadingZero(bool $value): static
{
$this->italianLeadingZero = $value;
return $this;
}
/**
* Returns whether this phone number uses an italian leading zero.
*
* @return bool|null True if it uses an italian leading zero, false it it does not, null if not set.
*/
public function isItalianLeadingZero(): ?bool
{
return $this->italianLeadingZero ?? null;
}
public function hasNumberOfLeadingZeros(): bool
{
return $this->hasNumberOfLeadingZeros;
}
public function getNumberOfLeadingZeros(): int
{
return $this->numberOfLeadingZeros;
}
public function setNumberOfLeadingZeros(int $value): static
{
$this->hasNumberOfLeadingZeros = true;
$this->numberOfLeadingZeros = $value;
return $this;
}
public function hasRawInput(): bool
{
return isset($this->rawInput);
}
public function getRawInput(): ?string
{
return $this->rawInput;
}
public function setRawInput(string $value): static
{
$this->rawInput = $value;
return $this;
}
public function hasCountryCodeSource(): bool
{
return $this->countryCodeSource !== CountryCodeSource::UNSPECIFIED;
}
public function getCountryCodeSource(): ?CountryCodeSource
{
return $this->countryCodeSource;
}
public function setCountryCodeSource(CountryCodeSource $value): static
{
$this->countryCodeSource = $value;
return $this;
}
public function hasPreferredDomesticCarrierCode(): bool
{
return isset($this->preferredDomesticCarrierCode);
}
public function getPreferredDomesticCarrierCode(): ?string
{
return $this->preferredDomesticCarrierCode;
}
public function setPreferredDomesticCarrierCode(string $value): static
{
$this->preferredDomesticCarrierCode = $value;
return $this;
}
/**
* Returns whether this phone number is equal to another.
*
* @param PhoneNumber $other The phone number to compare.
*
* @return bool True if the phone numbers are equal, false otherwise.
*/
public function equals(PhoneNumber $other): bool
{
if ($this === $other) {
return true;
}
return $this->countryCode === $other->countryCode
&& $this->nationalNumber === $other->nationalNumber
&& $this->extension === $other->extension
&& $this->italianLeadingZero === $other->italianLeadingZero
&& $this->numberOfLeadingZeros === $other->numberOfLeadingZeros
&& $this->rawInput === $other->rawInput
&& $this->countryCodeSource === $other->countryCodeSource
&& $this->preferredDomesticCarrierCode === $other->preferredDomesticCarrierCode;
}
/**
* Returns a string representation of this phone number.
*/
public function __toString(): string
{
$outputString = 'Country Code: ' . $this->countryCode;
$outputString .= ' National Number: ' . $this->nationalNumber;
if ($this->hasItalianLeadingZero()) {
$outputString .= ' Leading Zero(s): true';
}
if ($this->hasNumberOfLeadingZeros()) {
$outputString .= ' Number of leading zeros: ' . $this->numberOfLeadingZeros;
}
if ($this->hasExtension()) {
$outputString .= ' Extension: ' . $this->extension;
}
if ($this->hasCountryCodeSource()) {
$outputString .= ' Country Code Source: ' . $this->countryCodeSource->name;
}
if ($this->hasPreferredDomesticCarrierCode()) {
$outputString .= ' Preferred Domestic Carrier Code: ' . $this->preferredDomesticCarrierCode;
}
return $outputString;
}
public function serialize(): ?string
{
return serialize($this->__serialize());
}
public function __serialize(): array
{
return [
$this->countryCode,
$this->nationalNumber,
$this->extension,
$this->italianLeadingZero,
$this->numberOfLeadingZeros,
$this->rawInput,
$this->countryCodeSource,
$this->preferredDomesticCarrierCode,
];
}
public function unserialize($data): void
{
$this->__unserialize(unserialize($data, ['allowed_classes' => [__CLASS__]]));
}
/**
* @param array{int,string,string,bool|null,int,string|null,CountryCodeSource|null,string|null} $data
*/
public function __unserialize(array $data): void
{
[
$this->countryCode,
$this->nationalNumber,
$this->extension,
$this->italianLeadingZero,
$this->numberOfLeadingZeros,
$this->rawInput,
$countryCodeSource,
$this->preferredDomesticCarrierCode
] = $data;
// BC layer to allow this method to unserialize "old" phonenumbers
if (is_int($countryCodeSource)) {
$countryCodeSource = CountryCodeSource::from($countryCodeSource);
}
$this->countryCodeSource = $countryCodeSource;
if ($this->numberOfLeadingZeros > 1) {
$this->hasNumberOfLeadingZeros = true;
}
}
}
@@ -0,0 +1,140 @@
<?php
declare(strict_types=1);
namespace libphonenumber;
/**
* Phone Number Description
* @internal
*/
class PhoneNumberDesc
{
protected bool $hasNationalNumberPattern = false;
protected string $nationalNumberPattern = '';
protected bool $hasExampleNumber = false;
protected string $exampleNumber = '';
/**
* @var int[]
*/
protected array $possibleLength = [];
/**
* @var int[]
*/
protected array $possibleLengthLocalOnly = [];
/**
* @return int[]
*/
public function getPossibleLength(): array
{
return $this->possibleLength;
}
/**
* @param int[] $possibleLength
*/
public function setPossibleLength(array $possibleLength): static
{
$this->possibleLength = $possibleLength;
return $this;
}
public function addPossibleLength(int $possibleLength): void
{
if (!in_array($possibleLength, $this->possibleLength, true)) {
$this->possibleLength[] = $possibleLength;
}
}
public function clearPossibleLength(): void
{
$this->possibleLength = [];
}
/**
* @return int[]
*/
public function getPossibleLengthLocalOnly(): array
{
return $this->possibleLengthLocalOnly;
}
/**
* @param int[] $possibleLengthLocalOnly
*/
public function setPossibleLengthLocalOnly(array $possibleLengthLocalOnly): static
{
$this->possibleLengthLocalOnly = $possibleLengthLocalOnly;
return $this;
}
public function addPossibleLengthLocalOnly(int $possibleLengthLocalOnly): void
{
if (!in_array($possibleLengthLocalOnly, $this->possibleLengthLocalOnly, true)) {
$this->possibleLengthLocalOnly[] = $possibleLengthLocalOnly;
}
}
public function clearPossibleLengthLocalOnly(): void
{
$this->possibleLengthLocalOnly = [];
}
/**
* @return boolean
*/
public function hasNationalNumberPattern(): bool
{
return $this->hasNationalNumberPattern;
}
public function getNationalNumberPattern(): string
{
return $this->nationalNumberPattern;
}
public function setNationalNumberPattern(string $value): static
{
$this->hasNationalNumberPattern = true;
$this->nationalNumberPattern = $value;
return $this;
}
public function hasExampleNumber(): bool
{
return $this->hasExampleNumber;
}
public function getExampleNumber(): string
{
return $this->exampleNumber;
}
public function setExampleNumber(string $value): static
{
$this->hasExampleNumber = true;
$this->exampleNumber = $value;
return $this;
}
private static self $emptyObject;
/**
* Used for metadata as a shortcut to an empty object
* Use the same object to reduce load further
* @internal
*/
public static function empty(): self
{
if (!isset(self::$emptyObject)) {
self::$emptyObject = (new self())
->setPossibleLength([-1]);
}
return self::$emptyObject;
}
}
@@ -0,0 +1,25 @@
<?php
declare(strict_types=1);
namespace libphonenumber;
/**
* INTERNATIONAL and NATIONAL formats are consistent with the definition in ITU-T Recommendation
* E123. For example, the number of the Google Switzerland office will be written as
* "+41 44 668 1800" in INTERNATIONAL format, and as "044 668 1800" in NATIONAL format.
* E164 format is as per INTERNATIONAL format but with no formatting applied, e.g.
* "+41446681800". RFC3966 is as per INTERNATIONAL format, but with all spaces and other
* separating symbols replaced with a hyphen, and with any phone number extension appended with
* ";ext=". It also will have a prefix of "tel:" added, e.g. "tel:+41-44-668-1800".
*
* Note: If you are considering storing the number in a neutral format, you are highly advised to
* use the PhoneNumber class.
*/
enum PhoneNumberFormat: int
{
case E164 = 0;
case INTERNATIONAL = 1;
case NATIONAL = 2;
case RFC3966 = 3;
}
@@ -0,0 +1,86 @@
<?php
declare(strict_types=1);
namespace libphonenumber;
use InvalidArgumentException;
use Stringable;
use function mb_strlen;
/**
* @no-named-arguments
*/
class PhoneNumberMatch implements Stringable
{
/**
* The start index into the text.
*/
private int $start;
/**
* The raw substring matched.
*/
private string $rawString;
/**
* The matched phone number.
*/
private PhoneNumber $number;
/**
* Creates a new match
*
* @param int $start The start index into the target text
* @param string $rawString The matched substring of the target text
* @param PhoneNumber $number The matched phone number
*/
public function __construct(int $start, string $rawString, PhoneNumber $number)
{
if ($start < 0) {
throw new InvalidArgumentException('Start index must be >= 0.');
}
$this->start = $start;
$this->rawString = $rawString;
$this->number = $number;
}
/**
* Returns the phone number matched by the receiver.
*/
public function number(): PhoneNumber
{
return $this->number;
}
/**
* Returns the start index of the matched phone number within the searched text.
*/
public function start(): int
{
return $this->start;
}
/**
* Returns the exclusive end index of the matched phone number within the searched text.
*/
public function end(): int
{
return $this->start + mb_strlen($this->rawString);
}
/**
* Returns the raw string matched as a phone number in the searched text.
*/
public function rawString(): string
{
return $this->rawString;
}
public function __toString()
{
return "PhoneNumberMatch [{$this->start()},{$this->end()}) {$this->rawString}";
}
}
@@ -0,0 +1,56 @@
<?php
declare(strict_types=1);
namespace libphonenumber;
/**
* Type of phone numbers.
*/
enum PhoneNumberType: int
{
case FIXED_LINE = 0;
case MOBILE = 1;
/**
* In some regions (e.g. the USA), it is impossible to distinguish between fixed-line and
* mobile numbers by looking at the phone number itself.
*/
case FIXED_LINE_OR_MOBILE = 2;
/**
* Freephone lines
*/
case TOLL_FREE = 3;
case PREMIUM_RATE = 4;
/**
* The cost of this call is shared between the caller and the recipient, and is hence typically
* less than PREMIUM_RATE calls. See // http://en.wikipedia.org/wiki/Shared_Cost_Service for
* more information.
*/
case SHARED_COST = 5;
/**
* Voice over IP numbers. This includes TSoIP (Telephony Service over IP).
*/
case VOIP = 6;
/**
* A personal number is associated with a particular person, and may be routed to either a
* MOBILE or FIXED_LINE number. Some more information can be found here:
* http://en.wikipedia.org/wiki/Personal_Numbers
*/
case PERSONAL_NUMBER = 7;
case PAGER = 8;
/**
* Used for "Universal Access Numbers" or "Company Numbers". They may be further routed to
* specific offices, but allow one number to be used for a company.
*/
case UAN = 9;
/**
* A phone number is of type UNKNOWN when it does not fit any of the known patterns for a
* specific region.
*/
case UNKNOWN = 10;
case EMERGENCY = 27;
case VOICEMAIL = 28;
case SHORT_CODE = 29;
case STANDARD_RATE = 30;
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,42 @@
<?php
declare(strict_types=1);
namespace libphonenumber;
/**
* Class RegexBasedMatcher
* @package libphonenumber
* @internal
*/
class RegexBasedMatcher implements MatcherAPIInterface
{
/**
* Returns whether the given national number (a string containing only decimal digits) matches
* the national number pattern defined in the given {@code PhoneNumberDesc} message.
*/
public function matchNationalNumber(string $number, PhoneNumberDesc $numberDesc, bool $allowPrefixMatch): bool
{
$nationalNumberPattern = $numberDesc->getNationalNumberPattern();
// We don't want to consider it a prefix match when matching non-empty input against an empty
// pattern
if ($nationalNumberPattern === '') {
return false;
}
return $this->match($number, $nationalNumberPattern, $allowPrefixMatch);
}
private function match(string $number, string $pattern, bool $allowPrefixMatch): bool
{
$matcher = new Matcher($pattern, $number);
if (!$matcher->lookingAt()) {
return false;
}
return $matcher->matches() ? true : $allowPrefixMatch;
}
}
@@ -0,0 +1,17 @@
<?php
declare(strict_types=1);
namespace libphonenumber;
/**
* Cost categories of short numbers
* @package libphonenumber
*/
enum ShortNumberCost: int
{
case TOLL_FREE = 3;
case PREMIUM_RATE = 4;
case STANDARD_RATE = 30;
case UNKNOWN_COST = 10;
}
@@ -0,0 +1,613 @@
<?php
declare(strict_types=1);
/**
* Methods for getting information about short phone numbers, such as short codes and emergency
* numbers. Note that most commercial short numbers are not handled here, but by the
* {@link PhoneNumberUtil}.
*
* @author Shaopeng Jia
* @author David Yonge-Mallo
* @since 5.8
*/
namespace libphonenumber;
use RuntimeException;
/**
* @phpstan-consistent-constructor
* @no-named-arguments
*/
class ShortNumberInfo
{
protected static ?ShortNumberInfo $instance;
/**
* @var array<int,string[]>
*/
protected array $countryCallingCodeToRegionCodeMap = [];
protected const REGIONS_WHERE_EMERGENCY_NUMBERS_MUST_BE_EXACT = [
'BR',
'CL',
'NI',
];
protected function __construct(
protected MatcherAPIInterface $matcherAPI,
protected MetadataSourceInterface $metadataSource = new MultiFileMetadataSourceImpl(__NAMESPACE__ . '\data\ShortNumberMetadata_'),
) {
// TODO: Create ShortNumberInfo for a given map
$this->countryCallingCodeToRegionCodeMap = CountryCodeToRegionCodeMap::COUNTRY_CODE_TO_REGION_CODE_MAP;
// Initialise PhoneNumberUtil to make sure regex's are setup correctly
PhoneNumberUtil::getInstance();
}
/**
* Returns the singleton instance of ShortNumberInfo
*/
public static function getInstance(): ShortNumberInfo
{
if (!isset(static::$instance)) {
static::$instance = new self(new RegexBasedMatcher());
}
return static::$instance;
}
public static function resetInstance(): void
{
static::$instance = null;
}
/**
* Returns a list with the region codes that match the specific country calling code. For
* non-geographical country calling codes, the region code 001 is returned. Also, in the case
* of no region code being found, an empty list is returned.
*
* @return string[]
*/
protected function getRegionCodesForCountryCode(int $countryCallingCode): array
{
return $this->countryCallingCodeToRegionCodeMap[$countryCallingCode] ?? [];
}
/**
* Helper method to check that the country calling code of the number matches the region it's
* being dialed from.
*/
protected function regionDialingFromMatchesNumber(PhoneNumber $number, ?string $regionDialingFrom): bool
{
if ($regionDialingFrom === null || $regionDialingFrom === '') {
return false;
}
$regionCodes = $this->getRegionCodesForCountryCode($number->getCountryCode());
return in_array(strtoupper($regionDialingFrom), $regionCodes, true);
}
/**
* @return string[]
*/
public function getSupportedRegions(): array
{
return ShortNumbersRegionCodeSet::SHORT_NUMBERS_REGION_CODE_SET;
}
/**
* Gets a valid short number for the specified region.
*
* @param $regionCode String the region for which an example short number is needed
* @return string a valid short number for the specified region. Returns an empty string when the
* metadata does not contain such information.
*/
public function getExampleShortNumber(string $regionCode): string
{
$phoneMetadata = $this->getMetadataForRegion($regionCode);
if ($phoneMetadata === null) {
return '';
}
/** @var PhoneNumberDesc $desc */
$desc = $phoneMetadata->getShortCode();
if ($desc !== null && $desc->hasExampleNumber()) {
return $desc->getExampleNumber();
}
return '';
}
public function getMetadataForRegion(string $regionCode): ?PhoneMetadata
{
$regionCode = strtoupper($regionCode);
if (!in_array($regionCode, ShortNumbersRegionCodeSet::SHORT_NUMBERS_REGION_CODE_SET, true)) {
return null;
}
try {
return $this->metadataSource->getMetadataForRegion($regionCode);
} catch (RuntimeException) {
return null;
}
}
/**
* Gets a valid short number for the specified cost category.
*
* @param string $regionCode the region for which an example short number is needed
* @param ShortNumberCost $cost the cost category of number that is needed
* @return string a valid short number for the specified region and cost category. Returns an empty string
* when the metadata does not contain such information, or the cost is UNKNOWN_COST.
*/
public function getExampleShortNumberForCost(string $regionCode, ShortNumberCost $cost): string
{
$phoneMetadata = $this->getMetadataForRegion($regionCode);
if ($phoneMetadata === null) {
return '';
}
$desc = null;
switch ($cost) {
case ShortNumberCost::TOLL_FREE:
$desc = $phoneMetadata->getTollFree();
break;
case ShortNumberCost::STANDARD_RATE:
$desc = $phoneMetadata->getStandardRate();
break;
case ShortNumberCost::PREMIUM_RATE:
$desc = $phoneMetadata->getPremiumRate();
break;
default:
// UNKNOWN_COST numbers are computed by the process of elimination from the other cost categories
break;
}
if ($desc !== null && $desc->hasExampleNumber()) {
return $desc->getExampleNumber();
}
return '';
}
/**
* Returns true if the given number, exactly as dialed, might be used to connect to an emergency
* service in the given region.
* <p>
* This method accepts a string, rather than a PhoneNumber, because it needs to distinguish
* cases such as "+1 911" and "911", where the former may not connect to an emergency service in
* all cases but the latter would. This method takes into account cases where the number might
* contain formatting, or might have additional digits appended (when it is okay to do that in
* the specified region).
*
* @param string $number the phone number to test
* @param string $regionCode the region where the phone number if being dialled
* @return bool whether the number might be used to connect to an emergency service in the given region
*/
public function connectsToEmergencyNumber(string $number, string $regionCode): bool
{
return $this->matchesEmergencyNumberHelper($number, $regionCode, true /* allows prefix match */);
}
protected function matchesEmergencyNumberHelper(string $number, string $regionCode, bool $allowPrefixMatch): bool
{
$number = PhoneNumberUtil::extractPossibleNumber($number);
$matcher = new Matcher(PhoneNumberUtil::PLUS_CHARS_PATTERN, $number);
if ($matcher->lookingAt()) {
// Returns false if the number starts with a plus sign. We don't believe dialing the country
// code before emergency numbers (e.g. +1911) works, but later, if that proves to work, we can
// add additional logic here to handle it.
return false;
}
$metadata = $this->getMetadataForRegion($regionCode);
if ($metadata === null || !$metadata->hasEmergency()) {
return false;
}
$normalizedNumber = PhoneNumberUtil::normalizeDigitsOnly($number);
$emergencyDesc = $metadata->getEmergency();
$allowPrefixMatchForRegion = (
$allowPrefixMatch
&& !in_array(strtoupper($regionCode), static::REGIONS_WHERE_EMERGENCY_NUMBERS_MUST_BE_EXACT, true)
);
return $this->matcherAPI->matchNationalNumber($normalizedNumber, $emergencyDesc, $allowPrefixMatchForRegion);
}
/**
* Given a valid short number, determines whether it is carrier-specific (however, nothing is
* implied about its validity). Carrier-specific numbers may connect to a different end-point, or
* not connect at all, depending on the user's carrier. If it is important that the number is
* valid, then its validity must first be checked using {@see isValidShortNumber} or
* {@see isValidShortNumberForRegion}.
*
* @param PhoneNumber $number the valid short number to check
* @return bool whether the short number is carrier-specific, assuming the input was a valid short
* number
*/
public function isCarrierSpecific(PhoneNumber $number): bool
{
$regionCodes = $this->getRegionCodesForCountryCode($number->getCountryCode());
$regionCode = $this->getRegionCodeForShortNumberFromRegionList($number, $regionCodes);
$nationalNumber = $this->getNationalSignificantNumber($number);
$phoneMetadata = $this->getMetadataForRegion($regionCode);
return ($phoneMetadata !== null) && $this->matchesPossibleNumberAndNationalNumber(
$nationalNumber,
$phoneMetadata->getCarrierSpecific()
);
}
/**
* Given a valid short number, determines whether it is carrier-specific when dialed from the
* given region (however, nothing is implied about its validity). Carrier-specific numbers may
* connect to a different end-point, or not connect at all, depending on the user's carrier. If
* it is important that the number is valid, then its validity must first be checked using
* {@see isValidShortNumber} or {@see isValidShortNumberForRegion}. Returns false if the
* number doesn't match the region provided.
* @param PhoneNumber $number The valid short number to check
* @param string $regionDialingFrom The region from which the number is dialed
* @return bool Whether the short number is carrier-specific in the provided region, assuming the
* input was a valid short number
*/
public function isCarrierSpecificForRegion(PhoneNumber $number, string $regionDialingFrom): bool
{
if (!$this->regionDialingFromMatchesNumber($number, $regionDialingFrom)) {
return false;
}
$nationalNumber = $this->getNationalSignificantNumber($number);
$phoneMetadata = $this->getMetadataForRegion($regionDialingFrom);
return ($phoneMetadata !== null)
&& $this->matchesPossibleNumberAndNationalNumber($nationalNumber, $phoneMetadata->getCarrierSpecific());
}
/**
* Given a valid short number, determines whether it is an SMS service (however, nothing is
* implied about its validity). An SMS service is where the primary or only intended usage is to
* receive and/or send text messages (SMSs). This includes MMS as MMS numbers downgrade to SMS if
* the other party isn't MMS-capable. If it is important that the number is valid, then its
* validity must first be checked using {@see isValidShortNumber} or {@see isValidShortNumberForRegion}.
* Returns false if the number doesn't match the region provided.
*
* @param PhoneNumber $number The valid short number to check
* @param string $regionDialingFrom The region from which the number is dialed
* @return bool Whether the short number is an SMS service in the provided region, assuming the input
* was a valid short number.
*/
public function isSmsServiceForRegion(PhoneNumber $number, string $regionDialingFrom): bool
{
if (!$this->regionDialingFromMatchesNumber($number, $regionDialingFrom)) {
return false;
}
$phoneMetadata = $this->getMetadataForRegion($regionDialingFrom);
return ($phoneMetadata !== null)
&& $this->matchesPossibleNumberAndNationalNumber(
$this->getNationalSignificantNumber($number),
$phoneMetadata->getSmsServices()
);
}
/**
* Helper method to get the region code for a given phone number, from a list of possible region
* codes. If the list contains more than one region, the first region for which the number is
* valid is returned.
*
* @param string[] $regionCodes
* @return string|null Region Code (or null if none are found)
*/
protected function getRegionCodeForShortNumberFromRegionList(PhoneNumber $number, array $regionCodes): ?string
{
if (count($regionCodes) === 0) {
return null;
}
if (count($regionCodes) === 1) {
return $regionCodes[0];
}
$nationalNumber = $this->getNationalSignificantNumber($number);
foreach ($regionCodes as $regionCode) {
$phoneMetadata = $this->getMetadataForRegion($regionCode);
if ($phoneMetadata !== null
&& $this->matchesPossibleNumberAndNationalNumber($nationalNumber, $phoneMetadata->getShortCode())
) {
// The number is valid for this region.
return $regionCode;
}
}
return null;
}
/**
* Check whether a short number is a possible number. If a country calling code is shared by
* multiple regions, this returns true if it's possible in any of them. This provides a more
* lenient check than {@see isValidShortNumber}. See {@see isPossibleShortNumberForRegion}
* for details.
*
* @param $number PhoneNumber the short number to check
* @return bool whether the number is a possible short number
*/
public function isPossibleShortNumber(PhoneNumber $number): bool
{
$regionCodes = $this->getRegionCodesForCountryCode($number->getCountryCode());
$shortNumberLength = strlen($this->getNationalSignificantNumber($number));
foreach ($regionCodes as $region) {
$phoneMetadata = $this->getMetadataForRegion($region);
if ($phoneMetadata === null) {
continue;
}
if (in_array($shortNumberLength, $phoneMetadata->getGeneralDesc()->getPossibleLength(), true)) {
return true;
}
}
return false;
}
/**
* Check whether a short number is a possible number when dialled from a region, given the number
* in the form of a string, and the region where the number is dialled from. This provides a more
* lenient check than {@see isValidShortNumber}.
*
* @param PhoneNumber $shortNumber The short number to check
* @param string $regionDialingFrom Region dialing From
* @return bool whether the number is a possible short number
*/
public function isPossibleShortNumberForRegion(PhoneNumber $shortNumber, string $regionDialingFrom): bool
{
if (!$this->regionDialingFromMatchesNumber($shortNumber, $regionDialingFrom)) {
return false;
}
$phoneMetadata = $this->getMetadataForRegion($regionDialingFrom);
if ($phoneMetadata === null) {
return false;
}
$numberLength = strlen($this->getNationalSignificantNumber($shortNumber));
return in_array($numberLength, $phoneMetadata->getGeneralDesc()->getPossibleLength(), true);
}
/**
* Tests whether a short number matches a valid pattern. If a country calling code is shared by
* multiple regions, this returns true if it's valid in any of them. Note that this doesn't verify
* the number is actually in use, which is impossible to tell by just looking at the number
* itself. See {@see isValidShortNumberForRegion(PhoneNumber, String)} for details.
*
* @param $number PhoneNumber the short number for which we want to test the validity
* @return bool whether the short number matches a valid pattern
*/
public function isValidShortNumber(PhoneNumber $number): bool
{
$regionCodes = $this->getRegionCodesForCountryCode($number->getCountryCode());
$regionCode = $this->getRegionCodeForShortNumberFromRegionList($number, $regionCodes);
if ($regionCode !== null && count($regionCodes) > 1) {
// If a matching region had been found for the phone number from among two or more regions,
// then we have already implicitly verified its validity for that region.
return true;
}
return $this->isValidShortNumberForRegion($number, $regionCode);
}
/**
* Tests whether a short number matches a valid pattern in a region. Note that this doesn't verify
* the number is actually in use, which is impossible to tell by just looking at the number
* itself.
*
* @param PhoneNumber $number The Short number for which we want to test the validity
* @param string|null $regionDialingFrom the region from which the number is dialed
* @return bool whether the short number matches a valid pattern
*/
public function isValidShortNumberForRegion(PhoneNumber $number, ?string $regionDialingFrom): bool
{
if (!$this->regionDialingFromMatchesNumber($number, $regionDialingFrom)) {
return false;
}
$phoneMetadata = $this->getMetadataForRegion($regionDialingFrom);
if ($phoneMetadata === null) {
return false;
}
$shortNumber = $this->getNationalSignificantNumber($number);
$generalDesc = $phoneMetadata->getGeneralDesc();
if (!$this->matchesPossibleNumberAndNationalNumber($shortNumber, $generalDesc)) {
return false;
}
$shortNumberDesc = $phoneMetadata->getShortCode();
return $this->matchesPossibleNumberAndNationalNumber($shortNumber, $shortNumberDesc);
}
/**
* Gets the expected cost category of a short number when dialled from a region (however, nothing is
* implied about its validity). If it is important that the number is valid, then its validity
* must first be checked using {@link isValidShortNumberForRegion}. Note that emergency numbers
* are always considered toll-free.
* Example usage:
* <pre>{@code
* $shortInfo = ShortNumberInfo::getInstance();
* $shortNumber = PhoneNumberUtil::parse("110", "US);
* $regionCode = "FR";
* if ($shortInfo->isValidShortNumberForRegion($shortNumber, $regionCode)) {
* $cost = $shortInfo->getExpectedCostForRegion($shortNumber, $regionCode);
* // Do something with the cost information here.
* }}</pre>
*
* @param PhoneNumber $number the short number for which we want to know the expected cost category,
* as a string
* @param string $regionDialingFrom the region from which the number is dialed
* @return ShortNumberCost the expected cost category for that region of the short number. Returns ShortNumberCost::UNKNOWN_COST if
* the number does not match a cost category. Note that an invalid number may match any cost
* category.
*/
public function getExpectedCostForRegion(PhoneNumber $number, string $regionDialingFrom): ShortNumberCost
{
if (!$this->regionDialingFromMatchesNumber($number, $regionDialingFrom)) {
return ShortNumberCost::UNKNOWN_COST;
}
// Note that regionDialingFrom may be null, in which case phoneMetadata will also be null.
$phoneMetadata = $this->getMetadataForRegion($regionDialingFrom);
if ($phoneMetadata === null) {
return ShortNumberCost::UNKNOWN_COST;
}
$shortNumber = $this->getNationalSignificantNumber($number);
// The possible lengths are not present for a particular sub-type if they match the general
// description; for this reason, we check the possible lengths against the general description
// first to allow an early exit if possible.
if (!in_array(strlen($shortNumber), $phoneMetadata->getGeneralDesc()->getPossibleLength(), true)) {
return ShortNumberCost::UNKNOWN_COST;
}
// The cost categories are tested in order of decreasing expense, since if for some reason the
// patterns overlap the most expensive matching cost category should be returned.
if ($this->matchesPossibleNumberAndNationalNumber($shortNumber, $phoneMetadata->getPremiumRate())) {
return ShortNumberCost::PREMIUM_RATE;
}
if ($this->matchesPossibleNumberAndNationalNumber($shortNumber, $phoneMetadata->getStandardRate())) {
return ShortNumberCost::STANDARD_RATE;
}
if ($this->matchesPossibleNumberAndNationalNumber($shortNumber, $phoneMetadata->getTollFree())) {
return ShortNumberCost::TOLL_FREE;
}
if ($this->isEmergencyNumber($shortNumber, $regionDialingFrom)) {
// Emergency numbers are implicitly toll-free.
return ShortNumberCost::TOLL_FREE;
}
return ShortNumberCost::UNKNOWN_COST;
}
/**
* Gets the expected cost category of a short number (however, nothing is implied about its
* validity). If the country calling code is unique to a region, this method behaves exactly the
* same as {@see getExpectedCostForRegion(PhoneNumber, String)}. However, if the country calling
* code is shared by multiple regions, then it returns the highest cost in the sequence
* PREMIUM_RATE, UNKNOWN_COST, STANDARD_RATE, TOLL_FREE. The reason for the position of
* UNKNOWN_COST in this order is that if a number is UNKNOWN_COST in one region but STANDARD_RATE
* or TOLL_FREE in another, its expected cost cannot be estimated as one of the latter since it
* might be a PREMIUM_RATE number.
*
* <p>
* For example, if a number is STANDARD_RATE in the US, but TOLL_FREE in Canada, the expected
* cost returned by this method will be STANDARD_RATE, since the NANPA countries share the same
* country calling code.
* </p>
*
* Note: If the region from which the number is dialed is known, it is highly preferable to call
* {@see getExpectedCostForRegion(PhoneNumber, String)} instead.
*
* @param PhoneNumber $number the short number for which we want to know the expected cost category
* @return ShortNumberCost the highest expected cost category of the short number in the region(s) with the given
* country calling code
*/
public function getExpectedCost(PhoneNumber $number): ShortNumberCost
{
$regionCodes = $this->getRegionCodesForCountryCode($number->getCountryCode());
if ($regionCodes === []) {
return ShortNumberCost::UNKNOWN_COST;
}
if (count($regionCodes) === 1) {
return $this->getExpectedCostForRegion($number, $regionCodes[0]);
}
$cost = ShortNumberCost::TOLL_FREE;
foreach ($regionCodes as $regionCode) {
$costForRegion = $this->getExpectedCostForRegion($number, $regionCode);
switch ($costForRegion) {
case ShortNumberCost::PREMIUM_RATE:
return ShortNumberCost::PREMIUM_RATE;
case ShortNumberCost::UNKNOWN_COST:
$cost = ShortNumberCost::UNKNOWN_COST;
break;
case ShortNumberCost::STANDARD_RATE:
if ($cost !== ShortNumberCost::UNKNOWN_COST) {
$cost = ShortNumberCost::STANDARD_RATE;
}
break;
case ShortNumberCost::TOLL_FREE:
// Do nothing
break;
}
}
return $cost;
}
/**
* Returns true if the given number exactly matches an emergency service number in the given
* region.
* <p>
* This method takes into account cases where the number might contain formatting, but doesn't
* allow additional digits to be appended. Note that {@code isEmergencyNumber(number, region)}
* implies {@code connectsToEmergencyNumber(number, region)}.
*
* @param string $number the phone number to test
* @param string $regionCode the region where the phone number is being dialled
* @return bool whether the number exactly matches an emergency services number in the given region
*/
public function isEmergencyNumber(string $number, string $regionCode): bool
{
return $this->matchesEmergencyNumberHelper($number, $regionCode, false /* doesn't allow prefix match */);
}
/**
* Gets the national significant number of the a phone number. Note a national significant number
* doesn't contain a national prefix or any formatting.
* <p>
* This is a temporary duplicate of the {@code getNationalSignificantNumber} method from
* {@code PhoneNumberUtil}. Ultimately a canonical static version should exist in a separate
* utility class (to prevent {@code ShortNumberInfo} needing to depend on PhoneNumberUtil).
*
* @param PhoneNumber $number the phone number for which the national significant number is needed
* @return string the national significant number of the PhoneNumber object passed in
*/
protected function getNationalSignificantNumber(PhoneNumber $number): string
{
// If leading zero(s) have been set, we prefix this now. Note this is not a national prefix.
$nationalNumber = '';
if ($number->isItalianLeadingZero()) {
$zeros = str_repeat('0', $number->getNumberOfLeadingZeros());
$nationalNumber .= $zeros;
}
$nationalNumber .= $number->getNationalNumber();
return $nationalNumber;
}
/**
* TODO: Once we have benchmarked ShortnumberInfo, consider if it is worth keeping
* this performance optimization.
*/
protected function matchesPossibleNumberAndNationalNumber(string $number, PhoneNumberDesc $numberDesc): bool
{
if (count($numberDesc->getPossibleLength()) > 0 && !in_array(strlen($number), $numberDesc->getPossibleLength(), true)) {
return false;
}
return $this->matcherAPI->matchNationalNumber($number, $numberDesc, false);
}
}
@@ -0,0 +1,266 @@
<?php
/**
* libphonenumber-for-php-lite data file
* This file has been @generated from libphonenumber data
* Do not modify!
* @internal
*/
declare(strict_types=1);
namespace libphonenumber;
/**
* @internal
*/
class ShortNumbersRegionCodeSet
{
/**
* A set of all region codes for which data is available.
* @var string[]
*/
public const SHORT_NUMBERS_REGION_CODE_SET = [
'AC',
'AD',
'AE',
'AF',
'AG',
'AI',
'AL',
'AM',
'AO',
'AR',
'AS',
'AT',
'AU',
'AW',
'AX',
'AZ',
'BA',
'BB',
'BD',
'BE',
'BF',
'BG',
'BH',
'BI',
'BJ',
'BL',
'BM',
'BN',
'BO',
'BQ',
'BR',
'BS',
'BT',
'BW',
'BY',
'BZ',
'CA',
'CC',
'CD',
'CF',
'CG',
'CH',
'CI',
'CK',
'CL',
'CM',
'CN',
'CO',
'CR',
'CU',
'CV',
'CW',
'CX',
'CY',
'CZ',
'DE',
'DJ',
'DK',
'DM',
'DO',
'DZ',
'EC',
'EE',
'EG',
'EH',
'ER',
'ES',
'ET',
'FI',
'FJ',
'FK',
'FM',
'FO',
'FR',
'GA',
'GB',
'GD',
'GE',
'GF',
'GG',
'GH',
'GI',
'GL',
'GM',
'GN',
'GP',
'GR',
'GT',
'GU',
'GW',
'GY',
'HK',
'HN',
'HR',
'HT',
'HU',
'ID',
'IE',
'IL',
'IM',
'IN',
'IQ',
'IR',
'IS',
'IT',
'JE',
'JM',
'JO',
'JP',
'KE',
'KG',
'KH',
'KI',
'KM',
'KN',
'KP',
'KR',
'KW',
'KY',
'KZ',
'LA',
'LB',
'LC',
'LI',
'LK',
'LR',
'LS',
'LT',
'LU',
'LV',
'LY',
'MA',
'MC',
'MD',
'ME',
'MF',
'MG',
'MH',
'MK',
'ML',
'MM',
'MN',
'MO',
'MP',
'MQ',
'MR',
'MS',
'MT',
'MU',
'MV',
'MW',
'MX',
'MY',
'MZ',
'NA',
'NC',
'NE',
'NF',
'NG',
'NI',
'NL',
'NO',
'NP',
'NR',
'NU',
'NZ',
'OM',
'PA',
'PE',
'PF',
'PG',
'PH',
'PK',
'PL',
'PM',
'PR',
'PS',
'PT',
'PW',
'PY',
'QA',
'RE',
'RO',
'RS',
'RU',
'RW',
'SA',
'SB',
'SC',
'SD',
'SE',
'SG',
'SH',
'SI',
'SJ',
'SK',
'SL',
'SM',
'SN',
'SO',
'SR',
'SS',
'ST',
'SV',
'SX',
'SY',
'SZ',
'TC',
'TD',
'TG',
'TH',
'TJ',
'TL',
'TM',
'TN',
'TO',
'TR',
'TT',
'TV',
'TW',
'TZ',
'UA',
'UG',
'US',
'UY',
'UZ',
'VA',
'VC',
'VE',
'VG',
'VI',
'VN',
'VU',
'WF',
'WS',
'XK',
'YE',
'YT',
'ZA',
'ZM',
'ZW',
];
}
@@ -0,0 +1,47 @@
<?php
declare(strict_types=1);
namespace libphonenumber;
/**
* Possible outcomes when testing if a PhoneNumber is possible.
*/
enum ValidationResult: int
{
/**
* The number length matches that of valid numbers for this region
*/
case IS_POSSIBLE = 0;
/**
* The number has an invalid country calling code.
*/
case INVALID_COUNTRY_CODE = 1;
/**
* The number is shorter than all valid numbers for this region.
*/
case TOO_SHORT = 2;
/**
* The number is longer than all valid numbers for this region.
*/
case TOO_LONG = 3;
/**
* The number length matches that of local numbers for this region only (i.e. numbers that may
* be able to be dialled within an area, but do not have all the information to be dialled from
* anywhere inside or outside the country).
*/
case IS_POSSIBLE_LOCAL_ONLY = 4;
/**
* The number is longer than the shortest valid numbers for this region, shorter than the
* longest valid numbers for this region, and does not itself have a number length that matches
* valid numbers for this region. This can also be returned in the case where
* isPossibleNumberForTypeWithReason was called, and there are no numbers of this type at all
* for this region.
*/
case INVALID_LENGTH = 5;
}