Skip to main content

How to Build Your Own URL Shortener for Short any URL by KShare

How to Build Your Own URL Shortener for Short any URL

Short URL always a recommended way to share the web page URL. It easy to remember and can be shared easily on the web. There are many URL Shortener services are available that allows you to convert long URL to short URL online. But the main disadvantages of these services are you won’t be able to use your own domain in the short URL. If you want to create short URL with your own domain name, you need to use custom URL Shortener. 

Most of us are familiar with seeing URLs like bit.ly or t.co on our Twitter or Facebook feeds. These are examples of shortened URLs, which are a short alias or pointer to a longer page link. For example, I can send you the shortened URL http://bit.ly/SaaYw5 that will forward you to a very long Google URL with search results on how to iron a shirt. It would be much easier to text the 20-character bit.ly URL to your son who is in college and preparing for his first big job interview.
URL Shortener service takes a long URL and compresses it in a short link which is easier to share. You can create short URLs programmatically using PHP without any third party URL Shortener API. In this tutorial, we will show you how to build URL Shortener library and create short URL using PHP and MySQL. With PHP URL Shortener library you can shorten the long URLs and use your own domain in the short URLs.
How to Build Your Own URL Shortener for Short any URL by KShare

Answering Some Common Questions

So with bit.ly and many other URL shorteners like it out there and freely available, why should we bother building our own? Most of these shortening services even have an easy-to-use API so that we can programmatically generate a shortened URL, and use it within our PHP scripts.

The best reasons are for convenience, aesthetics and brand recognition. If for example your website has an application that creates a large amount of reports, a very active blog or a large photo album, there will be a lot of links. A URL shortener will allow you to programmatically create a clean, simple link that can be emailed to your readers or published on your website. The obvious advantage to having your own is that your readers have instant brand recognition with your website.

You may wonder why you always see letters mixed with numbers in shortened URL’s. By having more than ten options (0-9) per digit, we are able to have dramatically more combinations while keeping the code as short as possible.

The characters we’ll be using are the digits 1-9 along with various upper/lowercase letters. I have removed all of the vowels to prevent having links created which are unintended bad words, and I have removed any characters that could be confused with each other. This gives us a list of about 50 characters available for each digit, which means that with two characters, we have 2,500 possible combinations, 125,000 possibilities with three characters, and a whopping 6.5 million combinations with just four characters!

Planning the Database

CREATE TABLE IF NOT EXISTS short_urls (
  id INTEGER UNSIGNED NOT NULL AUTO_INCREMENT,
  long_url VARCHAR(255) NOT NULL,
  short_code VARBINARY(6) NOT NULL,
  date_created INTEGER UNSIGNED NOT NULL,
  counter INTEGER UNSIGNED NOT NULL DEFAULT '0',

  PRIMARY KEY (id),
  KEY short_code (short_code)
)
ENGINE=InnoDB;

Creating a URL Short Code Class(Shortener.class.php)

<?php
/** 
 * Class to create short URLs and decode shortened URLs
 */ 
class Shortener
{
    protected static $chars "abcdfghjkmnpqrstvwxyz|ABCDFGHJKLMNPQRSTVWXYZ|0123456789";
    protected static $table "short_urls";
    protected static $checkUrlExists false;
    protected static $codeLength 7;

    protected $pdo;
    protected $timestamp;

    public function __construct(PDO $pdo){
        $this->pdo $pdo;
        $this->timestamp date("Y-m-d H:i:s");
    }

    public function urlToShortCode($url){
        if(empty($url)){
            throw new Exception("No URL was supplied.");
        }

        if($this->validateUrlFormat($url) == false){
            throw new Exception("URL does not have a valid format.");
        }

        if(self::$checkUrlExists){
            if (!$this->verifyUrlExists($url)){
                throw new Exception("URL does not appear to exist.");
            }
        }

        $shortCode $this->urlExistsInDB($url);
        if($shortCode == false){
            $shortCode $this->createShortCode($url);
        }

        return $shortCode;
    }

    protected function validateUrlFormat($url){
        return filter_var($urlFILTER_VALIDATE_URLFILTER_FLAG_HOST_REQUIRED);
    }

    protected function verifyUrlExists($url){
        $ch curl_init();
        curl_setopt($chCURLOPT_URL$url);
        curl_setopt($chCURLOPT_NOBODYtrue);
        curl_setopt($ch,  CURLOPT_RETURNTRANSFERtrue);
        curl_exec($ch);
        $response curl_getinfo($chCURLINFO_HTTP_CODE);
        curl_close($ch);

        return (!empty($response) && $response != 404);
    }

    protected function urlExistsInDB($url){
        $query "SELECT short_code FROM ".self::$table." WHERE long_url = :long_url LIMIT 1";
        $stmt $this->pdo->prepare($query);
        $params = array(
            "long_url" => $url
        );
        $stmt->execute($params);

        $result $stmt->fetch();
        return (empty($result)) ? false $result["short_code"];
    }

