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 poslednja 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 nekompatibilnosti. Ukoliko niste sigurni u kojoj verziji se funkcija ili odlika nalaze, možete proveriti PHP dokumentaciju na php.net websajtu.

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. Mavericks dolazi sa verzijom 5.4.17, Yosemite sa 5.5.9, El Capitan sa 5.5.29 i Sierra sa 5.6.24, ali to nije dovoljno dobro pošto je dostupna novija verzija PHP 7.1.

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, php56, php70 ili php71 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 php54, php55, php56, php70 ili php71 pomoću port install komande, na primer:

sudo port install php56
sudo port install php71

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

sudo port select --set php php71

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 7.1. 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, OpenServer 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.

Generalno izvršavanje vaše aplikacije u drugačijim okruženjima u development i produkcionom modu može dovesti do čudnih grešaka koje će početi da iskaču kada odete uživo. Ako razvijate na Windows-u i deploy-ujete na Linux-u (ili bilo šta drugo osim Windows-a) onda biste trebali da razmislite da koristite Virtuelnu Mašinu.

Chris Tankersley ima veoma koristan blog post o alatima koje on koristi da bi razvijao uz pomoć PHP-a na Windows-u.

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, Laravel, 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 u cilju dobijanja rezultata provera u realnom vremenu.

Automatsku korekciju izgleda kôda možete obaviti korišćenjem jednog od sledećih alata:

phpcs možete pokrenuti ručno iz konzole:

phpcs -sw --standard=PSR2 file.php

Prikazać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 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.

Ukoliko imate PHP_CodeSniffer onda probleme sa izgledom kôda koje on prijavljuje možete automatski popraviti uz pomoć alata PHP Code Beautifier and Fixer.

phpcbf -w --standard=PSR2 file.php

Druga opcija je da koristite PHP Coding Standards Fixer. Ovaj alat će vam prikazati koje vrste grešaka je vaš izgled kôda imao, pre nego što ga je on popravio.

php-cs-fixer fix -v --level=psr2 file.php

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, koristite 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. Composer je analogan NPM-u u node.js svetu, ili Bundler-u u Ruby svetu.

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

Najbezbedniji način da preuzmete Composer jeste da pratite zvanične instrukcije. Ovo će proveriti ispravnost instalacione datoteke tako da ona nije oštećena ili kompromitovana. Instalaciona datoteka instalira Composer lokalno u vaš trenutni radni direktorijum.

Mi preporučujemo da ga instalirate globalno (npr. jedina kopija u /usr/local/bin) - da biste to uradili izvšite sledeću komandu:

mv composer.phar /usr/local/bin/composer

Napomena: Ukoliko zbog nedovoljnih ovlašćenja komanda iznad ne uspe, samo dodajte sudo ispred nje i ponovo je izvršite.

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ćete 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 projektu radite sa drugim programerima, osigurajte da je fajl composer.lock distribuiran (verzionisan), tako da nakon što oni pokrenu composer install, dobiće iste verzije kao i vi. Da biste ažurirali zavisnosti aplikacije, koristite composer update komandu. Ne koristite composer update komandu prilikom deploy-a, već samo composer install, ili u suprotnom možete završiti sa drugačijim verzijama paketa na produkciji.

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 neki 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() proizvodne (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 sa klasom DateTime 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') . ' ';
}

Carbon je popularna ekstenzija (produžetak) PHP API-ja za DateTime. Ona nasleđuje sve osobine od DateTime klase tako da zahteva minimalne izmene postojećeg kôda dok donosi dodatne osobine kao što su podrška za lokalizaciju (Localization), dodatni načini za dodavanje, oduzimanje i formatiranje objekata tipa DateTime, i dodatno sredstva za testiranje vašeg kôda tako da možete da simulirate datum/vreme po vašem izboru.

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 i 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 je zaista dobro sačuvan
$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 stranica</title>
    </head>
    <body>
        <?php
        foreach($result as $row){
            print($row->Body);  // Ovde bi trebalo da imamo ispravan ispis našeg UTF-8 stringa u browser-u
        }
        ?>
    </body>
</html>

Za dalje čitanje

Internacionalizacija (i18n) i Lokalizacija (l10n)

Objašnjenje za neupućene: i18n i l10n su numeromi, vrsta skraćenice kod koje se brojevi koriste da skrate reči - u našem slučaju, internacionalizacija postaje i18n i lokalizacija postaje l10n.

Pre svega, potrebno je da definišemo ova dva slična koncepta i ostale povezane stvari:

Uobičajeni načini implementacije

