Обновление клиента (apps, 3rdparty, install)
This commit is contained in:
@@ -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;
|
||||
}
|
||||
+269
@@ -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;
|
||||
}
|
||||
+84
@@ -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);
|
||||
}
|
||||
}
|
||||
+266
@@ -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;
|
||||
}
|
||||
Reference in New Issue
Block a user