Cała magia skracaczy zawarta jest w nagłówkach HTTP jakie otrzymuje przeglądarka od serwera WWW. Za przekierowanie najczęściej odpowiadają dwa kody HTTP, 301 oraz 302. Kod 301 (Moved Permanently) jest używany wtedy, gdy przekierowanie jest permanentne, czyli jest zrobione na stałe, natomiast kod 302 (Found), jest przekierowaniem tymczasowym i kolejne żądanie pobrania tej strony powinno odbywać się przy użyciu oryginalnego adresu. Kody HTTP są niezbędne w komunikacji, to dzięki nim przeglądarka wie, czy otrzymała stronę z danymi poprawnie (200), czy dana strona nie istnieje (404), lub czy np. wymagana jest autoryzacja (401) za pomocą loginu/hasła, więcej o kodach odpowiedzi HTTP przeczytasz tutaj.
Żeby dokładnie zrozumieć zasadę działania, spróbujmy zbudować własny skracacz... na początek prosta baza danych (MySQL):
mysql> create database DB_URL;
mysql> use DB_URL;
mysql> create table TB_SHORTURL ( id int not null auto_increment primary key, url text ) engine=innodb;
mysql> grant all privileges on DB_URL.* to 'short'@'localhost' identified by 'password';
Mając bazę, teraz pobawmy się trochę kodem PHP:
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
<?php $url = $_GET['url']; $shorturl = $_GET['u']; mysql_connect('localhost', 'short', 'password') || die('Cannot connect to datbase server'); mysql_select_db('DB_URL'); if ( !isset($shorturl) ) { ?> <head> <title>Skracam URLe</title> </head> <body> <h3>Podaj URL</h3> <form method="get" action="/"> http://<input type="text" name="url"> <input type="submit" value="skroc"> </form> <?php if ( isset($url) ) { echo "http://$url<br />"; mysql_query("START TRANSACTION"); mysql_query("INSERT INTO TB_SHORTURL values('','$url')"); $q = mysql_query("SELECT max(id) FROM TB_SHORTURL"); $r = mysql_fetch_row($q); mysql_query("COMMIT"); echo "http://shorturl/?u=$r[0]<br />"; } ?> </body> </html> <?php } else { if ( isset($shorturl) ) { $q = mysql_query("SELECT url FROM TB_SHORTURL WHERE id='$shorturl'"); $r = mysql_fetch_row($q); header("Location: http://$r[0]"); } } ?> |
Zaznaczam od razu, że skrypt nie ma żadnej walidacji danych wejściowych/wyjściowych jest tylko PoC'em, za wyrządzone krzywdy bazie danych, serwerowi nie biorę odpowiedzialności ;-) dodatkowo taki skrypt powinien dbać o to, by nie było duplikatów. Ten zdecydowanie tego nie robi ;-)
Co robi ten skrypt?
Skrypt wykonuje połączenie do bazy danych DB_URL, w przypadku kiedy w GET pojawi się zmienna ?url= skrypt przyjmuje wartość (pełny URL) i wstawia ją do bazy, uzyskując w odpowiedzi ID pod którym znalazł się w bazie. Dzięki temu jesteśmy w stanie powiązać nasz krótki url (1, 2, 3, itd...) z całym URLem. W przypadku kiedy w GET pojawi się ?u=, skrypt pobierze ID, sprawdzi w bazie jaki URL się znajduje pod tym wpisem, przekaże pełny URL do PHP który wyśle odpowiedni nagłówek HTTP:
header('Location: http://www.domena.pl');
funkcja PHP header(), wysyła żądany przez nas nagłówek HTTP, w tym przypadku Location, co w praktyce oznacza, wysłanie kodu 302 do przeglądarki, zmuszając ją do przekierowania na adres www.domena.pl:
jamzed@makufka:~$ telnet shorturl 80
Trying 192.168.1.228...
Connected to katha.
Escape character is '^]'.
GET /?u=6 HTTP/1.0
Host: shorturlHTTP/1.1 302 Found
Date: Sat, 17 Apr 2010 07:30:41 GMT
Server: Apache
Location: http://www.domena.pl
Vary: Accept-Encoding
Content-Length: 0
Connection: close
Content-Type: text/htmlConnection closed by foreign host.
Jedyne o czym należy pamiętać przy zabawach z wysyłaniem nagłówków HTTP, to konieczność wysłania ich przed jakimkolwiek kontentem, czyli header() musi być wywołany jeszcze przed wyświetleniem , ponieważ nagłówki od kontentu są oddzielone pustą linią i jeśli przeglądarka otrzyma nagłówek w sekcji kontentu nie zrobi z tym nic, a PHP zgłosi błąd wyglądający mniej więcej tak:
Warning: Cannot modify header information - headers already sent by (output started at /var/www/test/index.php:15) in /var/www/test/index.php on line 15
That's all. ;-)