Najlakši način da implementirate internacionalizaciju u PHP softveru jeste da koristite fajlove sa nizovima i da koristite njihove stringove u templejtima, kao na primer <h1><?=$TRANS['title_about_page']?></h1>. Ovo je, međutim, nepreporučeni način za ozbiljne projekte zato što ostavlja neke probleme za održavanje usput - neki će se pojaviti na početku, kao što je pluralizacija. Tako da, molimo vas, ne pokušavajte ovo ako će vaš projekat sadržati više od nekolicine stranica.

Najklasičniji način koji se često uzima kao primer za i18n i l10n je Unix alatka zvana gettext. Pojavila se

  1. godine i još uvek je potpuno rešenje za prevođenje softvera. Poprilično je lako započeti da je koristite, dok još uvek podržava moćne alate. Ovde ćemo govoriti o Gettext alatki. Takođe, kako vas ne bismo zbunjivali sa primerima sa konzolne linije (CLI), predstavićemo odličnu grafičku (GUI) aplikaciju koja se može koristiti za jednostavno ažuriranje vaših l10n izvornih fajlova.

Drugi alati

Postoje opšte biblioteke koje se koriste i koje podržavanju Gettext i druge i18n implementacije. Neke od njih vam mogu delovati jednostavnije za instalaciju ili za podržavanje dodatnih osobina ili formata i18n fajlova. U ovom dokumentu, mi se fokusiramo na alate koji su podržani u samom PHP jeziku, ali ćemo nabrojati ostale kako bismo bili kompletni:

Drugi frejmvorci takođe poseduju i18n module, ali nisu dostupni izvan njihovog sopstvenog kôda:

Ukoliko se odlučite za neku od biblioteka koja ne podržava ekstraktore, možda ćete poželeti da koristite gettext formate, kako biste mogli da koristite originalne gettext alate (uključujući Poedit) kao što je objašnjeno u ostatku ovog poglavlja.

Gettext

Instalacija

Možda će biti potrebno da instalirate Gettext i njegovu PHP biblioteku uz pomoć vašeg menadžera paketa, npr. apt-get ili yum. Nakon instalacije, potrebno je ga uključite tako što ćete dodati extension=gettext.so (Linux/Unix) ili extension=php_gettext.dll (Windows) u vaš php.ini fajl.

Ovde ćemo takođe koristiti Poedit za pravljenje fajlova za prevođenje (translation files). Ovaj alat ćete najverovatnije pronaći u vašem sistemskom menadžeru paketa; dostupan je za Unix, Mac, i Windows platforme, i takođe se može besplatno preuzeti sa njihovog sajta.

Struktura

Tipovi fajlova

Postoje tri fajla sa kojima se obično susrećete kada radite sa gettext alatom. Glavni fajlovi su PO (Portabilni Objekat) i MO (Mašinski Objekat), gde je prva vrsta fajla lista “prevedenih objekata” koje možete da čitate i druga vrsta pripadajući binarni podaci koje gettext tumači u procesu lokalizacije. Takođe, postoji i POT (Templejt) fajl, koji jednostavno sadrži sve postojeće ključeve iz vaših fajlova sa izvornim kôdom i koji se može koristiti kao priručnik za generisanje i ažuriranje svih PO fajlova. Ovi templejt fajlovi nisu obavezni: u zavisnosti od alata koji koristite za l10n, možete sasvim fino funkcionisati samo sa PO/MO fajlovima. Uvek ćete imati po jedan par PO/MO fajlova po jeziku i regionu, ali samo jedan POT fajl po području (domenu).

Domeni

Postoje slučajevi, u velikim projektima, kada će vam možda biti potrebno da odvojite prevode kada identične reči imaju različito značenje u zavisnosti od konteksta. U ovim slučajevima potrebno je da ih raspodelite po različitim domenima. Domeni su, u suštini, imenovane grupe POT/PO/MO fajlova, gde se za ime fajla kaže da je od navedenog domena prevoda. Mali i projekti srednje veličine obično, zbog jednostavnosti, koriste samo jedan domen; njegovo ime je obavezno ali ćemo mi ovde koristiti naziv “main” (glavni) u primerima ispod. U Symfony projektima, na primer, domeni se koriste da razgraniče prevođenje validacionih poruka.

Kôd lokala

Lokal je jednostavno kôd koji identifikuje jednu verziju jezika. Definisan je u saglasnosti sa ISO 639-1 i ISO 3166-1 alpha-2 specifikacijama: dva mala slova za jezik, opciono sledi donja crta i dva velika slova koja označavaju zemlju ili kôd regiona. Za retke jezike, koriste se tri slova.

