DIY: Приложение за съкращаване на URL

Goo.gl
Goo.gl е съкращаващата услуга на Google, достъпна само за регистрирани потребители на търсачката.

Има много причини да ползвате т.нар. съкратител на адреси, особено ако сте потребител на сайтове, където дължината на съобщенията, които изпращате, е ограничена. Или когато нямате възможност да форматирате своя коментар – дългите връзки разпокъсват написаното и идеите ви се губят. Или пък когато пишете SMS-и. Да не пропускаме и връзките с кирилски букви в тях – те често се конвертират към нечетимия за обикновения човек URI по учебник (по правило съдържащ само символите от ASCII, всеки друг знак се кодира). В резултат връзките са сякаш безкрайно дълги и абсолютно непрактични за споделяне.

Ето тук се намесват съкратителите – с помощта на кратък домейн от рода на is.gd или bit.ly и скрипт се създава кратка връзка от рода на bit.ly/NkMxvo, която пренасочва всеки любопитен посетител към желания от вас по-дълъг адрес.

Освен че е кратка, тази връзка често ви носи и допълнителни облаги, например възможността да следите статистики за нейното ползване – кой, кога и как е попаднал на нея. Всичко това зависи от вашия избор на услуга за скъсяване.

Не всички съкратители са направени да работят изцяло за вас – Туитър например ползва своя съкратител t.co за капсулиране на всеки един адрес, публикуван в съобщение из сайта с цел контрол над външното съдържание. Много медии, сред които National Geographic и New York Times също разполагат с подобни услуги, към които прибягват при споделяне на свое съдържание в интернет. Всъщност цели правителства са полудели по тази идея – руското например ползва krln.ru.

Е, в тази статия ще си създадем наш собствен скъсител на адреси.

На първо място ще отбележа, че знам, че съществуват няколко подобни проекта с отворен код, при това най-вероятно някои от тях са много добри. Не съм ги гледал. Всичко описано по-долу си е моя измислица и в качеството си на такава не претендира да е най-добрата измислица!

Задача

Имате дълга връзка, която желаете да скъсите. Чрез бланка посочвате въпросната връзка и по желание избирате кратко съчетание от букви, което да ѝ отговаря. Ако не посочите такова, системата сама ви избира.

Можете да следите последните връзки или да научите подробности за точно определен кратък адрес. Можете да следите кой и кога е използвал някоя препратка.

Веднъж съкратили каквото искате, споделяте краткия адрес с приятели,  колеги или непознати и когато те го посетят, системата ги отвежда на оригиналната локация, като си отбелязва откъде са се взели, какъв браузър и операционна система ползват, какъв е техният IP адрес и кога са дошли.

От какво се нуждаем?

На първо място – от каратък и лесен за запомняне домейн, който ще ползваме за нашия скъсител. Аз си имам bozhur.in – не е толкова кратък, но пък представлява т.нар. domain hack или с други думи комбинация от домейни от първо и второ ниво, подбрана така, че да изписва определена дума – в случая името Божурин. За примера ще ползваме By.Bozhur.in.

Останалото е като че ли по-лесно за осигуряване – трябва ни хостинг с поддръжка на PHP и MySQL.

Нека приемем, че всичко е налице и да пристъпим към действие!

Базата данни

Като начало ще си направим база от данни, където ще съхраняваме нашите данни.

Релационна диаграма за нашата база от данниНеобходими са ни само две таблици – „Връзки“, където ще записваме коя кратка връзка на кой URL отговаря. От любопитство ще си отбелязваме и датата на създаването ѝ – нищо чудно в последствие да ни е нужна при изготвяне на статистиките.

В „Посещения“ пазим куп любопитни работи – кой, кога и откъде идва. Таблицата ще свържем с „Връзки“ посредством външен ключ.

Сега да създадем и самите таблици.

Таблица „Връзки“:

