10 Sep 09

Two days ago a friend of mine suggested to me that it would be nice to post an article about URL shortening services. A post where i would showcase the prons and cons of each service etc… Although it is a very trending topic on most of the blogs, i thought of something else. Why not create a php script that would make the submited URL short in a rather new way (Don’t know if someone has already done this though).

What i thought of, was a service that would shorten the URL in no more than 6 chars. So i fired up my favorite Editor and started to write some PHP. It was easiest than i thought, since all i needed was 2 database tables. One for holding the domains and one for holding the links for each domain.

I created a database named short and 2 tables.

Table domains:

CREATE TABLE IF NOT EXISTS `domains` (
`id` int(11) NOT NULL auto_increment,
`domain` varchar(255) collate utf8_unicode_ci NOT NULL,
PRIMARY KEY  (`id`),
KEY `domain` (`domain`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=1 ;

Table links:

CREATE TABLE IF NOT EXISTS `links` (
`id` int(11) NOT NULL auto_increment,
`did` int(11) NOT NULL,
`link` text collate utf8_unicode_ci NOT NULL,
`hash` varchar(20) collate utf8_unicode_ci NOT NULL,
PRIMARY KEY  (`id`),
KEY `hash` (`hash`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=1 ;

The First Part.

Lets go to the PHP code. The URL shortener uses a 4kb file. You can download it from here.

We start from defining some values that we will need. We will need a small domain name for our shortener too. We suppose that the domain name is http://gi.ly . The database name, host,user and password must also be set here. We also create a function to connect to our database:

 define('DBHOST','localhost');
define('DBUSER','root');
define('DBPASS','');
define('DBNAME','short');
define('HOST','http://gi.ly/');

function connect(){
return new PDO('mysql:host='.DBHOST.';dbname='.DBNAME.'', DBUSER, DBPASS);
}
?>

Next we must create the makeShort function that will short our URLs:

 //...
function makeShort($url){
//We parse the url
$domain = parse_url($url);
//Now we have some info about the url
//If the domain is already in the DB
$inDB = check($domain,true);
if($inDB){
//the domain is already in the DB. Lets check if the URL to the file is in the DB also
$pathIn = check($domain,false,$inDB);
if($pathIn){

//The path is in the DB too. So we serve the previously generated hash
return $inDB.'.'.$pathIn['hash'];
}else{

//else we add the path to the links table
add($domain,false,$inDB);
return makeShort($url);
}
}else{

//else we add the domain in the domains table
add($domain,true);
return makeShort($url);
}
}
//...
?>

We see that the makeShort function uses two new functions. The check and the add function. The first one is responsible for checking if a link is already in the database (why do we need to create a new short url for the same link?) and the second one is responsible for adding the data into the database.

Function add:

 //...
function add($array,$host,$domainId=false){
//first we check what the function will do
if($host){
$sql = "INSERT INTO `domains`(`id`,`domain`)VALUES('',?);";
$data = array($array['host']);
$result = dbquery($sql,$data);
}else{
$sql = "INSERT INTO `links`(`id`,`did`,`link`,`hash`)VALUES('',?,?,?);";
//we need all the link without the hostname.
if($array['path']){
$str = $array['path'];
}else{
$str = '/';
}
//if the link has arguments
if($array['query']){
$str .= '?'.$array['query'];
}
//and if the link has an anchor
if($array['fragment']){
$str .= '#'.$array['fragment'];
}
//hash the string with our very simple func
$hash = hashIt($str);
$data = array($domainId,$str,$hash);
$result = dbquery($sql,$data);
}
}
//...
?>

Function check:

 //...
function check($array,$host,$domainId=false){
//first we check what the function will check
if($host){
$sql = "SELECT * FROM `domains` WHERE `domain`=?";
$data = array($array['host']);
$result = dbquery($sql,$data);

if($result['id']){
return $result['id'];
}else{
return false;
}
}else{
$sql = "SELECT * FROM `links` WHERE `link`=? AND `did`=?";
//we need all the link without the hostname
if($array['path']){
$str = $array['path'];
}else{
$str = '/';
}
//if the link has arguments
if($array['query']){
$str .= '?'.$array['query'];
}
//and if the link has an anchor
if($array['fragment']){
$str .= '#'.$array['fragment'];
}
$data = array($str,$domainId);
$result = dbquery($sql,$data);
if($result['id']){
return $result;
}else{
return false;
}
}
}
//...
?>

Two more functions are used here. The hashIt function and the dbquery function. The first one will be creating a hashed string from the specified URL and add some more randomness in it by using a random number, and the second one will do all the dirty work with our database.

Function hashIt:

 //...
function hashIt($url){
$url=str_replace('.','-',$url);
$rand = rand(0,100);
$url .= "$rand";
$suffled = str_shuffle($url);
return substr($suffled,0,4);
}
//...
?>

Function dbquery:

 //...
function dbquery($sql,$data,$mode=false,$justonce=false){
$conn = @connect();
$sth = $conn->prepare($sql);
$once = $sth->execute($data);
if($justonce){
//echo $once;
return $once;
}
if($mode){
return $sth->fetchall();
}else{
return $sth->fetch();
}
}
//...
?>

Now our shortening service is ready to do the magic. We just need some kind of command that will fire up the makeShort function. We want to make our service able of shortening an URL by a simple GET variable. So anyone that wishes to shorten a link would be able to do it by entering something like this in his browser:
http://gi.ly/s=http://jeez.eu/2009/09/06/twitter-alternatives/

So we will add the following code at the end of our file:

 //...
if($_GET['s']){
echo HOST.makeShort($_GET['s']);
}
//...
?>

The Second Part.

Now that our shortener is able to shorten links, we need to make it able to redirect to the original source of the short link. This is even easier since all we need are three new functions.

Function makeRedirect:

 //...
function makeRedirect($hash){

$hash = explode('.',$hash);
$domain = getDomain($hash[0]);
$path = getLink($hash[1]);
header("HTTP/1.1 301 Moved Permanently");
header ("Location: http://$domain$path");
}
//...
?>

Function getDomain:

 //...
function getDomain($did){
$sql = "SELECT * FROM `domains` WHERE `id`=?";
$data = array((int) $did);
$result = dbquery($sql,$data);
if($result['domain']){
return $result['domain'];
}else{
return null;
}
}
//...
?>

Function getLink:

 //...
function getLink($hash){
$sql = "SELECT * FROM `links` WHERE `hash`=?";
$data = array($hash);
$result = dbquery($sql,$data);
if($result['link']){
return $result['link'];
}else{
return null;
}
}
//...
?>

Our URL shortening service is almost ready. We again need to set an if statement in order to fire up the makeRedirect function:

 //...
if($_GET['r']){
makeRedirect($_GET['r']);
}
//...
?>

Now if someone opens an url like : http://gi.ly/r=2.oget , he will be redirected to the original link. But, this link can be even shorter with some mod_rewrite rules:

RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ index.php?r=$1 [QSA,L]

Thanks to Cherouvim for this.

Now the link a user has to use is something like this: http://gi.ly/2.oget and our URL shortener will redirect him to the original link.

Editorial

The URL shortener described in the above article needs some more things to be ready. We need to create an API for the service and to create a nice template for it too. If you find any bugs in the code, or if you think that it could be corrected in any way, don’t hesitate to comment on it. Oh and if you decide to use it somewhere, please post a link here to check it out.

VN:F [1.9.1_1087]
Rating: 10.0/10 (2 votes cast)
VN:F [1.9.1_1087]
Rating: +1 (from 1 vote)
A Jeez.eu implementation of an URL Shortener, 10.0 out of 10 based on 2 ratings

Popularity: 1%

  • Share/Bookmark

Related posts:

  1. Using YQL To Manage Your Yahoo! meme Account Yahoo! Query Language is an expressive SQL-like language that lets...
  2. Web 3.0 Will Be All About Web Services. Learn The Basics The web has evolved in many ways. From the static...
  3. Decrypting Bit.ly Links on Twitter and Not Only Although URL shorteners are something we can’t live without, there...
  4. Creating A Google Wave Extension In 5 Steps This is a simple to follow tutorial on how to...
  5. CAPTCHA Problems, A Suggestion and Alternatives You have your blog ,contact form, application setup and your...

About the Author:

Filed under: Tutorials - Trackback Uri


2 Comments.

  • Basilakis says:

    Jeezus that is great ;)

    Keep them comming, we enjoy your posts!

    VA:F [1.9.1_1087]
    Rating: 5.0/5 (2 votes cast)
    VA:F [1.9.1_1087]
    Rating: 0 (from 0 votes)
  • Short URL says:

    I really have enjoyed reading your post – very informative and useful information without a bunch of BS!
    I’ll be sure to give this URL to some friends

    Thanks Again

    VA:F [1.9.1_1087]
    Rating: 0.0/5 (0 votes cast)
    VA:F [1.9.1_1087]
    Rating: 0 (from 0 votes)