Simple URL Shortener With PHP MySQL (Free Code Download)

Welcome to a tutorial on how to create a simple URL shortener with PHP and MySQL. Want to share some promotion links and don’t want to use the “common” URL shorteners online? Here’s how you can create your own in just a few steps – Read on!

 

 

TABLE OF CONTENTS

 

DOWNLOAD & NOTES

Here is the download link to the example code, so you don’t have to copy-paste everything.

 

EXAMPLE CODE DOWNLOAD

Source code on GitHub Gist

Just click on “download zip” or do a git clone. I have released it under the MIT license, so feel free to build on top of it or use it in your own project.

 

SORRY FOR THE ADS...

But someone has to pay the bills, and sponsors are paying for it. I insist on not turning Code Boxx into a "paid scripts" business, and I don't "block people with Adblock". Every little bit of support helps.

Buy Me A Coffee Code Boxx eBooks

 

 

PHP MYSQL URL SHORTENER

All right, let us now get into some details of the URL shortener with PHP and MySQL.

 

STEP 1) THE DATABASE

1-database.sql
CREATE TABLE `short_url` (
  `short` varchar(16) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL,
  `full` varchar(255) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
 
ALTER TABLE `short_url`
  ADD PRIMARY KEY (`short`),
  ADD UNIQUE KEY `full` (`full`);

That’s right, only 2 fields.

  • short Primary key, the “URL shortcode”. Take note, this is case-sensitive.
  • full The full URL to redirect to. Unique to prevent duplicate URLs.

 

 

STEP 2) SHORTEN URL PHP LIBRARY

2a-shorten.php
<?php
class Shorten {
  // (A) CONSTRUCTOR - CONNECT TO THE DATABASE
  private $pdo = null;
  private $stmt = null;
  public $error;
  function __construct () {
    $this->pdo = new PDO(
      "mysql:host=".DB_HOST.";dbname=".DB_NAME.";charset=".DB_CHAR,
      DB_USER, DB_PASSWORD, [
      PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
      PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC
    ]);
  }

  // (B) DESTRUCTOR - CLOSE DATABSE CONNECTION
  function __destruct () {
    if ($this->stmt !== null) { $this->stmt = null; }
    if ($this->pdo !== null) { $this->pdo = null; }
  }
 
  // (C) HELPER - EXECUTE SQL QUERY
  function exec ($sql, $data=null) : void {
    $this->stmt = $this->pdo->prepare($sql);
    $this->stmt->execute($data);
  }
 
  // (D) HELPER - RANDOM STRING
  // CREDITS: https://stackoverflow.com/questions/4356289/php-random-string-generator
  function random ($length=6) {
    $char = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_.~";
    $clen = strlen($char);
    $rand = "";
    for ($i=0; $i<$length; $i++) { $rand .= $char[rand(0, $clen-1)]; }
    return $rand;
  }
 
  // (E) GET BY SHORT URL CODE
  function getShort ($short) {
    $this->exec("SELECT `full` FROM `short_url` WHERE `short`=?", [$short]);
    return $this->stmt->fetchColumn();
  }
 
  // (F) GET BY FULL URL
  function getFull ($full) {
    $this->exec("SELECT `short` FROM `short_url` WHERE `full`=?", [$full]);
    return $this->stmt->fetchColumn();
  }
 
  // (G) DELETE ENTRY
  function del ($short) {
    $this->exec("DELETE FROM `short_url` WHERE `url_id`=?", [$short]);
    return true;
  }
 
  // (H) GENERATE A NEW SHORT URL CODE
  function create ($full) {
    // (H1) CHECK IF FULL URL ALREADY EXIST
    if ($this->getFull($full) != "") {
      $this->error = "$full is already defined.";
      return false;
    }
 
    // (H2) GENERATE RANDOM SHORT CODE
    $short = null;
    while ($short === null) {
      $random = $this->random();
      if ($this->getShort($random) == "") { $short = $random; }
    }
 
    // (H3) INSERT ENTRY
    $this->exec(
      "INSERT INTO `short_url` (`short`, `full`) VALUES (?,?)",
      [$short, $full]
    );
    return $short;
  }
}
 