CREATE TABLE `links` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT PRIMARY KEY,
  `link` varchar(255) NOT NULL UNIQUE,
  `url` varchar(255) NOT NULL,
  `created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 COLLATE utf8_bin;

… и таблица „Посещения“:

CREATE TABLE `visits` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT PRIMARY KEY,
  `vip` varchar(16) DEFAULT NULL,
  `referer` varchar(255) DEFAULT NULL,
  `agent` varchar(255) DEFAULT NULL,
  `lid` int(10) unsigned DEFAULT NULL,
  `when` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  FOREIGN KEY `lid` REFERENCES links(`id`)
  ON DELETE CASCADE
 ) ENGINE=InnoDB  DEFAULT CHARSET=utf8 COLLATE utf8_bin;

След като сме готови с базата от данни, е време да напишем и малко код…

Програмна реализация

By.Bozhur.in – файловеЦялото действие ще е описано в рамките на три файла с общ размер около 7kB.

Накратко, index.php ще получава заявките от страна на потребителя, ще прави проверка в базата данни на какъв адрес отговаря подадената кратка връзка, ще пренасочва потребителя в тази посока и ще си записва данни за него в таблица „Посещения“.

Manage.php ще се грижи за административните функции – него ще ползваме, когато искаме да създадем нова препратка, да изтрием предишна или да преглеждаме събраните данни.

И най-накрая, link.php съдържа описанието на цялата тази функционалност, която първите два файла реализират. Именно този link.php ще напишем първо.

link.php

<?php
	/* By Bozhurin Quick Link Script (by.bozhur.in)
	 * 2012 © Ivaylo Bozhurin (dev at bozhur dot in)
	*/

class Linker {

	// Конструктор за клас Linker
	function Linker() {
		// Подразбираща се часова зона
		date_default_timezone_set('Europe/Helsinki');

		// Данни за връзка с базата данни – хост, потребителско име, парола
		$this->connexion = mysql_connect("localhost", "LinkDB", "PASSWORD") or die(mysql_error());
		mysql_query("SET CHARACTER SET utf8");
		mysql_query("SET NAMES utf8");
		mysql_query("SET SQL_SAFE_UPDATES=1");

		// Избираме базата данни
		mysql_select_db("LinkDB", $this->connexion) or die(mysql_error());
	}

	// Връща информация за конкретна кратка връзка
	function Get($link) {
		$q = "SELECT * FROM by_links WHERE link='" .mysql_real_escape_string($link). "' LIMIT 1";
		$res = mysql_query($q, $this->connexion);
		return mysql_fetch_array($res, MYSQL_ASSOC);
	}

	// Проверява дали предоставеното кратко име е заето (съществува ли в БД)
	function There($link) {
		$q = "SELECT id FROM by_links WHERE link='".mysql_real_escape_string($link)."'";
		$res = mysql_query($q, $this->connexion);
		return mysql_num_rows($res);
	}

	// Връща данните за определен брой кратки връзки
	function GetLinks($limit) {
		$q = "SELECT * FROM by_links WHERE 1 ORDER BY created DESC LIMIT $limit";
		$res = mysql_query($q, $this->connexion);
		for($i=0; $a[$i] = mysql_fetch_array($res, MYSQL_ASSOC); $i++); // Любимият ми for-цикъл!!!
		return $a;
	}		

	/* Вмъква информация за нова кратка връзка.
	 * За вход приема съкращаван адрес ($url) и
	 * кратка връзка ($link)
	 */
	function Insert($url, $link, $ip) {
		$q = "INSERT INTO by_links(url, link, ip) VALUES ('".
			 mysql_real_escape_string($url) ."', '".
			 mysql_real_escape_string($link) ."', '".
			 mysql_real_escape_string($ip) ."')";

		$res = mysql_query($q, $this->connexion);
		return mysql_affected_rows($res);
	}

	// Изтрива данни за връзка от БД
	function Delete($link) {
		$q = "DELETE FROM by_links WHERE link='" .mysql_real_escape_string($link). "'";
		$res = mysql_query($q, $this->connexion);
		return mysql_affected_rows($res);
	}		

	// Въвежда данни в таблица „Посещения“
	function AddStats($lid, $vip, $ref, $agent) {
		$q = "INSERT INTO by_visits(lid, vip, referer, agent) VALUES (".
			 mysql_real_escape_string($lid) . ", '" .
			 mysql_real_escape_string($vip) . "', '" .
			 mysql_real_escape_string($ref) . "', '" .
			 mysql_real_escape_string($agent) . "')";
		$res = mysql_query($q, $this->connexion);
		return mysql_affected_rows($res);
	}
}
$linker = new Linker;

?>

Вече имаме всичко необходимо, за да пишем и четем от базата данни. Време е да направим страницата за администрация.

manage.php

Този файл ще се състои от две части – PHP скрипт, описващ въвеждането на данни, изпратени от бланка с метод post и XHTML описание на самата страница, включително бланката.

Нека първо направим скриптовата част. За нуждите на нашия пример тя ще реализира три неща – добавяне на нова препратка, премахване на съществуваща и извеждане на кратки данни за такава.

<?php
	include_once 'link.php'; 

	// Добавяне на препратка
	if($_POST["act"] == "add"){
		/* Ако потребителят представи желана кратка връзка и тя не е заета
		 * я вписваме в БД.
		*/
		if(($_POST["link"] != "") && (!$linker->There($_POST["link"]))){
			$linker->Insert($_POST["url"], $_POST["link"]);
		}

		// Иначе скриптът генерира случайна и вписва нея в БД.
		else {
			for(; $linker->There($_POST["link"] = substr(md5(uniqid()), 0, 5)); );
			$linker->Insert($_POST["url"], $_POST["link"]);
		}
	}

	// Премахване на препратка
	if(isset($_GET["del"])) {
		$linker->Delete($_GET["del"]);
	}

	// Шпиониране за препратка
	if($_POST["act"] == "spy"){
		$data = $linker->Get($_POST["link"]);

		if($data["id"] == "") $error = "Въвели сте несъществуваща връзка.<br />Ако искате, можете да си създадете такава.";
	}
?>

Да разпишем сега и външния облик. Освен бланки, там ще се съдържа и малко PHP код, използван да покажем последните трийсет препратки, както и информация за конкретна връзка, когато такава е поискана.

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">

	<head>

		<meta http-equiv="content-type" content="text/html;charset=utf-8" />
		<meta http-equiv="Content-Style-Type" content="text/css" />

		<meta name="Author" content="Ивайло Божурин" />
		<meta name="copyright" content="Copyright 2012" /> 		

		<title>By Bozhurin > Управление</title>
	</head>
	<body>
		<div style="float: left; margin: 10px;">
		<div style="width: 360px; border-radius: 6px; padding: 0px 6px 6px 6px;
		            text-align: center; font-size: 16px; background: #92AECA; color: #292995;">

			<form action="manage" method="post">

				<h1>Създай нова връзка</h1>
				<input type="text" name="url" size="32" value="http://"
				       style="background: #9EC1E2; border-radius: 2px; border: 1px solid #5C88B2;" />
				<br />

				<h2>Пряк път по желание</h2>
				http://<strong>by.bozhur.in/</strong>
				<input type="text" name="link" size="5"
				       style="background: #9EC1E2; border-radius: 2px; border: 1px solid #5C88B2;" />
				<br /><br />

				<input type="hidden" name="act" value="add" />
				<input type="submit" value="Добави" />

			</form>
		</div>

		<div style="width: 360px; border-radius: 6px; padding: 0px 6px 6px 6px; 

		            text-align: center; font-size: 16px; background: #92AECA; color: #292995;">

			<form action="manage" method="post">

				<h1>Разузнай съществуваща</h1>

				http://<strong>by.bozhur.in/</strong>
				<input type="text" name="link" size="5" style="background: #9EC1E2;
				             border-radius: 2px; border: 1px solid #5C88B2;" />

				<input type="hidden" name="act" value="spy" />
				<input type="submit" value="Търси" />

			</form>
		</div>
		</div>

		<div style="float: left; margin: 10px;">
		<div style="width: 360px; border-radius: 6px; padding: 0px 6px 6px 6px;
		            text-align: center; font-size: 16px; background: #92AECA; color: #292995;">

			<h1>Списък адреси</h1>

			<ul style="text-align: left">

		<?
		  // Извеждаме последните трийсет създадени адреса
		   $list = $linker->GetLinks(30);

			foreach($list as $l) {
				if($l["id"] != "")
					echo '<li><a href="'.$l["url"].'">'.str_replace("http://", "", $l["url"]).
					     '</a> (<a href="http://by.bozhur.in/'.$l["link"].'">'
					     .$l["link"].'</a>) '.
						 '<a href="http://by.bozhur.in/manage&amp;del='.$l["link"].
						 '"><img src="http://media.tsarstva.bg/icons/delete.png" '.
						 'style="vertical-align: middle" title="Изтрий" alt="Изтрий" border="0" /></a></li>';
			}
		?>

			</ul>
		</div>

		<? // Ако потребителят е поискал справка за адрес...
			if(isset($data)) {
		?>

		<div style="width: 360px; border-radius: 6px; padding: 0px 6px 6px 6px;
		           text-align: center; font-size: 16px; background: #92AECA; color: #292995;">

			<h1>Данни за адрес</h1>

		<? if($error) echo $error;
			else echo '<a href="'.$data["url"].'">'.str_replace("http://", "", $data["url"]).
			          '</a> (<a href="http://by.bozhur.in/'.$data["link"].'">'.$data["link"].'</a>)<br />';
		?>

		<? } ?>

		</div>
	</body>
</html>

CSS стиловете в горния код могат да са малко объркващи при четене, но пък страницата ни няма да изглежда съвсем ужасно. Ето я и нея:

Външен вид на страницата за управление

Вече сме на финалната права. Ще завършим системата с направата на index.php.

index.php

<?php
	if($_GET["go"] != "") {
		if($_GET["go"] == "manage") {
			include_once 'manage.php';
			exit;
		}
		include_once 'link.php';
		$l = $linker->Get($_GET["go"]);

		if($l["id"] != "") {
			$linker->AddStats($l["id"], $_SERVER['REMOTE_ADDR'], $_SERVER['HTTP_REFERER'], $_SERVER['HTTP_USER_AGENT']);
			header("Location: ".$l["url"]."");
		}
		else {
			header("Location: http://tsarstva.bg/sh/372/url-shortener/#несъществуващ");
		}
	}
	else header("Location: http://tsarstva.bg/sh/372/url-shortener/");
?>

Ако я оставим в този вид, системата би работила с адреси от типа на by.bozhur.in/?go=blog. Ние обаче целим възможно най-кратки връзки, затова ни остава едно последно действие – да си направим похдодящ .htaccess файл, който да да взима всичко след последната наклонена черта и да го пренаписва по подходящ за нас начин.

Както можете да видите от индексния файл, нямаме намерение да осигуряваме директен достъп до manage.php. Напротив, дори ще го направим невъзможен. Вместо това ще го включваме, подавайки „manage“ като входна стойност на go (by.bozhur.in/manage).

.htaccess

Options +FollowSymLinks
RewriteEngine On

RewriteCond %{REQUEST_URI} !index
RewriteRule (.*) index.php?go=$1

 Това е всичко!

Разбира се, в този вид има още много неща, които бихме могли да подобрим. Най-малкото, към момента никъде не правим статистика. Но това вече остава за домашно. Важното е, че имаме една скромна, но все пак ефективна система, която да ни отърве от дългите хипервръзки и да улесни нашето онлайн общуване.

Join the Conversation

3 Comments

  1. Здравейте,искам да попитам защо като всичко съм качил и променил не ми действа сайта за скъсяване на url, къде може да бъркам,и ако е възможно да ми помогнете благодаря предварително…..

  2. Здравей, Краси,

    Извинявай, че ти отговарям със закъснение, но чак сега видях коментара ти.

    Можеш ли да ми опишеш какво се случва? Виждаш ли някаква грешка?

Leave a comment

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.