Za neka govorna područja, deo sa zemljom možda može izgledati suvišno. U suštini, neki jezici imaju dijalekte u različitim zemljama, kao na primer austrijski nemački (de_AT) ili brazilski portugalski (pt_BR). Drugi deo se koristi za razlikovanje između ovih dijalekata - ukoliko nije prisutan smatra se da se koristi “generička” ili “hibridna” verzija jezika.

Struktura direktorijuma

Ukoliko želite da koristite Gettext, moraćete da se pridržavate određene strukture foldera. Prvo, u vašem repozitorijumu biće potrebno da izaberete proizvoljnu korenu (root) lokaciju za vaše l10n fajlove. Unutar tog direktorijuma, imaćete po jedan direktorijum za svaki lokal koji podržavate, i fiksirani LC_MESSAGES direktorijum koji će čuvati sve vaše PO/MO parove: Na primer:

<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/
       └─ ...

Oblici množine

Kao što smo napomenuli u uvodu, različiti jezici mogu drugačije da upotrebljavaju pravila množine. Međutim, gettext nas lišava ovih muka još jedanput. Kada pravite novi .po fajl, potrebno je da deklarišete pravila množine za taj jezik, i delovi prevoda koji se menjaju u množini će imati drugačije oblike za svako od ovih pravila. Kada pozivate Gettext u kôdu, moraćete da navedete broj koji se odnosi na rečenicu, i on će izabrati pravilan oblik koji će se koristiti - čak će zameniti string ukoliko je to potrebno.

Pravila množine uključuju broj dostupnih oblika množine i bulov (boolean) test sa n koji definiše za koje pravilo zadati broj odgovara (odbrojavanje kreće od 0). Na primer:

Sada kada ste razumeli osnove kako pravila množine funkcionišu - a ukoliko niste, molimo vas da pogledate detaljnije objašnjenje u LingoHub tutorijalu -, možda ćete poželeti da prekopirate neka pravila sa liste umesto da ih ručno pišete.

Kada pozivate Gettext da uradi lokalizaciju u rečenicama sa brojiocima, biće potrebno da mu takođe obezbedite relevantan broj. Gettext će sam shvatiti koje pravilo da upotrebi i iskoristiti ispravnu lokalizovanu verziju. U .po fajlu moraćete da unesete različitu rečenicu za svako definisano pravilo množine.

Primer implementacije

Nakon sve te teorije, hajde da uradimo nešto praktično. Pred nama je jedan izvod .po fajla - ne obazirite se na njegov format, već na njegov sadržaj, kasnije ćete naučiti kako da ga jednostavno modifikujete:

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"

Prva sekcija služi kao zaglavlje, gde su msgid i msgstr posebno prazni. Ono opisuje koji enkoding fajl koristi, pravila množine i ostale stvari koje su manje važne. Druga sekcija prevodi jednostavan string iz engleskog u brazilski portugalski, i treća takođe, ali upotrebljava zamenu stringova uz pomoć funkcije sprintf tako da prevod može da sadrži korisnikovo ime i datum posete. Poslednja sekcija je primer oblika množine, koja prikazuje oblike jednine i množine kao msgid u engleskom i njihove odgovarajuće prevode kao msgstr 0 i 1 (prateći broj za zadato pravilo množine). Tu se takođe koristi zamena stringova, tako da se broj može direktno videti u rečenici ukoliko upotrebite %d. Oblik množine uvek ima dve verzije msgid (jednina i množina), tako da se ne preporučuje da koristite kompleksan jezik za izvor prevođenja.

Diskusija o l10n ključevima

Kao što ste verovatno primetili, mi koristimo stvarnu rečenicu u engleskom za ID izvora. To polje msgid je isto koje se koristi u svim vašim .po fajlovaima, što znači da će ostali jezici imati isti format i ista msgid polja ali prevedene msgstr linije.

