Muke po php-u

Php je odličan programski jezik za kreiranje dinamičkih web stranica, relativno je jednostavan, poprilično raširen i većina novih webmastera se zbog dobre podrške odluči za programiranje u php-u.

Prvi i osnovni korak svakog php web developera je instalirati na svoje vlastito računalo lokalni web server i podršku za php. Sam postupak je nadasve jednostavan, a na webu se lako mogu pronaći “all in one” paketi instalacije Apache/php/Mysql-a. Nakon lokalne instalacije slijedi razvoj željene aplikacije te nakon toga deployment na server.

Dobar dio novih (i starih) php developera se nađe ugodno iznenađen nakon što otkriju da im njihova aplikacija ne radi na shared hostingu.

U nastavku pročitajte kako riješiti nekoliko najčešćih problema uzrokovanih razlikama u lokalnoj i serverskoj instalaciji te postavkama php-a.


Prije samog rješavanja problema bitno je razumjeti zašto do problema uopće dolazi. Bitno je razlikovati namjenu lokalne i serverske instalacije poslužiteljskih servisa poput web servera, database servera i programske podrške kao što je php.

U oko 90% autoinstalera za lokalne potrebe (poput Wamp ili xampp paketa) imaju u sebi uključene defaultne postavke i uglavnom su uključeni svi mogući dodatni moduli i extenzije.

Ovakva filozofija je sasvim pogodna za lokalno testing okruženje gdje stranice nisu dostupne na internetu i podložne malicioznom utjecaju.

Na serveru s druge strane treba pripaziti na sigurnost web stranica i ostalih korisnika servera, te potrošnje resursa. Sukladno tome instalacija php-a na serveru neće imati u sebi uključene sve dostupne php module/extenzije niti će imati podešene defaultne konfiguracijske parametre.

U zadnjih nekoliko godina najčešći problemi na koje korisnici shared hostinga nailaze su problemi s postavkama register_globalsa, url_fopena i max_memory limita.

Kako prepoznati problem?

url_fopen

“Problem” se manifestira kao greška na stranici koja ukazuje na nemogućnost otvaranja remote filea.Npr. vremenske prognoze s prognoza.hr ili tečajne liste s hnb.hr…

Problem može nastati i pri učitavanju lokalnog filea ukoliko se poziva preko url-a.

Primjer greške:

Warning: fopen() [function.fopen]: URL file-access is disabled in the server configuration in /home/[USERNAME]/public_html/[IMEFILEA].php on line [LINIJA]

