Crear URLs cortas mediante acortador de URLs personalizado

Para compartir URLs de páginas web lo ideal es que sean cortitas. Así son más fáciles de recordar y pueden ser compartidas de forma más sencilla en redes sociales, sobre todo de microblogging. Hay muchos acortadores de URLs disponibles que te permiten convertir una URL larga en una más corta. Pero estos servicios cuentan con una desventaja, la de no poder utilizar tu propio dominio en las URLs cortas. Si quieres crear URLs cortas con tu propio nombre de dominio, necesitas utilizar un acortador de URLs personalizado.

Un acortador de URLs coge una URL larga y la comprime hasta conseguir enlace corto, el cual es más sencillo de compartir. Puedes crear URLs cortas mediante programación utilizando PHP sin tener que recurrir alguna API de terceros. En este tutorial te mostraremos cómo desarrollar un acortador de URLs mediante PHP y MySQL.

Crear una tabla en la base de datos

Utilizaremos la base de datos para gestionar las redirecciones basadas en los shortcodes. El siguiente SQL crea una tabla llamada short_urls en la base de datos para almacenar la información de la URL (URL larga, shortcode, hits y fecha de creación).

CREATE TABLE `short_urls` (
 `id` int(11) NOT NULL AUTO_INCREMENT,
 `long_url` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
 `short_code` varchar(25) COLLATE utf8_unicode_ci NOT NULL,
 `hits` int(11) NOT NULL,
 `created` datetime NOT NULL,
 PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

Librería acortadora de URLs (Shortener.class.php)

La clase Shortener te permite crear URLs cortas mediante programación utilizando PHP y MySQL. Esta clase utiliza la extensión PDO para trabajar con la base de datos MySQL.

Variables estáticas

  • $chars - Caracteres permitidos en el shortcode (separados por el carácter |)
  • $table – Nombre de la tabla de la base de datos donde se almacenará la información del shortcode
  • $checkUrlExists – Definida a TRUE, para comprobar si la URL larga existe
  • $codeLength – La longitud del shortcode

Funciones

  • __construct() - Define la referencia del objeto PDO y el timestamp.

  • urlToShortCode() - Valida la URL y crea el shortcode.
  • validateUrlFormat() - Valida el formato de la URL.
  • verifyUrlExists() - Verifica si existe la URL o no mediante cURL.
  • urlExistsInDB() - Comprueba si la URL larga existe en la base de datos. Si existe, devuelve el shortcode, y si no, devuelve FALSE.
  • createShortCode() - Crea un shortcode basándose en una URL larga e inserta las dos URLs (larga y corta) en la base de datos.
  • generateRandomString() - Generado un string aleatorio (shortcode) con los caracteres especificados en la variable $chars.
  • insertUrlInDB() - Inserta la URL en la base de datos utilizando la extensión PDO y devuelve el ID de la fila.
  • shortCodeToUrl() - Convierte el shortcode a una URL larga e inserta un nuevo hit en la BD.
  • validateShortCode() - Valida el shortcode basándose en los caracteres permitidos.
  • getURLFromDB() - Extrae la URL larga de la base de datos basándose en el shortcode.
  • incrementCounter() - Incrementa el contador de visitas de la URL.
<?php
/** 
 * Class to create short URLs and decode shortened URLs
 * 
 * @author CodexWorld.com <[email protected]> 
 * @copyright Copyright (c) 2018, CodexWorld.com
 * @url https://www.codexworld.com
 */ 
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($url, FILTER_VALIDATE_URL, FILTER_FLAG_HOST_REQUIRED);
    }

    protected function verifyUrlExists($url){
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_NOBODY, true);
        curl_setopt($ch,  CURLOPT_RETURNTRANSFER, true);
        curl_exec($ch);
        $response = curl_getinfo($ch, CURLINFO_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);
    }
}

Configuración de la base de datos (dbConfig.php)

En el fichero dbConfig.php, utilizaremos PDO para conectarnos a la base de datos. Debes definir el host ($dbHost), el nombre de usuario ($dbUsername), el password ($dbPassword) y el nombre de la base de datos ($dbName).

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

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

Crear una URL corta con PHP

El siguiente código crea un shortcode y genera una URL corta.

// 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://programacion.net/articulo/autocompletador_de_jquery_ui_con_imagenes_y_html_personalizado_en_php_2065';

// 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();
}

Redirigir hacia la URL corta

El siguiente código gestiona la redirección de URL corta a la URL original.

// 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();
}

Redirección URL con htaccess

Si quieres crear Url amigables, utilizar el HTACCESS con RewriteEngine. Crea un fichero .htaccess y pega el siguiente código.

<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>

 

COMPARTE ESTE ARTÍCULO

COMPARTIR EN FACEBOOK
COMPARTIR EN TWITTER
COMPARTIR EN LINKEDIN
COMPARTIR EN WHATSAPP