Dobrodošli

Na Internetu postoji mnogo zastarelih informacija koje nove PHP korisnike navode na pogrešan put, što doprinosi širenju loše prakse i nebezbednog kôda. PHP: Pravi način je lako čitljiva literatura o popularnim PHP standardima kodiranja, sa linkovima ka merodavnim tutorijalima i svim onim što se trenutno smatra za najbolju praksu.

Ne postoji opšte-prihvaćen standard za korišćenje PHP-a. Ovaj sajt ima za cilj da upozna nove PHP programere sa nekim temama koje oni možda neće otkriti sve dok ne bude kasno, kao i da iskusnim profesionalcima da neke nove ideje za stvari sa kojima godinama rade. Ovaj sajt vam neće reći koje alate treba da koristite, već će ponuditi više opcija, pritom objašnjavajući razlike u pristupu i načinu njihove upotrebe.

Ovo je ažuran dokument koji će nastaviti da se unapređuje i dopunjuje novim korisnim informacijama i primerima čim oni budu dostupni.

Prevodi

PHP: Pravi način je preveden na nekoliko jezika:

Book

Najnovija verzija PHP: Pravi način je dostupna i u PDF, EPUB i MOBI formatima. Posetite Leanpub

Kako da doprinesete

Pomozite da ovaj sajt postane najbolji resurs za nove PHP programere! Doprinesite na GitHub-u

Proširite vest!

PHP: Pravi način poseduje web banere koje možete da koristite na vašem sajtu. Pružite nam podršku tako što ćete ukazati PHP programerima gde mogu da pronađu kvalitetne informacije!

Web baneri

Povratak na početak

Početak

Koristite aktuelnu stabilnu verziju (7.1)

Ako počinjete da učite PHP, krenite sa aktuelnom stabilnom verzijom PHP 7.1. PHP 7.1 je dosta nov i dolazi sa dosta sjajnih novina u odnosu na starije 5.x verzije. Sam engine je u velikoj meri prepisan, pa je PHP sada drastično brži od starijih verzija.

Ipak, velika je verovatnoća da ćete se u bliskoj budućnosti najčešće susretati sa PHP 5.x, a najnovija 5.x verzija je 5.6. To je sasvim u redu, ali bi trebalo da pokušate da se upgrade-ujete na najnoviju verziju, jer bezbednosne ispravke za PHP 5.6 će se objavljivati do 2018. godine. Nadogradnja je poprilično jednostavna, jer nema toliko puno nekomatibilnosti.

Ugrađeni web server

Od verzije 5.4, mozete početi sa učenjem PHP-a bez instaliranja i podešavanja zasebnog web servera. Za pokretanje servera, u direktorijumu vašeg projekta, izvršite sledeću komandu u terminalu:

> php -S localhost:8000

Instalacija za Mac

OS X dolazi sa preinstaliranom PHP verzijom koja je obično malo starija u odnosu na poslednju stabilnu verziju. Mountain Lion dolazi sa verzijom 5.3.10, Mavericks sa 5.4.17, Yosemite sa 5.5.9, ali to nije dovoljno dobro pošto je dostupna novija verzija PHP 5.6.

Postoji nekoliko načina za instalaciju PHP-a na OS X-u.

Instalacija PHP-a preko Homebrew-a

Homebrew je moćan menadžer paketa za OS X, pomoću kojeg vrlo lako možete da instalirate PHP i njegove ekstenzije. Homebrew PHP je repozitorijum koji sadrži različite PHP “formule” za Homebrew, putem kojeg možete instalirati sâm PHP.

Trenutno, moguće je instalirati verzije php53, php54, php55 ili php56 pomoću brew install komande, a zatim ih menjati izmenom PATH varijable. Alternativa ovome je korišćenje brew-php-switcher alata, koji omogućava automatsku promenu verzije.

Instalacija PHP-a preko Macports-a

MacPorts projekat je open source inicijativa za dizajniranje jednostavnog sistema za kompajliranje, instalaciju i ažuriranje bilo command-line, X11 ili Aqua baziranih open source programa na OS X operativnom sistemu.

MacPorts podržava pre-kompajlirane fajlove, tako da ne morate ponovo da kompajlirate svaki dependency iz izvornih tarball fajlova, i olakšava vam život ako nemate nijedan instaliran paket na vašem sistemu.

Trenutno, moguće je instalirati verzije php53, php54, php55 ili php56 pomoću port install komande, na primer:

sudo port install php54
sudo port install php55

Nakon toga možete izvršavati select komandu za promenu aktivne verzije PHP-a:

sudo port select --set php php55

Instalacija PHP-a preko phpbrew-a

phpbrew je alat za instalaciju i upravljanje sa više verzija PHP-a. Ovo može biti veoma korisno ako dve različite aplikacije zahtevaju različite verzije PHP-a, pritom ne koristite virtuelne mašine.

Instalacija PHP-a putem Liip’s instalera

Još jedna popularna opcija je php-osx.liip.ch koje omogućava jednostavnu instalaciju verzija 5.3 do 5.6. On ne prepisuje PHP fajlove instalirane od strane Apple-a, već instalaciju vrši na zasebnoj lokaciji (/usr/local/php5).

Ručno kompajliranje source-a

Još jedna opcija koja vam daje kontrolu nad verzijom PHP-a koju instalirate je ručno kompajliranje. U tom slučaju vodite računa da instalirate bilo Xcode ili Apple-ov “Command Line Tools za XCode” dostupnu na Apple-ovom Mac Developer centru.

“Sve u jednom” instaleri

Prethodna rešenja će vam obezbediti sâm PHP, ali ne i stvari kao što su Apache, Nginx ili SQL server. “Sve u jednom” rešenja kao što su MAMP ili XAMPP će instalirati i sve te druge programe, ali ta jednostavnost instalacije za kompromis ima manjak fleksibilnosti.

Instalacija za Windows

Binarnu verziju možete preuzeti na windows.php.net/download. Nakon što raspakujete PHP, preporučuje se da podesite PATH na vaš PHP folder (lokacija php.exe fajla) kako biste mogli da izvršavate PHP sa bilo kog mesta.

Za učenje i razvoj u lokalu, možete koristiti ugrađeni web server sa PHP 5.4+ tako da se ne morate brinuti o njegovoj konfiguraciji. Ako preferirate “sve u jednom” rešenja koja sadrže kompletan web server sa sve MySQL bazom podataka, onda će vam alati kao što su Web Platform Installer, XAMPP, EasyPHP i WAMP pomoći da veoma brzo imate radno okruženje pod Windows-om. Treba napomenuti da će se okruženje koje ovi alati naprave verovatno razlikovati od onoga u produkciji, tako da morate obratiti pažnju na te razlike ako razvijate na Windows-u, a produkcijsko okruženje vam je na Linux-u.

Ako je potrebno da vam produkcijsko okruženje bude na Windows-u, onda će IIS7 pružiti najveću stabilnost i najbolje performanse. Možete koristiti phpmanager (GUI dodatak za IIS7) za pojednostavljenje konfiguracije i upravljanja PHP-om. IIS7 dolazi sa ugrađenim FastCGI-om, spremnim za upotrebu, i sve što je potrebno je konfigurisati PHP kao handler. Za podršku i dodatne informacije pogledajte deo posvećen PHP-u na iis.net.

Povratak na početak

Vodič za stil kodiranja

PHP zajednica je velika i raznolika, sastavljena od bezbroj biblioteka, frejmvorka i komponenti. PHP programeri se često odluče za njih nekoliko i kombinuju ih u okviru jednog projekta. Bitno je da PHP kôd bude u skladu sa (što je više moguće) opštim stilom kodiranja kako bi se programerima olakšalo kombinovanje i integrisanje različitih biblioteka u svoje projekte.

Framework Interop grupa je predložila i odobrila niz preporuka za stilove kodiranja. Pritom nisu sve vezane za stil pisanja kôda, a one koje jesu su sledeće: PSR-0, PSR-1, PSR-2 i PSR-4. Ove preporuke su zapravo setovi pravila koje neki projekti kao što su Drupal, Zend, Symfony, CakePHP, phpBB, AWS SDK, FuelPHP, Lithium, itd. počinju da usvajaju. I vi ih možete primenjivati u vašim ličnim projektima, ili nastaviti sa korišćenjem nekog vašeg ličnog stila.

Bilo bi idealno da pišete PHP kôd koji je u skladu sa poznatim standardima. Ovo može biti bilo koja kombinacija PSR-ova ili jedan od standarda kodiranja koje je definisao PEAR ili Zend. Ovo znači da će drugi programeri moći lako da čitaju i rade sa vašim kôdom, a da će aplikacije koje koriste komponente biti konzistentne čak i pri radu sa dosta 3rd party kôda.

Možete da koristite PHP_CodeSniffer kako biste proverili da li je vaš kôd u skladu sa nekom od ovih preporuka, ali i dodatke za tekst editore kao što je Sublime Text 2 u cilju dobijanja rezultata provera u realnom vremenu.

Automatksu korekciju izgleda kôda možete obaviti korišćenjem jednog od sledeća dva alata. Prvi je PHP Coding Standards Fixer, koji je veoma dobro testiran. Druga opcija je php.tools, koji je postao popularan zahvaljujući sublime-phpfmt dodatku. Iako je noviji, postiže značajne rezultate po pitanju performansi, pa je samim tim korekcija kôda u realnom vremenu dosta tečnija.

phpcs možete pokrenuti ručno iz konzole:

phpcs -sw --standard=PSR2 file.php

Prikaziće greške kao i predloge za njihovo rešavanje. Ova komanda može biti od koristi ako je uključite u neki git hook. Na taj način će grane koje sadrže kôd koji nije u skladu sa određenim standardom neće moći da uđu u repozitorijum dok se ne poprave.

Korišćenje engleskog jezika se preporučuje u slučaju svih imenovanja u kôdu i njegovoj infrastrukturi. Komentari mogu biti pisani u bilo kom jeziku poznatom svim trenutnim i budućim učesnicima u razvoju kôda.

Povratak na početak

Specifičnosti PHP jezika

Programerske paradigme

PHP je fleksibilan, dinamičan jezik koji podržava raznovrsne tehnike programiranja. Poslednjih godina se značajno razvio, naročito nakon uvođenja solidnog objektno-orijentisanog modela u verziji PHP 5.0 (2004), anonimnih (lambda) funkcija i namespace-ova u verziji PHP 5.3 (2009), kao i trait-ova u PHP 5.4 (2012).

Objektno-orijentisano programiranje

PHP ima vrlo upotpunjen set odlika objektno-orijentisanog programiranje, uključujući podršku za klase, apstraktne klase, interfejse, nasleđivanje, konstruktore, kloniranje, izuzetke, itd.

Funkcionalno programiranje

PHP podržava funkcije prve klase (first-class funkcije), što znači da funkcija može biti dodeljena promenljivoj. I korisnički definisane i ugrađene funkcije mogu biti referencirane promenljivom i pozivane dinamički. Funkcije se mogu prosleđivati kao argumenti drugim funkcijama (odlika nazvana Funkcije višeg reda) i funkcija može vratiti druge funkcije.

Rekurzija, osobina koja omogućava funkciji da poziva samu sebe, je podržana u samom jeziku, ali se većina PHP kôda fokusira na iteriranje.

Nove anonimne funkcije (sa podrškom za closures) su prisutne od verzije PHP 5.3 (2009).

PHP 5.4 je dodao mogućnost da se closure poveže sa scope-om objekta, a takođe i poboljšao podršku za callable tipove, tako da se oni praktično u skoro svim slučajevima mogu koristiti na isti način kao anonimne funkcije.

Meta programiranje

PHP podržava razne vidove meta programiranja preko mehanizama kao što su Reflection API i “magičnih” metoda (Magic Methods). Postoji dosta magičnih metoda, kao što su __get(), __set(), __clone(), __toString(), __invoke(), itd. koje omogućavaju programerima da se uključe u ponašanje klase. Ruby programeri često izjavljuju da PHP-u nedostaje method_missing, ali on postoji kao __call() i __callStatic().

Namespace-ovi

Kao što je prethodno navedeno, PHP zajednica se sastoji od mnogo programera koji stvaraju gomilu kôda. To znači da kôd jedne biblioteke može da koristi isto ime klase kao neka druga biblioteka. Kada se obe biblioteke koriste u istom namespace-u, dolazi do kolizije i problema.

Namespace-ovi rešavaju ovaj problem. Kao što je i opisano u PHP manual-u, namespace-ovi se mogu uporediti sa direktorijumima operativnog sistema koji fajlove dele na imenski prostor; dva istoimena fajla mogu postojati istovremeno u različitim direktorijumima. Isto tako, dve PHP klase istog imena mogu postojati u različitim PHP namespace-ovima. Jako jednostavno.

Veoma je važno je da koristite namespace-ove u vašem kôdu kako bi drugi programeri mogli da ga koriste bez straha od kolizije sa drugim bibliotekama.

Jedan od preporučenih načina za korišćenje namespace-ove opisan je u okviru PSR-4, koji ima za cilj da pruži standardizovanu konvenciju za fajlove, klase i namespace-ove, kako bi se omogućilo jednostavnije uključivanje kôda.

U oktobru 2014. godine, PHP-FIG je prethodni autoloading standard: PSR-0, označio kao zastareo/prevaziđen (deprecated), i zamenio ga je upravo PSR-4. Trenutno su oba standarda u upotrebi, pri čemu PSR-4 zahteva PHP 5.3, dok mnogi PHP 5.2 projekti koriste PSR-0. Ako nameravate da koristite autoloading standard za neku novu aplikaciju ili paket, onda definitivno treba da uzmete u obzir PSR-4.

Standardna PHP biblioteka (SPL)

Standardna PHP biblioteka (Standard PHP Library - SPL) dolazi u paketu sa PHP-om i na raspolaganje stavlja kolekciju klasa i interfejsa. Sastoji se pretežno od nekih najčešće korišćenih data-structure klasa (stack, queue, heap, itd.), kao i iteratora koji mogu da prolaze kroz te strukture podataka ili neke vaše klase koje implementiraju odgovarajući SPL interfejs.

Interfejs sa komandne linije (Command Line Interface - CLI)

PHP je napravljen prvenstveno radi pisanja web aplikacija, ali je takođe koristan za skriptne programe koji se izvršavaju sa komandne linije. PHP programi sa komandne linije mogu vam pomoći da automatizujete uobičajene zadatke kao što su testiranje, razvoj i administracija aplikacije.

CLI PHP programi su moćni jer možete da koristite kôd vaše aplikacije direktno, bez potrebe da napravite i obezbedite web GUI (grafički interfejs) za nju. Samo nikako nemojte smeštati vaše CLI PHP skripte u javni direktorijum vašeg sajta!

Pokušajte da pokrenete PHP sa komandne linije:

> php -i

Opcija -i će ispisati vašu PHP konfiguraciju na isti način kao i phpinfo funkcija.

Opcija -a omogućava interaktivni shell, sličan Ruby-jevom IRB ili Python-ovom interaktivnom shell-u. Postoji još dosta korisnih CLI opcija.

Hajde da napišemo jednostavan “Hello, $name” CLI program. Najpre napravite fajl sa imenom hello.php, sa sledećim sadržajem:

<?php
if ($argc !== 2) {
    echo "Usage: php hello.php [name].\n";
    exit(1);
}
$name = $argv[1];
echo "Hello, $name\n";

PHP postavlja dve specijalne promenljive na osnovu argumenata koje se prosleđuju pri pokretanju vaše skripte. $argc je integer koji sadrži broj argumenata, a $argv je niz koji sadrži vrednost svakog argumenta. Prvi argument je uvek naziv fajla vaše PHP skripte, a u ovom slučaju to je hello.php.

Komandi exit() se prosleđuje broj različit od nule kako bi se shell-u skrenula pažnja da komanda nije uspela. Najčešće korišćeni izlazni kodovi su dostupni ovde.

Pokretanje prethodne skripte iz komandne linije:

> php hello.php
Usage: php hello.php [name]
> php hello.php world
Hello, world

XDebug

Jedan od najkorisnijih alata u razvoju softvera je odgovarajući debugger. On vam omogućava da pratite izvršenje vašeg kôda i pratite sadržaj stack-a. XDebug, PHP-ov debugger, može se koristiti sa raznim IDE-ima kako bi omogućio postavljanje breakpoint-a i pregled stack-a. On takođe omogućava alatima kao što su PHPUnit i KCacheGrind da izvrše analizu pokrivenosti kôda, kao i profilisanje kôda (code profiling).

Ako pri kodiranju naiđete na problem, kojeg obično ispitujete korišćenjem var_dump()/print_r(), a i dalje ne nalazite rešenje - možda bi trebalo da počnete da koristite debugger.

Instalacija XDebug-a može da bude nezgodna, ali jedna od njegovih najznačajnijih opcija je “daljinski debugging” (Remote Debugging) - ako programirate u lokalu, a zatim testirate pomoću virtuelne mašine ili na drugom serveru, daljinski debugging je opcija koju ćete želeti odmah da uključite.

Obično ćete modifikovati vaš Apache VHost ili .htaccess fajl sa sledećim vrednostima:

php_value xdebug.remote_host=192.168.?.?

php_value xdebug.remote_port=9000

Opcije “remote host” i “remote port” se odnose na vaš lokalni računar i port na kojem vaš IDE konfigurisan da radi. Ostaje vam samo da stavite vaš IDE mod “osluškivanja konekcija” i zatim otvorite URL:

http://your-website.example.com/index.php?XDEBUG_SESSION_START=1

Vaš IDE će sada presretati trenutno stanje tokom izvršavanja skripte, omogućavajući vam da postavljate breakpoint-e i ispitujete vrednosti u memoriji.

Grafički debugger-i omogućavaju veoma jednostavno prolaženje kroz kôd, ispitivanje promenljivih i izvršavanje kôda nad kôdom koji se upravo izvršava (runtime). Mnogi IDE-i imaju ugrađenu podršku ili dodatke za grafičko debagovanje sa Xdebug-om. MacGDBp je besplatan, open source, samostalan Xdebug GUI za Mac.

Povratak na početak

Upravljanje zavisnostima (dependencies)

Na raspolaganju imate ogroman broj PHP biblioteka, frejmvorka i komponenti. Vaš projekat će verovatno koristiti njih nekoliko, a sve te stvari su zapravo zavisnosti projekta. Do nedavno, PHP nije imao adekvatan način za upravljanje ovim zavisnostima. Čak i ako ste njima upravljali ručno, i dalje ste morali da brinete o autoloading-u. Ali to više nije slučaj.

Trenutno postoje dva glavna sistema za upravljanja paketima (package managers) za PHP - Composer i PEAR. Composer je trenutno izbor broj jedan, ali treba pomenuti da je PEAR dosta dugo bio na toj poziciji. Poznavanje PEAR-a može biti korisno, jer i dalje možete naći reference za njega, čak iako ga ne koristite.

Composer i Packagist

Composer je sjajan menadžer zavisnosti za PHP. Navedite zavisnosti vašeg projekta u composer.json fajlu i sa par jednostavnih komandi, Composer će automatski preuzeti zavisnosti i podesiti autoloading umesto vas.

Postoji dosta PHP biblioteka koje su kompatibilne sa Composer-om, spremne za korišćenje u vašem projektu. Ovi “paketi” su dostupni na Packagist-u, zvaničnom repozitorijumu za PHP biblioteke kompatibilne sa Composer-om.

Instalacija Composer-a

Composer možete da instalirate lokalno (u vašem radnom direktorijumu; mada se to više ne preporučuje) ili globalno (npr. u /usr/local/bin). Ako želite da instalirate Composer lokalno, iz root direktorijuma vašeg projekta izvršite:

curl -s https://getcomposer.org/installer | php

Ovo će preuzeti composer.phar (PHP arhivu). Ovaj fajl možete pokretati putem php komande, kako biste upravljali zavisnostima projekta. Napomena: Ako nadovezujete (pipe) neki kôd kojeg preuzimate direktno u interpreter, obavezno prethodno proverite sadržaj kako biste se uverili da je bezbedan.

Instalacija na Windows-u

U slučaju Windows korisnika, najjednostavniji način za instalaciju je putem ComposerSetup instalera, koji radi globalnu instalaciju i podešava vaš $PATH, tako da možete pozivati composer komandu iz bilo kog foldera.

Ručna instalacija Composer-a

Ručna instalacija Composer-a je napredna tehnika. Međutim, postoje razlozi zbog kojih bi se programer radije odlučio za ovu opciju nego za interaktivnu proceduru instalacije. Interaktivna instalacija proverava vašu PHP instalaciju kako bi se obezbedilo sledeće:

S obzirom da ručna instalacija ne vrši nijednu od ovih provera, morate razmisliti da li vam se ovaj kompromis isplati. Imajući to u vidu, evo uputstva kako da ručno dođete do Composer-a:

curl -s https://getcomposer.org/composer.phar -o $HOME/local/bin/composer
chmod +x $HOME/local/bin/composer

Putanja $HOME/local/bin (ili direktorijum po vašem izboru) bi trebalo da se nalazi u vašoj promenljivoj okruženja $PATH. Na taj način će komanda composer postati dostupna.

Kada u dokumentaciji naiđete na objašnjenje u kojem stoji da Composer pokreće preko php composer.phar install, to sada možete zameniti sa:

composer install

Ovo poglavlje podrazumeva da je Composer instaliran globalno.

Kako definisati i instalirati zavisnosti

Composer čuva zavisnosti vašeg projekta u fajlu sa nazivom composer.json. Možete ga održavati ručno ako želite, ili možete koristiti sâm Composer. Komanda composer require dodaje neku zavisnost čak i ako ne postoji composer.json fajl, jer će on u tom slučaju biti napravljen. Sledeći primer dodaje Twig kao dependency vašeg projekta:

composer require twig/twig:~1.8

Nasuprot ovome, komanda composer init će vas voditi kroz pravljenje kompletnog composer.json fajla za vaš projekat. U svakom slučaju, nakon što jednom kada kreirate composer.json, možete dati instrukciju Composer-u da preuzme i instalira vaše zavisnosti u vendor/ direktorijum. Ovo važi i za projekte koje ste preuzeli, a koji već sadrže composer.json fajl:

composer install

Zatim, dodajte sledeću liniju u primarni PHP fajl vaše aplikacije. To će reći PHP-u da koristi Composer-ov autoloader za zavisnosti vašeg projekta:

<?php
require 'vendor/autoload.php';

Sada možete koristiti zavisnosti i one će se po zahtevu automatski učitavati.

Ažuriranje vaših zavisnosti

Composer kreira fajl composer.lock koji čuva tačnu verziju svakog paketa kojeg je preuzeo kada ste prvi put izvršili composer install komandu. Ako na vašem projektu radite sa drugim programerima, a fajl composer.lock je distribuiran (verzionisan), nakon što oni pokrenu composer install, dobiće iste verzije kao i vi. Da biste ažurirali vaše zavisnosti, koristite composer update komandu.

Ovo je korisno u situacijama kada fleksibilno definišete zahteve za verzije. Tako na primer zahtev verzije ~1.8 znači “sve što je novije od verzije 1.8.0, ali manje od 2.0.x-dev”. Takođe možete koristiti i * wildcard kao u slučaju 1.8.*. Sada će composer update komanda ažurirati sve vaše zavisnosti na najnoviju verziju koja odgovara ograničenjima koja ste definisali.

Obaveštenja o novim verzijama

Da biste dobijali obaveštenja o novim verzijama paketa možete se prijaviti na VersionEye web servisu koji može da prati composer.json fajlove na vašim GitHub ili BitBucket nalozima i da vam šalje mejlove sa novim verzijama paketa.

Proveravanje vaših zavisnosti sa aspekta bezbednosti

Security Advisories Checker je web servis i alat koji se izvršava sa komandne linije. Oba načina će pregledati vaš composer.lock fajl i ako je neophodno obavestiti vas da ažurirate neku od vaših zavisnosti.

Upravljanje globalnim zavisnostima sa Composer-om

Composer takođe može da upravlja globalnim zavisnostima. Korišćenje je jednostavno, i sve što treba da uradite jeste da dodate global prefiks na vaše komande. Ako na primer hoćete da instalirate PHPUnit globalno izvršili biste sledeću komandu:

composer global require phpunit/phpunit

Ovo će kreirati ~/.composer folder gde će se nalaziti vaše globalne zavisnosti. Kako biste instalirane pakete imali dostupne sa bilo kog mesta, dodajte ~/.composer/vendor/bin folder u vašu $PATH varijablu.

PEAR

Veteran među menadžerima paketa kojeg mnogi PHP programeri vrlo rado koriste je PEAR. Radi na sličnom principu kao Composer, ali postoje neke bitne razlike.

PEAR zahteva da svaki paket ima određenu strukturu, što znači da autor paketa mora da ga pripremi za korišćenje sa PEAR-om. Korišćenje projekta koji nije pripremljen da radi sa PEAR-om nije moguće.

PEAR instalira pakete globalno, što znači da kada ih jednom instalirate, oni su na raspolaganju svim projektima na tom serveru. Ovo može biti korisno ako se više projekata oslanja na isti paket i istu verziju, ali može dovesti do problema ako se pojave konflikti između dva projekta oko verzije.

Instalacija PEAR-a

Možete instalirati PEAR tako što ćete preuzeti i izvršiti .phar instaler. Dokumentacija za PEAR sadrži detaljne instrukcije za instaliranje za svaki operativni sistem.

Ako koristite Linux, možete baciti pogled i na menadžer paketa vaše distribucije. Tako na primer Debian i Ubuntu sadrže apt php-pear paket.

Instalacija paketa

Ako se paket nalazi na listi PEAR paketa, možete ga instalirati tako što ćete navesti njegov zvaničan naziv:

pear install foo

Ako je paket hostovan na drugom kanalu, potrebno je najpre da “otkrijete” (discover) kanal i da ga zatim navedete prilikom instalacije. Pogledajte dokumentaciju za rad sa kanalima radi više informacija o ovoj temi.

Upravljanje PEAR zavisnostima preko Composer-a

Ako već koristite Composer, a želeli biste da instalirate neki PEAR paket, možete koristiti Composer da obradi vaše PEAR zavisnosti. Primer koji sledi će instalirati pakete sa pear2.php.net:

{
    "repositories": [
        {
            "type": "pear",
            "url": "http://pear2.php.net"
        }
    ],
    "require": {
        "pear-pear2/PEAR2_Text_Markdown": "*",
        "pear-pear2/PEAR2_HTTP_Request": "*"
    }
}

Prva sekcija "repositories" govori Composer-u da treba da “inicijalizuje” (ili “otkrije” u PEAR terminologiji) pear repozitorijum. Zatim će require sekcija da doda prefiks na ime paketa na sledeći način:

pear-channel/Package

Prefiks “pear” je hardkodovan da bi se izbegli konflikti, jer pear kanal na primer može biti isti kao i ime paketa drugog vendora, a onda se kratko ime kanala (ili kompletan URL) može koristiti za referenciranje kanala u kojem se paket nalazi.

Nakon što je neki paket instaliran, on će biti dostupan u vašem vendor direktorijumu i automatski dostupan kroz Composer-ov autoloader:

vendor/pear-pear2.php.net/PEAR2_HTTP_Request/pear2/HTTP/Request.php

Da biste koristili ovaj PEAR paket, jednostavno ga pozovite, na primer:

<?php
$request = new pear2\HTTP\Request();

Povratak na početak

Programerske prakse

Osnove

PHP je obiman jezik koji omogućava programerima svih nivoa znanja da pišu kôd ne samo brzo, već i efikasno. Međutim kako napredujemo u jeziku, zaboravljamo osnove koje smo prvo naučili (ili prevideli) u korist određenih prečica i loših navika. U cilju rešavanja ovog čestog problema, ova sekcija ima za cilj da podseti programere na osnove programiranja u PHP-u.

Datum i vreme

PHP poseduje DateTime klasu za potrebe čitanja, pisanja, poređenja i računanja datuma i vremena. Postoji i dosta drugih funkcija za rad sa datumom/vremenom u PHP-u pored DateTime klase, ali ona pruža dobar objektno-orijentisani interfejs za najčešće slučajeve korišćenja. Ova klasa može da manipuliše i vremenskim zonama, ali to je van okvira ovog kratkog uvoda.

Da biste započeli sa radom sa DateTime klasom, konvertujte string koji sadrži datum i vreme u objekat pomoću createFromFormat() factory metode ili pozovite new \DateTime kako biste dobili trenutni datum/vreme. Koristite format() metodu za konvertovanje DateTime instance u string.

<?php
$raw = '22. 11. 1968';
$start = DateTime::createFromFormat('d. m. Y', $raw);

echo 'Početni datum: ' . $start->format('Y-m-d') . "\n";

Računanje je moguće korišćenjem DateInterval klase. DateTime ima metode kao što su add() and sub() koji prihvataju DateInterval instancu kao argument. Nemojte pisati kôd koji očekuje isti broj sekundi svakog dana, jer će letnje i zimsko računanje vremena i promene vremenske zone oboriti tu pretpostavku. Umesto toga koristite vremenske intervale. Za računanje razlike između datuma koristite metodu diff(). Ona vraća DateInterval instancu, koja u sebi sadrži sve potrebne informacije.

<?php
// kloniraj $start i dodaj 1 mesec i 6 dana
$end = clone $start;
$end->add(new DateInterval('P1M6D'));

