Apply contribution guidelines to "CountryCode" rule

The "AbstractSearcher" already does most of the job that "CountryCode"
was doing, so using it as parent class made more sense. That also makes
the validation case-sensitive which is not a problem since the standard
ISO 3166-1 in fact enforces an specific case for the country codes.

The documentation about the rule is also updated.

Co-Authored-By: Henrique Moody <henriquemoody@gmail.com>
This commit is contained in:
William Espindola 2018-06-13 00:01:03 -03:00 committed by Henrique Moody
parent 527553ce99
commit a79e702173
No known key found for this signature in database
GPG key ID: 221E9281655813A6
25 changed files with 165 additions and 362 deletions

View file

@ -1,20 +1,37 @@
# CountryCode
- `CountryCode()`
- `CountryCode(string $set)`
Validates an ISO country code like US or BR.
Validates whether the input is a country code in [ISO 3166-1][] standard.
```php
v::countryCode()->validate('BR'); // true
v::countryCode('alpha-2')->validate('NL'); // true
v::countryCode('alpha-3')->validate('USA'); // true
v::countryCode('numeric')->validate('504'); // true
```
This rule supports the three sets of country codes:
- ISO 3166-1 alpha-2 (`'alpha-2'` or `CountryCode::ALPHA2`)
- ISO 3166-1 alpha-3 (`'alpha-3'` or `CountryCode::ALPHA3`)
- ISO 3166-1 numeric (`'numeric'` or `CountryCode::NUMERIC`).
When no set is defined the rule uses `'alpha-2'` (`CountryCode::ALPHA2`).
## Changelog
Version | Description
--------|-------------
2.0.0 | Became case-sensitive
0.5.0 | Created
***
See also:
- [Tld](Tld.md)
[ISO 3166-1]: https://wikipedia.org/wiki/ISO_3166-1

View file

