This is my first tutorial on PHP. I hope you enjoy it as much as I did making it. :) In this tutorial, I will show you how to create a TinyURL-like service in a few steps with PHP and MySQL. You can download the files for offline usage. Or You can read it here. Download

Pre-requisites

This tutorial is intended to readers with basic knowledge of PHP and MySQL. I also assume you have PHP and MySQL installed in your system.

File Structure

Under the root folder we will have 5 files.

---Root Folder
-----.htaccess
-----config.php
-----functions.php
-----index.php
-----create.php

Create the table

First things first. We need a table in the database to hold the data.

CREATE DATABASE shortened;
DROP TABLE IF EXISTS `urls`;
CREATE TABLE IF NOT EXISTS `urls` (
  `uid` int(11) NOT NULL auto_increment,
  `url` varchar(1024) default NULL,
  `unique_chars` varchar(25) NOT NULL,
  PRIMARY KEY  (`uid`),
  UNIQUE KEY `unique_chars` (`unique_chars`)
);

Connect to database

We create a config.php file under the root folder. This is almost common to all php projects because it gives easy access to your database credentials.


<?php
 error_reporting(E_ALL);
 $hostname = "localhost";
 $username = "root";
 $password = "";
 $dbname = "shortened";
 $link = mysql_connect($hostname, $username, $password);
 mysql_select_db($dbname) or die("Unknown database!");
 
 $config["domain"] = "http://127.0.0.1/shrt.er"; //no trailing slashes
?>
The first line is added because we are doing it at a developmental level. So, if some error comes up, we can view it easily. Once you are finished with coding and ready for the "audiance", just comment out this line.
Connection to the database is quite self-explanatory. Just put your hostname, username and password. Also enter the name of the database.

In the last line, we introduce a new variable $config["domain"] which we will need later.
This is the domain of the site, e.g. if you put under a folder called shrt.er,
the domain would be http://127.0.0.1/shrt.er and subsequently the shorter links would be something like
http://127.0.0.1/shrt.er/aer2.

Create the functions

Once we are done with connection and database issues, we start creating the main functions in the functions.php file.

We have to generate random set of characters which will be added to the main domain. For this we create the following function.

