How to create a TinyURL clone in php

Post Reply
User avatar
Neo
Site Admin
Site Admin
Posts: 2642
Joined: Wed Jul 15, 2009 2:07 am
Location: Colombo

How to create a TinyURL clone in php

Post by Neo » Mon Mar 01, 2010 12:31 am

This tutorial will explain how to create a basic clone of the TinyURL service. If you've never heard of it, TinyURL allows you to turn long URLs into shorter links so they can be easily sent via email or other means. It might be fun to create your own personalized version that works just as well, and it's very easy to do. We'll go over the database structure, creating the required PHP script, and using mod_rewrite to make nicer URLs.

First of all, we need a database set up. Here is the SQL to create the database table:

Code: Select all

CREATE TABLE `turl` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `key` varchar(32) DEFAULT NULL,
  `url` text NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `key` (`key`)
);
This will create a table called "turl" (you can call it anything) with 2 fields:
"id" = type: int, length: 11, primary key, auto_increment, default: NULL
"key" = type: varchar, length: 32, unique key
"url" = type: text

If you use a graphical database management system, this should be enough info to get it set up. The "key" field will store a unique string to identify the link, and "url" will store the actual URL.

Now we need to start working with PHP. First we'll set up our configuration variables:

Code: Select all

<?php
$baseurl = 'http://example.com/'; //address to use as the first part of the short URLs (note trailing slash)
$sql_host = 'localhost'; //host of your mysql database (usually localhost)
$sql_user = 'turl'; //mysql user with access to your database
$sql_pass = 'password'; //password for this user
$sql_db = 'turl'; //the name of your database 
You can read the comments to figure out what each variable is. This will make it easier to move or reuse the script by only changing these variables.

Now we need to connect to the database:

Code: Select all

$db = mysql_connect($sql_host, $sql_user, $sql_pass);
mysql_select_db($sql_db, $db); 
Now, let's create the part that redirects your URL to the original. All we need to do is find the URL that matches the given key and set the headers to redirect to it. We'll check if the GET parameter called "k" exists and then use it as the key (after sanitizing it, of course):

Code: Select all

if(isset($_GET['k'])) {
   $k = mysql_real_escape_string($_GET['k'], $db);
   if($result = mysql_query("SELECT `url` FROM `turl` WHERE `key` = '" . $k . "'", $db)) {
      if(mysql_num_rows($result) > 0) {
         $row = mysql_fetch_row($result);
         header('HTTP/1.1 301 Moved Permanently');
         header('Location: ' . $row[0]);
         exit;
      }
   }
} 
Now it's time to handle the creation of the short URLs. We'll start with the PHP skeleton and the HTML form:

Code: Select all

if($_POST['submit'], $_POST['url'])) {
   // create short URL
}
else {
?>
<form method="post" action="short.php">
<label>URL: <input type="text" name="url" id="url" /></label>
<br />
<input type="submit" name="submit" id="submit" value="Shorten" />
</form>
<?php
}
?>
First we check if the "url" was posted by the form, and if it wasn't we show the form. The form only needs a URL field and a submit button. Also, be sure to change the action parameter to whatever your script is called.

Now for the part that creates the short URL. The process goes like this: check if the URL is valid (optional), check if the URL has already been shortened, insert the URL into the database, create the key, and show the new link.

Validate and sanitize URL. Here, we use the filter_var function as a quick and easy way to validate the URL:

Code: Select all