Dok govorimo o ključevima za prevode, postoje dve glavne “škole”:

  1. msgid kao stvarna rečenica.
    Glavne prednosti su:
    • ukoliko postoji neki deo softvera koji još uvek nije preveden u bilo kom jeziku, prikazani ključ će i dalje imati neko značenje. Na primer ako se potrudite iz sveg srca da prevedete engleski u španski, ali vam je potrebna pomoć da prevedete u francuski, možete da objavite novu stranicu sa francuskim prevodom kome nedostaju rečenice, i delovi websajta će se prikazati na engleskom;
    • prevodicu je mnogo lakše da razume o čemu se radi i da pravilno prevede oslanjajući se na msgid;
    • dobijate “besplatno” jedan l10n za jedan jezik - izvorni;
    • jedina mana: ukoliko je potrebno da promenite stvarni tekst, moraćete da zamenite isti msgid u više jezičkih fajlova.
  2. msgid kao jedinstveni, struktuirani ključ.
    Opisala bi ulogu rečenice u aplikaciji u struktuiranom smislu, uključujući šablon ili deo gde je string lociran umesto njegov sadržaj.
    • odlično je dobro organizovati kôd, tako da se razdvoji tekstualni sadržaj od logike templejta.
    • međutim, to može doneti probleme prevodiocu kojem bi nedostajao kontekst. Izvorni jezički fajl bi bio potreban kao osnova za ostale prevode. Na primer: idealno, developer bi mogao da ima jedan en.po fajl koji bi prevodioci pročitali kako bi znali šta da napišu u fr.po fajlu.
    • prevodi koji nedostaju bi se prikazali kao nerazumni ključevi na ekranu (top_menu.welcome umesto Hello there, User! na, recimo, nepotpuno prevedenoj stranici na francuskom jeziku). To je dobro jer bi se na taj način forsiralo potpuno prevođenje pre objave - ali je zato loše zato što bi se problemi sa prevodima prikazali zaista odvratno na ekranu. Neke biblioteke, međutim, imaju jednu opciju da navedete jedan “fallback” jezik, koji vam pruža slično ponašanje kao prethodni pristup.

Gettext priručnik favorizuje prvi pristup jer je, generalno, lakši za prevodioce i korisnike u slučaju nevolje. Taj pristup ćemo i mi ovde koristiti. Međutim, Symfony dokumentacija favorizuje prevode koji se baziraju na ključnim rečima, da bi omogućili nezavisne promene svih prevoda bez efekata na templejte.

Svakodnevna upotreba

Za uobičajene aplikacije, normalno biste koristili neke Gettext funkcije dok pišete statičan tekst u vašim stranicama. Te rečenice bi se onda pojavile u .po fajlovima, pa bi se prevele, pa kompajlirale u .mo fajlove i onda bi ih koristio Gettext prilikom prikazivanja sadržaja. Prema tome, hajde da ovo što smo diskutovali do sada povežemo u celinu u korak-po-korak primeru:

1. Primer templejt fajla koji sadrži različite pozive gettext alata

<?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. Primer konfiguracionog fajla (i18n_setup.php kao što je gore korišćen), koji bira pravilan lokal i konfiguriše Gettext

<?php
/**
 * Verifikuje da li je zadati $locale podržan u projektu
 *
 * @param string $locale
 * @return bool
 */
function valid($locale) {
   return in_array($locale, ['en_US', 'en', 'pt_BR', 'pt', 'es_ES', 'es');
}

// postavlja izvorni/podrazumevani lokal, za informativne potrebne
$lang = 'en_US';

if (isset($_GET['lang']) && valid($_GET['lang'])) {
    // lokal se može promeniti kroz query-string
    $lang = $_GET['lang'];    // ovo biste trebali sanirati!
    setcookie('lang', $lang); // sačuvan je u kolačiću kako bi se ponovo mogao koristiti
} elseif (isset($_COOKIE['lang']) && valid($_COOKIE['lang'])) {
    // ako kolačić postoji, hajde da ga zadržimo
    $lang = $_COOKIE['lang']; // ovo biste trebali sanirati!
} elseif (isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) {
    // podrazumevano: proverite koje jezike korisnik prihvata na osnovu informacije iz pregledača (browser)
    $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;
        }
    }
}

// ovde definišemo globalni sistemski lokal na osnovu pronađenog jezika
putenv("LANG=$lang");

// ovo na primer može biti korisno za funkcije za rad sa datumima (LC_TIME) ili za formatiranje novca (LC_MONETARY)
setlocale(LC_ALL, $lang);

// ovo ukazuje Gettext-u da traži ../locales/<lang>/LC_MESSAGES/main.mo
bindtextdomain('main', '../locales');

// naglasiti koje šifrovanje (encoding) se treba koristiti prilikom čitanja datoteke (fajla)
bind_textdomain_codeset('main', 'UTF-8');

// ukoliko vaša aplikacija ima više domena, kao što je pomenuto iznad, ovde biste ih trebali vezati
bindtextdomain('forum', '../locales');
bind_textdomain_codeset('forum', 'UTF-8');

// ovde naglašavamo na koji podrazumevani domen će gettext() pozivi da se odnose
textdomain('main');

