Domain Name class

As PHP gets more and more object oriented it becomes interesting to have a nice set of easy-to-use, everyday, really-quite-obvious classes. Today I'm posting three of these: a domain name class, an email class, and a class representing an IP (or IPv4) address.

Using these functions can make your life easier in two ways:

  1. They have static functions allowing you to check if a string is a valid domain name, email address or IPv4.
  2. If you need to pass an email address, domain name or IP address from script to script, or from method to method, it becomes hard to tell where the responsibility for validating it lies.

In a language like PHP, where types aren't very strict to begin with, placing the responsiblity for input validation right is a big issue, since every method using some user given data will have to know it was given right. If you're working within a larger structure it might happen that you have:

Rather than using methods accepting strings that could be empty, null, spam or anything but a valid email address, it makes sense to have the very first method transform the user input into something valid. This way, messages about invalid input can be dealt with straight away, meaning you don't get a long pass down of user error messages. Secondly, all methods down the flow that somehow deal with email addresses can then require their input parameter to be of the type EmailAddress, which saves them the need to check if it actually is an email address.

Very straightforward, but still good to get right

class.DomainName.php

<?php
/*
 * Represents a Domain Name in php
 * Warning: Works okay, not extensively tested
 */
class DomainName
{
    
/*
     * Constructs a new DomainName object from the given string
     */
    
public function __construct($domain$checkTopLevel=true)
    {
        
$parts self::parse($domain$checkTopLevel);
        if (
$parts === false) throw new Exception('Invalid domain name passed to constructor');
        
$this->parts $parts;
    }

    
/*
     * Test wether the givens string is a valid domain name. Returns
     *  false if an invalid domain name is given and an array of parts
     *  if the domain name is valid.
     */
    
public static function parse($domain$checkTopLevel=true)
    {
        if (!
is_string($domain)) return false;
        
        
// Remove final dot. Interestingly, domains officially end with a dot (which
        //  no-one uses) so you can type www.google.com. into your browser and end up
        //  at the right site.
        
if (substr($domain, -1) == '.'$domain substr($domain0strlen($domain)-1);        
        
        
$arr explode("."$domain);
    
        
// Check number of parts in domain
        
if (count($arr) < 2)  return false;
        
        foreach(
$arr as $part) {
            if (!
preg_match("/^(([A-Za-z0-9][A-Za-z0-9-]{0,61}[A-Za-z0-9])|([A-Za-z0-9]+))$/"$part)) {
                return 
false;
            }
        }
        
        if (
$checkTopLevel) {
            
$top $arr[count($arr)-1];
            if (!
self::checkTopLevelDomain($top)) return false;
        }
        
        return 
$arr;
    }

    
/*
     * Check the given TLD against a list of valid ones
     */
    
public static function checkTopLevelDomain($top)
    {
        return 
in_array(strToUpper($top), self::$tops);
    }
    
    
/*
     * Prints the domain to the output
     */
    
public function __toString()
    {
        return 
implode('.'$this->parts);
    }
    
    
/*
     * Tests if the given domain is a child of this domain IE if this domain
     *  is the parent of the domain passed.
     * If the domains are equal the function returns 'false'
     */
    
public function isParentOf($dom) {
        if (!(
$dom instanceOf DomainName)) $dom = new DomainName($dom);
        
$parts2 explode('.'$dom);
        
$dif count($parts2) - count($this->parts);
        if (
$dif <= 0) return false;
        foreach (
$this->parts as $key=>$part)
            if (
$part != $parts2[$dif $key]) return false;
        return 
true;            
    }

    
// It is alright to giggle at this point.
    
private $parts;
    
// If you like, you can even consider the  possibility of making your private
    //  $parts public, to stop them from being "encapsulated". This of course, means
    //  you lose the ability to maintain your data integrity and will have to resort
    //  to programming by contract, IE "collaborate with each other, on the basis of
    //  mutual obligations and benefits". 
    // Of course you could also keep them protected, and only allow access to people
    //  who extend you.

    /*
     * This may change over time.
     */
    
private static $tops = array(
        
'AD','AE','AERO','AF','AG','AI','AL','AM','AN','AO','AQ','AR','ARPA','AS','ASIA','AT','AU','AW','AX','AZ',
        
'BA','BB','BD','BE','BF','BG','BH','BI','BIZ','BJ','BM','BN','BO','BR','BS','BT','BV','BW','BY','BZ',
        
'CA','CAT','CC','CD','CF','CG','CH','CI','CK','CL','CM','CN','CO','COM','COOP','CR','CU','CV','CX','CY','CZ',
        
'DE','DJ','DK','DM','DO','DZ',
        
'EC','EDU','EE','EG','ER','ES','ET','EU',
        
'FI','FJ','FK','FM','FO','FR',
        
'GA','GB','GD','GE','GF','GG','GH','GI','GL','GM','GN','GOV','GP','GQ','GR','GS','GT','GU','GW','GY',
        
'HK','HM','HN','HR','HT','HU',
        
'ID','IE','IL','IM','IN','INFO','INT','IO','IQ','IR','IS','IT',
        
'JE','JM','JO','JOBS','JP',
        
'KE','KG','KH','KI','KM','KN','KP','KR','KW','KY','KZ',
        
'LA','LB','LC','LI','LK','LR','LS','LT','LU','LV','LY',
        
'MA','MC','MD','ME','MG','MH','MIL','MK','ML','MM','MN','MO','MOBI','MP','MQ','MR','MS','MT','MU','MUSEUM',
        
'MV','MW','MX','MY','MZ',
        
'NA','NAME','NC','NE','NET','NF','NG','NI','NL','NO','NP','NR','NU','NZ',
        
'OM','ORG',
        
'PA','PE','PF','PG','PH','PK','PL','PM','PN','PR','PRO','PS','PT','PW','PY',
        
'QA',
        
'RE','RO','RS','RU','RW',
        
'SA','SB','SC','SD','SE','SG','SH','SI','SJ','SK','SL','SM','SN','SO','SR','ST','SU','SV','SY','SZ',
        
'TC','TD','TEL','TF','TG','TH','TJ','TK','TL','TM','TN','TO','TP','TR','TRAVEL','TT','TV','TW','TZ',
        
'UA','UG','UK','UM','US','UY','UZ',
        
'VA','VC','VE','VG','VI','VN','VU',
        
'WF','WS',
//        'XN--0ZWM56D','XN--11B5BS3A9AJ6G','XN--80AKHBYKNJ4F','XN--9T4B11YI5A','XN--DEBA0AD','XN--G6W251D','XN--HGBK6AJ7F53BBA','XN--HLCJ6AYA9ESC7A','XN--JXALPDLP','XN--KGBECHTV','XN--ZCKZAH',
        
'YE','YT','YU',
        
'ZA','ZM','ZW'
    
);
}
?>

There's some comments you might want to get rid of, and a list of top level domain names that'll be out of date sooner or later, but I think the regex'es are right. Any input would be greatly appreciated.

Jul 9th, 2008

Comments

Michael wrote:

Updated to use preg_match instead of ereg

Apr 12th, 2010

Post your comments here

If you wish to add code to your comment you can use code tags, like this: <code class="php">yourCodeHere</code>.
Quite a large number of languages are supported, although I can't guarantee it'll be pretty. Inside the code tags you can use any characters except for the string "</code>".