UK PostCode Validation Function In PHP

21st May 2012 - 5 minutes read time

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.

  1. /**
  2.  * Function to see if a string is a UK postcode or not. The postcode is also
  3.  * formatted so it contains no strings. Full or partial postcodes can be used.
  4.  *
  5.  * @param string $toCheck
  6.  * @return boolean
  7.  */
  8. function postcode_check(&$toCheck) {
  9. // Permitted letters depend upon their position in the postcode.
  10. $alpha1 = "[abcdefghijklmnoprstuwyz]"; // Character 1
  11. $alpha2 = "[abcdefghklmnopqrstuvwxy]"; // Character 2
  12. $alpha3 = "[abcdefghjkstuw]"; // Character 3
  13. $alpha4 = "[abehmnprvwxy]"; // Character 4
  14. $alpha5 = "[abdefghjlnpqrstuwxyz]"; // Character 5
  15.  
  16. // Expression for postcodes: AN NAA, ANN NAA, AAN NAA, and AANN NAA with a space
  17. // Or AN, ANN, AAN, AANN with no whitespace
  18. $pcexp[0] = '^(' . $alpha1 . '{1}' . $alpha2 . '{0,1}[0-9]{1,2})([[:space:]]{0,})([0-9]{1}' . $alpha5 . '{2})?$';
  19.  
  20. // Expression for postcodes: ANA NAA
  21. // Or ANA with no whitespace
  22. $pcexp[1] = '^(' . $alpha1 . '{1}[0-9]{1}' . $alpha3 . '{1})([[:space:]]{0,})([0-9]{1}' . $alpha5 . '{2})?$';
  23.  
  24. // Expression for postcodes: AANA NAA
  25. // Or AANA With no whitespace
  26. $pcexp[2] = '^(' . $alpha1 . '{1}' . $alpha2 . '[0-9]{1}' . $alpha4 . ')([[:space:]]{0,})([0-9]{1}' . $alpha5 . '{2})?$';
  27.  
  28. // Exception for the special postcode GIR 0AA
  29. // Or just GIR
  30. $pcexp[3] = '^(gir)([[:space:]]{0,})?(0aa)?$';
  31.  
  32. // Standard BFPO numbers
  33. $pcexp[4] = '^(bfpo)([[:space:]]{0,})([0-9]{1,4})$';
  34.  
  35. // c/o BFPO numbers
  36. $pcexp[5] = '^(bfpo)([[:space:]]{0,})(c\/o([[:space:]]{0,})[0-9]{1,3})$';
  37.  
  38. // Overseas Territories
  39. $pcexp[6] = '^([a-z]{4})([[:space:]]{0,})(1zz)$';
  40.  
  41. // Anquilla
  42. $pcexp[7] = '^(ai\-2640)$';
  43.  
  44. // Load up the string to check, converting into lowercase
  45. $postcode = strtolower($toCheck);
  46.  
  47. // Assume we are not going to find a valid postcode
  48. $valid = false;
  49.  
  50. // Check the string against the six types of postcodes
  51. foreach ($pcexp as $regexp) {
  52. if (preg_match('/' . $regexp . '/i', $postcode, $matches)) {
  53.  
  54. // Load new postcode back into the form element
  55. $postcode = strtoupper($matches[1]);
  56. if (isset($matches[3])) {
  57. $postcode .= ' ' . strtoupper($matches[3]);
  58. }
  59.  
  60. // Take account of the special BFPO c/o format
  61. $postcode = preg_replace('/C\/O/', 'c/o ', $postcode);
  62.  
  63. // Remember that we have found that the code is valid and break from loop
  64. $valid = true;
  65. break;
  66. }
  67. }
  68.  
  69. // Return with the reformatted valid postcode in uppercase if the postcode was
  70. // valid
  71. if ($valid) {
  72. $toCheck = $postcode;
  73. return true;
  74. } else {
  75. return false;
  76. }
  77. }

Run the function in the following way.

  1. $postcode = 'WR5 3DA';
  2.  
  3. $valid = postcode_check($postcode);
  4.  
  5. if ($valid === FALSE) {
  6. print $postcode . ' is invalid';
  7. }
  8. else {
  9. print $postcode . ' is valid';
  10. }

Comments

Permalink
Hi Philip, I wasted three days and nights and found your this post and it just helped me a lot :) Thanks man and keep it up :)

Unaib Amir (Thu, 09/18/2014 - 22:08)

Permalink
Hello Philip, I know this was quite a while ago but thought i would take a punt. The above has helped me in many aspects but it doesn't add the space should a client add their postcode like: AN121AN. My question is, is it meant to and if so can you say why it wouldn't be working? Hopefully hear from you shortly. Cheers Julian

Julian Purser (Fri, 01/09/2015 - 15:34)

Permalink
I tried to pass your postcode through the function and it worked correctly.
Permalink

Hi this is a great piece of code but it doesn't seem to like AN NAA (L6 3AB for example)

SCOTT (Fri, 04/24/2020 - 15:48)

Permalink

Yes sorry it does work fine I was tampering before passing it through it seems.

SCOTT (Fri, 05/01/2020 - 23:07)

Add new comment

The content of this field is kept private and will not be shown publicly.