// ovo bi trebalo da traži string u forum.mo umesto main.mo
// echo dgettext('forum', 'Welcome back!');
?>

3. Pripremanje prevoda za prvu upotrebu

Da bi stvari bile jednostavnije - i jedna od moćnih prednosti koje Gettext nudi u odnosu na custom i18n pakete - je njegov lični tip fajla. “Čoveče, to je poprilično teško razumeti i izmeniti ručno, jednostavan niz bi bio lakši!” ne greši, aplikacije kao što je Poedit su ovde da bi pomogle - poprilično. Možete preuzeti program sa njihovog websajta i on je besplatan i dostupan za sve platforme. Veoma je lako navići se da koristite ovaj alat, ali je istovremeno veoma moćan - dok on koristi sve moćne osobine koje Gettext nudi.

Prilikom prvog pokretanja, potrebno je da iz menija izaberete “File > New Catalog”. Tu ćete imati mali ekran gde ćemo postaviti teren tako da sve ostalo radi lagodno. Kasnije, bićete u mogućnosti da pronađete ova podešavanja kroz “Catalog > Properties”:

Nakon podešavanja ovih tačaka bićete upitani da sačuvate fajl - koristeći strukturu direktorijuma koju smo spomenuli, i onda će se pokrenuti skeniranje vaših izvornih fajlova kako bi se pronašli pozivi za lokalizaciju. Oni će biti kreirani kao prazni redovi u vašoj tabeli sa prevodima, i vi ćete početi da unosite lokalizovane verzije ovih stringova. Sačuvajte je i .mo fajl će se (re)kompajlirati u istom folderu i na kraju: vaš projekat je internacionalizovan.

4. Prevođenje stringova

Kao što ste verovatno ranije primetili, postoje dva glavna tipa lokalizovanih stringova: jednostavni i oni sa oblicima množine. Prva grupa ima jednostavno dva polja: izvorni i lokalizovani string. Izvorni string se ne može modifikovati jer Gettext/Poedit nemaju tu mogućnost da menjate izvorne fajlove - potrebno je da sami izmenite izvor i ponovo skenirate fajlove. Savet: možete pritisnuti desni klik na liniju za prevođenje i on će vam prikazati izvorne fajlove i linije gde se taj string koristi. Na drugu stranu, stringovi sa oblicima množine sadrže dva polja da prikažu dva izvorna stringa, i tabove kako biste mogli da konfigurišete različite konačne oblike.

Svaki put kada izmenite vaše izvore i kada je potrebno da ažurirate prevode, samo pritisnite Osveži (Refresh) i Poedit će ponovo skenirati kôd, ukloniti nepostojeće zapise, i spojiti one koji su promenjeni i dodati nove. Možda će čak pokušati da pogodi neke prevode, oslanjajući se na one koje ste vi već uneli. Ova nagađanja i izmenjeni zapisi će dobiti obeleživač “Fuzzy” što ukazuje da im je potrebna revizija, i biće naglašeni u listi. Takođe je korisno ukoliko imate tim prevodioca i neko pokuša da napiše nešto u šta nije sasvim siguran: samo obeležite sa Fuzzy i neko drugi će revidirati kasnije.

Konačno, proporučljivo je da ostavite “View > Untranslated entries first” aktivirano, jer će vam mnogo pomoći da ne zaboravite nijedan zapis. Iz tog menija, takođe možete otvoriti delove UI-ja koji vam omogućavaju da ostavite informacije o kontekstu za prevodioce, ukoliko je potrebno.

Saveti i Trikovi

Mogući problemi sa keširanjem

Ukoliko koristite PHP kao modul na Apache-u (mod_php), možda ćete imati problema sa keširanim .mo fajlom. To se dešava kada se prvi put pročita, i onda, da biste ga ažurirali, možda ćete morati da restartujete server. Na Nginx i PHP verziji 5 obično je potrebno da nekoliko puta osvežite stranicu da biste osvežili keš prevoda, i u PHP verziji 7 retko da je i potrebno.

Dodatne pomoćne funkcije

Većina ljudi preferira da koristi lakši način _() umesto gettext(). Mnoge proizvoljne i18n biblioteke u frejmvorcima koriste nešto slično kao t(), kako bi kôd za prevođenje učinili kraćim. Međutim, to je jedina funkcija koja nudi skraćenicu. Možda ćete poželeti u vašem projektu da dodate neke druge, kao na primer __() ili _n() za ngettext(), ili možda simpatičnu _r() koja bi spojila pozive ka gettext() i sprintf(). Druge biblioteke, kao što je Gettext od oscarotero takođe obezbeđuju pomoćne funkcije kao što su ove.

