In PHP, we have a built-in function file_exist that can help us to verify whether a particular file exist in our directory. However, do you ever have the need to check whether a particular URL, email or image link exist? We can use regular express to validate that the syntax of an email is correct but won't it be nice to reduce the amount of spam mail received? How about those images you have on your site? Won't you want to check whether there is a broken image or url link? Well, i have! Its always good to be informed in advance than meeting dissatisfy visitors. Anyway, in this article you will get to find and learn some of the more efficient and quicker ways to verify whether a particular link exist to use on your web application.
Check Remote Image Link Exist
There are many ways to check whether a particular image link exist after the introduce of PHP 5, GetImageSize. GetImageSize allows us to take in a remote link to retrieve the size of the image. Hence, we can do a simple check such as the one shown below,
$external_link = 'http://www.example.com/example.jpg'; if (@GetImageSize($external_link)) { echo "image exists "; } else { echo "image does not exist "; }
The above work well for any server that had GD installed. But there are more problem than just the one mention. This method is actually inefficient as it will download the entire image into your server before checking it. Thus, making the process very long. There might also be security risk as mention on Secure File Upload Check List that many image format allow comment to be embedded within the image and these comment might just be some PHP code that hacker has written. In short, this method download file from remote server to your server and take the risk of hacker using it to run malicious code after it has been downloaded on your server. BOMB! So if you are using the above method i advice you to change it and if you insist to use this, you can provide more validation checking for it. Just drop it.
So how do we check Image link in a more secure and quick environment? If you are using curl, you can try the following script:
function checkRemoteFile($url) { $ch = curl_init(); curl_setopt($ch, CURLOPT_URL,$url); // don't download content curl_setopt($ch, CURLOPT_NOBODY, 1); curl_setopt($ch, CURLOPT_FAILONERROR, 1); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); if(curl_exec($ch)!==FALSE) { return true; } else { return false; } }
Like i said, this will depend on curl. However, it is secure and quick! How about the rest of us? Lucky, PHP also provides another method called file_get_contents. file_get_contents returns the file in a string, starting at the specified offset up to maxlen bytes. On failure, file_get_contents() will return FALSE. With these in mind we can create a function to check that the file downloaded is valid by taking only 1 bytes of information from the file. Hence, whatever evil things exist on the file will only be able to run 1 byte and furthermore the function returns a string where code cannot be run. Thus, this will gives us a simple solution such as the one below,
function url_exists($url) { if(@file_get_contents($url,0,NULL,0,1)) {return 1;} else { return 0;} }
The above one is more secure than the initial one that we have and it has no dependency. The speed for this method is also faster than the initial one. But is there a faster one than the curl version?
Check For Remote URL Link
There are many ways to check whether a remote url link exist. However, checking Remote URL required the page to return certain header code to indicate that the page is successfully loaded (200). Hence, you might not want to use the method for checking image link for url link. Furthermore, For different cases you might be interested with different solution. Assuming you are just checking whether an existing domain exist, you can use the following code
function url_exists($url){ if(strstr($url, "http:// ")) $url = str_replace( "http:// ", " ", $url); $fp = @fsockopen($url, 80); if($fp === false) return false; return true; }
which is written by adam at darkhousemedia dot com. The above method definitely run faster than fopen but for https and domain names but for path url such as 'http://example.com?p=231', the above won't work although the speed is definitely one of the fastest.
Nonetheless, there are still better alternative than the one presented. We can just tried to check the header with the following code:
function url_exists($url){ if ((strpos($url, "http ")) === false) $url = "http:// " . $url; if (is_array(@get_headers($url))) return true; else return false; }
The above work perfectly without the need to worry about complex code. However, the above method only work for HTTP and PHP 5 and above, other lower version of PHP will not. Therefore, we will need some modification on the above method to cater for lower PHP version.
function is_valid_url($url) { $url = @parse_url($url); if (!$url) { return false; } $url = array_map('trim', $url); $url['port'] = (!isset($url['port'])) ? 80 : (int)$url['port']; $path = (isset($url['path'])) ? $url['path'] : ''; if ($path == '') { $path = '/'; } $path .= (isset($url['query'])) ? "?$url[query] " : ''; if (isset($url['host']) AND $url['host'] != gethostbyname($url['host'])) { if (PHP_VERSION >= 5) { $headers = get_headers( "$url[scheme]://$url[host]:$url[port]$path "); } else { $fp = fsockopen($url['host'], $url['port'], $errno, $errstr, 30); if (!$fp) { return false; } fputs($fp, "HEAD $path HTTP/1.1\r\nHost: $url[host]\r\n\r\n "); $headers = fread($fp, 4096); fclose($fp); } $headers = (is_array($headers)) ? implode( "\n ", $headers) : $headers; return (bool)preg_match('#^HTTP/.*\s+[(200|301|302)]+\s#i', $headers); } return false; }
The code here is the more detail version of the previous one which is created by SecondV on forums Dot digitalpoint Dot com. The above code cater for lower PHP version while still using the same approach of getting the return header value. Furthermore, it also validate the URL by using the parse_url method. f_open can also be used to check remote URL link.
function image_exist($url) { if (@fclose(@fopen( $url, "r "))) { // true; } else { // false; } }
However, this method required allow_url_fopen to be enabled on your php.ini file or else it will fail. Furthermore, i will not prefer this method over the previous one as it seems more sense that the page return success due to header code to indicate a success.
Check Email Exist
We can't actually check whether a given email exist or not as it really depend on how the SMTP is being setup for each respective mail server. Nonetheless, we are still able to check whether a given domain exist to reduce the number of invalid ones. PHP has a function checkdnsrr which does the work nicely.
function email_exist($email) { list($userid, $domain) = split( "@ ", $email); if (checkdnsrr($domain, "MX ")) { return true;} else { return false;} }
Using the function above will help us to verify whether a particular domain exist to trick user that you have an email checker if they really intend to fake one. However, Windows doesn't support such function yet. Hence, we will need to create such function just for Windows server.
if(!function_exists('checkdnsrr')) function checkdnsrr($hostName, $recType = '') { if(!empty($hostName)) { if( $recType == '' ) $recType = "MX "; exec( "nslookup -type=$recType $hostName ", $result); // check each line to find the one that starts with the host // name. If it exists then the function succeeded. foreach ($result as $line) { if(eregi( "^$hostName ",$line)) { return true; } } // otherwise there was no mail handler for the domain return false; } return false; }
Once you have cater for both Linux and Windows server, you may want to create a full flag function to check on email such as the one shown below:
function check_email($email) { $email_error = false; $Email = htmlspecialchars(stripslashes(strip_tags(trim($email)))); //parse unnecessary characters to prevent exploits if ($Email == " ") { email_error = true; } elseif (!eregi( "^([a-zA-Z0-9._-])+@([a-zA-Z0-9._-])+\.([a-zA-Z0-9._-])([a-zA-Z0-9._-])+ ", $Email)) { email_error = true; } else { list($Email, $domain) = split( "@ ", $Email, 2); if (! checkdnsrr($domain, "MX ")) { email_error = true; } else { $array = array($Email, $domain); $Email = implode( "@ ", $array); } } if (email_error) { return false; } else{return true;} }
Now we know why we need to verify our email after we sign up for any particular services online! Since we cannot check whether a particular email exist, we will force to send out verification email to our user before they are able to access our portal. However, if you are interested to check through PHP forcefully, you may want to visit webdigi. They create a mail class to verify an email through checking the port of mail SMTP 25 but like i said previously it really depend on how each mail server is being design. This might not work. But we can still force user to verify their email through a simple script shown below,
function check_email($email) { $email_error = false; $Email = htmlspecialchars(stripslashes(strip_tags(trim($email)))); //parse unnecessary characters to prevent exploits if ($Email == " ") { email_error = true; } elseif (!eregi( "^([a-zA-Z0-9._-])+@([a-zA-Z0-9._-])+\.([a-zA-Z0-9._-])([a-zA-Z0-9._-])+ ", $Email)) { email_error = true; } else { list($Email, $domain) = split( "@ ", $Email, 2); if (! checkdnsrr($domain, "MX ")) { email_error = true; } else { $array = array($Email, $domain); $Email = implode( "@ ", $array); } } if (email_error) { return false; } else{return true;} } function EmailValidation($email) { if (check_email($email)) { $domain = explode( "@ ", $email ); if ( @fsockopen ($domain[1],80,$errno,$errstr,3)) { $code = "here we place a secret key with the email address: $email "; mail($email, "Your Verification Code ", "Please click the following URL to verify your email:\n\n ". $_SERVER['PHP_SELF']. "/?v=$code amp;email=$email ", "From: \ "example\ " <[email protected] > "); echo "Your account needs to be verify. We have send you an email, click the link provided and you are verified. "; return true; } else { return false; //if a connection cannot be established return false } } else { return false; //if email address is an invalid format return false } } function EmailForm(){ if(empty($_POST['email'])){ echo " <form action= ".$_SERVER['PHP_SELF']. " method='post' > <table border='0' > <tr > <td >Email </td > <td > <input name='email' type='text' id='email' / > </td > </tr > <tr > <td > amp;nbsp; </td > <td > <input type='submit' name='Submit' value='Validate' / > </td > </tr > </table > </form > "; } elseif(isset($_POST['email'])) { if(EmailValidation($_POST['email'])) { echo "An email has been sent to you. Please follow the instructions to activate your account. "; } else { echo "Your email address appears to be invalid. Please try again. "; } }else elseif(isset($_GET['v']) amp; amp; isset($_GET['email'])) { $clean['emai'] = $_GET['email']; //need to filter these data to be clean $clean['v'] = $_GET['v']; //need to filter these data to be clean $code = "here we place a secret key with the email address: $email "; $code = md5($code); if ($clean['v'] != $code) { echo "The Verification Code is invalid. Please Try Again. "; exit(0); }else echo "The email ".$clean['emai']. " has been verified "; }else { echo "An error has occured, please contact the administrator. "; } } EmailForm();
Might be a bit confusing but i believe you will get the above code since its quite simple. Instead of breaking them into different pages, i sum them up on a single one.
Quick check whether link is broken
Here is another tips to check whether a link is broken.
$file = 'http://www.domain.com/somefile.jpg'; $file_headers = @get_headers($file); if($file_headers[0] == 'HTTP/1.1 404 Not Found') { $exists = false; } else { $exists = true; }
And the same version on a curl version
function url_exists($url) { if (!$fp = curl_init($url)) return false; return true; }
credit goes to havran @ http://www.php.net/manual/en/function.file-exists.php#74469
Summary
The above solution can help many people to verify the content that the user has entered. Remember it is not safe to trust user input and such verification can come in handle. On the other hand, this can also help us to check broken and invalid links so that we get the information we need from our users. The information above might not be solid but it is good enough for me and hoping it will work for you.