Download project

This commit is contained in:
Roman Pyrih
2024-11-20 09:09:44 +01:00
parent 547a138d6a
commit 5ff041757f
40737 changed files with 7766183 additions and 0 deletions

View File

@@ -0,0 +1,20 @@
The MIT License (MIT)
Copyright (c) 2013-present Benjamin Morel
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@@ -0,0 +1,184 @@
# Brick\PhoneNumber
<img src="https://raw.githubusercontent.com/brick/brick/master/logo.png" alt="" align="left" height="64">
A phone number library for PHP.
[![Build Status](https://github.com/brick/phonenumber/workflows/CI/badge.svg)](https://github.com/brick/phonenumber/actions)
[![Coverage Status](https://coveralls.io/repos/github/brick/phonenumber/badge.svg?branch=master)](https://coveralls.io/github/brick/phonenumber?branch=master)
[![Latest Stable Version](https://poser.pugx.org/brick/phonenumber/v/stable)](https://packagist.org/packages/brick/phonenumber)
[![Total Downloads](https://poser.pugx.org/brick/phonenumber/downloads)](https://packagist.org/packages/brick/phonenumber)
[![License](https://img.shields.io/badge/license-MIT-blue.svg)](http://opensource.org/licenses/MIT)
This library is a thin wrapper around [giggsey/libphonenumber-for-php](https://github.com/giggsey/libphonenumber-for-php),
itself a port of [Google's libphonenumber](https://github.com/googlei18n/libphonenumber).
It provides an equivalent functionality, with the following implementation differences:
- `PhoneNumber` is an immutable class; it can be safely passed around without having to worry about the risk for it to be changed;
- `PhoneNumber` is not just a mere data container, but provides all the methods to parse, format and validate phone numbers; it transparently encapsulates `PhoneNumberUtil`.
## Installation
This library is installable via [Composer](https://getcomposer.org/):
```bash
composer require brick/phonenumber
```
## Requirements
This library requires PHP 7.1 or later. for PHP 5.6 and PHP 7.0 support, use version `0.1`.
## Project status & release process
While this library is still under development, it is well tested and should be stable enough to use in production environments.
The current releases are numbered `0.x.y`. When a non-breaking change is introduced (adding new methods, optimizing existing code, etc.), `y` is incremented.
**When a breaking change is introduced, a new `0.x` version cycle is always started.**
It is therefore safe to lock your project to a given release cycle, such as `0.4.*`.
If you need to upgrade to a newer release cycle, check the [release history](https://github.com/brick/phonenumber/releases) for a list of changes introduced by each further `0.x.0` version.
## Quick start
All the classes lie in the `Brick\PhoneNumber` namespace.
To obtain an instance of `PhoneNumber`, use the `parse()` method:
- Using an international number: `PhoneNumber::parse('+33123456789')`;
- Using a national number and a country code: `PhoneNumber::parse('01 23 45 67 89', 'FR')`;
### Validating a number
The `parse()` method is quite permissive with numbers; it basically attempts to match a country code,
and validates the length of the phone number for this country.
If a number is really malformed, it throws a `PhoneNumberParseException`:
```php
use Brick\PhoneNumber\PhoneNumber;
use Brick\PhoneNumber\PhoneNumberParseException;
try {
$number = PhoneNumber::parse('+333');
}
catch (PhoneNumberParseException $e) {
// 'The string supplied is too short to be a phone number.'
}
```
In most cases, it is recommended to perform an extra step of validation with `isValidNumber()` or `isPossibleNumber()`:
```php
if (! $number->isPossibleNumber()) {
// a more lenient and faster check than `isValidNumber()`
}
if (! $number->isValidNumber()) {
// strict check relying on up-to-date metadata library
}
```
As a rule of thumb, do the following:
- When the number comes from user input, do a full validation: `parse()` and catch `PhoneNumberParseException`, then call `isValidNumber()` (or `isPossibleNumber()` for a more lenient check) if no exception occurred;
- When the number is later retrieved from your database, and has been validated before, you can just perform a blind `parse()`.
### Formatting a number
#### Basic formatting
You can use `format()` with constants from the [PhoneNumberFormat](https://github.com/brick/phonenumber/blob/master/src/PhoneNumberFormat.php) class:
```php
$number = PhoneNumber::parse('+41446681800');
$number->format(PhoneNumberFormat::E164); // +41446681800
$number->format(PhoneNumberFormat::INTERNATIONAL); // +41 44 668 18 00
$number->format(PhoneNumberFormat::NATIONAL); // 044 668 18 00
$number->format(PhoneNumberFormat::RFC3966); // tel:+41-44-668-18-00
```
#### Formatting to call from another country
You may want to present a phone number to an audience in a specific country, with the correct international
prefix when required. This is what `formatForCallingFrom()` does:
```php
$number = PhoneNumber::parse('+447123456789');
$number->formatForCallingFrom('GB'); // 07123 456789
$number->formatForCallingFrom('FR'); // 00 44 7123 456789
$number->formatForCallingFrom('US'); // 011 44 7123 456789
```
### Number types
In certain cases, it is possible to know the type of a phone number (fixed line, mobile phone, etc.), using
the `getNumberType()` method, which returns a constant from the [PhoneNumberType](https://github.com/brick/phonenumber/blob/master/src/PhoneNumberType.php) class:
```php
PhoneNumber::parse('+336123456789')->getNumberType(); // PhoneNumberType::MOBILE
PhoneNumber::parse('+33123456789')->getNumberType(); // PhoneNumberType::FIXED_LINE
```
If the type is unknown, the `PhoneNumberType::UNKNOWN` value is returned.
Check the `PhoneNumberType` class for all possible values.
### Number information
You can extract the following information from a phone number:
```php
$number = PhoneNumber::parse('+447123456789');
echo $number->getRegionCode(); // GB
echo $number->getCountryCode(); // 44
echo $number->getNationalNumber(); // 7123456789
```
### Example numbers
You can get an example number for a country code and an optional number type (defaults to fixed line).
This can be useful to use as a placeholder in an input field, for example:
```php
echo PhoneNumber::getExampleNumber('FR'); // +33123456789
echo PhoneNumber::getExampleNumber('FR', PhoneNumberType::MOBILE); // +33612345678
```
The return type of `getExampleNumber()` is a `PhoneNumber` instance, so you can format it as you like:
```php
echo PhoneNumber::getExampleNumber('FR')->formatForCallingFrom('FR'); // 01 23 45 67 89
```
If no example phone number is available for the country code / number type combination, a `PhoneNumberException` is thrown.
### Casting to string
Casting a `PhoneNumber` to string returns its E164 representation (`+` followed by digits), so the following are equivalent:
```php
(string) $phoneNumber
```
```php
$phoneNumber->format(PhoneNumberFormat::E164)
```
You can serialize a `PhoneNumber` to string, then recover it using `parse()` without a country code:
```php
$phoneNumber = PhoneNumber::parse('02079834000', 'GB');
$phoneNumberAsString = (string) $phoneNumber; // +442079834000
$phoneNumber2 = PhoneNumber::parse($phoneNumberAsString);
$phoneNumber2->isEqualTo($phoneNumber); // true
```
### Doctrine mappings
You can use `PhoneNumber` objects in your Doctrine entities using the [brick/phonenumber-doctrine](https://github.com/brick/phonenumber-doctrine) package.

View File

@@ -0,0 +1,31 @@
{
"name": "brick/phonenumber",
"description": "Phone number library",
"type": "library",
"keywords": [
"Brick",
"PhoneNumber",
"Phone",
"Phone Number"
],
"license": "MIT",
"require": {
"php": "^7.1 || ^8.0",
"ext-json": "*",
"giggsey/libphonenumber-for-php": "^7.0 || ^8.0"
},
"require-dev": {
"phpunit/phpunit": "^7.0 || ^8.0 || ^9.0",
"php-coveralls/php-coveralls": "^2.0"
},
"autoload": {
"psr-4": {
"Brick\\PhoneNumber\\": "src/"
}
},
"autoload-dev": {
"psr-4": {
"Brick\\PhoneNumber\\Tests\\": "tests/"
}
}
}

View File

@@ -0,0 +1,11 @@
<?php
header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
header('Cache-Control: no-store, no-cache, must-revalidate');
header('Cache-Control: post-check=0, pre-check=0', false);
header('Pragma: no-cache');
header('Location: ../');
exit;

View File

@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" colors="true" bootstrap="vendor/autoload.php" xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.3/phpunit.xsd">
<testsuites>
<testsuite name="PhoneNumber tests">
<directory>tests</directory>
</testsuite>
</testsuites>
<coverage processUncoveredFiles="true">
<include>
<directory suffix=".php">src</directory>
</include>
</coverage>
</phpunit>

View File

@@ -0,0 +1,262 @@
<?php
declare(strict_types=1);
namespace Brick\PhoneNumber;
use JsonSerializable;
use libphonenumber\geocoding\PhoneNumberOfflineGeocoder;
use libphonenumber\NumberParseException;
use libphonenumber\PhoneNumberUtil;
/**
* A phone number.
*/
final class PhoneNumber implements JsonSerializable
{
/**
* The underlying PhoneNumber object from libphonenumber.
*
* @var \libphonenumber\PhoneNumber
*/
private $phoneNumber;
/**
* Private constructor. Use a factory method to obtain an instance.
*
* @param \libphonenumber\PhoneNumber $phoneNumber
*/
private function __construct(\libphonenumber\PhoneNumber $phoneNumber)
{
$this->phoneNumber = $phoneNumber;
}
/**
* Parses a string representation of a phone number.
*
* @param string $phoneNumber The phone number to parse.
* @param string|null $regionCode The region code to assume, if the number is not in international format.
*
* @return PhoneNumber
*
* @throws PhoneNumberParseException
*/
public static function parse(string $phoneNumber, ?string $regionCode = null) : PhoneNumber
{
try {
return new PhoneNumber(
PhoneNumberUtil::getInstance()->parse($phoneNumber, $regionCode)
);
} catch (NumberParseException $e) {
throw PhoneNumberParseException::wrap($e);
}
}
/**
* @param string $regionCode The region code.
* @param PhoneNumberType::* $phoneNumberType The phone number type, defaults to a fixed line.
*
* @return PhoneNumber
*
* @throws PhoneNumberException If no example number is available for this region and type.
*/
public static function getExampleNumber(string $regionCode, int $phoneNumberType = PhoneNumberType::FIXED_LINE) : PhoneNumber
{
$phoneNumber = PhoneNumberUtil::getInstance()->getExampleNumberForType($regionCode, $phoneNumberType);
if ($phoneNumber === null) {
throw new PhoneNumberException('No example number is available for the given region and type.');
}
return new PhoneNumber($phoneNumber);
}
/**
* Returns the country code of this PhoneNumber.
*
* The country code is a series of 1 to 3 digits, as defined per the E.164 recommendation.
*
* @return string
*/
public function getCountryCode() : string
{
return (string) $this->phoneNumber->getCountryCode();
}
/**
* Returns the geographical area code of this PhoneNumber.
*
* Notes:
*
* - geographical area codes change over time, and this method honors those changes; therefore, it doesn't
* guarantee the stability of the result it produces;
* - most non-geographical numbers have no area codes, including numbers from non-geographical entities;
* - some geographical numbers have no area codes.
*
* If this number has no area code, an empty string is returned.
*
* @return string
*/
public function getGeographicalAreaCode() : string
{
$phoneNumberUtil = PhoneNumberUtil::getInstance();
$nationalSignificantNumber = $phoneNumberUtil->getNationalSignificantNumber($this->phoneNumber);
$areaCodeLength = $phoneNumberUtil->getLengthOfGeographicalAreaCode($this->phoneNumber);
return substr($nationalSignificantNumber, 0, $areaCodeLength);
}
/**
* Returns the national number of this PhoneNumber.
*
* The national number is a series of digits.
*
* @return string
*/
public function getNationalNumber() : string
{
return $this->phoneNumber->getNationalNumber();
}
/**
* Returns the region code of this PhoneNumber.
*
* The region code is an ISO 3166-1 alpha-2 country code.
*
* If the phone number does not map to a geographic region
* (global networks, such as satellite phone numbers) this method returns null.
*
* @return string|null The region code, or null if the number does not map to a geographic region.
*/
public function getRegionCode() : ?string
{
$regionCode = PhoneNumberUtil::getInstance()->getRegionCodeForNumber($this->phoneNumber);
if ($regionCode === '001') {
return null;
}
return $regionCode;
}
/**
* Returns whether this phone number is a possible number.
*
* Note this provides a more lenient and faster check than `isValidNumber()`.
*
* @return bool
*/
public function isPossibleNumber() : bool
{
return PhoneNumberUtil::getInstance()->isPossibleNumber($this->phoneNumber);
}
/**
* Returns whether this phone number matches a valid pattern.
*
* Note this doesn't verify the number is actually in use,
* which is impossible to tell by just looking at a number itself.
*
* @return bool
*/
public function isValidNumber() : bool
{
return PhoneNumberUtil::getInstance()->isValidNumber($this->phoneNumber);
}
/**
* Returns the type of this phone number.
*
* @return PhoneNumberType::*
*/
public function getNumberType() : int
{
return PhoneNumberUtil::getInstance()->getNumberType($this->phoneNumber);
}
/**
* Returns a formatted string representation of this phone number.
*
* @param PhoneNumberFormat::* $format
*
* @return string
*/
public function format(int $format) : string
{
return PhoneNumberUtil::getInstance()->format($this->phoneNumber, $format);
}
/**
* Formats this phone number for out-of-country dialing purposes.
*
* @param string $regionCode The ISO 3166-1 alpha-2 country code
*
* @return string
*/
public function formatForCallingFrom(string $regionCode) : string
{
return PhoneNumberUtil::getInstance()->formatOutOfCountryCallingNumber($this->phoneNumber, $regionCode);
}
public function isEqualTo(PhoneNumber $phoneNumber): bool
{
return $this->phoneNumber->equals($phoneNumber->phoneNumber);
}
/**
* Required by interface JsonSerializable.
*/
public function jsonSerialize(): string
{
return (string) $this;
}
/**
* Returns a text description for the given phone number, in the language provided. The description might consist of
* the name of the country where the phone number is from, or the name of the geographical area the phone number is
* from if more detailed information is available.
*
* If $userRegion is set, we also consider the region of the user. If the phone number is from the same region as
* the user, only a lower-level description will be returned, if one exists. Otherwise, the phone number's region
* will be returned, with optionally some more detailed information.
*
* For example, for a user from the region "US" (United States), we would show "Mountain View, CA" for a particular
* number, omitting the United States from the description. For a user from the United Kingdom (region "GB"), for
* the same number we may show "Mountain View, CA, United States" or even just "United States".
*
* If no description is found, this method returns null.
*
* @param string $locale The locale for which the description should be written.
* @param string|null $userRegion The region code for a given user. This region will be omitted from the description
* if the phone number comes from this region. It is a two-letter uppercase CLDR
* region code.
*
* @return string|null
*/
public function getDescription(string $locale, ?string $userRegion = null) : ?string
{
$description = PhoneNumberOfflineGeocoder::getInstance()->getDescriptionForNumber(
$this->phoneNumber,
$locale,
$userRegion
);
if ($description === '') {
return null;
}
return $description;
}
/**
* Returns a string representation of this phone number in international E164 format.
*
* @return string
*/
public function __toString() : string
{
return $this->format(PhoneNumberFormat::E164);
}
}

View File

@@ -0,0 +1,12 @@
<?php
declare(strict_types=1);
namespace Brick\PhoneNumber;
/**
* Base class for phone number exceptions.
*/
class PhoneNumberException extends \Exception
{
}

View File

@@ -0,0 +1,50 @@
<?php
declare(strict_types=1);
namespace Brick\PhoneNumber;
/**
* Constants for the phone number formats.
*/
final class PhoneNumberFormat
{
/**
* The E164 format.
*
* This consists of a + sign followed by a series of digits,
* comprising the country code and national number.
*
* Example: `+41446681800`.
*/
public const E164 = 0;
/**
* The international format.
*
* This is similar to the E164 format, with extra formatting.
* This format is consistent with the definition in ITU-T Recommendation E123.
*
* Example: `+41 44 668 1800`.
*/
public const INTERNATIONAL = 1;
/**
* The national format.
*
* This is the number as it would be composed from within the country, with formatting.
* This format is consistent with the definition in ITU-T Recommendation E123.
*
* Example: `044 668 1800`.
*/
public const NATIONAL = 2;
/**
* The RFC 3966 format.
*
* This format outputs a `tel:` URI that can be used as an anchor link to start a VOIP call from a web page.
*
* Example: `tel:+41-44-668-1800`.
*/
public const RFC3966 = 3;
}

View File

@@ -0,0 +1,23 @@
<?php
declare(strict_types=1);
namespace Brick\PhoneNumber;
/**
* Exception thrown when a phone number cannot be parsed.
*/
final class PhoneNumberParseException extends PhoneNumberException
{
/**
* @internal
*
* @param \Exception $e
*
* @return PhoneNumberParseException
*/
public static function wrap(\Exception $e) : PhoneNumberParseException
{
return new PhoneNumberParseException($e->getMessage(), $e->getCode(), $e);
}
}

View File

@@ -0,0 +1,106 @@
<?php
declare(strict_types=1);
namespace Brick\PhoneNumber;
/**
* Constants for the phone number types.
*/
final class PhoneNumberType
{
/**
* Fixed line number.
*/
public const FIXED_LINE = 0;
/**
* Mobile number.
*/
public const MOBILE = 1;
/**
* Fixed line or mobile number.
*
* 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.
*/
public const FIXED_LINE_OR_MOBILE = 2;
/**
* Freephone number.
*/
public const TOLL_FREE = 3;
/**
* Premium rate number.
*/
public const PREMIUM_RATE = 4;
/**
* Shared cost number.
*
* 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
*/
public const SHARED_COST = 5;
/**
* Voice over IP number.
*
* This includes TSoIP (Telephony Service over IP).
*/
public const VOIP = 6;
/**
* Personal number.
*
* A personal number is associated with a particular person, and may be routed to either a
* MOBILE or FIXED_LINE number.
*
* @see http://en.wikipedia.org/wiki/Personal_Numbers
*/
public const PERSONAL_NUMBER = 7;
/**
* Pager number.
*/
public const PAGER = 8;
/**
* Universal Access Number or Company Number.
*
* The number may be further routed to specific offices, but allows one number to be used for a company.
*/
public const UAN = 9;
/**
* Unknown number type.
*
* A phone number is of type UNKNOWN when it does not fit any of the known patterns
* for a specific region.
*/
public const UNKNOWN = 10;
/**
* Emergency number.
*/
public const EMERGENCY = 27;
/**
* Voicemail number.
*/
public const VOICEMAIL = 28;
/**
* Short code number.
*/
public const SHORT_CODE = 29;
/**
* Standard rate number.
*/
public const STANDARD_RATE = 30;
}

View File

@@ -0,0 +1,11 @@
<?php
header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
header('Cache-Control: no-store, no-cache, must-revalidate');
header('Cache-Control: post-check=0, pre-check=0', false);
header('Pragma: no-cache');
header('Location: ../');
exit;

View File

@@ -0,0 +1,40 @@
<?php
declare(strict_types=1);
namespace Brick\PhoneNumber\Tests;
use Brick\PhoneNumber\PhoneNumberFormat;
use Brick\PhoneNumber\PhoneNumberType;
use PHPUnit\Framework\TestCase;
/**
* Tests that the constants are up-to-date with libphonenumber.
*/
class ConstantTest extends TestCase
{
/**
* Compares the constants of two classes.
*
* @param string $classExpected The name or the reference class.
* @param string $classActual The name of the class to test against the reference class.
*/
private static function assertConstantsEqual(string $classExpected, string $classActual) : void
{
$expected = new \ReflectionClass($classExpected);
$actual = new \ReflectionClass($classActual);
self::assertSame($expected->getConstants(), $actual->getConstants());
}
public function testPhoneNumberFormats() : void
{
self::assertConstantsEqual(\libphonenumber\PhoneNumberFormat::class, PhoneNumberFormat::class);
}
public function testPhoneNumberTypes() : void
{
self::assertConstantsEqual(\libphonenumber\PhoneNumberType::class, PhoneNumberType::class);
}
}

View File

@@ -0,0 +1,642 @@
<?php
declare(strict_types=1);
namespace Brick\PhoneNumber\Tests;
use Brick\PhoneNumber\PhoneNumber;
use Brick\PhoneNumber\PhoneNumberException;
use Brick\PhoneNumber\PhoneNumberParseException;
use Brick\PhoneNumber\PhoneNumberType;
use Brick\PhoneNumber\PhoneNumberFormat;
use PHPUnit\Framework\TestCase;
/**
* Tests for class PhoneNumber.
*/
class PhoneNumberTest extends TestCase
{
private const ALPHA_NUMERIC_NUMBER = '+180074935247';
private const AE_UAN = '+971600123456';
private const AR_MOBILE = '+5491187654321';
private const AR_NUMBER = '+541187654321';
private const AU_NUMBER = '+61236618300';
private const BS_MOBILE = '+12423570000';
private const BS_NUMBER = '+12423651234';
// Note that this is the same as the example number for DE in the metadata.
private const DE_NUMBER = '+4930123456';
private const DE_SHORT_NUMBER = '+491234';
private const GB_MOBILE = '+447912345678';
private const GB_NUMBER = '+442070313000';
private const IT_MOBILE = '+39345678901';
private const IT_NUMBER = '+390236618300';
private const JP_STAR_NUMBER = '+812345';
// Numbers to test the formatting rules from Mexico.
private const MX_MOBILE1 = '+5212345678900';
private const MX_MOBILE2 = '+5215512345678';
private const MX_NUMBER1 = '+523312345678';
private const MX_NUMBER2 = '+528211234567';
private const NZ_NUMBER = '+6433316005';
private const SG_NUMBER = '+6565218000';
// A too-long and hence invalid US number.
private const US_LONG_NUMBER = '+165025300001';
private const US_NUMBER = '+16502530000';
private const US_PREMIUM = '+19002530000';
// Too short, but still possible US numbers.
private const US_LOCAL_NUMBER = '+12530000';
private const US_SHORT_BY_ONE_NUMBER = '+1650253000';
private const US_TOLLFREE = '+18002530000';
private const INTERNATIONAL_TOLL_FREE = '+80012345678';
// We set this to be the same length as numbers for the other non-geographical country prefix that
// we have in our test metadata. However, this is not considered valid because they differ in
// their country calling code.
private const INTERNATIONAL_TOLL_FREE_TOO_LONG = '+800123456789';
private const UNIVERSAL_PREMIUM_RATE = '+979123456789';
private const UNKNOWN_COUNTRY_CODE_NO_RAW_INPUT = '+212345';
/**
* @dataProvider providerGetExampleNumber
*
* @param string $regionCode
* @param string $callingCode
* @param int|null $numberType
*/
public function testGetExampleNumber(string $regionCode, string $callingCode, ?int $numberType = null) : void
{
if ($numberType === null) {
$phoneNumber = PhoneNumber::getExampleNumber($regionCode);
} else {
$phoneNumber = PhoneNumber::getExampleNumber($regionCode, $numberType);
}
self::assertInstanceOf(PhoneNumber::class, $phoneNumber);
self::assertTrue($phoneNumber->isValidNumber());
if ($numberType !== null) {
self::assertSame($numberType, $phoneNumber->getNumberType());
}
self::assertSame($callingCode, $phoneNumber->getCountryCode());
self::assertSame($regionCode, $phoneNumber->getRegionCode());
}
/**
* @return array
*/
public function providerGetExampleNumber() : array
{
return [
['US', '1'],
['FR', '33', PhoneNumberType::FIXED_LINE],
['FR', '33', PhoneNumberType::MOBILE],
['GB', '44', PhoneNumberType::FIXED_LINE],
['GB', '44', PhoneNumberType::MOBILE],
];
}
public function testGetExampleNumberThrowsExceptionForInvalidRegionCode() : void
{
$this->expectException(PhoneNumberException::class);
PhoneNumber::getExampleNumber('ZZ');
}
/**
* @dataProvider providerGetNationalNumber
*
* @param string $expectedNationalNumber
* @param string $phoneNumber
*/
public function testGetNationalNumber(string $expectedNationalNumber, string $phoneNumber) : void
{
self::assertSame($expectedNationalNumber, PhoneNumber::parse($phoneNumber)->getNationalNumber());
}
/**
* @return array
*/
public function providerGetNationalNumber() : array
{
return [
['6502530000', self::US_NUMBER],
['345678901', self::IT_MOBILE],
['236618300', self::IT_NUMBER],
['12345678', self::INTERNATIONAL_TOLL_FREE]
];
}
/**
* @dataProvider providerParseNationalNumber
*
* @param string $expectedNumber
* @param string $numberToParse
* @param string $regionCode
*/
public function testParseNationalNumber(string $expectedNumber, string $numberToParse, string $regionCode) : void
{
self::assertSame($expectedNumber, (string) PhoneNumber::parse($numberToParse, $regionCode));
}
/**
* @return array
*/
public function providerParseNationalNumber() : array
{
return [
// National prefix attached.
[self::NZ_NUMBER, '033316005', 'NZ'],
[self::NZ_NUMBER, '33316005', 'NZ'],
// National prefix attached and some formatting present.
[self::NZ_NUMBER, '03-331 6005', 'NZ'],
[self::NZ_NUMBER, '03 331 6005', 'NZ'],
// Testing international prefixes.
// Should strip country calling code.
[self::NZ_NUMBER, '0064 3 331 6005', 'NZ'],
// Try again, but this time we have an international number with Region Code US.
// It should recognise the country calling code and parse accordingly.
[self::NZ_NUMBER, '01164 3 331 6005', 'US'],
[self::NZ_NUMBER, '+64 3 331 6005', 'US'],
// @todo
// ['+6464123456', '64(0)64123456', 'NZ'],
// Check that using a '/' is fine in a phone number.
[self::DE_NUMBER, '301/23456', 'DE'],
// Check it doesn't use the '1' as a country calling code
// when parsing if the phone number was already possible
// @todo
// ['+11234567890', '123-456-7890', 'US']
];
}
/**
* @dataProvider providerGetRegionCode
*
* @param string|null $expectedRegion
* @param string $phoneNumber
*/
public function testGetRegionCode(?string $expectedRegion, string $phoneNumber) : void
{
self::assertSame($expectedRegion, PhoneNumber::parse($phoneNumber)->getRegionCode());
}
/**
* @return array
*/
public function providerGetRegionCode() : array
{
return [
['BS', self::BS_NUMBER],
['US', self::US_NUMBER],
['GB', self::GB_MOBILE],
[null, self::INTERNATIONAL_TOLL_FREE],
];
}
/**
* @dataProvider providerGetNumberType
*
* @param int $numberType
* @param string $phoneNumber
*/
public function testGetNumberType(int $numberType, string $phoneNumber) : void
{
self::assertSame($numberType, PhoneNumber::parse($phoneNumber)->getNumberType());
}
/**
* @return array
*/
public function providerGetNumberType() : array
{
return [
[PhoneNumberType::PREMIUM_RATE, self::US_PREMIUM],
[PhoneNumberType::PREMIUM_RATE, '+39892123'],
[PhoneNumberType::PREMIUM_RATE, '+449187654321'],
[PhoneNumberType::PREMIUM_RATE, '+499001654321'],
[PhoneNumberType::PREMIUM_RATE, '+4990091234567'],
[PhoneNumberType::PREMIUM_RATE, self::UNIVERSAL_PREMIUM_RATE],
// @todo doesn't work in online r557 either
// [PhoneNumberType::TOLL_FREE, '+18881234567'],
[PhoneNumberType::TOLL_FREE, '+39803123'],
// @todo doesn't work in online r557 either
// [PhoneNumberType::TOLL_FREE, '+448012345678'],
[PhoneNumberType::TOLL_FREE, '+498001234567'],
[PhoneNumberType::TOLL_FREE, self::INTERNATIONAL_TOLL_FREE],
[PhoneNumberType::MOBILE, self::BS_MOBILE],
[PhoneNumberType::MOBILE, self::GB_MOBILE],
// @todo doesn't work in online r557 either
// [PhoneNumberType::MOBILE, self::IT_MOBILE],
// [PhoneNumberType::MOBILE, self::AR_MOBILE],
// @todo this matches both fixedLine & mobile, but is still reported as MOBILE in the java version
// [PhoneNumberType::MOBILE, '+4915123456789'],
// @todo doesn't work in online r557 either
// [PhoneNumberType::MOBILE, self::MX_MOBILE1],
// @todo changed from MOBILE to FIXED_LINE_OR_MOBILE in 8.10.17
// [PhoneNumberType::MOBILE, self::MX_MOBILE2],
[PhoneNumberType::FIXED_LINE, self::BS_NUMBER],
[PhoneNumberType::FIXED_LINE, self::IT_NUMBER],
[PhoneNumberType::FIXED_LINE, self::GB_NUMBER],
[PhoneNumberType::FIXED_LINE, self::DE_NUMBER],
[PhoneNumberType::FIXED_LINE_OR_MOBILE, self::US_NUMBER],
// @todo doesn't work in online r557 either
// [PhoneNumberType::FIXED_LINE_OR_MOBILE, '+541987654321'],
// @todo not a good example, changed from SHARED_COST (v7) to PREMIUM_RATE (v8)
// [PhoneNumberType::SHARED_COST, '+448431231234'],
[PhoneNumberType::VOIP, '+445631231234'],
[PhoneNumberType::PERSONAL_NUMBER, '+447031231234'],
[PhoneNumberType::UNKNOWN, self::US_LOCAL_NUMBER]
];
}
/**
* @dataProvider providerValidNumbers
* @dataProvider providerPossibleButNotValidNumbers
*
* @param string $phoneNumber
*/
public function testIsPossibleNumber(string $phoneNumber) : void
{
self::assertTrue(PhoneNumber::parse($phoneNumber)->isPossibleNumber());
}
/**
* @dataProvider providerNotPossibleNumbers
*
* @param string $phoneNumber
*/
public function testIsNotPossibleNumber(string $phoneNumber) : void
{
self::assertFalse(PhoneNumber::parse($phoneNumber)->isPossibleNumber());
}
/**
* @dataProvider providerValidNumbers
*
* @param string $phoneNumber
*/
public function testIsValidNumber(string $phoneNumber) : void
{
self::assertTrue(PhoneNumber::parse($phoneNumber)->isValidNumber());
}
/**
* @dataProvider providerNotPossibleNumbers
* @dataProvider providerPossibleButNotValidNumbers
*
* @param string $phoneNumber
*/
public function testIsNotValidNumber(string $phoneNumber) : void
{
self::assertFalse(PhoneNumber::parse($phoneNumber)->isValidNumber());
}
/**
* @return array
*/
public function providerValidNumbers() : array
{
return [
[self::US_NUMBER],
[self::IT_NUMBER],
[self::GB_MOBILE],
[self::INTERNATIONAL_TOLL_FREE],
[self::UNIVERSAL_PREMIUM_RATE],
['+6421387835']
];
}
/**
* @return array
*/
public function providerPossibleButNotValidNumbers() : array
{
return [
[self::US_LOCAL_NUMBER],
['+3923661830000'],
['+44791234567'],
['+491234'],
['+643316005'],
['+39232366']
];
}
/**
* @return array
*/
public function providerNotPossibleNumbers() : array
{
return [
[self::INTERNATIONAL_TOLL_FREE_TOO_LONG],
['+1253000']
];
}
/**
* @dataProvider providerParseException
*
* @param string $phoneNumber
* @param string|null $regionCode
*/
public function testParseException(string $phoneNumber, ?string $regionCode = null) : void
{
$this->expectException(PhoneNumberParseException::class);
PhoneNumber::parse($phoneNumber, $regionCode);
}
/**
* @return array
*/
public function providerParseException() : array
{
return [
// Empty string.
[''],
['', 'US'],
['This is not a phone number', 'NZ'],
['1 Still not a number', 'NZ'],
['1 MICROSOFT', 'NZ'],
['12 MICROSOFT', 'NZ'],
['01495 72553301873 810104', 'GB'],
['+---', 'DE'],
['+***', 'DE'],
['+*******91', 'DE'],
['+ 00 210 3 331 6005', 'NZ'],
// Too short.
['+49 0', 'DE'],
// Does not match a country code.
['+02366'],
['+210 3456 56789', 'NZ'],
// A region code must be given if not in international format.
['123 456 7890'],
// Unknown region code (deprecated and removed from ISO 3166-2).
['123 456 7890', 'CS'],
// No number, only region code.
['0044', 'GB'],
['0044------', 'GB'],
// Only IDD provided.
['011', 'US'],
// Only IDD and then 9.
['0119', 'US']
];
}
/**
* @dataProvider providerFormatNumber
*
* @param string $expected
* @param string $phoneNumber
* @param int $numberFormat
*/
public function testFormatNumber(string $expected, string $phoneNumber, int $numberFormat) : void
{
self::assertSame($expected, PhoneNumber::parse($phoneNumber)->format($numberFormat));
}
/**
* @return array
*/
public function providerFormatNumber() : array
{
return [
// US
['(650) 253-0000', self::US_NUMBER, PhoneNumberFormat::NATIONAL],
['+1 650-253-0000', self::US_NUMBER, PhoneNumberFormat::INTERNATIONAL],
['(800) 253-0000', self::US_TOLLFREE, PhoneNumberFormat::NATIONAL],
['+1 800-253-0000', self::US_TOLLFREE, PhoneNumberFormat::INTERNATIONAL],
['(900) 253-0000', self::US_PREMIUM, PhoneNumberFormat::NATIONAL],
['+1 900-253-0000', self::US_PREMIUM, PhoneNumberFormat::INTERNATIONAL],
['tel:+1-900-253-0000', self::US_PREMIUM, PhoneNumberFormat::RFC3966],
// BS
['(242) 365-1234', self::BS_NUMBER, PhoneNumberFormat::NATIONAL],
['+1 242-365-1234', self::BS_NUMBER, PhoneNumberFormat::INTERNATIONAL],
// GB
['020 7031 3000', self::GB_NUMBER, PhoneNumberFormat::NATIONAL],
['+44 20 7031 3000', self::GB_NUMBER, PhoneNumberFormat::INTERNATIONAL],
['07912 345678', self::GB_MOBILE, PhoneNumberFormat::NATIONAL],
['+44 7912 345678', self::GB_MOBILE, PhoneNumberFormat::INTERNATIONAL],
// DE
['030 1234', '+49301234', PhoneNumberFormat::NATIONAL],
['+49 30 1234', '+49301234', PhoneNumberFormat::INTERNATIONAL],
['tel:+49-30-1234', '+49301234', PhoneNumberFormat::RFC3966],
['0291 123', '+49291123', PhoneNumberFormat::NATIONAL],
['+49 291 123', '+49291123', PhoneNumberFormat::INTERNATIONAL],
['0291 12345678', '+4929112345678', PhoneNumberFormat::NATIONAL],
['+49 291 12345678', '+4929112345678', PhoneNumberFormat::INTERNATIONAL],
['09123 12345', '+49912312345', PhoneNumberFormat::NATIONAL],
['+49 9123 12345', '+49912312345', PhoneNumberFormat::INTERNATIONAL],
['08021 2345', '+4980212345', PhoneNumberFormat::NATIONAL],
['+49 8021 2345', '+4980212345', PhoneNumberFormat::INTERNATIONAL],
['030 123456', self::DE_NUMBER, PhoneNumberFormat::NATIONAL],
['04134 1234', '+4941341234', PhoneNumberFormat::NATIONAL],
// IT
['02 3661 8300', self::IT_NUMBER, PhoneNumberFormat::NATIONAL],
['+39 02 3661 8300', self::IT_NUMBER, PhoneNumberFormat::INTERNATIONAL],
['+390236618300', self::IT_NUMBER, PhoneNumberFormat::E164],
['345 678 901', self::IT_MOBILE, PhoneNumberFormat::NATIONAL],
['+39 345 678 901', self::IT_MOBILE, PhoneNumberFormat::INTERNATIONAL],
['+39345678901', self::IT_MOBILE, PhoneNumberFormat::E164],
// AU
['(02) 3661 8300', self::AU_NUMBER, PhoneNumberFormat::NATIONAL],
['+61 2 3661 8300', self::AU_NUMBER, PhoneNumberFormat::INTERNATIONAL],
['+61236618300', self::AU_NUMBER, PhoneNumberFormat::E164],
['1800 123 456', '+611800123456', PhoneNumberFormat::NATIONAL],
['+61 1800 123 456', '+611800123456', PhoneNumberFormat::INTERNATIONAL],
['+611800123456', '+611800123456', PhoneNumberFormat::E164],
// AR
['011 8765-4321', self::AR_NUMBER, PhoneNumberFormat::NATIONAL],
['+54 11 8765-4321', self::AR_NUMBER, PhoneNumberFormat::INTERNATIONAL],
['+541187654321', self::AR_NUMBER, PhoneNumberFormat::E164],
['011 15-8765-4321', self::AR_MOBILE, PhoneNumberFormat::NATIONAL],
['+54 9 11 8765-4321', self::AR_MOBILE, PhoneNumberFormat::INTERNATIONAL],
['+5491187654321', self::AR_MOBILE, PhoneNumberFormat::E164],
// MX
// @todo bad tests, MX rules changed in upstream 8.10.17
// ['044 234 567 8900', self::MX_MOBILE1, PhoneNumberFormat::NATIONAL],
// ['+52 1 234 567 8900', self::MX_MOBILE1, PhoneNumberFormat::INTERNATIONAL],
// ['+5212345678900', self::MX_MOBILE1, PhoneNumberFormat::E164],
//
// ['044 55 1234 5678', self::MX_MOBILE2, PhoneNumberFormat::NATIONAL],
// ['+52 1 55 1234 5678', self::MX_MOBILE2, PhoneNumberFormat::INTERNATIONAL],
// ['+5215512345678', self::MX_MOBILE2, PhoneNumberFormat::E164],
//
// ['01 33 1234 5678', self::MX_NUMBER1, PhoneNumberFormat::NATIONAL],
// ['+52 33 1234 5678', self::MX_NUMBER1, PhoneNumberFormat::INTERNATIONAL],
// ['+523312345678', self::MX_NUMBER1, PhoneNumberFormat::E164],
//
// ['01 821 123 4567', self::MX_NUMBER2, PhoneNumberFormat::NATIONAL],
// ['+52 821 123 4567', self::MX_NUMBER2, PhoneNumberFormat::INTERNATIONAL],
// ['+528211234567', self::MX_NUMBER2, PhoneNumberFormat::E164]
];
}
/**
* @dataProvider providerFormatForCallingFrom
*
* @param string $phoneNumber
* @param string $countryCode
* @param string $expected
*/
public function testFormatForCallingFrom(string $phoneNumber, string $countryCode, string $expected) : void
{
self::assertSame($expected, PhoneNumber::parse($phoneNumber)->formatForCallingFrom($countryCode));
}
/**
* @return array
*/
public function providerFormatForCallingFrom() : array
{
return [
['+33123456789', 'FR', '01 23 45 67 89'],
['+33123456789', 'BE', '00 33 1 23 45 67 89'],
['+33123456789', 'CH', '00 33 1 23 45 67 89'],
['+33123456789', 'DE', '00 33 1 23 45 67 89'],
['+33123456789', 'GB', '00 33 1 23 45 67 89'],
['+33123456789', 'US', '011 33 1 23 45 67 89'],
['+33123456789', 'CA', '011 33 1 23 45 67 89'],
['+16502530000', 'US', '1 (650) 253-0000'],
['+16502530000', 'CA', '1 (650) 253-0000'],
['+16502530000', 'FR', '00 1 650-253-0000'],
['+16502530000', 'BE', '00 1 650-253-0000'],
['+16502530000', 'CH', '00 1 650-253-0000'],
['+16502530000', 'DE', '00 1 650-253-0000'],
['+16502530000', 'GB', '00 1 650-253-0000'],
];
}
/**
* @dataProvider providerGetGeographicalAreaCode
*/
public function testGetGeographicalAreaCode(string $phoneNumber, string $areaCode) : void
{
self::assertSame($areaCode, PhoneNumber::parse($phoneNumber)->getGeographicalAreaCode());
}
public function providerGetGeographicalAreaCode() : array
{
return [
['+442079460585', '20'],
['+441132224444', '113'],
['+447553840000', ''],
['+33123000000', '1'],
['+33234000000', '2'],
['+33345000000', '3'],
['+33456000000', '4'],
['+33567000000', '5'],
];
}
/**
* @dataProvider providerIsEqualTo
*/
public function testIsEqualTo(string $phoneNumber1, string $phoneNumber2, bool $isEqual): void
{
$phoneNumber1 = PhoneNumber::parse($phoneNumber1);
$phoneNumber2 = PhoneNumber::parse($phoneNumber2);
self::assertSame($isEqual, $phoneNumber1->isEqualTo($phoneNumber2));
}
public function providerIsEqualTo(): array
{
return [
['+442079460585', '+442079460585', true],
['+442079460585', '+442079460586', false],
];
}
public function testJsonSerializable(): void
{
$data = [
'phoneNumber' => PhoneNumber::parse('0123000000', 'FR')
];
self::assertSame('{"phoneNumber":"+33123000000"}', json_encode($data));
}
/**
* @dataProvider providerGetDescription
*/
public function testGetDescription(string $phoneNumber, string $locale, ?string $userRegion, ?string $expected) : void
{
self::assertSame($expected, PhoneNumber::parse($phoneNumber)->getDescription($locale, $userRegion));
}
public function providerGetDescription() : array
{
return [
['+16509036313', 'EN', null, 'Mountain View, CA'],
['+16509036313', 'EN', 'US', 'Mountain View, CA'],
['+16509036313', 'EN', 'GB', 'United States'],
['+16509036313', 'EN', 'FR', 'United States'],
['+16509036313', 'EN', 'XX', 'United States'],
['+16509036313', 'FR', null, 'Mountain View, CA'],
['+16509036313', 'FR', 'US', 'Mountain View, CA'],
['+16509036313', 'FR', 'GB', 'États-Unis'],
['+16509036313', 'FR', 'FR', 'États-Unis'],
['+16509036313', 'FR', 'XX', 'États-Unis'],
['+33381251234', 'FR', null, 'Besançon'],
['+33381251234', 'FR', 'FR', 'Besançon'],
['+33381251234', 'FR', 'US', 'France'],
['+33381251234', 'FR', 'XX', 'France'],
['+33381251234', 'EN', null, 'Besançon'],
['+33381251234', 'EN', 'FR', 'Besançon'],
['+33381251234', 'EN', 'US', 'France'],
['+33381251234', 'EN', 'XX', 'France'],
['+33328201234', 'FR', null, 'Dunkerque'],
['+33328201234', 'FR', 'FR', 'Dunkerque'],
['+33328201234', 'FR', 'US', 'France'],
['+33328201234', 'FR', 'XX', 'France'],
['+33328201234', 'GB', null, 'Dunkirk'],
['+33328201234', 'XX', null, 'Dunkirk'],
['+41229097000', 'FR', null, 'Genève'],
['+41229097000', 'FR', 'CH', 'Genève'],
['+41229097000', 'FR', 'US', 'Suisse'],
['+41229097000', 'XX', null, 'Geneva'],
['+37328000000', 'XX', null, null],
];
}
}

View File

@@ -0,0 +1,11 @@
<?php
header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
header('Cache-Control: no-store, no-cache, must-revalidate');
header('Cache-Control: post-check=0, pre-check=0', false);
header('Pragma: no-cache');
header('Location: ../');
exit;