It's often undesirable to mail items to customers who provide post office boxes or locked bags for their delivery address when placing an order on a Magento site. Most commonly this is a requirement for fraud prevention or shipping reasons. Unfortunately, simply stating that PO boxes cannot be accepted somewhere on the site is often not enough to stop some customers from still placing an order with one. Rather then be left with orders that either require a followup or can't be fulfilled, an easy solution is to add validation code to prevent PO boxes and/or locked bags from being entered in the first place.

Thankfully, the code we need to add only needs to be added to one file.

The code to be added to block PO boxes is as follows:

if (preg_match("/p\.* *o\.* *box/i", $this->getStreet(1))) {
 $errors[] = $helper->__('We cannot deliver to PO boxes.');
}

The code to block locked bags is as follows:

if (preg_match("/locked *bag/i", $this->getStreet(1))) {
 $errors[] = $helper->__('We cannot deliver to locked bags.');
}

The above code needs to be added to the following file:

magento/app/code/core/Mage/Customer/Model/Address/Abstract.php

in the validate() function which can be found towards the end of the file. Once added, the function should look something like this:

public function validate()
{
 $errors = array();
 $helper = Mage::helper('customer');
 $this->implodeStreetAddress();
 if (!Zend_Validate::is($this->getFirstname(), 'NotEmpty')) {
 $errors[] = $helper->__('Please enter first name.');
 }

 if (!Zend_Validate::is($this->getLastname(), 'NotEmpty')) {
 $errors[] = $helper->__('Please enter last name.');
 }

 if (!Zend_Validate::is($this->getStreet(1), 'NotEmpty')) {
 $errors[] = $helper->__('Please enter street.');
 }
 
 if (preg_match("/p\.* *o\.* *box/i", $this->getStreet(1))) { 
 $errors[] = $helper->__('We cannot deliver to PO boxes.');
 }

 if (preg_match("/locked *bag/i", $this->getStreet(1))) {
 $errors[] = $helper->__('We cannot deliver to locked bags.');
 }

 if (!Zend_Validate::is($this->getCity(), 'NotEmpty')) {
 $errors[] = $helper->__('Please enter city.');
 }

 if (!Zend_Validate::is($this->getTelephone(), 'NotEmpty')) {
 $errors[] = $helper->__('Please enter telephone.');
 }

 if (!Zend_Validate::is($this->getPostcode(), 'NotEmpty')) {
 $errors[] = $helper->__('Please enter zip/postal code.');
 }

 if (!Zend_Validate::is($this->getCountryId(), 'NotEmpty')) {
 $errors[] = $helper->__('Please enter country.');
 }

 if ($this->getCountryModel()->getRegionCollection()->getSize()
 && !Zend_Validate::is($this->getRegionId(), 'NotEmpty')) {
 $errors[] = $helper->__('Please enter state/province.');
 }

 if (empty($errors)) {
 return true;
 }
 return $errors;
}

As usual, please note that it is not wise to make changes in the core directory. The best option is to copy the affected file to the local directory, under the same directory structure as before i.e. the edited file should be located here:

magento/app/code/local/Mage/Customer/Model/Address/Abstract.php

Update: Follow-up post detailing how to prevent PO boxes only for shipping address at checkout, as opposed to disallowing PO boxes for any customer address.