$diff = $end->diff($start);
echo 'Razlika: ' . $diff->format('%m mesec, %d dana (ukupno: %a dana)') . "\n";
// Razlika: 1 mesec, 6 dana (ukupno: 37 dana)

Nad DateTime objektima je moguće raditi standardno poređenje:

<?php
if ($start < $end) {
    echo "Početni je pre kranjeg datuma!\n";
}

Poslednji primer demonstrira korišćenje DatePeriod klase. Ona se koristi za iteraciju nad periodičnim događajima. Može da primi dva DateTime objekta, početni i krajni datum, kao i interval po kojem treba da vraća rezultujuće datume.

<?php
// ispisuj svaki četvrtak između $start i $end
$periodInterval = DateInterval::createFromDateString('first thursday');
$periodIterator = new DatePeriod($start, $periodInterval, $end, DatePeriod::EXCLUDE_START_DATE);
foreach ($periodIterator as $date) {
    // ispisuj svaki datum u periodu
    echo $date->format('Y-m-d') . ' ';
}

Dizajn paterni (Design patterns)

Pri razvoju vaše aplikacije korisno je upotrebljavati neke uobičajene paterne (šablone) u kôdu, kao i paterne po pitanju same strukture projekta. Korišćenje i primena paterna je korisna, jer pojednostavljuje upravljanje vašim kôdom i omogućava drugim programerima da brže shvate kako je sve organizovano i uklopljeno.

Ako koristite frejmvork, onda će većina kôda višeg nivoa i struktura projekta biti bazirana na tom frejmvorku, tako da će dosta posla biti urađeno umesto vas. Ali na vama je i dalje da primenjujete paterne u kôdu kojeg ćete pisati nad tim frejmvorkom. S druge strane, ako ne koristite frejmvork, onda je neophodno da se odlučite za paterne koji najviše odgovaraju tipu i veličini aplikacije koju pravite.

Rad sa UTF-8

_Ovu sekciju je inicijalno napisao Alex Cabal na PHP Best Practices sajtu, što je iskorišćeno kao osnova za naše savete na temu UTF-8.

Nema jednostavnog rešenja. Budite pažljivi, temeljni i konzistenti.

Trenutno, PHP u osnovi ne podržava Unicode. Postoje načini da se omogući da se UTF-8 stringovi ispravno procesiraju, ali nisu nimalo jednostavni i zahtevaju zadiranje u praktično sve nivoe web aplikacije, počevši od HTML-a, pa do SQL-a i PHP-a. Pokušaćemo da na sažet i praktičan način predstavimo ovu problematiku.

UTF-8 na nivou PHP-a

Osnovne operacije sa stringovima, kao što su konkatenacija dva stringa ili definisanje string varijabli, ne zahtevaju ništa specijalno po pitanju UTF-8. S druge strane, većina string funkcija, kao što su strpos() i strlen() zahtevaju poseban tretman. Ove funkcije obično imaju pandane prefiksovane sa mb_*, npr. mb_strpos() i mb_strlen() funkcije. One su dostupne zahvaljujući Multibyte String ekstenziji, i dizajnirane su upravo za rad sa Unicode stringovima.

Korišćenje mb_* funkcija je neophodno kada radite nad Unicode stringom. Na primer, ako koristite substr() u slučaju UTF-8 stringa, velika je verovatnoća da će dobiti neke čudne karaktere u rezultatu. Funkcija koju treba da koristite u tom slučaju je odgovarajući multibyte pandan - mb_substr().

Problem je što dosta često zaboravimo da treba da koristimo mb_* funkcije. Dovoljno je da samo jednom napravite taj propust pa da vaš Unicode string bude neupotrebljiv za svako dalje procesiranje.

Ali nemaju sve string funkcije svog mb_* dvojnika. Ako ne postoji odgovarajuća funkcija za ono što vam treba, onda jednostavno nemate sreće.

Funkciju mb_internal_encoding() bi trebalo koristiti na početku svake PHP skripte koju pišete (ili na početku nekog globalnog skripta kojeg posle učitavate), a mb_http_output() odmah nakon nje u slučaju da vaša skripta radi neki ispis. Eksplicitno definisanje enkodinga će vas lišiti dosta problema.

Takođe, dosta PHP funkcija za rad sa stringovima ima opcioni parametar putem kojeg je moguće podesiti enkoding. Preporuka je da uvek eksplicitno prosleđujete UTF-8 kao argument. Tako na primer htmlentities() funkcija ima opciju za enkoding i uvek bi trebalo da kao vrednost tog parametra prosleđujete UTF-8. Pritom, od PHP verzije 5.4.0, UTF-8 će biti podrazumevani enkoding za htmlentities() i htmlspecialchars() funkcije.

Konačno, ako razvijate distribuiranu aplikaciju i ne možete biti sigurni da ćete imati omogućenu mbstring ekstenziju, onda razmislite o korišćenju patchwork/utf8 Composer biblioteke. Ona će koristiti mbstring ako je dostupan, a u suprotnom, koristiće odgovarajuće ne-UTF-8 funkcije.

UTF-8 na nivou baze podataka

Ako vaša PHP aplikacija pristupa MySQL bazi, postoji verovatnoća da će vaši podaci biti sačuvani kao ne-UTF-8 stringovi u bazi, čak i ako se pridržavate saveta iz prethodnog dela teksta.

Kako biste bili sigurni da se stringovi iz PHP-a u MySQL šalju UTF-8 enkodovani, vaša baza i tabele u njoj bi trebale da imaju podešen utf8mb4 set karaktera (character set) i kolaciju (collation), kao i da se utf8mb4 set karaktera koristi za PDO konekciju. Proučite primer kôda koji sledi. Ovo je veoma bitno.

Imajte u vidu da za kompletnu UTF-8 podršku morate da koristite utf8mb4 set karaktera, a ne utf8! Ako vas zanimaju razlozi, pogledajte sekciju “Za dalje čitanje”.

UTF-8 na nivou browser-a

Koristite mb_http_output() funkciju kako biste bili sigurni da vaša PHP skripta ispisuje UTF-8 stringove u browser-u.