<?php
include("config.php");
function generate_chars()
{
 $num_chars = 4; //max length of random chars
 $i = 0;
 $my_keys = "123456789abcdefghijklmnopqrstuvwxyz"; //keys to be chosen from
 $keys_length = strlen($my_keys);
 $url  = "";
 while($i<$num_chars)
 {
  $rand_num = mt_rand(1, $keys_length-1);
  $url .= $my_keys[$rand_num];
  $i++;
 }
 return $url;
}
?>
What it does is randomly choose a number from the length of $my_keys (which is the set of characters we would make the random chars from) and then this process is continued until we reach the max char length which is specified by $num_chars. Finally, it returns the randomly generated characters.
Next, we are to create a function that checks whether the randomly generated characters are already been used before (some kind of hash collision we are trying to prevent).
function isUnique($chars)
{
 //check the uniqueness of the chars
 global $link;
 $q = "SELECT * FROM `urls` WHERE `unique_chars`='".$chars."'";
 $r = mysql_query($q, $link);
 if( mysql_num_rows($r)>0 ): 
  return false;
 else: 
  return true;
 endif;
}
The above function does the job well. And it is pretty self-explanatory. The only bit is that we make the $link variable global so that we can access it inside the function. Rememeber we created $link in config.php. We will need the function later.
Chances are that you already have a shortened url for a site and some user tries to create a shortened url for the same url (Don't blame him. He doesn't have access to your database :P ). To avoid such situations we create another function that checks whether the url has already been there in the database.
function isThere($url)
{
 global $link;
 $q = "SELECT * FROM `urls` WHERE `url`='".$url."'";
 $r = mysql_query($q);
 if(mysql_num_rows($r)>0): return true;
 else: return false;
 endif;
}
Now, we create the function that creates shortened urls. Ok, the aim is to provide a GET variable in the url (like create.php?u=http://some.big/url/) and create a shortened URL.
function create()
{
 global $link; //make the link variable in the config.php, global
 global $config; //make the $config array in the config.php, global
 $chars = generate_chars(); //generate random characters
/* We check the uniqueness of the characters. The following loop
continues until it generates unique characters */
 while( !isUnique($chars) )
 {
  $chars = generate_chars();
 }
 $url = $_GET["u"];//get the url
 $url = trim($url);//trim it to remove whitespace
 $url = mysql_real_escape_string($url);//sanitize data
/* Now we check whether the url is already there in the database. */
 if(!isThere($url))
 {
 //url is not in the database
  $q = "INSERT INTO `urls` (url, unique_chars) VALUES ('".$url."', '".$chars."')";
  $r = mysql_query($q, $link); //insert into the database
  if(mysql_affected_rows()):
  //ok, inserted. now get the data
   $q = "SELECT * FROM `urls` WHERE `url`='".$url."'";
   $r = mysql_query($q);
   $row = mysql_fetch_row($r);
   echo $config["domain"]."/".$row[2]; //$row[2] is where the random chars are
  else:
  //problem with the database
   echo "Sorry, some problem with the database. Please try again.";
  endif;
 }
 else
 {
 //url is already there. so no need to insert again. Just get the data from database
  $q = "SELECT * FROM `urls` WHERE `url` = '".$url."'";
  $r = mysql_query($q);
  $row = mysql_fetch_row($r);
  echo $config["domain"]."/".$row[2];
 }
}
Study the code closely. I have commented out almost everything. You should not have any problem.
Now, the create() function is ready. We need to create a create.php file visiting which (and providing with a link) we can create a shorter url. So make a file called create.php and insert the following code and save.
<?php
include("functions.php"); //include functions.php where all the functions are defined.
create(); //call the function create()
?>
Let's create a new shortened url (or rather I add a data to the database) by going to http://127.0.0.1/shrt.er/create.php?u=http://flickr.com/photos/moody_guy/ (Your configuration may be different) to create a shorter url of http://flickr.com/photos/moody_guy/. You will be greeted with a shorter url. Now, visiting it won't do anything since we have not made any file or function to redirect to the url. Copy the last part of the url (say "vlk3"). (We will need this later.)
Now, let's create the main index.php file that redirects to the desired url.
ob_start(); //start output buffering.

include("config.php"); // don't forget the config.php. All our db credentials are there.
$shortened = $_GET["u"];//shortened chars
$q = "SELECT * FROM `urls` WHERE `unique_chars` = '".$shortened."'";
$r = mysql_query($q, $link);
if(mysql_num_rows($r)>0):
 // i.e. the link is found in db
 $info = mysql_fetch_array($r);
 $url = $info["url"]; // the desired url
 header("Location:".$url); //redirect to the desired url
else:
 // link is not found in the db
 echo "Sorry, link not found!";
endif;
mysql_close(); //don't forget to close the connection
ob_end_flush(); // end output buffering.
Follow the code closely. Every bit is commented. Now, if you visit http://127.0.0.1/shrt.er/index.php?u=vlk3, you will be taken to http://flickr.com/photos/moody_guy. But our objective is to visit http://127.0.0.1/shrt.er/vlk3 to do the same work.
This is where we need mod_rewrite feature. If you are complete newbie, you can learn about it here. The official documentation is here. To do the above job, create a file called .htaccess and put the following code there.
RewriteEngine On
RewriteRule   ^([1-9a-z]*)$ index.php\?s=$1 [L]
Done! Now, if you visit http://127.0.0.1/shrt.er/vlk3, you will be taken to http://flickr.com/photos/moody_guy

Function Reference

Here are all the php functions I have used in this tutorial.

Labels: , ,

blog comments powered by Disqus
Blogger Ariyo said...
Loved it, Thanks
Blogger Dustin said...
Oh wow. This was an adventure. I'm no PHP programmer at all. But I can follow along, and figure stuff out. But I usually don't understand what I'm doing.

After some trial and error, I've finally gotten this to work. But have noticed a few errors in your tutorial that you may want to clear up.

The functions.php need to all be contained in &gt?php ?&lt. This took me a while to figure out why my code was not changing color.

Also, I had to add an include to the config.php file in the functions.php file. This was not mentioned at all. Again, I was scratching my head on to why it wasn't connecting to my DB. Doing this resolved the issue.

Those things above might have been omitted on purpose because maybe those things should be known for to the general PHP user. But the next one I believe is a true error.

On line 4 of index.php, you have "$shortened = $_GET["s"];//shortened chars" It kept throwing an error on this line, pointing out the "s" was the problem. I did some educational guessing, and figured out it should be "u", from your functions.php file. Also, index.php needs to be contained in a &gt?php ?&lt

Hope this helps others who are beginners at PHP. Veterans probably won't have any issues.

Now the next step is to build this into a form.
Blogger Abhisek said...
@Dustin, thank you so much for pointing the errors. The last one was pathetic. I updated the code and I hope it won't be a problem for PHP newbies.
Blogger Rene Kriest said...
Simply awesome.

Thank you for your very detailed source code explanations. I am realy new to PHP coding and I love tutorials which comment a lot.

Your tutorial is bookmarked for further projects.

A little suggestion: may you pls offer a zip file containing all files? That would be nice.

Kind regards,

René
Blogger Abhisek said...
@René, i have updated the zip. it now contains the source code as well. :)
Blogger L said...
GREAT!
This tutorial/full script is just the thing i was looking for...