Warning: fopen(http://[REMOTEURL]) [function.fopen]: failed to open stream: no suitable wrapper could be found in /home/[USERNAME]/public_html/[IMEFILEA].php on line [LINIJA]

Max_memory

“Problem” se manifestira u obliku greški na stranici koje ukazuju na nemogućnost alociranja dodatne memorije

Problem nastaje uslijed neoptimiziranog koda ili unosa (uploada slike ili nekog drugog filea) kojeg php mora obraditi.

Primjer greške:

Fatal error: Allowed memory size of 64777216 bytes exhausted (tried to allocate 16 bytes) [...] in line 25.

register_globals

“Problem” se ne manifestira u vidu specifične greške na stranici nego više kao neispravnost funkcioniranja stranice, npr:

– ukoliko sijedite linkove po stranici, url-ovi se uredno mjenjaju u npr. index.php?a=o_nama i index.php?a=kontakt no sadržaj se ne otvara
– ako na stranici imate kontakt obrazac koji vam na mail dolazi prazan
– ne radi vam kako treba login ili postanje sadržaja unutar administracije custom cms-a

Kako riješiti problem?

Krenimo od najkompliciranijeg.

max_memory problematika i rješenje:

Generalno ne postoji neko step by step riješenje. Vrlo često je potrebno dobro proanalizirati kod i logiku koda oko linije gdje je greška prijavljena.

Zašto se problem javlja?

Većinom slučajeva skripte “ulete” u loop te nepotrebno zapisuju vrijednosti u varijable pritom konzumirajući velike količine cpu time-a i memorije. U ovom slučaju treba pronaći loop i razlog zašto skripta u loop uljeće te napraviti provjere i mehanizme zaštite od ovakvog “ponašanja”.

Petlje nisu jedini uzrok. Dosta često uzročnik može biti i neoptimizirani kod koji npr. izvrši database query, query zatim vrati 600 000 rezultata (rezultat može biti veličine i do par gb podataka, ovisno strukturi tablice i količini podataka), skripta zatim sve rezultate zapiše u array i tek ih onda krene obrađivati. Najčešće ovakav programski kod uredno radi neko vrijeme dok ne naraste količina podataka u samoj bazi. Ovakve situacije se mogu izbjeći limitiranim step by step query-em baze podataka. Npr. query 10-tak unosa, obrada podataka, query sljedečih 10 unosa itd…

Sljedeći najčešći uzrok su funkcije za manipulaciju datoteka bez sigurnosnih provjera. Npr. funkcije za manipulaciju slikama, resize, okretanje, crtanje po slikama…

Sve radi savršeno dok se ne uploada neoptimizirana slika od nekoliko Mb ili dok skripta ne mora odjednom izvršiti kompleksne manipulacije na nekoliko slika odjednom. Ukoliko posjedujete photoshop pokušajte na lokalnom računalu otvoriti 10-tak slika od po 5Mb te simultano vršiti resizeanje, rotiranje, dodavanje filtera itd… primjetit ćete kako su radnje poprilično gladne memorije. Znači skripte bi trebalo optimizirati za step by step radnje, s provjerom veličine input filea, pražnjenjem nepotrebnih varijabli i što je više moguće koristiti cache, a ne on-the-fyl generirati 30-tak thumbnailova po stranici.

Url_fopen problem i rješenje:

Url_fopen je zapravo postavka ponašanja fopen() funkcije php-a u ophođenju s remote file-ovima. U defaultnim lokalnim instalacijama url_fopen je dozvoljen, dok je na serverskim konfiguracijama zbog sigurnosnih razloga zabranjen.

Razlog je cross site skirpting i iskorištavanje propusta u nekim poznatim free cms/forum aplikacijama. Dozvoljenim url_fopenom malicioznom korisniku su otvorena vrata da pomoću jednog ili više poznatih sigurnosnih propusta na određenim php aplikacijama includa svoj vlastiti konfiguracijski file te pridobije kompletnu kontrolu nad hosting accountom.

Neka vas ne tješi to što imate svoju vlastiti custom aplikaciju, maliciozni korisnici će uz malo truda prije ili kasnije reverse engineeringom napipati ranjivost vaše aplikacije.

Kao što sam već naveo u prepoznavanju problema, problematiku url_fopen-a ćete najčešće primijetiti prilikom legitimnog pokušaja includanja servisnih informacija poput vremenske prognoze, tečajne liste i sl.

Iako izgleda kao najjednostavnije i najbolje riješenje, url_fopen u ovim slučajevima nije niti najbolje, niti najsigurnije, a niti pristojno rješenje.

Mnogi će se zapitati zašto nije pristojno?

Odličan primjer je tečajna lista koja se mijenja uglavnom jednom dnevno, no za svakog posjetitelja koji dođe na vaš site vaša skripta s fopenom otvara konekciju prema remote serveru, downloada i procesira podatke. Vaš site s vremenom “naraste”, primate po preko 20 000 unique posjeta dnevno i svaki od tih posjetitelja prilikom svakog učitavanja vašeg site-a trigera konekciju prema remote serveru. Naravno da niste jedini s takvom skriptom nego ih ima još barem stotinjak. Možete li si zamisliti koliki nepotrebni promet remote server s kojeg informaciju kupite mora dnevno procesirati?!

Kako onda pametno izvesti prikupljanje informacija?

Idealno bi bilo downloadati servisnu informaciju s remote servera u nekom prihvatljivom intervalu (svakih 6-12h). Na taj način posjedujete svježu informaciju lokalno, a remote serveru generirate samo 2-4 posjeta i uvelike mu olakšavate posao. Ne samo da je remote serveru “lakše” već se i vaša stranica puno brže učitava jer ne mora prilikom svakog učitavanja čekati odaziv s remote servera, već sve podatke ima lokalno.

* U slučaju kvara remote servera, vaša stranica zastaje na mjestu gdje se očekuje include remote file-a dok konekcija ne istekne. Zbog toga se vaša stranica prividno sporo učitava ili se prestane učitavati. Povremenim downloadom informacije s remote servera u slučajevima havarije remote stroja, vaša stranica i dalje radi jednako brzo sa starim servisnim podacima

Kako izvesti povremeni download informacija?

Na linux poslužiteljima postoji odlična komanda wget koju možete postaviti u cronjob da se izvršava svakih par sati te downloada svježe stanje remote filea. Ukoliko se radi o jednom remote file-u onda je sljedeća komanda kao stvorena za vas:

/usr/bin/wget -q [REMOTEURL] -O /home/[USERNAME]/public_html/[LOCALFILE]

Naravno zamjenite polja [REMOTEURL] s željenom remote stranicom, [USERNAME] s vašim cpanel useranameom i [LOCALFILE] s imenom filea u koji želite da se sadržaj spremi. Komandu možete staviti u cronjob (kroz cpanel) s željenim intervalom.

Ukoliko niste sigurni u postupak, slobodno otvorite ticket ili pošaljite mail na support s potrebnim informacijama (remoteurl, vaša domena, interval, lokaciju gdje želite da se file sprema) pa će vam netko iz naše tehničke podrške podesiti cronjob.

Ukoliko vam je od iznimne važnosti da su podaci vrlo svježi, možete koristiti alternativu url_fopena a to je curl() funkcija. Dokumentaciju možete pronaći na php.net stranici, no ukratko radi se o sljedećem:

dosadašnji fopen:

<?
$rezultat
= fopen($url,r);
?>

zamjenite s curl-om:

<?
$ch
= curl_init($url);
curl_setopt($ch, CURLOPT_HEADER, 0);
$rezultat = curl_exec($ch);
curl_close($ch);
?>

Dodatno se jos curl komandama može dodati i zapisivanje rezultata u lokalni file, što je uz dodatne funkcije provjera vremenskih intervala vrlo zgodna simulacija wgeta na windows serverima. Osim toga curl, za razliku od fopena, je u stanju razlučiti response servera te prije samog downloda može razaznati dali je remote file prisutan ili javlja grešku (HTTP codovi 4xx, 5xx) te se uz par logičkih provjera može obustaviti download takvog nepotpunog filea. Primjere upotrebe curl-a možete pronaći na: http://www.php.net/manual/en/ref.curl.php

Problematika register_globalsa i rješenja:

Kao i url_fopen, register globals je postavka koja definira “ponašanje” global varijabli te je u serverskoj konfiguraciji zbog sigurnosnih razloga isključena. Detaljnije o samoj raspravi zašto ne koristiti register globals varijable možete pronaći na službenim stranicama php-a

Kako onda rješiti problem. Prvi i najbolji način je tocno definirati varijablu a ne se oslanjati na globals varijable.

Npr.

url index.php?a=o_nama ili index.php?a=kontakt

<?

switch($a){
case
“o_nama”:
include(
“onama.php”);
break;

case “kontakt”:
include(
“kontakt.php”);
break:
}

?>

Ukoliko primjetite switch funkcija se oslanja na global varijablu, i ovakav kod neće raditi s register_globals-ima isključenim.

Pravilno bi trebalo izgledati ovako:

<?
$a
= $_GET['a'];

switch($a){
case
“o_nama”:
include(
“onama.php”);
break;

case “kontakt”:
include(
“kontakt.php”);
break:
}
?>

Alternativno možete u jedan od config fileova koji se includa po svim ostalim fileovima staviti sljedeći kod koji će emulirati register globalse:

// Emulate register_globals on
if (!ini_get('register_globals')) {
$superglobals = array($_SERVER, $_ENV,
$_FILES, $_COOKIE, $_POST, $_GET);
if (isset(
$_SESSION)) {
array_unshift($superglobals, $_SESSION);
}
foreach (
$superglobals as $superglobal) {
extract($superglobal, EXTR_SKIP);
}
}

Ovim zadnjim rješenjem bih zaključio ovaj malo duži tutorial o rješavanju najčešćih problema s php skriptama na shared hostingu. Ukoliko se ne pronađete u gore navedenim problemima ili rješenjima, toplo preporučam proučavanje dokumentacije na www.php.net support forumima instaliranih skripti, a ako vam ništa ne polazi za rukom slobodno nam se obratite da vam sugeriramo rješenje problema.

Povezani članci

  1. da, u principu curl je ok, ali sam skuzio da njegova php implementacija podosta opterecuje server sa kojeg se izvrsava, tako da i njega sad izbjegavam u sirokom luku i koristim samo za “very light” upite. njegova visestruka uporaba u kratkom vremenskom intervalu “zagusiti” ce server do max. ako je moguce bolje koristiti nesto trece ,,,,,

  2. Definitivno se slazem, najbolje od rijesenja bi bilo koristiti wget funkciju pod linuxom i downloadati jednom (ili vise puta dnevno) file lokalno pa vrsiti manipulacije s lokalnim fileom.

    Na windowsima nazalost wget nije dostupan, no moze se napraviti funkcija koja ce provjeravati timestamp lokalnog filea te nece downloadati novi ukoliko je lokalna verzija mladja od definiranog vremena (simulacija crona i wgeta).

    Znaci sto je vise moguce smanjiti opetovane upite na remote servere.

Odgovori