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:
- They have static functions allowing you to check if a string is a valid domain name, email address or IPv4.
- 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:
- A form where users can input their name, email etc.
- Some object representing a user and storing these things
- A database object storing users
- Some script that sends emails to certain users
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($domain, 0, strlen($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
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>".