@ -13,8 +13,16 @@ declare(strict_types=1);
namespace Respect\Validation\Exceptions;
class CountryCodeException extends ValidationException
/**
* @author Alexandre Gomes Gaigalas <alexandre@gaigalas.net>
* @author Henrique Moody <henriquemoody@gmail.com>
* @author William Espindola <oi@williamespindola.com.br>
*/
final class CountryCodeException extends ValidationException
{
/**
* {@inheritdoc}
*/
public static $defaultTemplates = [
self::MODE_DEFAULT => [
self::STANDARD => '{{name}} must be a valid country',

View file

@ -14,22 +14,60 @@ declare(strict_types=1);
namespace Respect\Validation\Rules;
use Respect\Validation\Exceptions\ComponentException;
use function array_column;
use function array_keys;
use function implode;
/**
* Validates countries in ISO 3166-1.
* Validates whether the input is a country code in ISO 3166-1 standard.
*
* This rule supports the three sets of country codes (alpha-2, alpha-3, and numeric).
*
* @author Alexandre Gomes Gaigalas <alexandre@gaigalas.net>
* @author Felipe Martins <me@fefas.net>
* @author Henrique Moody <henriquemoody@gmail.com>
* @author William Espindola <oi@williamespindola.com.br>
*/
class CountryCode extends AbstractRule
final class CountryCode extends AbstractSearcher
{
/**
* The ISO representation of a country code.
*
* @var string
*/
public const ALPHA2 = 'alpha-2';
/**
* The ISO3 representation of a country code.
*
* @var string
*/
public const ALPHA3 = 'alpha-3';
/**
* The ISO-number representation of a country code.
*
* @var string
*/
public const NUMERIC = 'numeric';
/**
* Position of the indexes of each set in the list of country codes.
*
* @var array
*/
private const SET_INDEXES = [
self::ALPHA2 => 0,
self::ALPHA3 => 1,
self::NUMERIC => 2,
];
/**
* @see http://download.geonames.org/export/dump/countryInfo.txt
*
* @var array
*/
protected $countryCodeList = [
private const COUNTRY_CODES = [
['AD', 'AND', '020'], // Andorra
['AE', 'ARE', '784'], // United Arab Emirates
['AF', 'AFG', '004'], // Afghanistan
@ -284,45 +322,38 @@ class CountryCode extends AbstractRule
['ZW', 'ZWE', '716'], // Zimbabwe
];
public $set;
public $index;
/**
* @var string
*/
private $set;
public function __construct($set = self::ALPHA2)
/**
* Initializes the rule.
*
* @param string $set
*
* @throws ComponentException If $set is not a valid set
*/
public function __construct(string $set = self::ALPHA2)
{
$index = array_search($set, self::getAvailableSets(), true);
if (false === $index) {
throw new ComponentException(sprintf('"%s" is not a valid country set for ISO 3166-1', $set));
if (!isset(self::SET_INDEXES[$set])) {
throw new ComponentException(
sprintf(
'"%s" is not a valid set for ISO 3166-1 (Available: %s)',
$set,
implode(', ', array_keys(self::SET_INDEXES))
)
);
}
$this->set = $set;
$this->index = $index;
}
public static function getAvailableSets()
/**
* {@inheritdoc}
*/
protected function getDataSource(): array
{
return [
self::ALPHA2,
self::ALPHA3,
self::NUMERIC,
];
}
private function getCountryCodeList($index)
{
$countryList = [];
foreach ($this->countryCodeList as $country) {
$countryList[] = $country[$index];
}
return $countryList;
}
public function validate($input): bool
{
return in_array(
mb_strtoupper($input),
$this->getCountryCodeList($this->index),
true
);
return array_column(self::COUNTRY_CODES, self::SET_INDEXES[$this->set]);
}
}

View file

@ -46,7 +46,7 @@ use Respect\Validation\Rules\Key;
* @method static Validator consonant(string $additionalChars = null)
* @method static Validator contains($containsValue, bool $identical = false)
* @method static Validator countable()
* @method static Validator countryCode()
* @method static Validator countryCode(string $set = null)
* @method static Validator currencyCode()
* @method static Validator cpf()
* @method static Validator creditCard(string $brand = null)

View file

@ -0,0 +1,37 @@
--FILE--
<?php
require 'vendor/autoload.php';
use Respect\Validation\Exceptions\CountryCodeException;
use Respect\Validation\Exceptions\NestedValidationException;
use Respect\Validation\Validator as v;
try {
v::countryCode()->check('1');
} catch (CountryCodeException $exception) {
echo $exception->getMessage().PHP_EOL;
}
try {
v::not(v::countryCode())->check('BR');
} catch (CountryCodeException $exception) {
echo $exception->getMessage().PHP_EOL;
}
try {
v::countryCode()->assert('1');
} catch (NestedValidationException $exception) {
echo $exception->getFullMessage().PHP_EOL;
}
try {
v::not(v::countryCode())->assert('BR');
} catch (NestedValidationException $exception) {
echo $exception->getFullMessage().PHP_EOL;
}
?>
--EXPECTF--
"1" must be a valid country
"BR" must not be a valid country
- "1" must be a valid country
- "BR" must not be a valid country

View file

@ -1,12 +0,0 @@
--TEST--
countryCode()
--FILE--
<?php
require 'vendor/autoload.php';
use Respect\Validation\Validator;
var_dump(Validator::countryCode()->validate('BR'));
?>
--EXPECTF--
bool(true)

View file

@ -1,12 +0,0 @@
--FILE--
<?php
require 'vendor/autoload.php';
use Respect\Validation\Validator as v;
v::countryCode()->assert('BR');
v::countryCode()->assert('DE');
v::countryCode()->check('BR');
v::countryCode()->check('DE');
?>
--EXPECTF--

View file

@ -1,15 +0,0 @@
--FILE--
<?php
require 'vendor/autoload.php';
use Respect\Validation\Exceptions\CountryCodeException;
use Respect\Validation\Validator as v;
try {
v::countryCode()->check('1');
} catch (CountryCodeException $exception) {
echo $exception->getMessage();
}
?>
--EXPECTF--
"1" must be a valid country

View file

@ -1,15 +0,0 @@
--FILE--
<?php
require 'vendor/autoload.php';
use Respect\Validation\Exceptions\AllOfException;
use Respect\Validation\Validator as v;
try {
v::countryCode()->assert('1');
} catch (AllOfException $exception) {
echo $exception->getFullMessage();
}
?>
--EXPECTF--
- "1" must be a valid country

View file

@ -1,15 +0,0 @@
--FILE--
<?php
require 'vendor/autoload.php';
use Respect\Validation\Exceptions\CountryCodeException;
use Respect\Validation\Validator as v;
try {
v::not(v::countryCode())->check('BR');
} catch (CountryCodeException $exception) {
echo $exception->getMessage();
}
?>
--EXPECTF--
"BR" must not be a valid country

View file

@ -1,15 +0,0 @@
--FILE--
<?php
require 'vendor/autoload.php';
use Respect\Validation\Exceptions\AllOfException;
use Respect\Validation\Validator as v;
try {
v::not(v::countryCode())->assert('BR');
} catch (AllOfException $exception) {
echo $exception->getFullMessage();
}
?>
--EXPECTF--
- "BR" must not be a valid country

View file

@ -1,13 +0,0 @@
--TEST--
countryCode()
--FILE--
<?php
require 'vendor/autoload.php';
use Respect\Validation\Rules\CountryCode;
use Respect\Validation\Validator;
var_dump(Validator::countryCode(CountryCode::ALPHA3)->validate('BRA'));
?>
--EXPECTF--
bool(true)

View file

@ -1,13 +0,0 @@
--FILE--
<?php
require 'vendor/autoload.php';
use Respect\Validation\Rules\CountryCode;
use Respect\Validation\Validator as v;
v::countryCode(CountryCode::ALPHA3)->assert('BRA');
v::countryCode(CountryCode::ALPHA3)->assert('DEU');
v::countryCode(CountryCode::ALPHA3)->check('BRA');
v::countryCode(CountryCode::ALPHA3)->check('DEU');
?>
--EXPECTF--

View file

@ -1,16 +0,0 @@
--FILE--
<?php
require 'vendor/autoload.php';
use Respect\Validation\Exceptions\CountryCodeException;
use Respect\Validation\Rules\CountryCode;
use Respect\Validation\Validator as v;
try {
v::countryCode(CountryCode::ALPHA3)->check('1');
} catch (CountryCodeException $exception) {
echo $exception->getMessage();
}
?>
--EXPECTF--
"1" must be a valid country

View file

@ -1,16 +0,0 @@
--FILE--
<?php
require 'vendor/autoload.php';
use Respect\Validation\Exceptions\AllOfException;
use Respect\Validation\Rules\CountryCode;
use Respect\Validation\Validator as v;
try {
v::countryCode(CountryCode::ALPHA3)->assert('1');
} catch (AllOfException $exception) {
echo $exception->getFullMessage();
}
?>
--EXPECTF--
- "1" must be a valid country

View file

@ -1,16 +0,0 @@
--FILE--
<?php
require 'vendor/autoload.php';
use Respect\Validation\Exceptions\CountryCodeException;
use Respect\Validation\Rules\CountryCode;
use Respect\Validation\Validator as v;
try {
v::not(v::countryCode(CountryCode::ALPHA3))->check('BRA');
} catch (CountryCodeException $exception) {
echo $exception->getMessage();
}
?>
--EXPECTF--
"BRA" must not be a valid country

View file

@ -1,16 +0,0 @@
--FILE--
<?php
require 'vendor/autoload.php';
use Respect\Validation\Exceptions\AllOfException;
use Respect\Validation\Rules\CountryCode;
use Respect\Validation\Validator as v;
try {
v::not(v::countryCode(CountryCode::ALPHA3))->assert('BRA');
} catch (AllOfException $exception) {
echo $exception->getFullMessage();
}
?>
--EXPECTF--
- "BRA" must not be a valid country

View file

@ -1,13 +0,0 @@
--TEST--
countryCode()
--FILE--
<?php
require 'vendor/autoload.php';
use Respect\Validation\Rules\CountryCode;
use Respect\Validation\Validator;
var_dump(Validator::countryCode(CountryCode::NUMERIC)->validate('076'));
?>
--EXPECTF--
bool(true)

View file

@ -1,13 +0,0 @@
--FILE--
<?php
require 'vendor/autoload.php';
use Respect\Validation\Rules\CountryCode;
use Respect\Validation\Validator as v;
v::countryCode(CountryCode::NUMERIC)->assert('076');
v::countryCode(CountryCode::NUMERIC)->assert('276');
v::countryCode(CountryCode::NUMERIC)->check('076');
v::countryCode(CountryCode::NUMERIC)->check('276');
?>
--EXPECTF--

View file

@ -1,16 +0,0 @@
--FILE--
<?php
require 'vendor/autoload.php';
use Respect\Validation\Exceptions\CountryCodeException;
use Respect\Validation\Rules\CountryCode;
use Respect\Validation\Validator as v;
try {
v::countryCode(CountryCode::NUMERIC)->check('BRA');
} catch (CountryCodeException $exception) {
echo $exception->getMessage();
}
?>
--EXPECTF--
"BRA" must be a valid country

View file

@ -1,16 +0,0 @@
--FILE--
<?php
require 'vendor/autoload.php';
use Respect\Validation\Exceptions\AllOfException;
use Respect\Validation\Rules\CountryCode;
use Respect\Validation\Validator as v;
try {
v::countryCode(CountryCode::NUMERIC)->assert('BRA');
} catch (AllOfException $exception) {
echo $exception->getFullMessage();
}
?>
--EXPECTF--
- "BRA" must be a valid country

View file

@ -1,16 +0,0 @@
--FILE--
<?php
require 'vendor/autoload.php';
use Respect\Validation\Exceptions\CountryCodeException;
use Respect\Validation\Rules\CountryCode;
use Respect\Validation\Validator as v;
try {
v::not(v::countryCode(CountryCode::NUMERIC))->check('076');
} catch (CountryCodeException $exception) {
echo $exception->getMessage();
}
?>
--EXPECTF--
"076" must not be a valid country

View file

@ -1,16 +0,0 @@
--FILE--
<?php
require 'vendor/autoload.php';
use Respect\Validation\Exceptions\AllOfException;
use Respect\Validation\Rules\CountryCode;
use Respect\Validation\Validator as v;
try {
v::not(v::countryCode(CountryCode::NUMERIC))->assert('076');
} catch (AllOfException $exception) {
echo $exception->getFullMessage();
}
?>
--EXPECTF--
- "076" must not be a valid country

View file

@ -13,76 +13,58 @@ declare(strict_types=1);
namespace Respect\Validation\Rules;
use PHPUnit\Framework\TestCase;
use Respect\Validation\Test\RuleTestCase;
/**
* @group rule
* @group rule
*
* @covers \Respect\Validation\Rules\CountryCode
*
* @author Felipe Martins <me@fefas.net>
* @author Gabriel Caruso <carusogabriel34@gmail.com>
* @author Henrique Moody <henriquemoody@gmail.com>
* @author William espindola <oi@williamespindola.com.br>
*/
class CountryCodeTest extends TestCase
final class CountryCodeTest extends RuleTestCase
{
/**
* @expectedException \Respect\Validation\Exceptions\ComponentException
* @expectedExceptionMessage "whatever" is not a valid country set
* @test
*
* @expectedException \Respect\Validation\Exceptions\ComponentException
* @expectedExceptionMessage "whatever" is not a valid set for ISO 3166-1 (Available: alpha-2, alpha-3, numeric)
*/
public function testShouldThrowsExceptionWhenInvalidFormat(): void
public function itShouldThrowsExceptionWhenInvalidFormat(): void
{
new CountryCode('whatever');
}
public function testShouldUseISO3166Alpha2ByDefault(): void
{
$country = new CountryCode();
self::assertEquals(CountryCode::ALPHA2, $country->set);
}
public function testShouldDefineACountryFormatOnConstructor(): void
{
$country = new CountryCode(CountryCode::NUMERIC);
self::assertEquals(CountryCode::NUMERIC, $country->set);
}
public function providerForValidCountryCode()
/**
* {@inheritdoc}
*/
public function providerForValidInput(): array
{
return [
[CountryCode::ALPHA2, 'BR'],
[CountryCode::ALPHA3, 'BRA'],
[CountryCode::NUMERIC, '076'],
[CountryCode::ALPHA2, 'DE'],
[CountryCode::ALPHA3, 'DEU'],
[CountryCode::NUMERIC, '276'],
[CountryCode::ALPHA2, 'US'],
[CountryCode::ALPHA3, 'USA'],
[CountryCode::NUMERIC, '840'],
];
}
public function providerForInvalidCountryCode()
{
return [
[CountryCode::ALPHA2, 'USA'],
[CountryCode::ALPHA3, 'US'],
[CountryCode::NUMERIC, '000'],
[new CountryCode(CountryCode::ALPHA2), 'BR'],
[new CountryCode(CountryCode::ALPHA3), 'BRA'],
[new CountryCode(CountryCode::NUMERIC), '076'],
[new CountryCode(CountryCode::ALPHA2), 'DE'],
[new CountryCode(CountryCode::ALPHA3), 'DEU'],
[new CountryCode(CountryCode::NUMERIC), '276'],
[new CountryCode(CountryCode::ALPHA2), 'US'],
[new CountryCode(CountryCode::ALPHA3), 'USA'],
[new CountryCode(CountryCode::NUMERIC), '840'],
];
}
/**
* @dataProvider providerForValidCountryCode
* {@inheritdoc}
*/
public function testValidContryCodes($format, $input): void
public function providerForInvalidInput(): array
{
$rule = new CountryCode($format);
self::assertTrue($rule->validate($input));
}
/**
* @dataProvider providerForInvalidCountryCode
*/
public function testInvalidContryCodes($format, $input): void
{
$rule = new CountryCode($format);
self::assertFalse($rule->validate($input));
return [
[new CountryCode(CountryCode::ALPHA2), 'USA'],
[new CountryCode(CountryCode::ALPHA3), 'US'],
[new CountryCode(CountryCode::NUMERIC), '000'],
];
}
}

View file

@ -34,14 +34,6 @@ class PostalCodeTest extends TestCase
self::assertEquals($expectedPattern, $actualPattern);
}
public function testShouldNotBeCaseSensitiveWhenChoosingPatternAccordingToCountryCode(): void
{
$rule1 = new PostalCode('BR');
$rule2 = new PostalCode('br');
self::assertEquals($rule1->regex, $rule2->regex);
}
public function testShouldUseDefaultPatternWhenCountryCodeDoesNotHavePostalCode(): void
{
$rule = new PostalCode('ZW');