Skip to content

Commit

Permalink
Add validateFormat method.
Browse files Browse the repository at this point in the history
  • Loading branch information
dannyvankooten committed Apr 9, 2016
1 parent 6e72450 commit 4d4b18d
Show file tree
Hide file tree
Showing 3 changed files with 92 additions and 20 deletions.
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,11 @@ Rates::country( 'NL', 'reduced' ); // 6
Rates::all(); // array in country code => rates format

Validator::isEuCountry('NL'); // true
Validator::check('NL50123'); // false
Validator::check('NL203458239B01'); // true (if this were really a valid VAT #)

Validator::validate('NL50123'); // false
Validator::validateFormat('NL203458239B01'); // true (checks just format)
Validator::validate('NL203458239B01'); // false (checks format + existence)
Validator::validate('NL203458239B01', 'GB'); // false (checks format + existence + country match)
```

If you'd prefer to use dependency injection, you can easily inject the class like this.
Expand Down
96 changes: 81 additions & 15 deletions src/Validator.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,18 +20,45 @@ class Validator {
/**
* @var SoapClient
*/
private $client;
protected $client;

/**
* All valid country codes.
* Regular expression patterns per country code
*
* @var array
* @link http://ec.europa.eu/taxation_customs/vies/faq.html?locale=lt#item_11
*/
private static $validCountries = array(
'AT', 'BE', 'BG', 'CY', 'CZ', 'DE', 'DK', 'EE', 'ES', 'FI', 'FR', 'GB',
'EL', 'HR', 'HU', 'IE', 'IT', 'LT', 'LU', 'LV', 'MT', 'NL', 'PL', 'PT',
'RO', 'SE', 'SI', 'SK'
protected static $patterns = array(
'AT' => 'U[A-Z\d]{8}',
'BE' => '0\d{9}',
'BG' => '\d{9,10}',
'CY' => '\d{8}[A-Z]',
'CZ' => '\d{8,10}',
'DE' => '\d{9}',
'DK' => '(\d{2} ?){3}\d{2}',
'EE' => '\d{9}',
'EL' => '\d{9}',
'ES' => '[A-Z]\d{7}[A-Z]|\d{8}[A-Z]|[A-Z]\d{8}',
'FI' => '\d{8}',
'FR' => '([A-Z]{2}|\d{2})\d{9}',
'GB' => '\d{9}|\d{12}|(GD|HA)\d{3}',
'HR' => '\d{11}',
'HU' => '\d{8}',
'IE' => '[A-Z\d]{8}|[A-Z\d]{9}',
'IT' => '\d{11}',
'LT' => '(\d{9}|\d{12})',
'LU' => '\d{8}',
'LV' => '\d{11}',
'MT' => '\d{8}',
'NL' => '\d{9}B\d{2}',
'PL' => '\d{10}',
'PT' => '\d{9}',
'RO' => '\d{2,10}',
'SE' => '\d{12}',
'SI' => '\d{8}',
'SK' => '\d{10}'
);

/**
* VatValidator constructor.
*
Expand All @@ -53,37 +80,58 @@ public function __construct( $client = null ) {
*/
public function isEuCountry( $country ) {
$country = strtoupper( $country );
return in_array( $country, self::$validCountries );
$country = $this->fixCountryCode($country);
return isset( self::$patterns[$country] );
}

/**
* Validate a VAT number format. This does not check whether the VAT number was really issued.
*
* @param string $vatNumber
*
* @return boolean
*/
public function validateFormat( $vatNumber ) {
$country = substr( $vatNumber, 0, 2 );
$number = substr( $vatNumber, 2 );

if( ! isset( self::$patterns[$country]) ) {
return false;
}

return preg_match( '/' . self::$patterns[$country] . '$/', $number );
}

/**
* Validates a VAT number using format + existence check.
*
* Pass a country code as the second parameter if you want to make sure a number is valid for the given ISO-3166-1-alpha2 country.
*
* @param string $vatNumber
* @param string $countryCode
* @return boolean
*
* @throws Exception
*/
public function check( $vatNumber, $countryCode = '' ) {
public function validate( $vatNumber, $countryCode = '' ) {
$vatNumber = strtoupper( $vatNumber );
$countryCode = strtoupper( $countryCode );

// if country code is omitted, use first two chars of vat number
if( empty( $countryCode ) ) {
$countryCode = substr( $vatNumber, 0, 2 );
} else {
// otherwise, transform country code to ISO-3166-1-alpha2
$countryCode = $this->fixCountryCode( $countryCode );
}

// strip first two characters of VAT number if it matches the country code
if( substr( $vatNumber, 0, 2 ) === $countryCode ) {
$vatNumber = substr( $vatNumber, 2 );
}

// check if country is in EU, bail early otherwise.
if( ! $this->isEuCountry( $countryCode ) ) {
return false;
}

// check vat number format (loose)
if( ! preg_match('/^[0-9A-Z\-]{2,14}$/', $vatNumber) ) {
// check VAT number format
if( ! $this->validateFormat( $countryCode . $vatNumber ) ) {
return false;
}

Expand All @@ -102,5 +150,23 @@ public function check( $vatNumber, $countryCode = '' ) {
return !! $response->valid;
}

/**
* @param string $country
*
* @return string
*/
protected function fixCountryCode( $country ) {
static $exceptions = array(
'GR' => 'EL',
'UK' => 'GB',
);

if( isset( $exceptions[$country] ) ) {
return $exceptions[$country];
}

return $country;
}


}
10 changes: 7 additions & 3 deletions tests/ValidatorTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,15 @@

use PHPUnit_Framework_TestCase;

/**
* Class ValidatorTest
* @package DvK\Tests\Laravel\Vat
*
* TODO: Tests for validateFormat method
* Todo: Tests for validate method
*/
class ValidatorTest extends PHPUnit_Framework_TestCase
{
public function test_check() {
// TODO
}

public function test_isEuCountry() {
$validator = new Validator();
Expand Down

0 comments on commit 4d4b18d

Please sign in to comment.