U jednom od prethodnih članaka pisao sam o najčešćim problemima s php-om. Jedan od njih je bio problem s url_fopenom koji je iz sigurnosnih razloga ugašen na našim serverima. Zbog sve češćih upita odlučio sam napraviti ovaj članak koji će se osvrnuti i na drugu stranu problematike url_fopen.
Zašto curl, a ne url_fopen?
Jedan od osnovnih razloga zašto curl, a ne url_forpen je sigurnost. Fopenom dozvoljavate da se u vaš site includa remote maliciozni php kod i time riskirate defaceanje stranice.
Osim samih sigurnosnih razloga povlači se i pitanje performansi. U nastavku možete pronaći opis problematike kao i skriptu za rješenje ovog problema.
Prvi problem je nepotrebni promet i sporo učitavanje vašeg sitea.
Vrlo često velik broj webmastera međusobno razmjenjuju sadržaje ili rss feedovima ili direktnim parsanjem sadržaja. U 90% slučajeva se koristi fopen za pristupanje remote contentu. Najčešće takva razmjena sadržaja nije prethodno dogovorena nego se uzima zdravo za gotovo.
Koristeći fopen prilikom svakog učitavanja vašeg sitea, za svakog posjetitelja server mora napraviti request na remote server. Pritom se oslanja na brzinu veze, dns lookupe, zagušenje veza između dva servera i na koncu samu brzinu i opterećenje remote servera. Naravno bilo kakvo usporenje ili prekid funkcionalnosti na bilo kojem od ovih segmenta, se direktno osjeti na vašem siteu jer on s fopenom ovisi o remote site-u. U najgorim slučajevima zbog prekida rada remote sitea i vaš može prestat radit.
Čak i u slučajevima kad sve funkcionira normalno, stvara se overhead od barem 1-2 sekunde po posjetitelju. Naravno i promet se nepotrebno povećava, a to osjete i webmasteri remote sitea koji mogu s vremenom odlučiti zabranit pristup serveru na kojem se nalazi vaš site.
Elegantno rješenje ovog problema je povremeno downloadanje remote sadržaja lokalno, te posluživanje lokalno spremljenog file-a. Ovo se može izvest ili programiranjem php skripte ili kroz cron jobove u cpanelu na linux hosting paketima.
Drugi problem je ovisnost sitea o remote sadržaju
Drugi problem je relativno usko vezan uz prvi, a radi se o ovisnosti vašeg sitea o remote sadržajima.
Url_Fopen nažalost nema mogućnost provjere dostupnosti sadržaja. Ako je sadržaj na remote serveru uklonjen fopen će i dalje uredno downloadati 404 (file not found) page. Curl s druge strane ima mogućnost provjere http headera i filtriranja downloada po istima.
Da skratim priču, pripremili smo dio koda koji bi zamijenio dosadašnje fopen funkcije. Kod vam omogućava jednostavno downloadanje remote sadržaja i periodičke update tog sadržaja ako je lokalni sadržaj stariji od definiranog vremena.
Isto tako skripta provjerava je li remote sadržaj uistinu dostupan. Ako nije nastavlja se koristiti zadnji validni lokalni sadržaj.
Znači dosadašnji kod:
$handle = fopen(“http://www.example.com/”, “r”);
je potrebno zamijeniti sa:
// variables to set
$remoteurl = "http://plus.hr"; //Url koji zelite downloadati
$chtime = "2"; //lokalni sadrzaj nece biti stariji od koliko sati?
$timeout = "10"; //Koliko se dugo u sekundama ceka remote server?
//ne editirati $localfile = preg_replace("/[^A-Za-z0-9_\.]/", "_", $remoteurl); if (file_exists($localfile)){ $localfile_stat = stat($localfile); if ($localfile_stat['mtime'] < strtotime("-$chtime hours")){ $chresponse = curl_init($remoteurl); $ret = curl_setopt($chresponse, CURLOPT_HEADER, 1); $ret = curl_setopt($chresponse, CURLOPT_FOLLOWLOCATION,1); $ret = curl_setopt($chresponse, CURLOPT_TIMEOUT,$timeout); $ret = curl_setopt($chresponse, CURLOPT_RETURNTRANSFER, 1); $ret = curl_exec($chresponse); if (empty($ret)) { die(curl_error($chresponse)); curl_close($chresponse); } else { $info = curl_getinfo($chresponse); curl_close($chresponse); if ($info['http_code'] == "200") { $ch = curl_init($remoteurl); $fp = fopen($localfile, "w"); curl_setopt($ch, CURLOPT_FILE, $fp); curl_setopt($ch, CURLOPT_HEADER, 0); curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1); curl_exec($ch); curl_close($ch); fclose($fp); }else{ touch($localfile); } } } }else{ $ch = curl_init($remoteurl); $fp = fopen($localfile, "w"); curl_setopt($ch, CURLOPT_FILE, $fp); curl_setopt($ch, CURLOPT_HEADER, 0); curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1); curl_exec($ch); curl_close($ch); fclose($fp); } $handle = fopen($localfile, "r");
Za intenzivnije uporabe kod se može vrlo jednostavno konvertirati u funkciju koja bi se nalazila u nekom global include fileu, pa bi se umjesto fopena pozivala ta funkcija.