if(isset($_POST['submit'], $_POST['url'])) {
   if(filter_var($_POST['url'], FILTER_VALIDATE_URL, FILTER_FLAG_HOST_REQUIRED) !== false) {
      $url = mysql_real_escape_string($_POST['url'], $db); 
Check if the URL already exists and get the key if it exists:

Code: Select all

      if($result = mysql_query("SELECT `key` FROM `turl` WHERE `url` = '" . $url . "'", $db)) {
         if(mysql_num_rows($result) > 0) {
            $row = mysql_fetch_row($result);
            $key = $row[0];
         }
      } 
If the URL is fresh, insert it into the database, then convert the id to base 36 to use as the visible key. Using a key based on an auto_increment field ensures there are no duplicate keys. After we are done checking/inserting, we display the final link:

Code: Select all

      if(!isset($key) && mysql_query("INSERT INTO `turl` (`url`) VALUES ('" . $url . "')", $db)) {
         $id = mysql_insert_id($db);
         $key = base_convert($id, 10, 36);
         mysql_query("UPDATE `turl` SET `key` = '" . $key . "' WHERE `id` = '" . $id . "'");
      }
      echo '<a href="' . $baseurl . $key . '" target="_blank">' . $baseurl . $key . '</a>';
   }
} 
Here is the finished product:

Code: Select all

<?php
$baseurl = 'http://example.com/'; //address to use as the first part of the short URLs (note trailing slash)
$sql_host = 'localhost'; //host of your mysql database (usually localhost)
$sql_user = 'turl'; //mysql user with access to your database
$sql_pass = 'password'; //password for this user
$sql_db = 'turl'; //the name of your database
 
$db = mysql_connect($sql_host, $sql_user, $sql_pass);
mysql_select_db($sql_db, $db);
 
if(isset($_GET['k'])) {
   $k = mysql_real_escape_string($_GET['k'], $db);
   if($result = mysql_query("SELECT `url` FROM `turl` WHERE `key` = '" . $k . "'", $db)) {
      if(mysql_num_rows($result) > 0) {
         $row = mysql_fetch_row($result);
         header('HTTP/1.1 301 Moved Permanently');
         header('Location: ' . $row[0]);
         exit;
      }
   }
}
 
if(isset($_POST['submit'], $_POST['url'])) {
   if(filter_var($_POST['url'], FILTER_VALIDATE_URL, FILTER_FLAG_HOST_REQUIRED) !== false) {
      $url = mysql_real_escape_string($_POST['url'], $db);
      if($result = mysql_query("SELECT `key` FROM `turl` WHERE `url` = '" . $url . "'", $db)) {
         if(mysql_num_rows($result) > 0) {
            $row = mysql_fetch_row($result);
            $key = $row[0];
         }
      }
      if(!isset($key) && mysql_query("INSERT INTO `turl` (`url`) VALUES ('" . $url . "')", $db)) {
         $id = mysql_insert_id($db);
         $key = base_convert($id, 10, 36);
         mysql_query("UPDATE `turl` SET `key` = '" . $key . "' WHERE `id` = '" . $id . "'");
      }
      echo '<a href="' . $baseurl . $key . '" target="_blank">' . $baseurl . $key . '</a>';
   }
}
else {
?>
<form method="post" action="short.php">
<label>URL: <input type="text" name="url" id="url" /></label>
<br />
<input type="submit" name="submit" id="submit" value="Shorten" />
</form>
<?php
}
?>
Of course, you can wrap a nice HTML template around the whole thing to make it look nice, but this is the basic setup for a URL shortener service.

Now, as a finishing touch, we can use mod_rewrite to make URLs look nicer (and shorter) if your server supports it. Place this code in a file named ".htaccess" in the same directory as your script:

Code: Select all

RewriteEngine On
RewriteCond %{REQUEST_URI} \/([0-9a-z]+)$ [NC]
RewriteRule ^(.*) short.php?k=%1 [L]
This allows you to use example.com/xxxxxx instead of example.com/short.php?k=xxxxxx. Make sure to change the file name on the last line to reflect the actual name of your script.

So there you go, you now know how to make your own TinyURL clone!

Update: Changed from generating random strings to using an auto_increment converted to base 36 to avoid checking for duplicates. Also added checking for existing shortened URLs before generating a new one.
Post Reply

Return to “PHP & MySQL”