U tim slučajevima, biće potrebno da date instrukcije pomoćnom Gettext alatu kako da izvuče stringove iz ovih novih funkcija. Ne plašite se, veoma je jednostavno. U pitanju je samo jedno polje u .po fajlu, ili ekran za podešavanje u Poedit-u. U editoru, ta opcija je u “Catalog > Properties > Source keywords”. Tu ćete uključiti specifikaciju za ove nove funkcije, pri čemu ćete poštovati specifičan format:

Nakon što uključite ova nova pravila u .po fajl, novo skeniranje će doneti vaše nove stringove isto onako lako kao pre.

Reference

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 (zavisnost) 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 ubacujemo (injektujemo) samo one 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 refaktorisanjem, 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 je neverovatno stara i prevaziđena sa druge dve ekstenzije:

mysql ekstenzija za PHP ne samo da više nije u aktivnom razvoju, već je prevaziđena od verzije PHP 5.5.0 i zvanično uklonjena u verziji PHP 7.0.

Da biste se sačuvali od kopanja po podešavanjima u vašem php.ini fajlu da biste videli koji modul koristite, jedna od opcija jeste da u vašem tekst editoru izvršite pretragu za mysql_* termin. Ukoliko pronađete bilo koju od funkcija kao što su mysql_connect() i mysql_query() to znači da je mysql ekstenzija u upotrebi.

Čak ukoliko još uvek ne koristite PHP verziju 7.x, nerazmatranje da izvršite ovu nadogradnju što pre je moguće će vam samo otežati nadogradnju kada se zaista odlučite za PHP 7.x. Najbolja opcija je da zamenite korišćenje mysql esktenzije sa mysqli ili PDO u skladu sa sopstvenim tempom razvoja.

Ukoliko vršite nadogradnju sa mysql na mysqli čuvajte se lenjog uputstva za nadogradnju koji predlaže da je samo dovoljno da pretražite i zamenite mysql_* sa mysqli_*. Ne samo da je to preveliko uprošćavanje, već se i ne spominju prednosti koje mysqli pruža, kao što su vezivanje parametara (parameter binding), koje PDO takođe nudi.

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, Brainy, ili 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 keširanje ispravno koristi.

*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. U debug modu (ili “dev” modu) oba ova frejmvorka će prikazati lep i čist stack trace.

Takođe, postoje neki paketi za bolje obrađivanje grešaka i izuzetaka i obaveštavanje o njima. Na primer Whoops! koji dolazi sa podrazumevanom Laravel instalacijom ali se može koristiti u bilo kojem drugom frejmvorku.

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 morate da tražite grešku i čitate dokumentaciju kako biste pronaš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 mogle 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.

Lozinke takođe trebaju biti pojedinačno posoljene (salted) tako što ćete dodati proizvoljni string svakoj lozinci pre nego što je hešujete. Ovo sprečava napade uz pomoć rečnika i upotreba “rainbow tabela” (obrnuta lista kriptografskih heševa za uobičajene lozinke).

Hešovanje i soljenje (salting) su od vitalnog značaja jer korisnici često koriste iste lozinke za više servisa i kvalitet same lozinke može biti niskog nivoa.