// (I) DATABASE SETTINGS - CHANGE TO YOUR OWN!
define("DB_HOST", "localhost");
define("DB_NAME", "test");
define("DB_CHAR", "utf8mb4");
define("DB_USER", "root");
define("DB_PASSWORD", "");
 
// (J) INIT
$_SURL = new Shorten();

Yikes, this library looks pretty massive at first. But keep calm and look carefully.

  • (A, B, J) The Constructor connects to the database when new Shorten() is created, the destructor closes the connection.
  • (C & D) Helper functions.
    • exec() Runs an SQL query.
    • random() Generates a random string.
  • (E & F) Get URL entries from the database.
    • getShort() Give the short URL code, get the full URL.
    • getFull() Give the full URL, get the short URL code.
  • (G & H)  Create and delete entries.
    • del() Delete the given short URL code.
    • create() Generate a shortcode for the given full URL.
  • (I) Remember to change the database settings to your own.

That’s all, pretty much just SQL queries. Remember to change the database settings to your own.

 

 

STEP 3) REDIRECTION

3A) CREATE A DUMMY SHORT URL CODE

3a-dummy.php
<?php
// (A) ADD ENTRIES
require "2-lib-shorten.php";
$short = $_SURL->create("https://code-boxx.com");
echo $short==false ? $_SURL->error : $short;

// (B) TO DELETE
// echo $_SURL->del(SHORT) ? "OK" : $_SURL->error ;

Now that the library is in place – Go ahead and create your first short URL.

 

3B) HTACCESS FILE

.htaccess
RewriteEngine On
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
RewriteBase /
RewriteRule ^3c-index\.php$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /3c-index.php [L]

This is “borrowed” from WordPress. What this does:

  • Turn the rewrite engine on.
  • Redirect http://site.com/SHORTCODE to 3c-index.php.
  • Don’t redirect existing files and folders alone.
  • Don’t redirect HTTP auth requests.

Take note – the mod_rewrite module must be enabled in Apache for this to work.

P.S. IIS and NGINX users, you have to “translate” this on your own.

 

 

3C) INDEX PAGE

3c-index.php
<?php
// (A) GET SHORT URL CODE
$short = parse_url($_SERVER["REQUEST_URI"], PHP_URL_PATH);
$short = explode("/", trim($short, "/"));
$short = count($short)==1 ? $short[0] : null ;
$full = null;
 
// (B) GET FULL URL
if ($short!==null) {
  require "2-lib-shorten.php";
  $full = $_SURL->getShort($short);
}
 
// (C) REDIRECT IF VALID
if ($full!==false) { header("Location: $full"); }
else { echo "INVALID REQUEST"; }

Finally, this should be straightforward:

  1. Get the “shortcode section” from the URL.
  2. Check the shortcode against the database.
  3. Redirect if valid.

 

 

EXTRAS

That’s all for the main tutorial, and here is a small section on some extras and links that may be useful to you.

 

I DON’T WANT TO REDIRECT!

If you want to load a page using this short URL system:

  • Instead of the URL, store the file name in the database full column.
  • Update section C of 3c-index.php – If valid, require $full instead of redirecting.
  • This pretty much turns it into a “pretty URL” system.

 

A POSSIBLE ISSUE

Here is a small possible problem that I foresee in create() of 2-lib-shorten.php.

  • The shortcode is generated using a random string generator $random = $this->random().
  • It is then checked against the database to make sure there are no duplicate shortcodes if ($this->getShort($random) == "").

Yes, this works. But when the number of shortcodes grows over time, the chances of hitting duplicates get higher. This segment will end up looping more to generate a unique shortcode. But the good news is, even at 6 characters, there are billions of unique shortcodes. So yeah… Unless you are serving the entire world, this is not going to be a big issue.

 

LINKS & REFERENCES

 

THE END

Thank you for reading, and we have come to the end. I hope that it has helped you to better understand, and if you want to share anything with this guide, please feel free to comment below. Good luck and happy coding!