Wie man einen Shortlink Service baut.

Das Ziel eines Shortlink-Services sollte es sein kurze Links zu produzieren. Das heißt, dass möglichst wenige Zeichen hinter dem Slash ("/") sein sollten, aber trotzdem eine Verknüpfung zu der Ursprungs-URL vorhanden seien muss und sich die Zeichenkombinationen nicht bei verschiedenen Links doppeln dürfen. Das klingt schwerer als es ist, wie mir dank Jonathan Snook bewusst wurde. Jonathan stellt in seinem Artikel zwei PHP-Fetzen vor, welche Zahlen zu möglichst kurzen Zeichenketten kodieren und von diesen wieder zurück in die Ursprungszahl enkodieren können.

Die Ursprungszahl ist in unserem Beispiel eine ID, die zusammen mit ihrer kombinierten Variante und der URL in eine Datenbank gespeichert werden. Das heißt unsere Datenbankstruktur sieht so aus, dass jede Zeile eine ID ("id"), die kodierte Version der ID ("converted"), die URL ("url") und, damit wir die Links auch auswerten können, eine klickezählende Zahl ("stats") beinhaltet.

CREATE TABLE IF NOT EXISTS `shortlink` (

  `id` int(100) NOT NULL AUTO_INCREMENT,

  `url` text NOT NULL,

  `converted` varchar(100) NOT NULL,

  `stats` int(25) NOT NULL DEFAULT '0',

  PRIMARY KEY (`id`)

) ENGINE=MyISAM  DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;

Dank der PHP-Funktionen, die aus den Code-Fetzen von Jonathan entstanden sind, ist es nun möglich die IDs zu kodieren. Da hier 62 mögliche, verschiedene Zeichen ("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") kombiniert werden können (ACHTUNG: MATHE) gibt es für ein Zeichen hinter dem Slash 62 (62^1), für zwei Zeichen hinter dem Slash 3.844 (62^2) und für drei Zeichen hinter dem Slash 238.328 (62^3) verschiedene, mögliche Kombinationen, etc.

Der Code

Die En- und Dekodier-Funktionen (und eine Link-Überprüfung) - geschrieben in PHP - sehen bei mir so aus:

function is_url($url) {

	if(!preg_match("#^http(s)?://[a-z0-9-_.]+\.[a-z]{2,4}#i", $url)) {

    	return false;

    } else {

    	return true;

    }

}

function url_encrypt($id) {
$codeset = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
$base = strlen($codeset);
$converted = "";

while ($id > 0) {
$converted = substr($codeset, ($id % $base), 1) . $converted;
$id = floor($id/$base);
}

return $converted;
}

function url_decrypt($converted) {
$codeset = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
$base = strlen($codeset);
$c = 0;

for ($i = strlen($converted); $i; $i--) {
$c += strpos($codeset, substr($converted, (-1 * ( $i - strlen($converted) )),1))
* pow($base,$i-1);
}

return $c;
}

Aber die Funktionen alleine reichen natürlich noch nicht aus - die Links müssen ja auch noch generiert, gespeichert und verarbeitet werden. Dafür brauchen wir erstmal Datenbank-Zugriff. Dafür benutze ich in diesem Beispiel meine PHP-Datenbankklasse (database.class.php), die ihr euch hier abspeichern könnt.

Die Initialisierung der Datenbank und ein paar Grundeinstellungen erfolgen dann wie folgt:

require_once('database.class.php');

$db = new database('hostname', 'benutzername', 'passwort', 'datenbankname');

$pass = 'passwort';   // Irgendein Passwort, dass ihr später zum Speichern und Löschen von Links braucht

$base_url = 'http://stadtpir.at/';    // Der Slash am Ende ist wichtig!

Der nächste Teil ermöglicht das Speichern und Löschen von Links, sowie das Ansehen der Statistiken für den Link und die Automatische Weiterleitung. Ist alles im Code nochmal gesondert gekennzeichnet.

if($_GET['do'] != '' && isset($_GET['url']) && $_GET['pass'] == $pass) {

	$url = rawurldecode($_GET['url']);

if($_GET['do'] == 'remove') { // Löschen von gespeicherten URLs
if($db->delete('urls', '`url`=\''.$url.'\'')) {
$note = 'true';
} else {
$note = 'false';
}
} elseif($_GET['do'] == 'stats') { // Statistiken ansehen
$note = (($db->get_element('urls', 'stats', '`url`=\''.$url.'\'')) ? $db->get_element('urls', 'stats', '`url`=\''.$url.'\'') : '0');
} elseif($_GET['do'] == 'add') { // Link hinzufügen
if(!$db->get_element('urls', 'converted', '`url`=\''.$url.'\'')) {
$id = $db->get_next_id('urls');

if($db->insert('urls', array('id' => $id, 'url' => $url, 'converted' => $converted, 'stats' => 0))) {
$note = $base_url.url_encrypt($id);
} else {
$note = 'false';
}
} else {
$note = $base_url.$db->get_element('urls', 'converted', '`url`=\''.$url.'\'');
}
}
} elseif(isset($_GET['converted'])) { // Weiterleiten
$id = url_decrypt($_GET['converted']);
$url = $db->get_element('urls', 'url', '`id`=\''.$id.'\''); // URL abrufen
if($url) {
$db->update('urls', array('stats' => ((int)$db->get_element('urls', 'stats', '`id`=\''.$id.'\'') + 1)), '`id`=\''.$id.'\''); // Statistiken aktualisieren
header('HTTP/1.1 301 Moved Permanently'); // Weiterleiten
header('Location: '.$url);
exit;
} else {
$note = 'false';
}
}

if($note != '') {
echo $note;
}

Erklärung

Jetzt das wichtigste: Wie benutze ich das Ganze? Ganz einfach:

URL benutzen:
http://deineurl.ly/zeichenkette-die-beim-hinzufügen-angezeigt-wird

URL hinzufügen:
http://deineurl.ly?do=add&pass=deinpasswort&url=deineurl

URL entfernen:
http://deineurl.ly?do=remove&pass=deinpasswort&url=deineurl

Statistiken abrufen:
http://deineurl.ly?do=stats&pass=deinpasswort&url=deineurl

---

Der komplette Code nochmal hier. Für Fragen steht euch wie immer der Kommentarbereich offen.

---

UPDATE: Johannes hat mich gerade richtig darauf hingewiesen, dass das dekodieren im Endeffekt unnötig ist, da die enkodierte Version ja auch in der Datenbank gespeichert ist.