    protected function createShortCode($url){
        $shortCode $this->generateRandomString(self::$codeLength);
        $id $this->insertUrlInDB($url$shortCode);
        return $shortCode;
    }
    
    protected function generateRandomString($length 6){
        $sets explode('|'self::$chars);
        $all '';
        $randString '';
        foreach($sets as $set){
            $randString .= $set[array_rand(str_split($set))];
            $all .= $set;
        }
        $all str_split($all);
        for($i 0$i $length count($sets); $i++){
            $randString .= $all[array_rand($all)];
        }
        $randString str_shuffle($randString);
        return $randString;
    }

    protected function insertUrlInDB($url$code){
        $query "INSERT INTO ".self::$table." (long_url, short_code, created) VALUES (:long_url, :short_code, :timestamp)";
        $stmnt $this->pdo->prepare($query);
        $params = array(
            "long_url" => $url,
            "short_code" => $code,
            "timestamp" => $this->timestamp
        );
        $stmnt->execute($params);

        return $this->pdo->lastInsertId();
    }
    
    public function shortCodeToUrl($code$increment true){
        if(empty($code)) {
            throw new Exception("No short code was supplied.");
        }

        if($this->validateShortCode($code) == false){
            throw new Exception("Short code does not have a valid format.");
        }

        $urlRow $this->getUrlFromDB($code);
        if(empty($urlRow)){
            throw new Exception("Short code does not appear to exist.");
        }

        if($increment == true){
            $this->incrementCounter($urlRow["id"]);
        }

        return $urlRow["long_url"];
    }

    protected function validateShortCode($code){
        $rawChars str_replace('|'''self::$chars);
        return preg_match("|[".$rawChars."]+|"$code);
    }

    protected function getUrlFromDB($code){
        $query "SELECT id, long_url FROM ".self::$table." WHERE short_code = :short_code LIMIT 1";
        $stmt $this->pdo->prepare($query);
        $params=array(
            "short_code" => $code
        );
        $stmt->execute($params);

        $result $stmt->fetch();
        return (empty($result)) ? false $result;
    }

    protected function incrementCounter($id){
        $query "UPDATE ".self::$table." SET hits = hits + 1 WHERE id = :id";
        $stmt $this->pdo->prepare($query);
        $params = array(
            "id" => $id
        );
        $stmt->execute($params);
    }

Database Configuration (dbConfig.php)

<?php
// Database configuration
$dbHost     "localhost";
$dbUsername "root";
$dbPassword "root";
$dbName     "codexworld";

// Create database connection
try{
    $db = new PDO("mysql:host=$dbHost;dbname=$dbName"$dbUsername$dbPassword);
}catch(PDOException $e){
    echo "Connection failed: " $e->getMessage();
}

Creating Index file (index.php)

// Include database configuration file
require_once 'dbConfig.php';

// Include URL Shortener library file
require_once 'Shortener.class.php';

// Initialize Shortener class and pass PDO object
$shortener = new Shortener($db);

// Long URL
$longURL 'https://www.codexworld.com/tutorials/php/';

// Prefix of the short URL 
$shortURL_Prefix 'https://xyz.com/'// with URL rewrite
$shortURL_Prefix 'https://xyz.com/?c='// without URL rewrite

try{
    // Get short code of the URL
    $shortCode $shortener->urlToShortCode($longURL);
    
    // Create short URL
    $shortURL $shortURL_Prefix.$shortCode;
    
    // Display short URL
    echo 'Short URL: '.$shortURL;
}catch(Exception $e){
    // Display error
    echo $e->getMessage();
}

Redirect to Long URL

// Include database configuration file
require_once 'dbConfig.php';

// Include URL Shortener library file
require_once 'Shortener.class.php';

// Initialize Shortener class and pass PDO object
$shortener = new Shortener($db);

// Retrieve short code from URL
$shortCode $_GET["c"];

try{
    // Get URL by short code
    $url $shortener->shortCodeToUrl($shortCode);
    
    // Redirect to the original URL
    header("Location: ".$url);
    exit;
}catch(Exception $e){
    // Display error
    echo $e->getMessage();
}

URL Rewrite with HTACCESS

If you want to make the URL user-friendly, use HTACCESS with RewriteEngine. Create a .htaccess file and add the following code.
<IfModule mod_rewrite.c> 
RewriteEngine On RewriteCond %{REQUEST_FILENAME} !-f 
RewriteCond %{REQUEST_FILENAME} !-d 
RewriteRule ^([a-zA-Z0-9]+)/?$ redirect.php?c=$1 [L] 
</IfModule>

Post a comment

0 Comments