Srećom, u današnje vreme PHP sve ovo rešava na lak način.

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’ u poređenju sa ‘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() funkcija će se za vas pobrinuti za “soljenje (salting)”. So (salt) je sačuvana zajedno sa algoritmom i “cenom” (cost) kao jedan deo heša. Funkcija password_verify() se koristi da izdvoji ovu informaciju kako bi proverila lozinku tako da vama nije potrebno odvojeno polje u bazi podataka da biste sačuvali vaše soli (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 telo 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, spajati 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

Deserijalizacija

Opasno je koristiti funkciju unserialize() kako biste deserijalizovali podatke koje dolaze od korisnika ili drugih nepoverljivih izvora. Ukoliko to uradite moguće je da dozvolite zlonamernim korisnicima da instanciraju objekte (sa poljima objekta koje su oni definisali) čiji će se destruktori izvršiti, čak iako se sami objekti ne koriste. Prema tome, trebali biste da izbegavate deserijalizaciju nepoverljivih podataka.

Ako je baš neophodno da deserijalizujete podatke od nepouzdanih izvora, koristite opciju allowed_classes koja je dostupna u PHP verziji 7 kako biste ograničili koje tipove objekata je dozvoljeno deserijalizovati.

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

Dodatni alati za 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 produkcionim 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đenog PHP-ovog 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.

Ukoliko koristite Apache verziju 2.4 ili noviju, možete koristiti mod_proxy_fcgi kako biste dobili odlične performanse koje se lako podešavaju.

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.

Kako biste bili sigurni da vaši deljeni serveri nude poslednje verzije PHP-a proverite PHP verzije.

Izgradnja i isporučivanje (deploy) 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 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:

Deploy alati

Deploy alati se mogu opisati kao skup skripti koje se bave određenim učestalim zadacima deploy-a (isporuke) softvera. Alat za izgradnju nije deo vašeg softvera, on deluje na vaš softver ‘spolja’.

Postoji puno alata otvorenog kôda 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 može da kontroliše pakovanje, isporuku ili proces testiranja iz XML build 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. Phing je stabilan i robustan alat koji je na tržištu već duži period, međutim može se smatrati pomalo staromodnim zbog načina na koji se konfiguriše (XML fajlovi).

Capistrano je sistem namenjen srednjim-do-naprednim programerima za izvršavanje komandi na struktuiran, ponovljiv način na jednoj ili više udaljenih mašina. Unapred je podešen za deploy-ovanje Ruby on Rails aplikacija, međutim vi možete uspešno deploy-ovati i 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.

Rocketeer inspiraciju i filozofiju pronalazi iz Laravel frejmvorka. Cilj mu je da bude brz, elegantan i lak za upotrebu sa pametnim podrazumevanim vrednostima. Podržava višestruke servere, višestruke etape (stage), atomične deploy-ove i proces deployment-a se može obaviti paralelnim izvršavanjem. Sve u alatu se može “na vruće” (hot swap) zameniti ili proširiti, i sve je napisano u PHP-u.

Deployer je deploy alat napisan u PHP-u. Jednostavan je i funkcionalan. Funkcionalnosti uključuju paralelno izvršavanje zadataka, atomično deploy-ovanje i očuvanje konzistentnosti između servera. Dostupni su recepti uobičajenih zadataka za Symfony, Laravel, Zend Framework i Yii. Artikal Easy Deployment of PHP Applications with Deployer od autora Younes Rafie je odličan tutorijal kako da deploy-ujete vašu aplikaciju sa ovim alatom.

Magallanes je još jedan alat napisan u PHP-u sa jednostavnom konfiguracijom u YAML fajlovima. On podržava višestruke servere i okruženja, atomičan deployment, i sadrži neke ugrađene zadatke koje možete iskoristiti za uobičajene alate i frejmvorke.

Za dalje čitanje:

Server Provisioning

Upravljanje serverima i njihovo podešavanje može biti zastrašujuć posao kada se suočite sa mnogo servera. Postoje alati koji se bave ovim problemom tako da možete da automatizujete vašu infrastrukturu kako biste bili sigurni da imate prave servere i da su oni ispravno konfigurisani. Ovi alati se često integrišu sa većim cloud hosting provajderima (Amazon Web Services, Heroku, DigitalOcean itd) u cilju upravljanja instancama, što skaliranje aplikacije čini dosta lakšim.

Ansible je alat koji upravlja vašom infrastrukturom kroz YAML fajlove. Lako je početi i može upravljati sa kompleksnim i aplikacijama velikih razmera. Postoji API za upravljanje sa cloud instancama i on može upravljati sa njima kroz dinamički inventar koristeći određene alate.

Puppet je alat koji ima sopstveni jezik i tipove fajlova za upravljanje serverima i konfiguracijama. Može se koristiti u gospodar/mušterija (master/client) podešavanju ili se može koristiti u modu “bez gospodara” (“master-less”). U master/client modu u određenim zakazanim intervalima mušterije (client) će upitati centralne gospodare (master) ukoliko postoji nova konfiguracija i sami se ažurirati ukoliko je to potrebno. U “master-less” modu možete poslati konfiguraciju vašim čvorovima (node).

Chef je moćni frejmvork za integraciju sistema, baziran na Ruby-ju, uz pomoć kojeg možete izgraditi kompletno serversko okruženje ili virtuelne kutije. Dobro se integriše sa Amazon Web Servisima kroz njihov servis OpsWorks.

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 grešaka prilikom prebacivanja aplikacije na živ mod. Takođe, kada 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” softver kao što su Puppet ili Chef koji će ovo obaviti 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 virtuelne mašine, tako da možete kreirati i menjati fajlove na host mašini i potom izvršiti kôd unutar virtuelne mašine.

Mala pomoć

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

Docker

Docker - lagana alternativa celokupnoj virtuelnoj mašini - se tako zove zato što je sve u “kontejnerima”. Kontejner je jedan gradivni blok, koji u najjednostavnijem slučaju, radi jedan specifični posao, na primer izvršava web server. Slika (image) je paket koji koristite da izgradite kontejner - Docker ima repozitorijum pun njih.

Klasična LAML aplikacija može imati tri kontejnera: web server, PHP-FPM proces i MySQL. Isto kao i sa deljenim folderima u Vagrantu, možete ostaviti aplikacione fajlove tamo gde jesu i reći Docker-u gde da ih pronađe.

Kontejnere možete generisati sa komandne linije (pogledajte primer ispod) ili, radije lakšeg održavanja, možete kreirati fajl docker-compose.yml za vaš projekat i navesti koje kontejnere da kreira i način na koji će oni komunicirati.

Docker vam može pomoći ukoliko izrađujete više web sajtova i želite razdvojenost koja dolazi sa instaliranjem svakog u svojoj virtuelnoj mašini, ali nemate dovoljno prostora na disku ili vremena da sve redovno ažurirate. Docker je efikasan: instalacija i preuzimanja su brža, potrebno je da sačuvate samo jednu kopiju od svake slike (image) bez obzira koliko puta se koristi, kontejneri zahtevaju manje RAM-a i dele isti kernel operativnog sistema, tako da možete imati više servera koji se izvršavaju istovremeno, i potrebno je nekoliko sekundi da ih zaustavite i pokrenete, nije potrebno da čekate za potpuno pokretanje celog sistema.

Primer: Pokretanje vaših PHP aplikacija u Docker-u

Nakon što ste instalirali Docker na vašoj mašini, možete pokrenuti web server sa jednom komandom. Sledeća komanda će preuzeti potpuno funkcionalnu 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

Ovo će inicijalizovati i pokrenuti vaš kontejner. -d znači da će se izvršavati u pozadini. Da biste ga zaustavili i pokrenuli, jednostavno izvršite docker stop my-php-webserver i docker start my-php-webserver (ostali parametri nisu potrebni ponovo).

Naučite više o Docker-u

Gore navedena komanda prikazuje brz način za pokretanje bazičnog servera. Još mnogo toga možete uraditi (kao i hiljade prekonfigurisanih slika u Docker Hub). Odvojite vreme da naučite terminologiju i pročitate Docker-ov korisnički priručnik kako biste izvukli najviše iz njega, i nemojte pokretati nasumični kôd koji ste preuzeli bez prethodne provere da li je bezbedan - nezvanične slike možda nemaju poslednje sigurnosne zakrpe. Ukoliko ste sumnjičavi, držite se zvaničnih repozitorijuma.

Sajt PHPDocker.io će automatski generisati sve neophodne fajlove koji su vam potrebni za LAMP/LEMP stack, uključujući vaš izbor verzije PHP-a kao i ekstenzija.

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 prvo mora iskompajlirati u opkodove (opcodes), mašinske instrukcije za procesor. Ukoliko izvorni kôd nije modifikovan, opkodovi će biti isti, tako da ovaj korak kompilacije predstavlja samo trošenje procesorskih resursa.

Opcode keš sprečava suvišno kompajliranje, tako što se rezultujući opcode čuva u memoriji, a zatim se koristi u narednim izvršavanjima. Obično će prvo proveriti potpis ili vreme poslednje izmene fajla, u slučaju ako je bilo bilo kakvih izmena.

Opcode keš će vašoj aplikaciji najverovatnije doneti značajno poboljšanje u brzini. Od verzije PHP 5.5 postoji ugrađen opcode keš nazvan Zend OPcache. U zavisnosti od vašeg PHP paketa/distribucije, uglavnom je uključen kao podrazumevana vrednost - proverite opcache.enable kao i rezultat poziva funkcije phpinfo() kako biste bili sigurni. Za prethodne verzije postoji PECL ekstenzija.

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 neke podatke koji 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 bajtkôd 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-je 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. Ukoliko ne očekujete da će se vaša aplikacija izvršavati 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

Da biste videli koje verzije ovi PaaS provajderi koriste posetite PHP verzije.

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 predstavljaju još jedan pristup u cilju kreiranja, distribucije i implementacije 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

Vesti iz sveta PHP-a i zajednica web developera

Možete se pretplatiti na nedeljni bilten kako biste bili u toku sa novim bibliotekama, najnovijim vestima, događajima i generalnim obaveštenjima, kao i dodatnim resursima koji se objavljuju s’ vremena na vreme:

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će glavno izdanje PHP-a posle verzije 5.6 je bilo “PHP 7”, delimično zbog ovoga.

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 zapratiti @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 grupama 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