update for version 1.0.1

This commit is contained in:
sujitprasad
2015-10-23 14:15:29 +05:30
parent 82b878e93b
commit 3d425dc380
8348 changed files with 10020 additions and 4171 deletions

View File

@@ -0,0 +1,4 @@
/vendor
composer.phar
composer.lock
.DS_Store

View File

@@ -0,0 +1,17 @@
filter:
paths:
- 'src/*'
excluded_paths:
- 'tests/*'
- 'bin/*'
- 'spec/*'
- 'stub/*'
- 'coverage/*'
- 'vendor/*'
checks:
php: true
tools:
external_code_coverage:
timeout: 1200
runs: 3
php_code_coverage: false

View File

@@ -0,0 +1,18 @@
language: php
php:
- 5.4
- 5.5
- 5.6
- hhvm
before_script:
- composer self-update
- composer install --prefer-source --no-interaction --dev
script:
- phpunit --coverage-clover=coverage.clover
after_script:
- wget https://scrutinizer-ci.com/ocular.phar
- php ocular.phar code-coverage:upload --format=php-clover coverage.clover

View File

@@ -0,0 +1,20 @@
The MIT License (MIT)
Copyright (c) 2014 Propaganistas
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,101 @@
# Laravel Phone Validator
[![Build Status](https://travis-ci.org/Propaganistas/Laravel-Phone.svg)](https://travis-ci.org/Propaganistas/Laravel-Phone)
[![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/Propaganistas/Laravel-Phone/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/Propaganistas/Laravel-Phone/?branch=master)
[![Code Coverage](https://scrutinizer-ci.com/g/Propaganistas/Laravel-Phone/badges/coverage.png?b=master)](https://scrutinizer-ci.com/g/Propaganistas/Laravel-Phone/?branch=master)
[![Latest Stable Version](https://poser.pugx.org/propaganistas/laravel-phone/v/stable)](https://packagist.org/packages/propaganistas/laravel-phone)
[![Total Downloads](https://poser.pugx.org/propaganistas/laravel-phone/downloads)](https://packagist.org/packages/propaganistas/laravel-phone)
[![License](https://poser.pugx.org/propaganistas/laravel-phone/license)](https://packagist.org/packages/propaganistas/laravel-phone)
Adds a phone validator to Laravel 4 and 5 based on the [PHP port](https://github.com/giggsey/libphonenumber-for-php) of [Google's libphonenumber API](https://github.com/googlei18n/libphonenumber) by [giggsey](https://github.com/giggsey).
### Installation
1. In the `require` key of `composer.json` file add the following
```json
"propaganistas/laravel-phone": "~2.0"
```
2. Run the Composer update command
```bash
$ composer update
```
3. In your app config, add the Service Provider to the end of the `$providers` array
**Laravel 5**
```php
'providers' => [
App\Providers\EventServiceProvider::class,
App\Providers\RouteServiceProvider::class,
...
Propaganistas\LaravelPhone\LaravelPhoneServiceProvider::class,
],
```
**Laravel 4**
```php
'providers' => [
'Illuminate\Foundation\Providers\ArtisanServiceProvider',
'Illuminate\Auth\AuthServiceProvider',
...
'Propaganistas\LaravelPhone\LaravelPhoneServiceProvider',
],
```
4. In your languages directory, add for each language an extra language line for the validator:
```php
"phone" => "The :attribute field contains an invalid number.",
```
### Usage
To validate a field using the phone validator, use the `phone` keyword in your validation rules array. The phone validator is able to operate in **three** ways.
- You either specify [*ISO 3166-1 alpha-2 compliant*](http://en.wikipedia.org/wiki/ISO_3166-1_alpha-2#Officially_assigned_code_elements) country codes yourself as parameters for the validator, e.g.:
```php
'phonefield' => 'phone:US,BE',
```
The validator will check if the number is valid in at least one of provided countries, so feel free to add as many country codes as you like.
- You don't specify any parameters but you plug in a dedicated country input field (keyed by *ISO 3166-1 compliant* country codes) to allow end users to supply a country on their own. The easiest method by far is to install the [CountryList package by monarobase](https://github.com/Monarobase/country-list). The country field has to be named similar to the phone field but with `_country` appended:
```php
'phonefield' => 'phone',
'phonefield_country' => 'required_with:phonefield',
```
If using the CountryList package, you could then use the following snippet to populate a country selection list:
```php
Countries::getList(App::getLocale(), 'php', 'cldr'))
```
- You instruct the validator to detect which country the number belongs to using the `AUTO` keyword:
```php
'phonefield' => 'phone:AUTO',
```
The validator will try to extract the country from the number itself and then check if the number is valid for that country. Note that this will only work when phone numbers are entered in *international format* (prefixed with a `+` sign, e.g. +32 ....). Leading double zeros will **NOT** be parsed correctly as this isn't an established consistency.
To specify constraints on the number type, just append the allowed types to the end of the parameters, e.g.:
```php
'phonefield' => 'phone:US,BE,mobile',
```
The most common types are `mobile` and `fixed_line`, but feel free to use any of the types defined [here](https://github.com/giggsey/libphonenumber-for-php/blob/master/src/libphonenumber/PhoneNumberType.php).
### Display
Format a fetched phone value using the helper function:
```php
phone_format($phone_number, $country_code, $format = null)
```
The `$format` parameter is optional and should be a constant of `\libphonenumber\PhoneNumberFormat` (defaults to `\libphonenumber\PhoneNumberFormat::INTERNATIONAL`)

View File

@@ -0,0 +1,30 @@
{
"name": "propaganistas/laravel-phone",
"description": "Adds a phone validator to Laravel based on Google's libphonenumber API.",
"keywords": ["laravel", "libphonenumber", "validation", "phone"],
"license": "MIT",
"authors": [
{
"name": "Propaganistas",
"email": "Propaganistas@users.noreply.github.com"
}
],
"require": {
"php": ">=5.4.0",
"illuminate/support": "~4.0|~5.0",
"illuminate/validation": "~4.0|~5.0",
"giggsey/libphonenumber-for-php": "~7.0"
},
"suggest": {
"monarobase/country-list": "Adds a compatible (and fully translated) country list API."
},
"autoload": {
"psr-4": {
"Propaganistas\\LaravelPhone\\": "src/"
},
"files": [
"src/helpers.php"
]
},
"prefer-stable": true
}

View File

@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit backupGlobals="false"
backupStaticAttributes="false"
bootstrap="vendor/autoload.php"
colors="true"
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
processIsolation="false"
stopOnFailure="false"
syntaxCheck="false"
>
<testsuites>
<testsuite name="Package Test Suite">
<directory suffix=".php">./tests/</directory>
</testsuite>
</testsuites>
<filter>
<whitelist addUncoveredFilesFromWhitelist="true">
<directory suffix=".php">./src</directory>
<exclude>
<file>./src/LaravelPhoneServiceProvider.php</file>
</exclude>
</whitelist>
</filter>
<logging>
<log type="coverage-clover" target="./clover.xml"/>
</logging>
</phpunit>

View File

@@ -0,0 +1,3 @@
<?php namespace Propaganistas\LaravelPhone\Exceptions;
class InvalidParameterException extends \Exception {}

View File

@@ -0,0 +1,3 @@
<?php namespace Propaganistas\LaravelPhone\Exceptions;
class NoValidCountryFoundException extends \Exception {}

View File

@@ -0,0 +1,36 @@
<?php namespace Propaganistas\LaravelPhone;
use Illuminate\Support\ServiceProvider;
use libphonenumber\PhoneNumberUtil;
class LaravelPhoneServiceProvider extends ServiceProvider
{
/**
* Indicates if loading of the provider is deferred.
*
* @var bool
*/
protected $defer = false;
/**
* Bootstrap the application events.
*
* @return void
*/
public function boot()
{
$this->app['validator']->extend('phone', 'Propaganistas\LaravelPhone\PhoneValidator@validatePhone');
}
/**
* Register the service provider.
*
* @return void
*/
public function register() {
// Make libphonenumber available in the application container.
$this->app->singleton('libphonenumber', function ($app) {
return PhoneNumberUtil::getInstance();
});
}
}

View File

@@ -0,0 +1,193 @@
<?php namespace Propaganistas\LaravelPhone;
use libphonenumber\PhoneNumberUtil;
use libphonenumber\NumberParseException;
use libphonenumber\PhoneNumberType;
use Propaganistas\LaravelPhone\Exceptions\NoValidCountryFoundException;
use Propaganistas\LaravelPhone\Exceptions\InvalidParameterException;
class PhoneValidator {
/**
* The phone number attribute.
* @var string
*/
protected $attribute;
/**
* Data from the validator instance.
* @var array
*/
protected $data;
/**
* Countries to validate against.
* @var array
*/
protected $allowedCountries = array();
/**
* Untransformed phone number types to validate against.
* @var array
*/
protected $untransformedTypes = array();
/**
* Transformed phone number types to validate against.
* @var array
*/
protected $allowedTypes = array();
/**
* Supplied validator parameters.
* @var array
*/
protected $parameters;
/**
* Validates a phone number.
*/
public function validatePhone($attribute, $value, $parameters, $validator)
{
$this->attribute = $attribute;
$this->data = $validator->getData();
$this->parameters = array_map('strtoupper', $parameters);
$this->determineCountries();
$this->determineTypes();
$this->checkLeftoverParameters();
$phoneUtil = PhoneNumberUtil::getInstance();
// Perform validation.
foreach ($this->allowedCountries as $country) {
try {
// For default countries or country field, the following throws NumberParseException if
// not parsed correctly against the supplied country.
// For automatic detection: tries to discover the country code using from the number itself.
$phoneProto = $phoneUtil->parse($value, $country);
// For automatic detection, the number should have a country code.
// Check if type is allowed.
if ($phoneProto->hasCountryCode() && empty($this->allowedTypes) || in_array($phoneUtil->getNumberType($phoneProto), $this->allowedTypes)) {
// Automatic detection:
if ($country == 'ZZ') {
// Validate if the international phone number is valid for its contained country.
return $phoneUtil->isValidNumber($phoneProto);
}
// Force validation of number against the specified country.
return $phoneUtil->isValidNumberForRegion($phoneProto, $country);
}
} catch (NumberParseException $e) {
// Proceed to default validation error.
}
}
return false;
}
/**
* Checks if the supplied string is a valid country code using some arbitrary country validation.
* If using a package based on umpirsky/country-list, invalidate the option 'ZZ => Unknown or invalid region'.
*
* @param string $country
* @return bool
*/
public function isPhoneCountry($country)
{
return (strlen($country) === 2 && ctype_alpha($country) && ctype_upper($country) && $country != 'ZZ');
}
/**
* Checks if the supplied string is a valid phone number type.
*
* @param string $type
* @return bool
*/
public function isPhoneType($type)
{
// Legacy support.
$type = ($type == 'LANDLINE' ? 'FIXED_LINE' : $type);
return defined($this->constructPhoneTypeConstant($type));
}
/**
* Constructs the corresponding namespaced class constant for a phone number type.
*
* @param string $type
* @return string
*/
protected function constructPhoneTypeConstant($type)
{
return '\libphonenumber\PhoneNumberType::' . $type;
}
/**
* Sets the countries to validate against.
*
* @throws \Propaganistas\LaravelPhone\Exceptions\NoValidCountryFoundException
*/
protected function determineCountries()
{
// Check for the existence of a country field.
$field = $this->attribute . '_country';
if (isset($this->data[$field])) {
$this->allowedCountries = ($this->isPhoneCountry($this->data[$field])) ? array($this->data[$field]) : array();
// No exception should be thrown since empty country fields should validate to false.
}
// Or if we need to parse for automatic detection.
elseif (in_array('AUTO', $this->parameters)) {
$this->allowedCountries = array('ZZ');
}
// Else use the supplied parameters.
else {
$this->allowedCountries = array_filter($this->parameters, function($item) {
return $this->isPhoneCountry($item);
});
if (empty($this->allowedCountries)) {
throw new NoValidCountryFoundException;
}
}
}
/**
* Sets the phone number types to validate against.
*/
protected function determineTypes()
{
// Get phone types.
$this->untransformedTypes = array_filter($this->parameters, function($item) {
return $this->isPhoneType($item);
});
// Transform valid types to their namespaced class constant.
$this->allowedTypes = array_map(function($item) {
return constant($this->constructPhoneTypeConstant($item));
}, $this->untransformedTypes);
// Add in the unsure number type if applicable.
if (array_intersect(['FIXED_LINE', 'MOBILE'], $this->parameters)) {
$this->allowedTypes[] = PhoneNumberType::FIXED_LINE_OR_MOBILE;
}
}
/**
* Checks for parameter leftovers to force developers to write proper code.
*
* @throws \Propaganistas\LaravelPhone\Exceptions\InvalidParameterException
*/
protected function checkLeftoverParameters()
{
// Remove the automatic detection option if applicable.
$leftovers = array_diff($this->parameters, $this->allowedCountries, $this->untransformedTypes, array('AUTO'));
if (!empty($leftovers)) {
throw new InvalidParameterException(implode(', ', $leftovers));
}
}
}

View File

@@ -0,0 +1,8 @@
<?php
function phone_format($phone, $country, $format = null) {
$phoneUtil = \libphonenumber\PhoneNumberUtil::getInstance();
$phoneProto = $phoneUtil->parse($phone, $country);
$format = is_null($format) ? \libphonenumber\PhoneNumberFormat::INTERNATIONAL : $format;
return $phoneUtil->format($phoneProto, $format);
}

View File

View File

@@ -0,0 +1,168 @@
<?php
use Symfony\Component\Translation\Translator;
use Symfony\Component\Translation\MessageSelector;
use Symfony\Component\Translation\Loader\ArrayLoader;
use Illuminate\Validation\Validator;
use Propaganistas\LaravelPhone\PhoneValidator;
class PhoneValidatorTest extends PHPUnit_Framework_TestCase {
private $translator;
private $validator;
public function setUp()
{
$this->translator = new Translator('en', new MessageSelector());
$this->translator->addLoader('array', new ArrayLoader);
$this->validator = new Validator($this->translator, array(), array());
}
private function createPhoneValidator()
{
return new PhoneValidator();
}
private function performValidation($data)
{
$validatorData = array('field' => $data['value']);
$validatorData['field_country'] = isset($data['country']) ? $data['country'] : null;
$this->validator->setData($validatorData);
$parameters = isset($data['params']) ? explode(',',$data['params']) : array();
return $this->createPhoneValidator()
->validatePhone('field', $data['value'], $parameters, $this->validator);
}
public function testValidatePhoneWithDefaultCountryNoType()
{
// Validator with correct country value.
$this->assertTrue($this->performValidation(['value' => '016123456', 'params' => 'BE']));
// Validator with wrong country value.
$this->assertFalse($this->performValidation(['value' => '016123456', 'params' => 'NL']));
// Validator with multiple country values, one correct.
$this->assertTrue($this->performValidation(['value' => '016123456', 'params' => 'BE,NL']));
// Validator with multiple wrong country values.
$this->assertFalse($this->performValidation(['value' => '016123456', 'params' => 'DE,NL']));
}
public function testValidatePhoneWithCountryFieldNoType()
{
// Validator with correct country field supplied.
$this->assertTrue($this->performValidation(['value' => '016123456', 'country' => 'BE']));
// Validator with wrong country field supplied.
$this->assertFalse($this->performValidation(['value' => '016123456', 'country' => 'NL']));
}
public function testValidatePhoneWithDefaultCountryWithType()
{
// Validator with correct country value, correct type.
$this->assertTrue($this->performValidation(['value' => '0499123456', 'params' => 'BE,mobile']));
// Validator with correct country value, wrong type.
$this->assertFalse($this->performValidation(['value' => '016123456', 'params' => 'BE,mobile']));
// Validator with wrong country value, correct type.
$this->assertFalse($this->performValidation(['value' => '0499123456', 'params' => 'NL,mobile']));
// Validator with wrong country value, wrong type.
$this->assertFalse($this->performValidation(['value' => '016123456', 'params' => 'NL,mobile']));
// Validator with multiple country values, one correct, correct type.
$this->assertTrue($this->performValidation(['value' => '0499123456', 'params' => 'BE,NL,mobile']));
// Validator with multiple country values, one correct, wrong type.
$this->assertFalse($this->performValidation(['value' => '016123456', 'params' => 'BE,NL,mobile']));
// Validator with multiple country values, none correct, correct type.
$this->assertFalse($this->performValidation(['value' => '0499123456', 'params' => 'DE,NL,mobile']));
// Validator with multiple country values, none correct, wrong type.
$this->assertFalse($this->performValidation(['value' => '016123456', 'params' => 'DE,NL,mobile']));
}
public function testValidatePhoneWithCountryFieldWithType()
{
// Validator with correct country field supplied, correct type.
$this->assertTrue($this->performValidation(['value' => '0499123456', 'params' => 'mobile', 'country' => 'BE']));
// Validator with correct country field supplied, wrong type.
$this->assertFalse($this->performValidation(['value' => '016123456', 'params' => 'mobile', 'country' => 'BE']));
// Validator with wrong country field supplied, correct type.
$this->assertFalse($this->performValidation(['value' => '0499123456', 'params' => 'mobile', 'country' => 'NL']));
// Validator with wrong country field supplied, wrong type.
$this->assertFalse($this->performValidation(['value' => '016123456', 'params' => 'mobile', 'country' => 'NL']));
}
public function testValidatePhoneAutomaticDetectionFromInternationalInput()
{
// Validator with correct international input.
$this->assertTrue($this->performValidation(['value' => '+3216123456', 'params' => 'AUTO']));
// Validator with wrong international input.
$this->assertFalse($this->performValidation(['value' => '003216123456', 'params' => 'AUTO']));
// Validator with wrong international input.
$this->assertFalse($this->performValidation(['value' => '+321456', 'params' => 'AUTO']));
}
public function testValidatePhoneNoDefaultCountryNoCountryField()
{
$this->setExpectedException('Propaganistas\LaravelPhone\Exceptions\NoValidCountryFoundException');
// Validator with no country field or given country.
$this->performValidation(['value' => '016123456']);
// Validator with no country field or given country, wrong type.
$this->performValidation(['value' => '016123456', 'params' => 'mobile']);
// Validator with no country field or given country, correct type.
$this->performValidation(['value' => '0499123456', 'params' => 'mobile']);
// Validator with no country field or given country, correct type, faulty parameter.
$this->performValidation(['value' => '0499123456', 'params' => 'mobile,xyz']);
}
public function testValidatePhoneFaultyParameters()
{
$this->setExpectedException('Propaganistas\LaravelPhone\Exceptions\InvalidParameterException');
// Validator with given country, correct type, faulty parameter.
$this->performValidation(['value' => '016123456', 'params' => 'BE,mobile,xyz']);
// Validator with country field, correct type, faulty parameter.
$this->performValidation(['value' => '016123456', 'params' => 'mobile,xyz', 'country' => 'BE']);
}
public function testPhoneFormatHelperFunction()
{
// Test landline number without format parameter.
$actual = phone_format('016123456', 'BE');
$expected = '+32 16 12 34 56';
$this->assertEquals($expected, $actual);
// Test landline number with format parameter.
$actual = phone_format('016123456', 'BE', \libphonenumber\PhoneNumberFormat::NATIONAL);
$expected = '016 12 34 56';
$this->assertEquals($expected, $actual);
// Test mobile number without format parameter.
$actual = phone_format('0499123456', 'BE');
$expected = '+32 499 12 34 56';
$this->assertEquals($expected, $actual);
// Test mobile number with format parameter.
$actual = phone_format('0499123456', 'BE', \libphonenumber\PhoneNumberFormat::NATIONAL);
$expected = '0499 12 34 56';
$this->assertEquals($expected, $actual);
}
}