Creating a function to validate UK postcodes would seem like a simple task, but there is a little more to it than checking the number of characters. In fact there are several different variants of UK postal codes, especially if you include BFPO and overseas addresses. The official postcode specification details the structure of the postcode, and there is also a list of BFPO numbers from mod.uk.
Thankfully, John Gardner took the time to write a little function that is able to validate a UK postcode, and this has been coppied all over the internet. I came across this function a while ago in a project, but as it wasn't attributed to anyone I assumed that it was written by another coder on the same project. It was nice to track down the original author and give them the credit. :)
The original version I found was a little out of date as it used the old ereg PHP regular expression functions, which caused deprecated errors in PHP 5.3. At the time I was creating a search feature that used full and partial postcodes so I also changed the function to be able to detect both. The code block below is my take on the function. Note that the postcode is passed by reference so if a postcode is found it is standardised into a correct format.
/**
* Function to see if a string is a UK postcode or not. The postcode is also
* formatted so it contains no strings. Full or partial postcodes can be used.
*
* @param string $toCheck
* @return boolean
*/
function postcode_check(&$toCheck) {
// Permitted letters depend upon their position in the postcode.
$alpha1 = "[abcdefghijklmnoprstuwyz]"; // Character 1
$alpha2 = "[abcdefghklmnopqrstuvwxy]"; // Character 2
$alpha3 = "[abcdefghjkstuw]"; // Character 3
$alpha4 = "[abehmnprvwxy]"; // Character 4
$alpha5 = "[abdefghjlnpqrstuwxyz]"; // Character 5
// Expression for postcodes: AN NAA, ANN NAA, AAN NAA, and AANN NAA with a space
// Or AN, ANN, AAN, AANN with no whitespace
$pcexp[0] = '^(' . $alpha1 . '{1}' . $alpha2 . '{0,1}[0-9]{1,2})([[:space:]]{0,})([0-9]{1}' . $alpha5 . '{2})?$';
// Expression for postcodes: ANA NAA
// Or ANA with no whitespace
$pcexp[1] = '^(' . $alpha1 . '{1}[0-9]{1}' . $alpha3 . '{1})([[:space:]]{0,})([0-9]{1}' . $alpha5 . '{2})?$';
// Expression for postcodes: AANA NAA
// Or AANA With no whitespace
$pcexp[2] = '^(' . $alpha1 . '{1}' . $alpha2 . '[0-9]{1}' . $alpha4 . ')([[:space:]]{0,})([0-9]{1}' . $alpha5 . '{2})?$';
// Exception for the special postcode GIR 0AA
// Or just GIR
$pcexp[3] = '^(gir)([[:space:]]{0,})?(0aa)?$';
// Standard BFPO numbers
$pcexp[4] = '^(bfpo)([[:space:]]{0,})([0-9]{1,4})$';
// c/o BFPO numbers
$pcexp[5] = '^(bfpo)([[:space:]]{0,})(c\/o([[:space:]]{0,})[0-9]{1,3})$';
// Overseas Territories
$pcexp[6] = '^([a-z]{4})([[:space:]]{0,})(1zz)$';
// Anquilla
$pcexp[7] = '^(ai\-2640)$';
// Load up the string to check, converting into lowercase
$postcode = strtolower($toCheck);
// Assume we are not going to find a valid postcode
$valid = false;
// Check the string against the six types of postcodes
foreach ($pcexp as $regexp) {
if (preg_match('/' . $regexp . '/i', $postcode, $matches)) {
// Load new postcode back into the form element
$postcode = strtoupper($matches[1]);
if (isset($matches[3])) {
$postcode .= ' ' . strtoupper($matches[3]);
}
// Take account of the special BFPO c/o format
$postcode = preg_replace('/C\/O/', 'c/o ', $postcode);
// Remember that we have found that the code is valid and break from loop
$valid = true;
break;
}
}
// Return with the reformatted valid postcode in uppercase if the postcode was
// valid
if ($valid) {
$toCheck = $postcode;
return true;
} else {
return false;
}
}
Run the function in the following way.
$postcode = 'WR5 3DA';
$valid = postcode_check($postcode);
if ($valid === FALSE) {
print $postcode . ' is invalid';
}
else {
print $postcode . ' is valid';
}
Comments
Submitted by Unaib Amir on Thu, 09/18/2014 - 22:08
PermalinkSubmitted by Julian Purser on Fri, 01/09/2015 - 15:34
PermalinkSubmitted by giHlZp8M8D on Thu, 01/15/2015 - 17:02
PermalinkHi this is a great piece of code but it doesn't seem to like AN NAA (L6 3AB for example)
Submitted by SCOTT on Fri, 04/24/2020 - 15:48
PermalinkBut "L6 3AB" is returned as a valid postcode?...
Submitted by giHlZp8M8D on Sat, 04/25/2020 - 18:32
PermalinkYes sorry it does work fine I was tampering before passing it through it seems.
Submitted by SCOTT on Fri, 05/01/2020 - 23:07
PermalinkAdd new comment