I made some modifications, so you can check the URL (if is alive) and also if is real (in the actual "version", you can put anything and is added to the db)

I also added a "bookmarklet", a "title" field in the db, a click counter, even a simple template class for the initial page....

I'm working on a way to auto-get the title of the URL (this field is automatcly inserted trhu javascript, much simpler)
Also i like to add a "most visited" and "newest" links... just for the fun of it ...

Really great script you have here.
Just contact me and i send you the link to check my modifications
(in this moment is a personal services for my blog and twitter)

Almost forget ... also I like to create the simplest API possible (but maybe in a near future)

I would like to share the code when is more finished...for example to add every credit for all the snippets i found and give proper credit to the authors)
Blogger Abhisek said...
@L, congrats on your work. Would look forward to your release! :)
Blogger L said...
@Abhisek
You can visit the "site" here
http://kyl.cl/corto
(very very beta...but the whole concept works)

Is entirely in spanish, but is very simple (put the URL and Voila)

I added a bunch of things already...
- white list for IP addres
- counter of "uses" (no more than 10 a day)
- date of last visit (only internal)
- date of creation (to keep track, maybe to do maintence)

Also i was thinking to do something similar to TinyURL, when the user wants to see the URL before visit... cookie based... is really simple..maybe tomorrow



I will appreciate very much your opinion


P.D.: "Corto" means "short" in spanish ... also is the name of a canine friend of mine (in the picture, cloud number 1)
Blogger Abhisek said...
@L, wow! that's super cool. I really liked the "Top 5" feature. Helps users sharing links. Congratulations on this work! :)
Blogger youfoundjake said...
I luckily found this post and I've got to say great job.
I'm trying to extend it out, so that it can be a wordpress plugin, where your blog is the root url shortneing service, forwarding on to the long post names...
Any ideas?
Blogger Abhisek said...
@youfoundjake, cool idea, man! Almost all premium websites have such services. A wordpress plugin would be great. Something like http://mywpblog.tld/redirect/rndm_chars?
Blogger youfoundjake said...
yeah, i figure why use tinyurl or kl.am when you can have an inhouse rewriting service and building branding at the same time..