Browser bi na osnovu HTTP odgovora trebalo da zna da li je neka stranica UTF-8 enkodovana. Prevaziđen način je bio putem odgovarajućeg charset <meta> taga u <head> sekciji stranice. Ovaj pristup je u potpunosti validan, ali podešavanje seta karaktera (charset) kroz Content-Type HTTP header je zapravo [mnogo brže] (https://developers.google.com/speed/docs/best-practices/rendering#SpecifyCharsetEarly).

<?php
// Saopštimo PHP-u da koristimo UTF-8 stringove u celoj skripti
mb_internal_encoding('UTF-8');

// Saopštimo PHP-u da ispisujemo UTF-8 browser-u
mb_http_output('UTF-8');

// Naš UTF-8 testni string
$string = 'Êl síla erin lû e-govaned vîn.';

// Obrada stringa putem multibyte funkcije
// Primetite da "sečemo" string baš na ne-ASCII karakteru
$string = mb_substr($string, 0, 15);

// Otvaramo konekciju sa bazom kako bismo uneli obrađeni string
// Za više informacija pogledajte PDO primer u ovom dokumentu
// Primetite `charset=utf8mb4` u okviru Data Source Name-a (DSN)
$link = new PDO(
    'mysql:host=hostname;dbname=baza;charset=utf8mb4',
    'username',
    'password',
    array(
        PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
        PDO::ATTR_PERSISTENT => false
    )
);

// Upisujemo obrađeni string kao UTF-8 u bazi
// Vaša baza and tabele su na utf8mb4 character set-u i kolaciji, jel tako?
$handle = $link->prepare('insert into ElvishSentences (Id, Body) values (?, ?)');
$handle->bindValue(1, 1, PDO::PARAM_INT);
$handle->bindValue(2, $string);
$handle->execute();

// Dohvatanje upravo sačuvanog stringa kako bismo proverili da li zaista dobro unet
$handle = $link->prepare('select * from ElvishSentences where Id = ?');
$handle->bindValue(1, 1, PDO::PARAM_INT);
$handle->execute();

// Upisujemo rezultat u objekat kojeg ćemo posle ispisati
$result = $handle->fetchAll(\PDO::FETCH_OBJ);

header('Content-Type: text/html; charset=UTF-8');
?><!doctype html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>UTF-8 test page</title>
    </head>
    <body>
        <?php
        foreach($result as $row){
            print($row->Body);  // Ovde bi trebalo da imamo ispravan ispis našeg UTF-8 stringa
        }
        ?>
    </body>
</html>

Za dalje čitanje

Internationalization (i18n) and Localization (l10n)

Disclaimer for newcomers: i18n and l10n are numeronyms, a kind of abbreviation where numbers are used to shorten words - in our case, internationalization becomes i18n and localization, l10n.

First of all, we need to define those two similar concepts and other related things:

Common ways to implement

The easiest way to internationalize PHP software is by using array files and using those strings in templates, such as <h1><?=$TRANS['title_about_page']?></h1>. This is, however, hardly a recommended way for serious projects, as it poses some maintenance issues along the road - some might appear in the very beginning, such as pluralization. So, please, don’t try this if your project will contain more than a couple of pages.

The most classic way and often taken as reference for i18n and l10n is a Unix tool called gettext. It dates back to 1995 and is still a complete implementation for translating software. It is pretty easy to get running, while it still sports powerful supporting tools. It’s about Gettext we will be talking here. Also, to help you not get messy over the command-line, we will be presenting a great GUI application that can be used to easily update your l10n source files.

Other tools

There are common libraries used that support Gettext and other implementations of i18n. Some of them may seem easier to install or sport additional features or i18n file formats. In this document, we focus on the tools provided with the PHP core, but here we list others for completion:

Other frameworks also include i18n modules, but those are not available outside of their codebases:

If you decide to go for one of the libraries that provide no extractors, you may want to use the gettext formats, so you can use the original gettext toolchain (including Poedit) as described in the rest of the chapter.

Gettext

Installation

You might need to install Gettext and the related PHP library by using your package manager, like apt-get or yum. After installed, enable it by adding extension=gettext.so (Linux/Unix) or extension=php_gettext.dll (Windows) to your php.ini.

Here we will also be using Poedit to create translation files. You will probably find it in your system’s package manager; it’s available for Unix, Mac, and Windows, and can be downloaded for free on their website as well.

Structure

Types of files

There are three files you usually deal with while working with gettext. The main ones are PO (Portable Object) and MO (Machine Object) files, the first being a list of readable “translated objects” and the second, the corresponding binary to be interpreted by gettext when doing localization. There’s also a POT (Template) file, that simply contains all existing keys from your source files, and can be used as a guide to generate and update all PO files. Those template files are not mandatory: depending on the tool you’re using to do l10n, you can go just fine with only PO/MO files. You’ll always have one pair of PO/MO files per language and region, but only one POT per domain.

Domains

There are some cases, in big projects, where you might need to separate translations when the same words convey different meaning given a context. In those cases, you split them into different domains. They’re basically named groups of POT/PO/MO files, where the filename is the said translation domain. Small and medium-sized projects usually, for simplicity, use only one domain; its name is arbitrary, but we will be using “main” for our code samples.
In Symfony projects, for example, domains are used to separate the translation for validation messages.

Locale code

A locale is simply a code that identifies one version of a language. It’s defined following the ISO 639-1 and ISO 3166-1 alpha-2 specs: two lower-case letters for the language, optionally followed by an underline and two upper-case letters identifying the country or regional code. For rare languages, three letters are used.

For some speakers, the country part may seem redundant. In fact, some languages have dialects in different countries, such as Austrian German (de_AT) or Brazilian Portuguese (pt_BR). The second part is used to distinguish between those dialects - when it’s not present, it’s taken as a “generic” or “hybrid” version of the language.

Directory structure

To use Gettext, we will need to adhere to a specific structure of folders. First, you’ll need to select an arbitrary root for your l10n files in your source repository. Inside it, you’ll have a folder for each needed locale, and a fixed LC_MESSAGES folder that will contain all your PO/MO pairs. Example:

<project root>
 ├─ src/
 ├─ templates/
 └─ locales/
    ├─ forum.pot
    ├─ site.pot
    ├─ de/
    │  └─ LC_MESSAGES/
    │     ├─ forum.mo
    │     ├─ forum.po
    │     ├─ site.mo
    │     └─ site.po
    ├─ es_ES/
    │  └─ LC_MESSAGES/
    │     └─ ...
    ├─ fr/
    │  └─ ...
    ├─ pt_BR/
    │  └─ ...
    └─ pt_PT/
       └─ ...

Plural forms

As we said in the introduction, different languages might sport different plural rules. However, gettext saves us from this trouble once again. When creating a new .po file, you’ll have to declare the plural rules for that language, and translated pieces that are plural-sensitive will have a different form for each of those rules. When calling Gettext in code, you’ll have to specify the number related to the sentence, and it will work out the correct form to use - even using string substitution if needed.

Plural rules include the number of plurals available and a boolean test with n that would define in which rule the given number falls (starting the count with 0). For example:

Now that you understood the basis of how plural rules works - and if you didn’t, please look at a deeper explanation on the LingoHub tutorial -, you might want to copy the ones you need from a list instead of writing them by hand.

When calling out Gettext to do localization on sentences with counters, you’ll have to give him the related number as well. Gettext will work out what rule should be in effect and use the correct localized version. You will need to include in the .po file a different sentence for each plural rule defined.

Sample implementation

After all that theory, let’s get a little practical. Here’s an excerpt of a .po file - don’t mind with its format, but instead the overall content, you’ll learn how to edit it easily later:

msgid ""
msgstr ""
"Language: pt_BR\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"

msgid "We're now translating some strings"
msgstr "Nós estamos traduzindo algumas strings agora"

msgid "Hello %1$s! Your last visit was on %2$s"
msgstr "Olá %1$s! Sua última visita foi em %2$s"

msgid "Only one unread message"
msgid_plural "%d unread messages"
msgstr[0] "Só uma mensagem não lida"
msgstr[1] "%d mensagens não lidas"

The first section works like a header, having the msgid and msgstr especially empty. It describes the file encoding, plural forms and other things that are less relevant. The second section translates a simple string from English to Brazilian Portuguese, and the third does the same, but leveraging string replacement from sprintf so the translation may contain the user name and visit date.
The last section is a sample of pluralization forms, displaying the singular and plural version as msgid in English and their corresponding translations as msgstr 0 and 1 (following the number given by the plural rule). There, string replacement is used as well so the number can be seen directly in the sentence, by using %d. The plural forms always have two msgid (singular and plural), so it’s advised to not use a complex language as the source of translation.

Discussion on l10n keys

As you might have noticed, we’re using as source ID the actual sentence in English. That msgid is the same used throughout all your .po files, meaning other languages will have the same format and the same msgid fields but translated msgstr lines.

Talking about translation keys, there are two main “schools” here:

  1. msgid as a real sentence.
    The main advantages are:
    • if there are pieces of the software untranslated in any given language, the key displayed will still maintain some meaning. Example: if you happen to translate by heart from English to Spanish but need help to translate to French, you might publish the new page with missing French sentences, and parts of the website would be displayed in English instead;
    • it’s much easier for the translator to understand what’s going on and make a proper translation based on the msgid;
    • it gives you “free” l10n for one language - the source one;
    • The only disadvantage: if you need to change the actual text, you would need to replace the same msgid across several language files.
  2. msgid as a unique, structured key.
    It would describe the sentence role in the application in a structured way, including the template or part where the string is located instead of its content.
    • it’s a great way to have the code organized, separating the text content from the template logic.
    • however, that could bring problems to the translator that would miss the context. A source language file would be needed as a basis for other translations. Example: the developer would ideally have an en.po file, that translators would read to understand what to write in fr.po for instance.
    • missing translations would display meaningless keys on screen (top_menu.welcome instead of Hello there, User! on the said untranslated French page). That’s good it as would force translation to be complete before publishing - but bad as translation issues would be really awful in the interface. Some libraries, though, include an option to specify a given language as “fallback”, having a similar behavior as the other approach.

The Gettext manual favors the first approach as, in general, it’s easier for translators and users in case of trouble. That’s how we will be working here as well. However, the Symfony documentation favors keyword-based translation, to allow for independent changes of all translations without affecting templates as well.

Everyday usage

In a common application, you would use some Gettext functions while writing static text in your pages. Those sentences would then appear in .po files, get translated, compiled into .mo files and then, used by Gettext when rendering the actual interface. Given that, let’s tie together what we have discussed so far in a step-by-step example:

1. A sample template file, including some different gettext calls

<?php include 'i18n_setup.php' ?>
<div id="header">
    <h1><?=sprintf(gettext('Welcome, %s!'), $name)?></h1>
    <!-- code indented this way only for legibility -->
    <?php if ($unread): ?>
        <h2><?=sprintf(
            ngettext('Only one unread message',
                     '%d unread messages',
                     $unread),
            $unread)?>
        </h2>
    <?php endif ?>
</div>

<h1><?=gettext('Introduction')?></h1>
<p><?=gettext('We\'re now translating some strings')?></p>

2. A sample setup file (i18n_setup.php as used above), selecting the correct locale and configuring Gettext

<?php
/**
 * Verifies if the given $locale is supported in the project
 * @param string $locale
 * @return bool
 */
function valid($locale) {
   return in_array($locale, ['en_US', 'en', 'pt_BR', 'pt', 'es_ES', 'es');
}

//setting the source/default locale, for informational purposes
$lang = 'en_US';

if (isset($_GET['lang']) && valid($_GET['lang'])) {
    // the locale can be changed through the query-string
    $lang = $_GET['lang'];    //you should sanitize this!
    setcookie('lang', $lang); //it's stored in a cookie so it can be reused
} elseif (isset($_COOKIE['lang']) && valid($_COOKIE['lang'])) {
    // if the cookie is present instead, let's just keep it
    $lang = $_COOKIE['lang']; //you should sanitize this!
} elseif (isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) {
    // default: look for the languages the browser says the user accepts
    $langs = explode(',', $_SERVER['HTTP_ACCEPT_LANGUAGE']);
    array_walk($langs, function (&$lang) { $lang = strtr(strtok($lang, ';'), ['-' => '_']); });
    foreach ($langs as $browser_lang) {
        if (valid($browser_lang)) {
            $lang = $browser_lang;
            break;
        }
    }
}

// here we define the global system locale given the found language
putenv("LANG=$lang");

// this might be useful for date functions (LC_TIME) or money formatting (LC_MONETARY), for instance
setlocale(LC_ALL, $lang);

// this will make Gettext look for ../locales/<lang>/LC_MESSAGES/main.mo
bindtextdomain('main', '../locales');

// indicates in what encoding the file should be read
bind_textdomain_codeset('main', 'UTF-8');

// if your application has additional domains, as cited before, you should bind them here as well
bindtextdomain('forum', '../locales');
bind_textdomain_codeset('forum', 'UTF-8');

// here we indicate the default domain the gettext() calls will respond to
textdomain('main');

// this would look for the string in forum.mo instead of main.mo
// echo dgettext('forum', 'Welcome back!');
?>

3. Preparing translation for the first run

To make matters easier - and one of the powerful advantages Gettext has over custom framework i18n packages - is its custom file type. “Oh man, that’s quite hard to understand and edit by hand, a simple array would be easier!” Make no mistake, applications like Poedit are here to help - a lot. You can get the program from their website, it’s free and available for all platforms. It’s a pretty easy tool to get used to, and a very powerful one at the same time - using all powerful features Gettext has available.

In the first run, you should select “File > New Catalog” from the menu. There you’ll have a small screen where we will set the terrain so everything else runs smoothly. You’ll be able to find those settings later through “Catalog > Properties”:

After setting those points you’ll be prompted to save the file - using that directory structure we mentioned as well, and then it will run a scan through your source files to find the localization calls. They’ll be fed empty into the translation table, and you’ll start typing in the localized versions of those strings. Save it and a .mo file will be (re)compiled into the same folder and ta-dah: your project is internationalized.

4. Translating strings

As you may have noticed before, there are two main types of localized strings: simple ones and the ones with plural forms. The first ones have simply two boxes: source and localized string. The source string can’t be modified as Gettext/Poedit do not include the powers to alter your source files - you should change the source itself and rescan the files. Tip: you may right-click a translation line and it will hint you with the source files and lines where that string is being used.
On the other hand, plural form strings include two boxes to show the two source strings, and tabs so you can configure the different final forms.

Whenever you change your sources and need to update the translations, just hit Refresh and Poedit will rescan the code, removing non-existent entries, merging the ones that changed and adding new ones. It may also try to guess some translations, based on other ones you did. Those guesses and the changed entries will receive a “Fuzzy” marker, indicating it needs review, being highlighted in the list. It’s also useful if you have a translation team and someone tries to write something they’re not sure about: just mark Fuzzy and someone else will review later.

Finally, it’s advised to leave “View > Untranslated entries first” marked, as it will help you a lot to not forget any entry. From that menu, you can also open parts of the UI that allow you to leave contextual information for translators if needed.

Tips & Tricks

Possible caching issues

If you’re running PHP as a module on Apache (mod_php), you might face issues with the .mo file being cached. It happens the first time it’s read, and then, to update it, you might need to restart the server. On Nginx and PHP5 it usually takes only a couple of page refreshes to refresh the translation cache, and on PHP7 it is rarely needed.

Additional helper functions

As preferred by many people, it’s easier to use _() instead of gettext(). Many custom i18n libraries from frameworks use something similar to t() as well, to make translated code shorter. However, that’s the only function that sports a shortcut. You might want to add in your project some others, such as __() or _n() for ngettext(), or maybe a fancy _r() that would join gettext() and sprintf() calls. Other libraries, such as oscarotero’s Gettext also provide helper functions like these.

In those cases, you’ll need to instruct the Gettext utility on how to extract the strings from those new functions. Don’t be afraid, it’s very easy. It’s just a field in the .po file, or a Settings screen on Poedit. In the editor, that option is inside “Catalog > Properties > Source keywords”. You need to include there the specifications of those new functions, following a specific format:

After including those new rules in the .po file, a new scan will bring in your new strings just as easy as before.

References

Povratak na početak

Dependency Injection

Sa Wikipedia-je:

Dependency Injection je softverski dizajn patern koji omogućava uklanjanje hardkodovanih zavisnosti i čini ih zamenjivim, bilo u vreme izvršavanja ili kompajliranja kôda.

Ovaj citat predstavlja koncept mnogo komplikovanijim nego što on to zaista jeste. Dependency Injection nekoj komponenti dostavlja neophodne zavisnosti (dependencies) bilo putem konstruktora, poziva odgovarajućih setter metoda ili direktnim postavljanjem polja. Jako jednostavno.

Osnovni koncept

Ovaj koncept se može demonstrirati jednim jednostavnim primerom.

Imamo klasu Database koja zahteva adapter kako bi komunicirala sa bazom podataka. Instanciranjem adaptera direktno u konstruktoru kreiramo “čvrstu” zavisnost. Ovo znatno otežava testiranje i čini klasu Database usko vezanom za konkretnu adapter implementaciju.

<?php
namespace Database;

class Database
{
    protected $adapter;

    public function __construct()
    {
        $this->adapter = new MySqlAdapter;
    }
}

class MysqlAdapter {}

Ovaj kôd možemo refaktorisati na način da koristi Dependency Injection princip, sa ciljem da “olabavimo” zavisnost.

<?php
namespace Database;

class Database
{
    protected $adapter;

    public function __construct(MySqlAdapter $adapter)
    {
        $this->adapter = $adapter;
    }
}

class MysqlAdapter {}

Sada klasi Database ubacujemo njen dependency umesto da ga ona sama kreira. Mogli smo čak da napravimo i metod za njegovo postavljanje, ili u slučaju da je polje $adapter javno, mogli smo da ga postavimo direktno.

Složeniji problem

Ako ste ikada čitali na temu Dependency Injection-a, onda ste se sigurno susretali sa izrazima “Inverzija kontrole (Inversion of Control)” ili “Princip inverzije zavisnosti (Dependency Inversion Principle)”. Ovo su složeni problemi koje Dependency Injection rešava.

Inverzija kontrole

Inversion of Control je kao što i sam naziv kaže, “zamena kontrole” sistema tako što držimo organizacioni kôd potpuno odvojeno od naših objekata. U kontekstu Dependency Injection-a, ovo se odnosi na “popuštanje” zavisnosti tako što ih kontrolišemo i instanciramo na drugom mestu u sistemu.

PHP frejmvorci su godinama implementirali Inverziju kontrole, međutim, postavlja se pitanje koji deo kontrole vi zamenjujete i gde? Na primer, MVC frejmvorci obično na raspolaganje stavljaju super objekat ili osnovnu kontroler klasu koju ostali kontroleri moraju da naslede da bi dobili pristup njenim zavisnostima. Ovo jeste Inverzija kontrole, međutim, umesto da olabavi zavisnosti, ovaj metod ih jednostavno izmešta.

Dependency Injection nam omogućava da na elegantniji način rešimo ovaj problem tako što ćemo ubacujemo zavisnosti koje su nam potrebne i kada su nam potrebne, umesto da ih hardkodujemo i direktno instanciramo.

Dependency Inversion princip

Dependency Inversion princip se odnosi na slovo “D” u SOLID setu principa objektno-orijentisanog programiranja, koji glasi: “oslanjaj se na apstrakcije, a ne na konkretne implementacije”. Jednostavno, ovo znači da naše zavisnosti treba da budu interfejsi/ugovori ili apstraktne klase, a ne konkretne implementacije. Vrlo jednostavno možemo refaktorisati prethodni primer kako bi bio u skladu sa ovim principom.

<?php
namespace Database;

class Database
{
    protected $adapter;

    public function __construct(AdapterInterface $adapter)
    {
        $this->adapter = $adapter;
    }
}

interface AdapterInterface {}

class MysqlAdapter implements AdapterInterface {}

Postoji nekoliko prednosti toga što klasa Database sada zavisi od interfejsa umesto od konkretne implementacije.

Pretpostavimo da radite u timu, a vaš kolega radi na adapteru. U našem prvom primeru, morali bismo da čekamo na kolegu da završi adapter pre nego što bismo mogli da mock-ujemo isti za potrebe unit testova. A sada kada je zavisnost u vidu interfejsa/ugovora, možemo bez problema da mock-ujemo taj interfejs znajući da će naš kolega napraviti adapter u skladu sa tim ugovorom.

Još veći benefit ovog pristupa jeste to što je naš kôd mnogo skalabilniji. Ako posle nekog vremena odlučimo da pređemo na drugi tip baze podataka, dovoljno je da napišemo adapter koji implementira postojeći interfejs i zatim ga koristimo kao dependency naše Database klase, pri čemu neće biti potrebe za bilo kakvim dodatnim refaktorom, jer smo sigurni da je adapter u skladu sa interfejsom.

Kontejneri (Containers)

Prva stvar koju treba razumeti u vezi Dependency Injection kontejnera jeste da oni nisu isto što i Dependency Injection. Kontejner je praktičan alat koji nam pomaže u implementaciji Dependency Injection pristupa, pri čemu oni mogu biti i loše iskorišćeni za implementaciju anti-paterna - Servis lokacije (Service Location). Korišćenje DI kontejnera kao lokatora servisa obično proizvodi jaču zavisnost na sâm kontejner nego na stvaran dependency kojeg vaša klasa treba da koristi. To takođe čini vaš kôd nepreglednim, manje transparentnim i u krajnjem slučaju težim za testiranje.

Većina modernih frejmvorka ima svoj Dependency Injection kontejner koji omogućava da definišete vaše zavisnosti putem konfiguracije. Ovo zapravo znači da možete pisati kôd aplikacije koji je čist i razdvojen, baš kao i sâm frejmvork nad kojim je izgrađen.

Za dalje čitanje

Povratak na početak

Baze podataka

Vaš PHP kôd će često koristiti bazu podataka za čuvanje informacija. Na raspolaganju je nekoliko opcija za povezivanje i interakciju sa bazom. Preporučena opcija do verzije PHP 5.1.0 je bila korišćenje nativnih drajvera kao što su mysqli, pgsql, mssql, itd.

Nativni drajveri su odličan izbor ako koristite samo jednu bazu u aplikaciji, ali ako na primer koristite MySQL i malo MSSQL, ili se povezujete i na Oracle bazu, onda nećete moći da koristite iste drajvere. Moraćete da naučite potpuno nov API za svaki tip baze podataka — a to je apsurdno.

MySQL ekstenzija

mysql ekstenzija za PHP više nije u aktivnom razvoju, prevaziđena je od verzije PHP 5.5.0 i zvanično je uklonjena u verziji PHP 7.0.0. Ako u vašim aplikacijama koristite mysql_* funkcije, kao što su mysql_connect(), onda ćete u nekom momentu biti prinuđeni da prepravite kôd, a najbolja opcija je da zamenite korišćenje mysql esktenzije sa mysqli ili PDO u skladu sa sopstvenim tempom razvoja.

Ako počinjete od nule onda nikako nemojte koristiti mysql ekstenziju, već koristite ili MySQLi ekstenziju ili PDO.

PDO ekstenzija

PDO je biblioteka za apstrakciju rada sa bazom podataka — dostupna od verzije PHP 5.1.0 — koja obezbeđuje zajednički interfejs za komunikaciju sa više različitih tipova baza podataka. Tako na primer možete imati praktično identičan kôd koji radi sa MySQL i SQLite bazom:

<?php
// PDO + MySQL
$pdo = new PDO('mysql:host=example.com;dbname=database', 'user', 'password');
$statement = $pdo->query("SELECT some_field FROM some_table");
$row = $statement->fetch(PDO::FETCH_ASSOC);
echo htmlentities($row['some_field']);

// PDO + SQLite
$pdo = new PDO('sqlite:/path/db/foo.sqlite');
$statement = $pdo->query("SELECT some_field FROM some_table");
$row = $statement->fetch(PDO::FETCH_ASSOC);
echo htmlentities($row['some_field']);

PDO neće prevoditi vaše SQL upite ili emulirati nedostajuće opcije; on služi isključivo za povezivanje sa različitim tipovima baza podataka pomoću istog API-ja.

Ono što je još bitnije, PDO omogućava da bezbedno ubacite podatke koji potiču iz nekog stranog izvora (npr. ID-eve) u vaš SQL upit bez bojazni od SQL Injection napada. Ovo je moguće korišćenjem PDO naredbi (statements) i bound parametara.

Pretpostavimo da PHP skripta prima numerički ID kao parametar upita. Ovaj ID se potom koristi da vrati podatak o korisniku iz baze. Ovo je pogrešan način:

<?php
$pdo = new PDO('sqlite:/path/db/users.db');
$pdo->query("SELECT name FROM users WHERE id = " . $_GET['id']); // <-- NE!

Ovo je katastrofalan kôd. Ubacujete “sirov” parametar u SQL upit. Bićete veoma lako hakovani putem SQL Injection napada. Zamislite da haker prosledi maliciozan id parametar putem URL-a kao što je http://domain.com/?id=1%3BDELETE+FROM+users. Ovo će postaviti promenljivu $_GET['id'] na 1;DELETE FROM users, što će obrisati sve vaše korisnike! Umesto toga, trebalo bi da sanirate ID input korišćenjem PDO bound parametara:

<?php
$pdo = new PDO('sqlite:/path/db/users.db');
$stmt = $pdo->prepare('SELECT name FROM users WHERE id = :id');
$id = filter_input(INPUT_GET, 'id', FILTER_SANITIZE_NUMBER_INT); // <-- najpre filtrirajte podatke (vidi [Filtriranje podataka](#data_filtering)), posebno bitno za upite kao što su INSERT, UPDATE, itd.
$stmt->bindParam(':id', $id, PDO::PARAM_INT); // <-- automatski sanirano
$stmt->execute();

Ovo je ispravan kôd koji radi bind-ovanje parametra u PDO naredbu. Na taj način se sanira strani ID podatak pre nego što se uopšte pošalje bazi, čime se sprečava potencijalni SQL injection napad.

U slučaju upita kao što su INSERT ili UPDATE, svejedno morate sami filtrirati podatke zbog drugih stvari (uklanjanje HTML tagova, JavaScript-a i slično). PDO će sanirati podatke samo u slučaju SQL upita, a ne za potrebe vaše aplikacije.

Trebate imati svest o tome da konekcije sa bazom podataka troše sistemske resurse i neretko se dešava da dođe do trošenja resursa ako se konekcije ne zatvaraju implicitno. Ipak, to se dosta češće dešava u slučaju drugih programskih jezika. Pomoću PDO-a možete implicitno zatvoriti konekciju uništavanjem samog PDO objekta, na primer postavljanjem njegove vrednosti na NULL. Ako ne uradite ovo eksplicitno, PHP će automatski zatvoriti konekciju po završetku izvršavanja vaše skripte, osim ako naravno ne koristite perzistentne konekcije.

Interakcija sa bazom podataka

Kada programeri počnu da uče PHP, obično dođu u situaciju da mešaju logiku interakcije sa bazom i prezentacione logiku, kao na primer u ovom slučaju:

<ul>
<?php
foreach ($db->query('SELECT * FROM table') as $row) {
    echo "<li>".$row['field1']." - ".$row['field1']."</li>";
}
?>
</ul>

Ovo je loša praksa iz više razloga, a ponajviše zbog toga što je ovakav kôd teško debug-ovati, testirati i uopšte čitati.

Iako postoji dosta načina da se interakcija ostvari, u zavisnosti da li preferirate OOP ili proceduralno programiranje, mora postojati taj momenat razdvajanja logike.

Razmotrimo ovaj najosnovniji korak:

<?php
function getAllFoos($db) {
    return $db->query('SELECT * FROM table');
}

foreach (getAllFoos($db) as $row) {
    echo "<li>".$row['field1']." - ".$row['field1']."</li>"; // LOŠE!!
}

Ovo je dobar početak. Ako stavite ova dva segmenta u zasebne fajlove imaćete čisto razdvajanje.

Napravite klasu sa ovim metodama i imate “Model”. Napravite jednostavan .php fajl sa prezentacionom logikom i imate “View”, što je već dosta blizu MVC-a — najčešćeg koncepta u većini frejmvorka.

foo.php

<?php
$db = new PDO('mysql:host=localhost;dbname=testdb;charset=utf8', 'username', 'password');

// Učitajte klasu modela
include 'models/FooModel.php';

// Kreirajte instancu
$fooModel = new FooModel($db);
// Dohvatite listu svih entiteta
$fooList = $fooModel->getAllFoos();

// Prikažite view
include 'views/foo-list.php';

models/FooModel.php

<?php
class FooModel
{
    protected $db;

    public function __construct(PDO $db)
    {
        $this->db = $db;
    }

    public function getAllFoos() {
        return $this->db->query('SELECT * FROM table');
    }
}

views/foo-list.php

<?php foreach ($fooList as $row): ?>
    <?= $row['field1'] ?> - <?= $row['field1'] ?>
<?php endforeach ?>

Ovo u suštini radi i većina modernih frejmvorka. Možda nećete imati stalnu potrebu za ovim, ali mešanje logike interakcije sa bazom i prezentacione logike može biti ozbiljan problem ako ikada budete želeli da pišete unit testove za vašu aplikaciju.

PHPBridge ima sjajan članak na sličnu temu nazvan Kreiranje Data klase, koji je odličan za programere koji se tek uhodavaju sa konceptom interakcije sa bazom podataka.

Slojevi za apstrakciju (Database Abstraction Layers - DAL)

Mnogi frejmvorci imaju sopstveni DAL koji može, a ne mora da bude “nad” PDO-om. Oni najčešće emuliraju mogućnosti jednog sistema baze podataka koje nedostaju u drugom, tako što pretvaraju upite u PHP metode, dajući nam apstrakciju same baze, a ne samo konekcije na bazu kao što je to slučaj sa PDO-om. Ovo će naravno uneti i malo dodatnih “troškova”, ali ako pravite portabilnu aplikaciju koja treba da radi sa MySQL, PostgreSQL i SQLite bazama, onda se dodatni troškovi isplate u korist čistote kôda.

Neki slojevi za apstrakciju su napravljeni u skladu sa PSR-0 i PSR-4 namespace standardima, tako da mogu da se integrišu u bilo koju aplikaciju:

Povratak na početak

Rad sa templejtima

Templejti obezbeđuju praktičan način za razdvajanje kontrolerske i domenske logike od prezentacione. Obično sadrže HTML vaše aplikacije, ali i druge formate podataka, kao što je XML. Templejti se često definišu kao view-ovi (views), koji čine deo druge komponente Model-View-Controller (MVC) softverskog arhitekturalnog paterna.

Prednosti

Glavna korist od korišćenja templejta je jasno razdvajanje prezentacione logike od ostatka aplikacije. Templejti su isključivo u nadležnosti formatiranja i prikazivanja podataka. Oni nisu zaduženi za dohvatanje i čuvanje podataka, niti za neke druge kompleksnije poslove. To za rezultat ima čistiji, čitljiviji kôd, što je posebno korisno u timovima gde programeri rade na server-side kôdu (kontroleri, modeli), a dizajneri na client-side delu (markup).

Takođe, templejti unapređuju organizaciju prezentacione logike. Oni su najčešće smešteni u “views” folderu, svaki u posebnom fajlu. Ovo doprinosi praktičnijem iskorišćenju kôda, jer su veći blokovi podeljeni na manje, iskoristive (reusable) delove, takozvane parcele (partials). Na primer, header i footer sekije vašeg sajta mogu biti definisane kao templejti, koji se onda učitavaju pre i posle templejta za svaku pojedinačnu stranicu.

Konačno, u zavisnosti od biblioteke koju koristite, templejti obično brinu o bezbednosti, tako što automatski escape-uju sadržaj koji potiče od korisnika. Neke biblioteke čak omogućavaju limitirano korišćenje, pri čemu je dizajnerima omogućen pristup isključivo određenim, proverenim promenljivama i funkcijama.

Nativni PHP templejti

Nativni PHP templejti u osnovi koriste standardnu PHP sintaksu. Oni predstavljaju logičan izbor pošto je PHP sam po sebi templejt jezik. Ovo znači da je moguće kombinovati PHP kôd u okviru nečeg drugog, kao što je HTML. Ovo dosta znači PHP programerima, jer nije neophodno učiti novi sintaksu, znaju koje su im funkcije dostupne, a njihovi editori omogućavaju isticanje (highlighting) sintakse, kao i auto-complete. Takođe, nativni PHP templejti su veoma brzi imajući u vidu da u njihovom slučaju nije potrebno kompajliranje.

Svaki moderan PHP frejmvork poseduje neku vrstu templejt sitema, pri čemu većina podrazumevano koristi nativan PHP. Pored frejmvorka, biblioteke kao Plates i Aura.View omogućavaju jednostavniji rad sa nativnim PHP templejetima, putem funkcionalnosti kao što su nasleđivanje, layout-i i ekstenzije.

Jednostavan primer nativnog PHP templejta

Primer baziran na Plates biblioteci.

<?php // user_profile.php ?>

<?php $this->insert('header', ['title' => 'User Profile']) ?>

<h1>User Profile</h1>
<p>Hello, <?=$this->escape($name)?></p>

<?php $this->insert('footer') ?>

Primer nativnog PHP templejta koji koristi nasleđivanje

Primer baziran na Plates biblioteci.

<?php // template.php ?>

<html>
<head>
    <title><?=$title?></title>
</head>
<body>

<main>
    <?=$this->section('content')?>
</main>

</body>
</html>
<?php // user_profile.php ?>

<?php $this->layout('template', ['title' => 'User Profile']) ?>

<h1>User Profile</h1>
<p>Hello, <?=$this->escape($name)?></p>

Kompajlirani templejti

PHP jeste evoluirao u zreo, objektno-orijenisan jezik, ali nije mnogo napredovao kao templejt jezik. Kompajlirani templejt sistemi (template engines), kao što su Twig i Smarty*, upotpunjuju ovaj aspekt sa novom sintaksom osmišljenom upravo za potrebe templejta. Od automatskog escape-ovanja, preko nasleđivanja i pojednostavljenih kontrolnih struktura, kompajlirani templejti su mnogo čitljiviji, lakši za pisanje, ali i bezbedniji. Oni takođe mogu biti kompatibilni sa više različitih programskih jezika, za šta je dobar primer Mustache. Pošto se templejti moraju kompajlirati, neizbežan je određeni negativan uticaj na performanse, koji je ipak zanemarljiv ako se koristi keširanje.

*Smarty omogućava automatsko _escape-ovanje_, ali ova funkcionalnost NIJE podrazumevano uključena.

Jednostavan primer kompajliranog templejta

Primer baziran na Twig biblioteci.

{% include 'header.html' with {'title': 'User Profile'} %}

<h1>User Profile</h1>
<p>Hello, {{ name }}</p>

{% include 'footer.html' %}

Primer kompajliranog templejta koji koristi nasleđivanje

Primer baziran na Twig biblioteci.

// template.html

<html>
<head>
    <title>{% block title %}{% endblock %}</title>
</head>
<body>

<main>
    {% block content %}{% endblock %}
</main>

</body>
</html>
// user_profile.html

{% extends "template.html" %}

{% block title %}User Profile{% endblock %}
{% block content %}
    <h1>User Profile</h1>
    <p>Hello, {{ name }}</p>
{% endblock %}

Za dalje čitanje

Članci i tutorijali

Biblioteke

Povratak na početak

Greške i izuzeci (exceptions)

Greške

U mnogim tkz. “exception-intenzivnim” programskim jezicima, kada god nešto pođe po zlu baciće se izuzetak. To svakako jeste ispravan način za postupanje u takvim situacijama, ali PHP spada u “polu-exception” programske jezike. Iako podržava izuzetke i njegov core ih sve više upražnjava pri radu sa objektima, veći deo samog PHP-a će pokušati da nastavi izvršavanje, bez obzira šta se dogodilo, osim u slučaju fatalne greške (fatal error).

Na primer:

$ php -a
php > echo $foo;
Notice: Undefined variable: foo in php shell code on line 1

Ovo je samo obaveštenje (notice) i PHP će bez problema nastaviti da se izvršava. Ovo može biti zbunjujuće onima koji dolaze iz “exception-intenzivnih” jezika, zato što bi referenciranje nepostojeće promenljive u Python-u rezultovalo bacanjem izuzetka:

$ python

>>> print foo
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'foo' is not defined

Jedina prava razlika je ta da će se Python “buniti” i za najmanji problem, kako bi programeri bili sigurni da će svaki potencijalni problem ili nepredviđena situacija biti primećena, dok će s druge strane PHP nastaviti da se izvršava, osim u slučaju nekih većih problema, kada će reagovati i prijaviti grešku.

Nivoi grešaka

PHP ima nekoliko nivoa koji definišu stepen ozbiljnosti grešaka. Tri najčešća tipa poruka su greške (error), obaveštenja (notice) i upozorenja (warning). Za njih se vezuju odgovarajući nivoi: E_ERROR, E_NOTICE, and E_WARNING. Greške (errors) su fatalne greške u izvršavanju i obično su uzrokovane greškama u kôdu koje moraju biti ispravljene, jer će zbog njih neka PHP skripta prekinuti sa izvršavanjem. Obaveštenja (notice) su savetodavne poruke izazvane kôdom koji može, a ne mora da izazove probleme tokom izvršavanja skripte, pri čemu se samo izvršavanje ne prekida. Upozorenja (warning) su ne-fatalne greške, koje neće izazvati prekid u izvršavanju skripte.

Još jedan tip grešaka i poruka tokom kompajliranja su E_STRICT poruke. Ove poruke sugerišu izmene u vašem kôdu u cilju postizanja interoperabilnosti i kompatibilnosti sa novim verzijama PHP-a.

Izmena PHP-ovog mehanizma prijave grešaka

Prijava grešaka se može prilagođavati pomoću odgovarajućih PHP podešavanja i/ili funkcija. Pomoću ugrađene PHP error_reporting() funkcije, možete podesiti vidljivost grešaka tokom izvršavanje skripte, prosleđujući joj neku od predefinisanih konstanti. Tako da ako želite da vidite samo upozorenja (warning) i greške (error), ali ne i obaveštenja (notice), to možete podesiti na sledeći način:

<?php
error_reporting(E_ERROR | E_WARNING);

Takođe možete imati kontrolu nad tim da li će se same greške biti prikazivane (korisno tokom razvoja) ili će biti sakrivene, pa eventualno logovane (korisno u produkciji). Za više informacija o ovome pogledajte sekciju Prijava grešaka.

Inline “suzbijanje” (suppress) grešaka

Možete naložiti PHP-u da suzbije određene greške putem operatora za kontrolu grešaka - @. Ovaj operator možete postaviti na početak nekog izraza, nakon čega će svaka greška koja je direktan rezultat tog izraza biti potisnuta.

<?php
echo @$foo['bar'];

Ovaj kôd će ispisati $foo['bar'] ako postoji, ali će jednostavno vratiti null i neće ispisati ništa ako promenjiva $foo ili 'bar' indeks ne postoji. Bez operatora za kontrolu grešaka, ova linija kôda bi izazvala PHP Notice: Undefined variable: foo ili PHP Notice: Undefined index: bar grešku.

Ovo možda deluje kao nešto veoma korisno, ali sa sobom nosi nekoliko neželjenih kompromisa. PHP obrađuje izraze sa @ operatorom dosta sporije nego one bez njega. Prevremena optimizacija može biti ključni argument u tom slučaju, ali kako su performanse od posebne važnosti za vašu aplikaciju/biblioteku, neophodno je da razumete posledice koje prouzrokuje operator za kontrolu grešaka.

Druga stvar, operator za kontrolu grešaka će “progutati” neku grešku u potpunosti. Greška neće biti prikazana, niti logovana (error log). Takođe, nije moguće isključivanje ovog operatora u stock/produkcionim PHP sistemima. Iako ta neka greška koju vidite jeste bezopasna, neka druga, manje bezopasna greška će takođe ostati “pritajena”.

Ako postoji način da izbegnete @ operator, uradite to. Na primer, prethodni primer je mogao biti napisan na sledeći način:

<?php
echo isset($foo['bar']) ? $foo['bar'] : '';

Slučaj u kojima suzbijanje grešaka može imati smisla je kada fopen() pokuša da učita nepostojeći fajl. Možete prethodno da proverite da li taj fajl postoji, ali ako je fajl obrisan nakon provere i pre fopen() poziva (što možda zvuči nemoguće, ali može da se desi), onda će fopen() da vrati false, a pritom će izbaciti i grešku. Ovo je nešto što PHP treba da reši, ali to je jedini primer gde suzbijanje grešaka ima smisla.

Ranije je pomenuto da nema načina da se u stock PHP sistemima isključi operator za kontrolu grešaka. Ipak, Xdebug poseduje xdebug.scream ini podešavanje, koje će isključiti ovaj operator. Ovo podešavanje možete postaviti putem vašeg php.ini fajla na sledeći način:

xdebug.scream = On

Takođe možete izmeniti ovo podešavanje i tokom izvršavanja pomoću ini_set funkcije:

<?php
ini_set('xdebug.scream', '1')

Scream” PHP ekstenzija nudi sličnu funkcionalnost kao Xdebug, ali se u njenom slučaju odgovarajuće ini podešavanje zove scream.enabled.

Ovo je korisno kada debug-ujete kôd i sumnjate da su neke informativne greške potisnute. Koristite scream oprezno, kao privremen debugging alat. Postoji dosta PHP biblioteka koje ne mogu da rade kada je operator za kontrolu grešaka isključen.

ErrorException

PHP je i te kako sposoban da bude “exception-intenzivan” programski jezik i za to je potrebno svega nekoliko linija kôda. U principu, vaše greške i izuzetke možete bacati uz pomoć ErrorException klase, koja se izvodi iz Exception klase.

Ovo je česta praksa u mnogim modernim frejmvorcima kao što su Symfony i Laravel. Podrazumevano, Laravel prikazuje sve greške i izuzetke uz pomoć Whoops! biblioteke ako je app.debug podešavanje uključeno, a sakriva ih ako je isključeno.

Bacanjem grešaka i izuzetaka u toku razvoja, možete njima da manipulišete bolje nego inače, jer ako naiđete na izuzetak tokom razvoja, možete da ga uokvirite catch blokom kako biste ga na određen način obradili. Svaki izuzetak kojeg uhvatite, automatski čini vašu aplikaciju malo više robustnijom.

Za više informacija na ovu temu i detalja kako se koristi ErrorException klasa pročitajte u poglavlju ErrorException klasa.

Izuzeci (exceptions)

Izuzeci su sastavni deo većine popularnih programskih jezika, ali ih PHP programeri često previđaju. Jezici kao što je Ruby su izuzetno “exception-intenzivnim”, tako da kad god nešto pođe po zlu, kao na primer neuspeo HTTP request ili upit ka bazi podataka, ili čak u slučaju nepostojeće slike, Ruby (odnosno gem-ovi koji se koriste) će ispisati izuzetak na ekranu tako da odmah znate da je došlo do greške.

PHP je sam po sebi tolerantan što se ovoga tiče, tako da poziv file_get_contents() funkcije može rezultovati samo sa vraćenim FALSE i upozorenjem (warning). Mnogi stariji PHP frejmvorci kao što je CodeIgniter će samo vratiti false, logovati poruku u njegov odgovarajući log i eventualno će vam omogućiti da koristite metod kao što je $this->upload->get_error() kako biste videli šta se zapravo desilo. Problem s tim je što mora da tražite grešku i čitate dokumentaciju kako biste našli odgovarajući error metod, umesto da to bude veoma očigledno.

Još jedan problem postoji u slučaju kada klase automatski ispisuju grešku i prekidaju izvršavanje. Kada uradite tako nešto, sprečavate drugog programera da dinamički obradi tu grešku. Izuzetke treba bacati sa svrhom obaveštavanja programera o grešci, a oni onda mogu da odluče šta da rade sa njim. Na primer:

<?php
$email = new Fuel\Email;
$email->subject('My Subject');
$email->body('How the heck are you?');
$email->to('guy@example.com', 'Some Guy');

try
{
    $email->send();
}
catch(Fuel\Email\ValidationFailedException $e)
{
    // Neuspela validacija
}
catch(Fuel\Email\SendingFailedException $e)
{
    // Nuspelo slanje email poruke
}
finally
{
    // Deo koji se izvršava uvek, bez obzira da li je izuzetak bačen i pre nego što se nastavi dalje izvršavanje
}

SPL izuzeci

Generička Exception klasa pruža vrlo malo debug prostora za programera. Ipak, u cilju bolje situacije po tom pitanju, moguće je kreirati specijalizovanu Exception klasu, izvođenjem iz generičke:

<?php
class ValidationException extends Exception {}

Ovo zapravo znači da možete imati nekoliko catch blokova i ponaosob obrađivati različite izuzetke. Ovo može dovesti do toga da imate mnogo custom Exception klasa, a pritom su umesto nekih od njih mogli biti korišćene SPL Exception klase, dostupne kroz SPL ekstenziju.

Ako na primer koristite __call() magični metod i zatražen je neki nepostojeći metod, onda umesto bacanja standardnog izuzetka, što bi bilo nejasno, ili kreiranja custom Exception klase samo u te svrhe, možete baciti BadMethodCallException.

Povratak na početak

Bezbednost

Bezbednost web aplikacije

Postoje loši ljudi koji žele i spremni su da naude vašoj web aplikaciji. Važno je da preduzmete neophodne mere predostrožnosti kako biste povećali bezbednost vaše aplikacije. Srećom, dobri ljudi koji rade na The Open Web Application Security Project (OWASP) projektu su napravili obiman spisak poznatih bezbednosnih propusta i metoda kako biste se zaštitili od njih. Ovo je obavezno štivo za svakog programera koji je svestan bezbednosnih aspekata. Survive The Deep End: PHP Security autora Padreika Brejdija je još jedan dobar vodič o bezbednosti web aplikacija za PHP.

Hešovanje (Hashing) lozinki

Većina PHP aplikacija ima funkcionalnost koja podrazumeva prijavu korisnika (login). Korisnička imena i lozinke se čuvaju u bazi podataka i kasnije se koriste za autentifikaciju korisnika pri prijavi.

Veoma je važno da pravilno hešujete lozinke pre nego što ih sačuvate. Hešovanje lozinki je nepovratan, jednosmeran proces, koji se vrši nad korisničkom lozinikom. Pritom se kreira string fiksne dužine koji se ne može lako rekonstruisati. Ovo znači da možete da uporedite dva heša da biste utvrdili da li potiču od istog stringa, ali ne možete saznati vrednost izvornog stringa. Ako lozinke nisu hešovane, a vašoj bazi pristupa neovlašćena osoba, svi korisnički nalozi biće kompromitovani. Neki korisnici (nažalost) koriste istu lozinku i za druge sajtove i servise. Zbog toga je neophodno da bezbednosne aspekte tretirate ozbiljno.

Hešovanje lozinki pomoću password_hash

U verziji PHP 5.5 je uvedena password_hash() funkcija. Ona trenutno koristi BCrypt, najjači algoritam koji PHP trenutno podržava. U budućnosti će biti ažurirana da bi podržala više algoritama ako bude neophodno. Biblioteka The password_compat je napravljena kako bi pružila kompatibilnost za starije verzije (PHP >= 5.3.7).

U primeru koji sledi hešujemo string, a onda proveravamo heš sa novim stringom. Pošto se naša dva izvorna stringa razlikuju (‘secret-password’ vs. ‘bad-password’) ovo logovanje će biti neuspešno.

<?php
require 'password.php';

$passwordHash = password_hash('secret-password', PASSWORD_DEFAULT);

if (password_verify('bad-password', $passwordHash)) {
    // Ispravna lozinka
} else {
    // Pogrešna lozinka
}

password_hash() takes care of password salting for you. The salt is stored, along with the algorithm and “cost”, as part of the hash. password_verify() extracts this to determine how to check the password, so you don’t need a separate database field to store your salts.

Filtriranje podataka

Apsolutno nikada ne verujte “stranom” (korisnikovom) ulaznom podatku (input) koji se šalje u vaš PHP kôd. Uvek filtrirajte i validirajte strani input pre nego što ga upotrebite u kôdu. Funkcije filter_var() i filter_input() se koriste za filtriranje nekog teksta, kao i za njegovu validiciju (npr. email adrese).

Strani input može biti bilo šta: $_GET i $_POST podaci iz forme, neke vrednosti u superglobalnoj promenljivoj $_SERVER ili body HTTP zahteva dobijen putem fopen('php://input', 'r'). Zapamtite, strani input nije ograničen samo na podatke iz forme koje je poslao korisnik. Uploadovani i preuzeti fajlovi, vrednosti iz sesije, podaci iz cookie-a i podaci iz 3rd party web servisa su takođe strani input.

Iako se ti podaci mogu čuvati, kombinovati i može im se pristupiti kasnije, oni su ipak strani input. Svaki put kad obradite, prikažete, spojite ili uključite podatke u vaš kôd, zapitajte se da li su podaci pravilno filtrirani i da li su bezbedni.

Podaci se mogu različito filtrirati u zavisnosti od njihove namene. Na primer, nefiltrirani strani input prosleđen HTML stranici za prikaz može izvršiti HTML i JavaScript na vašem sajtu! Ovo se naziva Cross-Site Scripting (XSS) i predstavlja vrlo opasan vid napada. Jedan od načina da sprečite XSS napade je da filtrirate sve podatke koje je korisnik generisao pre nego što ih ispišete, tako što ćete ukloniti HTML tagove pomoću strip_tags() funkcije ili escape-ovati karaktere koji imaju specijalno značenje u odgovarajuće HTML entitete pomoću htmlentities() ili htmlspecialchars() funkcija.

Još jedan primer je prosleđivanje opcija putem komandne linije. Ovo može biti vrlo opasno (i najčešće je loša ideja), ali možete da upotrebite ugrađenu escapeshellarg() funkciju da prečistite argumente komande.

Poslednji primer je prihvatanje stranog input-a sa ciljem učitavanja određenog fajla. Ovo se može zloupotrebiti promenom imena fajla u putanju fajla. Na vama je da uklonite "/", "../", null bajtove i druge karaktere iz putanje fajla kako biste sprečili učitavanje skrivenih, privatnih i sistemskih fajlova.

Filtriranje / sanacija (sanitization)

Filtriranje uklanja (ili escape-uje) ilegalne ili nebezbedne karaktere iz stranog input-a.

Na primer, trebalo bi da filtrirate strane podatake pre njihovog uključivanja u HTML ili ubacivanja u SQL upit. Ako koristite PDO bound parametre, oni će biti automatski sanirani.

Ponekad je neophodno da namerno dozvolite unos određenih HTML tagova u input-u prilikom ispisa. Ovo je donekle teško za implementirati i mnogi pribegavaju korišćenju striktnijeg formatiranja kao što je Markdown ili BBCode, iako biblioteke kao što je HTML Purifier postoje baš iz ovog razloga.

Pogledaj filtere za prečišćavanje

Validacija

Validacija obezbeđuje da strani input bude u skladu sa onim što vaša aplikacija očekujete. Na primer, možda će biti potrebno da validirate email adresu, broj telefona ili godište prilikom procesiranja registracione forme.

Pogledaj validacione filtere

Konfiguracioni fajlovi

Pri kreiranju konfiguracionih fajlova vaše aplikacije, preporuka je da to bude u skladu sa jednom od sledećih metoda:

Register Globals

NAPOMENA: Od PHP verzije 5.4.0 podešavanje register_globals je uklonjeno i više se ne može koristiti. Ovo poglavlje postoji samo kao upozorenje svima onima koji su u procesu upgrade-a legacy aplikacije.

Kada je uključeno, register_globals podešavanje omogućava da podaci iz nekoliko tipova promenljivih (uključujući one iz $_POST, $_GET i $_REQUEST) bude dostupno u globalnom scope-u aplikacije. Ovo vrlo lako može prouzrokovati bezbednosne probleme, jer aplikacija ne može sa sigurnošću znati odakle ti podaci dolaze.

Na primer: $_GET['foo'] bi bilo dostupno i kao $foo, što može override-ovati promenljive koje još uvek nisu definisane. Ako koristite PHP < 5.4.0 postarajte se da je register_globals podešavanje isključeno.

Izveštaji o greškama (Error Reporting)

Logovanje grešaka može biti korisno prilikom traženja problematičnih mesta u vašoj aplikaciji, ali istovremeno može javno otkriti informacije o strukturi aplikacije. Da biste na pravi način zaštitili vašu aplikaciju od problema koje može izazvati javno prikazivanje poruka o greškama, morate posebno podesiti server u slučaju razvojnog (development) i produkcionog (live) okruženja aplikacije.

Razvojno okruženje

Da biste prikazali svaku moguću grešku prilikom razvoja, postavite sledeće vrednosti u vašem php.ini fajlu:

display_errors = On

display_startup_errors = On

error_reporting = -1

log_errors = On

Prosleđivanje vrednosti -1 će prikazati svaku grešku, čak i nakon što budu dodati novi nivoi i konstante u budućim verzijama PHP-a. Konstanta E_ALL takođe ima isti efekat od verzije 5.4 - php.net.

E_STRICT nivo je uveden u verziji 5.3.0 i nije bio deo E_ALL sve do verzije 5.4.0. Šta to tačno znači? Apropo prikazivanja svih grešaka u verziji 5.3, to znači da morate da koristite ili -1 ili E_ALL | E_STRICT.

Prijavljivanje svih grešaka po verzijama PHP-a

Produkciono (live) okruženje

Da biste sakrili greške u vašem produkcionom okruženju, podesite php.ini na sledeći način:

display_errors = Off

display_startup_errors = Off

error_reporting = E_ALL

log_errors = On

Sa ovim podešavanjima, greške će se i dalje upisivati u log za greške (error log), ali se neće prikazivati korisniku. Za više informacija o ovim podešavanjima, proučite PHP manual:

Povratak na početak

Testiranje

Pisanje automatizovanih testova za vaš PHP kôd se smatra veoma dobrom praksom koja za rezultat ima kvalitetno napravljene aplikacije. Automatizovani testovi predstavljaju odličan alat koji obezbeđuje da aplikacija neće “pucati” nakon izmene ili dodavanja novih funkcionalnosti, te ih ne bi trebalo ignorisati.

Postoji nekoliko različitih tipova alata za testiranje (ili frejmvorka) u PHP-u, koji imaju različite pristupe, ali svi teže tome da se izbegne ručno testiranje i potreba za velikim Q/A (Quality Assurance) timovima, sve u cilju da najnovije izmene ne “razbiju” postojeću funkcionalnost aplikacije.

Razvoj vođen testiranjem (Test Driven Development - TDD)

Sa Wikipedia-je:

Razvoj vođen testiranjem (TDD) je proces razvoja softvera koji se bazira na ponavljanju kratkih razvojnih krugova: programer najpre piše neispravan automatizovan testni slučaj koji definiše željeno poboljšanje ili novu funkcionalnost, a onda piše kôd koji će zadovoljiti test i na kraju refaktoriše novi kôd u skladu sa standardima. Kent Beck, koji je zaslužan za razvoj ili ‘ponovno otkrivanje’ ove tehnike, izjavio je 2003. godine da TDD podstiče jednostavan dizajn i podiže samopouzdanje.

Postoji nekoliko različitih tipova testiranja koje možete koristiti za testiranje vaše aplikacije:

Unit testiranje

Unit testiranje je pristup koji obezbeđuje da funkcije, klase i metode rade onako kako se očekuje, od momenta kada ste ih napravili i tokom celokupnog razvojnog ciklusa. Proverom ulaznih i izlaznih vrednosti različitih funkcija i metoda, možete biti sigurni da je njihova interna logika ispravna. Korišćenjem dependency injection-a i kreiranjem mock (“lažnih”) klasa i stub-ova možete utvrditi da li se zavisnosti ispravno koriste u cilju još bolje pokrivenosti kôda testovima.

Kada kreirate klasu ili funkciju, treba da kreirate i odgovarajući unit test za sve različite slučajeve njenog korišćenja. Osnova stvar koju treba obezbediti jeste prijava grešaka u slučaju prosleđivanja loših argumenata, odnosno ispravan rad u slučaju dobrih argumenata koji joj se proslede. Na taj način ćete biti sigurni da ako kasnije napravite određene izmene u toj klasi ili funkciji, da će prethodne funkcionalnosti nastaviti da rade ispravno. Jedina alternativa ovome je var_dump() u test.php, što naravno nikako nije način za izradu aplikacije, bila ona mala ili velika.

Još jedna primena unit testova je doprinošenje (contribute) u open source projekte. Ako umete da napišete test koji prikazuje neispravnu funkcionalnost, a onda je popravite tako da test prolazi, mnogo su veće šanse da će vaše popravke (patch) biti prihvaćene. Ako imate projekat koji prihvata pull request-ove, onda bi ovo trebalo da bude uslov za njihovo slanje.

PHPUnit frejmvork je de fakto standard za pisanje unit testova za PHP aplikacije, ali postoji i nekoliko alternativa:

Intergraciono testiranje

Sa Wikipedia-je:

Integraciono testiranje (poznato i kao Integracija i testiranje, skraćeno “I&T”) je faza u testiranju softvera u kojoj se individualni moduli kombinuju i testiraju grupno. Obavlja se nakon unit testova, a pre validacionog testiranja. Integraciono testiranje kao ulaz prima module koji su bili predmet unit testova, grupiše ih u veće celine, pa na njih primenjuje testove definisane u planu integracionog testa i zatim kao izlaz dostavlja integrisani sistem spreman za sistemsko testiranje.

Mnogi od alata koji se koriste za unit testove mogu biti korišćeni i za integraciono testiranje, jer se baziraju na istim principima.

Funkcionalno testiranje

Ponekad poznato i kao tkz. acceptance testiranje, funkcionalno testiranje se svodi na korišćenja alata za kreiranje automatizovanih testova koji zapravo koriste vašu aplikaciju umesto da samo proveravaju da pojedinačni delovi kôda rade i komuniciraju ispravno. Ovi alati obično rade na način da koriste realne podatke i simuliraju prave korisnike aplikacije.

Alati za funkcionalno testiranje

Razvoj vođen ponašanjanjem (Behavior Driven Development - BDD)

Postoje dva različita tipa BDD-a: SpecBDD i StoryBDD. SpecBDD se fokusira na tehničko ponašanje kôda, dok je StoryBDD fokusiran na proizvod, ponašanje određene funkcionalnosti i interakciju. PHP ima frejmvorke za oba tipa BDD-a.

U slučaju StoryBDD-a, pišu se čitljive “priče” koje opisuje ponašanje aplikacije. One se zatim mogu pokrenuti kao testovi nad vašom aplikacijom. Frejmvork koji se koristi u PHP aplikacijama za StoryBDD je Behat, koji je inspirisan Ruby Cucumber projektom i koji implementira Gherkin DSL za opis ponašanja funkcionalnosti.

U slučaju SpecBDD-a, pišu se specifikacije koje opisuju kako sâm kôd treba da se ponaša. Umesto testiranja funkcije ili metode, vi opisujete kako ta funkcija ili metod treba da se ponaša. PHP nudi PHPSpec frejmvork u te svrhe. Ovaj frejmvork je inspirisan RSpec projektom za Ruby.

BDD linkovi

Alati za komplementarno testiranje

Pored pojedinačnih TDD i BDD frejmvorka, postoji i niz generičkih frejmvorka i biblioteka koje mogu biti od koristi za bilo koji od ova dva pristupa.

Linkovi

Povratak na početak

Serveri i isporuka (deployment)

PHP aplikacije mogu biti isporučene i pokrenute na produkciskim web serverima na više načina.

Platforma kao servis (PaaS)

PaaS obezbeđuje sistem i mrežnu arhitekturu neophodnu za pokretanje PHP aplikacija na webu. To znači malo ili nimalo konfigurisanja za pokretanje PHP aplikacija ili PHP frejmvorka.

PaaS je nedavno postao popularan metod za isporučivanje, hosting i skaliranje PHP aplikacija svih veličina. Možete naći spisak pružalaca usluge PHP PaaS “Platforma kao servis” u našoj sekciji resursa.

Virtuelni ili namenski serveri

Ako ste upućeni u administraciju sistema ili želite da je naučite, virtuelni ili namenski serveri daju potpunu kontrolu nad produkcionim okruženjem Vaše aplikacije.

nginx i PHP-FPM

PHP, preko ugrađenih PHP-ovih FastCGI proces menadžera (FPM) je lepo uparen sa nginx, laganim web serverom visokih performansi. Koristi manje memorije od Apache-a i bolje rukuje sa više istovremenih zahteva. Ovo je naročito važno na virtuelnim serverima koji nemaju mnogo memorije za trošenje.

Apache i PHP

PHP i Apache imaju dugu zajedničku istoriju. Apache je izuzetno konfigurabilan i ima mnogo dostupnih modula za proširenje funkcionalnosti. To je popularan izbor na deljenim serverima koji se lako podešava za PHP frejmvorke i aplikacije otvorenog koda kao što je WordPress. Nažalost, Apache koristi više resursa nego što ih koristi nginx po default-u i ne može da rukuje sa toliko posetilaca istovremeno.

Apache ima nekoliko mogućih konfiguracija za pokretanje PHP-a. Najčešća i najlakša za podešavanje je prefork MPM sa mod_php5. Iako nije najefikasnija po pitanju memorije, najjednostavnija je za početak rada i za korišćenje. Ovo je verovatno najbolji izbor ukoliko ne želite da se previše upuštate u aspekte serverske administracije. Imajte na umu da ako koristite mod_php5 MORATE koristiti prefork MPM.

Alternativno, ukoliko želite da iz Apache-a izvučete bolje performanse i stabilnost, možete koristiti prednost istog FPM sistema kao nginx i pokrenuti worker MPM ili event MPM sa mod_fastcgi ili mod_fcgid. Ova konfiguracija je po pitanju korišćenja memorije značajno efikasnija i mnogo brža, ali zahteva i više posla oko podešavanja.

Deljeni serveri

PHP na svojoj popularnosti treba da zahvali deljenim serverima. Teško da možete pronaći host(server) bez instaliranog PHP-a, ali proverite da li je instalirana najnovija vezija PHP-a. Deljeni serveri dopuštaju vama i drugim programerima da isporučite sajtove na jednoj zajedničkoj mašini. Dobra strana ovoga je da je to postalo jeftino rešenje. Loša strana je da nikad ne znate kakvu vrstu loma mogu vaši susedi napraviti; preopterećenje servera ili stvaranje bezbednosnih rupa su glavni problemi. Treba izbeći deljene servere, ako budžet namenjen za projekat to može da obezbedi.

Izgradnja i isporučivanje vaše aplikacije

Ako zateknete sebe da ručno radite promene šeme baze podataka ili ručno pokrećete testove pre nego što ažurirate svoje fajlove (ručno), razmislite još jednom! Svaki dodatni ručni zadatak tokom deploy-a nove verzije vaše aplikacije povećava šanse za fatalne greške. Bez obzira da li se bavite jednostavnim ažuriranjem, obimnim procesom izgradnje ili čak strategijom kontinuirane integracije, automatska izgradnja je Vaš prijatelj.

Među zadacima koje biste možda želeli da automatizujete su:

Alati za automatizaciju izgradnje

Alati za izgradnju (build) mogu biti shvaćeni kao skup skripti koje se bave određnim učestalim zadacima isporuke (deploy-a) softvera. Alat za izgradnju nije deo vašeg softvera, on deluje na vaš softver ‘spolja’.

Postoji puno alata otvorenog koda koji vam mogu pomoći pri automatizovanoj izgradnji (build), neki su napisani u PHP-u ali postoje oni koji nisu. To ne treba da vas sputava da ih koristite, ako više odgovaraju za određeni posao. Evo nekoliko primera:

Phing je najlakši način da počnete sa primenom automatizacije isporučivanja koda u svetu PHP-a. Pomoću Phing-a možete da kontrolišete pakovanje, isporuku ili proces testiranja unutar jednostavnog XML fajla. Phing (koji se zasniva na Apache Ant) obezbeđuje bogat set zadataka, obično potrebnih za instalaciju ili ažuriranje web aplikacija i može se proširiti dodatnim prilagođenim zadacima, pisanim u PHP-u.

Capistrano je sistem namenjen srednjim-do-naprednim programerima za izvršavanje komandi na struktuiran, ponovljivi način na jednoj ili više udaljenih mašina. Prekonfigurisan je za primenu na Ruby on Rails aplikacije, međutim ljudi uspešno isporučuju PHP sisteme sa njim. Uspešno korišćenje Capistrana zavisi od radnog iskustva sa Ruby i Rake.

Blog članak Dejva Gardnera PHP Deployment with Capistrano je dobra polazna tačka za PHP programere zainteresovane za Capistrano.

Chef je više od frejmvorka za deploy, to je veoma moćan Ruby baziran sistemski integracioni frejmvork koji ne samo da radi deploy Vaše aplikacije već može izgraditi i celokupno serversko okruženje ili virtuelne kutije(virtual boxes).

Chef resursi za PHP programere:

Za dalje čitanje:

Kontinuirana integracija

Kontinuirana integracija je praksa softverskog razvoja u kojoj članovi tima često integrišu svoj rad, uglavnom svaka osoba integriše najmanje jednom dnevno — što rezultira više integracija dnevno. Mnogi timovi nalaze da ovakav pristup vodi do značajnog smanjenja integracionih problema i omogućava timu da razvija kohezivni softver mnogo brže.

– Martin Fowler

Postoje različiti načini za implementaciju kontinuirane integracije za PHP. Travis CI je odradio odličan posao i učinio kontinuiranu integraciju olakšanom čak i u slučaju manjih projekata. Travis CI je hostovan servis kontinuirane integracije namenjen open source zajednici. Integrisan je sa GitHub-om i nudi prvoklasnu podršku za mnoge jezike, uključujući i PHP.

Za dalje čitanje:

Povratak na početak

Virtuelizacija

Pokretanje vaših aplikacija na različitim okruženjima, razvojno i produkcijsko okruženje, može dovesti do različitih bagova prilikom prebacivanja aplikacije na živ mod. Takođe, kad radite u timu programera, postaje teško vaše okruženje održavati sa najnovijim verzijama svih biblioteka.

Ako razvijate aplikaciju na Windows OS, a deploy radite na Linux OS ili radite u timu, trebalo bi uzeti u obzir korišćenje virtuelnih mašina. Ovo možda izgleda teško, ali pored dobro poznatih virtuelnih okruženja kao što su VMware ili VirtualBox, postoje i dodatni alati koji vam pomažu da podesite virtuelno okruženje iz nekoliko jednostavnih koraka.

Vagrant

Vagrant vam pomaže u kreiranju virtuelnih kutija, oslanjaju ći se na poznato virtuelno okruženje koga konfiguriše na osnovu jednog konfiguracionog fajla. Virtuelne kutije možete podesiti ručno ili koristeći “provisioning” softvere kao što su Puppet ili Chef koji će obaviti manuelni posao za vas.

Kreiranje osnovne virtuelne kutije je dobar način da se uverite da su sve ostale kutije podešene na isti način i otklanja potrebu za ručnim izvršavanjem komplikovanih inicijalnih komandi. Takođe, možete “uništiti” vašu osnovnu virtuelnu kutiju i ponovo je kreirati bez dodatnih koraka. Na taj način omogućeno je da lako kreirate svežu instalaciju.

Vagrant kreira folder za deljenje vašeg koda između hosta i vaše virtuene mašine, tako da možete kreirati i menjati fajlove na host mašini i potom ih pokrenuti unutar virtuelne mašine.

Mala pomoć

Ako vam treba pomoć za prve korake u korišćenju Vagrant alata, pogledajte sledeće linkove/servise:

Docker

Pored Vagranta, još jedan lak način da podesite virtuelno okruženje, bilo razvojno ili produkcijsko, je Docker. Docker vam pomaže da obezbedite Linux kontejner za sve frste aplikacija. Postoji puno korisnih docker slika/kopija (OS image) koje vam obezbeđuju različite servise, pa nemate potrebu za instalacijom tih servisa na vašoj lokalnoj mašini, npr. MySQL ili PostgreSQL i puno drugih. Pogledajte na Docker Hub Registry i naći ćete listu pre-instaliranih kontejnera, koje možete pokrenuti nakon samo nekoliko koraka.

Primer: Pokretanje vaše PHP aplikacije u Docker-u

Posle instaliranog Docker-a na vašoj mašini, u jednom koraku možete pokrenuti Apache sa podrškom za PHP.

Sledeća komanda će preuzeti Apache instalaciju sa ugrađenim PHP-om poslednje verzije i učiniti direktorijum /putanja/do/vasih/php/fajlova dostupnim na URL-u http://localhost:8080:

docker run -d --name my-php-webserver -p 8080:80 -v /putanja/do/vasih/php/fajlova:/var/www/html/ php:apache

Posle pokretanja docker run vaš kontejner je inicijalizovan i pokrenut.

Ako želite da stopirate ili pokrenete ponovo vaš kontejner, možete koristiti postojeće atribute, pa jednostavno pokrenite docker stop my-php-webserver i docker start my-php-webserver bez dodavanja gore navedenih parametara.

Naučite više o Docker-u

Gore navedene komande prikazuju brz način za pokretanje Apache web servera sa ugrađenim PHP-om ali postoji još puno stvari koje možete uraditi uz pomoć Docker-a.

Jedna od najvažnijih koraka za PHP programere je povezivanje web servera sa DB instancom. Kako se ovo radi je vrlo dobro opisano u Docker korisničkom uputstvu.

Povratak na početak

Keširanje

PHP je samo po sebi dosta brz, ali “uska grla” mogu nastati usled operacija kao što su remote konekcije, učitavanje fajlova i slično. Srećom, postoje različiti alati za ubrzavanje određenih delova vaše aplikacije, kao i za smanjivanje učestalosti izvršavanja ovih vremenski zahtevnih radnji.

Opcode keš

Kada se PHP fajl izvršava, on se u pozadini najpre kompajlira u opcode, a zatim se taj opcode izvršava. Ako se PHP fajl ne menja, odgovarajući opcode će biti isti. To znači da korak kompajliranja predstavlja samo bespotrebno trošenje procesorskih resursa.

Upravo ovde opcode keširanje stupa na scenu. Ono sprečava suvišno kompajliranje, tako što se rezultujući opcode čuva u memoriji, a zatim se koristi u narednim izvršavanjima. Instalacija opcode keša je brza i jednostavna, a vaša aplikacija će raditi znatno brže. Zaista nema razloga da ga ne koristite.

Od verzije PHP 5.5 postoji ugrađen opcode keš nazvan OPcache. On je takođe dostupan i za prethodne verzije.

Saznajte više o opcode keširanju:

Keširanje podataka/objekata

Postoje situacije u kojima je isplativo keširati određene objekte, kao na primer neki podatke koju su komplikovani za dohvatanje ili upiti ka bazi čiji se rezultat retko menja. Tada možete koristiti alate za keširanje objekata, kako bi ti podaci bili u memoriji radi njihovog bržeg dohvatanja. Ako sačuvate te podatke u neko skladište (storage) nakon što ih dohvatite, a zatim ih izvlačite iz keša u svakom narednom zahtevu, dobićete znatno poboljšanje performansi, kao i smanjeno opterećenje (load) na vaše DB servere.

Dosta popularnih opcode keš rešenja omogućava keširanje i nekih custom podataka, pa je to još jedan razlog da ih koristite. APCu, XCache i WinCache na raspolaganje imaju API-e za čuvanje podataka iz vašeg PHP kôda u njihov memorijski keš.

Najčešće korišćeni memorijski sistemi za keširanje su APCu i Memcached. APCu je odličan izbor za keširanje objekata, koji poseduje jednostavan API za ubacivanje vaših podataka u njegov memorijski keš i veoma se jednostavno instalira i koristi. Jedini nedostatak APCu-a je to što je vezan za server na kojem je instaliran. S druge strane, Memcached se instalira kao zaseban servis i može mu se pristupati putem mreže, što znači da vaše podatke možete čuvati u veoma brzom skladištu na centralizovanoj lokaciji, a više različitih sistema može da čita podatke iz njega.

Imajte na umu da ako PHP radi kao (Fast-)CGI aplikacija na vašem web serveru, svaki PHP proces će imati svoj keš, pa na primer APCu podaci neće biti deljeni među procesima. U tom slučaju bi trebalo razmisliti o korišćenju Memcached-a, zato što se on ne vezuje za PHP proces.

U umreženoj konfiguraciji APCu se obično bolje pokazuje od Memcached-a u smislu brzine pristupa, ali Memcached se brže i bolje skalira. Ako vaša aplikacija ne radi na više servera ili vam ne trebaju dodatne mogućnosti koje Memcached nudi, onda je APCu verovatno najbolja opcija za keširanje.

Primer korišćenja APCu-a:

<?php
// provera da li postoji unos u kešu pod ključem 'expensive_data'
$data = apc_fetch('expensive_data');
if ($data === false) {
    // podaci nisu keširani; sačuvaj podatke "skupocenog" poziva za sledeći poziv
    apc_add('expensive_data', $data = get_expensive_data());
}

print_r($data);

Pre verzije PHP 5.5, APC je omogućavao keširanje i objekata i opcode-a. APCu je projekat koji omogućava APC-ovo keširanje objekata za PHP verzije >= 5.5, pošto PHP sada ima ugrađen opcode keš (OPcache).

Saznajte više o popularnim sistemima za keširanje:

Povratak na početak

Dokumentovanje kôda

PHPDoc

PHPDoc je de fakto standard za komentarisanje PHP kôda. Postoji dosta različitih tagova. Kompletna lista tagova i primera je dostupna u PHPDoc manual-u.

Sledi primer dokumentovanja klase koja ima nekoliko metoda:

<?php
/**
 * @author A Name <a.name@example.com>
 * @link http://www.phpdoc.org/docs/latest/index.html
 */
class DateTimeHelper
{
    /**
     * @param mixed $anything Anything that we can convert to a \DateTime object
     *
     * @throws \InvalidArgumentException
     *
     * @return \DateTime
     */
    public function dateTimeFromAnything($anything)
    {
        $type = gettype($anything);

        switch ($type) {
            // Some code that tries to return a \DateTime object
        }

        throw new \InvalidArgumentException(
            "Failed Converting param of type '{$type}' to DateTime object"
        );
    }

    /**
     * @param mixed $date Anything that we can convert to a \DateTime object
     *
     * @return void
     */
    public function printISO8601Date($date)
    {
        echo $this->dateTimeFromAnything($date)->format('c');
    }

    /**
     * @param mixed $date Anything that we can convert to a \DateTime object
     */
    public function printRFC2822Date($date)
    {
        echo $this->dateTimeFromAnything($date)->format('r');
    }
}

Dokumentacija za samu klasu ima @author i @link tagove. @author tag daje informaciju o autoru kôda i može ih biti više od jednog u slučaju da je na klasi radilo nekoliko programera. @link tag se koristi za linkovanje na web sajt, i predstavlja vezu između odgovarajućeg web sajta i kôda.

Unutar klase, prvi metod ima @param tag, kojim se dokumentuje tip, ime i opis parametra kojeg ovaj metod prihvata. Takođe, definisani su i @return i @throws tagovi za dokumentovanje povratnog tipa, odnosno izuzetka kojeg ovaj metod može da baci.

Drugi i treći metod su dosta slični i imaju po jedan @param tag kao i prvi metod. Bitna razlika između ova dva metoda je ta što u jednom doc block-u postoji @return tag, a u drugom ne. @return void nam eksplicitno stavlja do znanja da nema povratne vrednosti. Izostavljanje @return void izraza ima isti efekat.

Povratak na početak

Resursi

Zvanični izvori

Preporuke za praćenje

U početku, teško je naći zanimljive i dobro upućene članove PHP zajednice. Iscrpna lista članova PHP zajednice i njihovih Twitter naloga je dostupna ovde:

Mentorisanje

PHP PaaS provajderi

Frejmvorci

Umesto da “izmišljaju toplu vodu”, dosta PHP programera koristi frejmvorke (frameworks) za razvoj web aplikacija. Frejmvork apstrakuje većinu low-level pitanja i pruža koristan, jednostavan interejs za rešavanje nekih uobičajenih zadataka.

Ne morate da koristite frejmvork za svaki projekat. Nekada je “čist” PHP bolji izbor, ali ako vam je potreban frejmvork postoje tri glavne kategorije:

Mikro frejmvorci su u suštini maksimalno optimizovani wrapper-i za rutiranje HTTP request-a na callback, kontroler, metod, itd. Ponekad dolaze sa dodatnim bibliotekama koje potpomažu razvoj, kao što su komponente za komunikaciju sa bazom i slično. Najčešće se koriste za razvoj web servisa.

Mnogi frejmvorci dodaju priličan broj funkcionalnosti na ono što nude mikro frejmvorci, i takvi su poznati kao Full-Stack frejmvorci. Oni često poseduju ugrađene ORM-ove, komponente za autentifikaciju, itd.

Frejmvorci bazirani na komponentama su zapravo kolekcije usko specijalizovanih i jednonamenskih biblioteka. Moguće je iskombinovati komponente nekoliko frejmvorka ovog tipa i tako napraviti mikro ili full-stack frejmvork.

Komponente

Kao što je prethodno pomenuto, komponente predstavljau još jedan pristup u cilju kreiranja, distribuiranja i implementiranja deljenog kôda. Postoje različiti repozitorijumi (repositories) komponenata, od kojih su najpoznatija sledeća dva:

Za oba repozitorijuma postoje CLI alati putem kojih se obavlja instalacija i update-ovanje komponenti, što je detaljnije objašnjeno u poglavlju “Upravljanje zavisnostima”.

Takođe, postoje frejmvorci bazirani na komponentama i vendori komponenti koji zapravo nisu frejmvorci. Ovi projekti nude još jedan izvor paketa koji imaju malo ili u najboljem slučaju uopšte nemaju zavisnosti ka drugim paketima ili određenim frejmvorcima.

Tako na primer možete koristiti FuelPHP paket za validaciju, bez potrebe da koristite sam FuelPHP frejmvork.

Laravel-ove Illuminate komponente će biti još bolje izdvojene iz samog Laravel frejmvorka. Za sada, samo one koju su najbolje izdvojene su na prethodnom spisku.

Ostali korisni resursi

Cheatsheets

Dobre prakse

PHP univerzum

Video tutorijali

Youtube kanali

Video materijali koji se naplaćuju

Knjige

Postoji obilje knjiga o PHP-u, ali neke su dosta zastarele i samim tim ne sadrže tačne informacije. Postoje čak i knjige o “PHP 6” koji ne postoji niti će ikada postojati. Sledeća glavna verzija PHP će se zvati “PHP 7” upravo zbog tih knjiga.

Ovaj dokument ima za cilj da bude dokument u kojem će biti preporuke knjiga na temu PHP razvoja uopšte. Ako želite da vaša knjiga bude dodata, pošaljite pull request, pa će vaš predlog biti razmotren.

Besplatne knjige

Knjige koje se plaćaju

Povratak na početak

Zajednica

PHP zajednica je veoma velika, ali isto tako i dosta raznolika. Njeni članovi su spremni i voljni da daju podršku novim PHP programerima. Razmislite o pridruživanju vašoj lokalnoj PHP grupi ili posećivanju većih PHP konferencija kako biste naučili o najboljim praksama koje se na njima prezentuju. Možete se i družiti na #phpc kanalu IRC-a, na irc.freenode.com ili pratili @phpc Twitter nalog. Pridružite se, upoznajte nove programere, naučite nove stvari i što je najvažnije, steknite nove prijatelje! Što se tiče nekih ostalih resursa, to su Google+ PHP zajednica programera i StackOverflow.

Pogledajte zvaničan kalendar PHP događaja

Korisničke grupe

Ako živite u većem gradu, velike su šanse da se u vašoj blizini nalazi neka PHP korisnička grupa. Možete vrlo lako pronaći lokalnu grupu na php.net listi korisničkih grupa, koja je bazirana na PHP.ug. Alternativni izvori bi bili Meetup.com ili traženje pojma php user group near me pomoću vašeg omiljenog pretraživača (npr. Google). Ako živite u manjem gradu, u njemu verovatno nema lokalne grupe, a ako je već tako, oformite novu!

Posebno priznanje treba odati dvema globalnim korisničkim grupama: NomadPHP i PHPWomen. NomadPHP nudi online sastanke dvaput mesečno, sa prezentacijama koje drže neki od vodećih predavača u PHP zajednici. PHPWomen je korisnička grupa prevashodno namenjena ženama u PHP svetu. Članstvo je otvoreno za sve koji podržavaju raznoliku zajednicu. PHPWomen ima mrežu za podršku, mentorstvo i edukaciju, i uopšte promoviše stvaranje “ženski nastrojene” i profesionalne atmosfere.

Pročitajte o korisničkim grupam na PHP Wiki stranici

Konferencije

PHP zajednica takođe organizuje velike regionalne i nacionalne konferencije u zemljama širom sveta. Poznati članovi PHP zajednice obično govore na većim događajima, te je to dobra prilika da učite direktno od vodećih ljudi iz ove branše.

Pretražite PHP konferencije

ElePHPants

ElePHPant je divna maskota PHP projekta sa slikom slona u dizajnu. Originalno je dizajniran za PHP projekat iz 1998 od strane Vincent Pontier - duhovni otac hiljade elePHPants maskota širom sveta i 10 godina kasnije neodoljiva plišana igračka slona postaje simbol. Sada elePHPants možete naći na skoro svim PHP konferencijama i kod mnogih PHP programera kao inspiraciju i zabavu.

Intervju sa Vincent Pontier