Postal Code Validation in PHP
  October 21, 2009

I don't consider myself a PHP developer.  I've used it, but always in the context of maintaining someone else's code.  I've never sat down with a book (or web site) and attempted to really learn PHP.

Instead, I just sort of muddle along and when I need to figure something out I invest a very small amount of time in learning that specific topic.  By the time it's all said and done this may end up being inefficient, but for now it works.

Recently I needed to enhance our web site's PHP code so that it can accept book orders from countries other than the US and Canada.  A key to success with mailing books internationally is to prevent (as much as possible) the entry of invalid addresses.  An invalid address results in returned mail, which is money wasted. 

Validating the postal code is a key piece of address validation.  For example, if the postal code format does not match the country that was selected then that's an error.

So our PHP code needed some international postal code validation.  It took me a couple of rounds of refactoring, but what I eventually ended up with was a table-driven approach.

Here's an abbreviated listing of the array that defines the postal codes:

$foreignCountries = array (
"Canada" => array ( "regex" => "/^[ABCEGHJKLMNPRSTVXY]d[A-Z] d[A-Z]d$/i", "display" => "A9A 9A9" ),
"Finland" => array ( "regex" => "/^ddddd$/", "display" => "99999", "prefix" => "FI-" ),
"Ireland" => array ( "method" => "validateIrelandPostalCode" )

Each entry in the $foreignCountries array contains a country name that is associated with another array.  That second array contains values that are specific to that country.

There are two broad categories: countries that have postal codes that can be easily validated with a regular expression and those that need a custom method.  Fortunately, the the vast majority of countries are in the former category.

Canada's postal code can be validated with a regular expression, so its array needs two values: one for the regular expression and one for a human-readable string to use in an error message if the user enters an invalid postal code.  The entry for Finland is the same, but with an additional entry: "prefix."  Some countries require their two character ISO code pre-pended to the postal code on international mail, so the algorithm allows for that case.

Using just one regular expression and one error message for Ireland would be tough, so the array for Ireland has just a single entry: the name of a custom method for validating an Irish postal code (forgive the funky spacing - I lost my wrestling match with Typepad's editor :-/ ).

function validateIrelandPostalCode ( )
global $input;
// Ireland is a bit different - there are no national postal codes.
// For *some* addresses in Dublin and Cork a 1 or 2 digit zone might
// be used, so allow for null or for a 1 or 2 digit value.
// An additional wrinkle is that there is a single zone
// in Dublin that actually has a digit and a letter instead of just digits: 6W
// (see
$input['postalCode'] = trim ( strtoupper( $input['postalCode'] ) );
if ( ( strcmp ( $input['postalCode'], "6W" ) != 0 ) && (! preg_match ( "/^d{0,2}$/", $input['postalCode'] ) ) )
$errorMsg = "Invalid Ireland postcode. Please use one of these formats:<br><br> ";
$errorMsg .= "   9<br>";
$errorMsg .= "   99<br>";
$errorMsg .= "   6W for southwestern Dublin<br>";
$errorMsg .= "<br>This is necessary for the shipping address.";
$input['error'][] = $errorMsg;

To make all this work, there is a top-level method that examines the appropriate entry in the $foreignCountries array:

function validateIntlPostalCode ( )
global $foreignCountries;
global $input;
$values = $foreignCountries[$input['country']];
// There are some countries that have to have their
// own method for validation. If so, just call it.
if ( array_key_exists ( 'method', $values ) ) {
$values['method'] ( );
else {
// Completely generic - just pass in the regex and
// the human-readable value in case the user needs to
// see an error messages. Also pass in a hard-coded
// country prefix, if it was specified.
validateGenericPostalCode ( $values['regex'],

And then, to handle the many countries that use a regular expression, the code for validateGenericPostalCode() is:
// used for validating postal code for non-US countries.
// If the regular expression does not find a match, display the
// error to the user. If it does, do a bit of post-processing:
// change to all upper case and stick on the prefix, if there is one.
function validateGenericPostalCode ( $regex, $display, $prefix = "" )
global $input;
if ( ! preg_match ( $regex, $input['postalCode'] ) )
$error = "Invalid postal code for ";
$error .= $input['country'];
$error .= ". Please use a format of: ";
$error .= $display;
$error .= " - this is necessary for the shipping address.";
$input['error'][] = $error;
$result = $prefix;
$result .= strtoupper( $input['postalCode'] );
$input['postalCode'] = $result;