1Kako PHP razlikuje slabu i strogu tipizaciju i šta menja declare(strict_types=1)?
PHP obično dozvoljava kompatibilne skalarne konverzije za deklarisane parametre i povratne tipove, poput numeričkog stringa za int parametar. declare(strict_types=1) uključuje stroge skalarne provere: nekompatibilan argument ili povratna vrednost izaziva TypeError, mada je int i dalje prihvatljiv za float. Za argumente korisničkih funkcija strogost određuje pozivajuća datoteka; za povratne vrednosti datoteka sa definicijom. Pozivi iz internih PHP funkcija imaju posebne uslove i ne podležu jednostavno strict_types podešavanju spoljašnjeg pozivaoca. Direktiva mora biti na početku PHP datoteke. Ona ne čini PHP statički tipiziranim niti isključuje konverzije operatora u celom jeziku.
// THREE SEPARATE FILES: save each PHP block under its indicated filename.
// File: lib.php
<?php
function takesInt(int $x): int { return $x; }
?>
// File: weak.php
<?php
require __DIR__ . '/lib.php';
echo takesInt('123'); // 123: coercive caller
?>
// File: strict.php
<?php
declare(strict_types=1);
require __DIR__ . '/lib.php';
try {
echo takesInt('123');
} catch (TypeError $e) {
echo 'TypeError';
}
?>
2Objasnite razliku između == i === u PHP-u, uključujući zamke sa numeričkim stringovima, nizovima, objektima i in_array().
== koristi labavo poređenje, uz konverziju tipova kada je primenljiva; === zahteva podudarne tipove i vrednosti bez skalarne konverzije. Numerički stringovi mogu se porediti kao brojevi, pa labavo poređenje ne koristite kada je potrebna tačna podudarnost koja uzima tip u obzir. Nizovi su jednaki prema == ako po pravilima poređenja sadrže iste parove ključ/vrednost, nezavisno od redosleda. === dodatno zahteva isti redosled i podudarne tipove ključeva i vrednosti. Objektno == poredi svojstva objekata iste klase; === zahteva istu instancu. in_array podrazumevano poredi labavo; prosledite true kao treći argument za strogu proveru. Labavo poređenje brojeva i stringova promenjeno je u PHP 8, zato ne koristite istorijske primere konverzije bez navođenja verzije.
3Kako se razlikuju mixed, void i never kao povratni tipovi PHP-a i šta garantuju?
mixed dopušta svaku povratnu vrednost, uključujući null. void označava da funkcija ne vraća smislenu vrednost: može doći do kraja ili koristiti return;, ali ne sme vratiti izraz. Normalan poziv void funkcije daje null. never, dostupan od PHP 8.1, znači da se funkcija nikada normalno ne vraća pozivaocu. Može baciti izuzetak, prekinuti izvršavanje sa exit ili ostati u beskonačnoj petlji; nije obavezna da na kraju završi proces. Funkcija never ne sme sadržati return. Ključna razlika je da void može normalno završiti i pozivalac nastavlja, dok never nema normalan nastavak.
<?php
function parse(string $raw): mixed {
return json_decode($raw, true);
}
function logMessage(string $message): void {
error_log($message);
return; // allowed
}
function fail(string $message): never {
throw new RuntimeException($message);
}
logMessage('starting');
fail('stop');
echo 'unreachable';
4Šta je iterable u PHP-u i kako je povezan sa array, Traversable, Iterator i Generator?
iterable znači array|Traversable. Generator implementira Iterator, koji proširuje Traversable, pa zadovoljava iterable. Korisničke klase pružaju Traversable ponašanje kroz Iterator ili IteratorAggregate, umesto direktnog implementiranja Traversable u konkretnoj klasi. Iterator nudi metode za kontrolu iteracije; IteratorAggregate daje iterator. Generatorska funkcija sa yield vraća Generator. Važna razlika: foreach može prolaziti i kroz vidljiva svojstva običnog objekta koji ne implementira Traversable, ali objekat time ne zadovoljava deklaraciju parametra iterable.
function printItems(iterable $items): void {
foreach ($items as $item) {
echo $item, PHP_EOL;
}
}
printItems(['a', 'b']);
function letters(): Generator {
yield 'x';
yield 'y';
}
printItems(letters());
5Koja su pravila vidljivosti public, protected i private u PHP-u i kako deluju sa nasleđivanjem i trait-ovima?
public članovi su dostupni svuda gde se može pristupiti objektu ili klasi. protected članovi su dostupni u klasi koja ih deklariše i povezanoj hijerarhiji roditeljskih i izvedenih klasa, ne proizvoljnom spoljašnjem kodu. private članovi su dostupni u klasi koja ih je deklarisala; podklasa ne može koristiti privatni roditeljski član kao nasleđeni interfejs. Obična redefinisana metoda ne sme suziti nasleđenu vidljivost, ali konstruktori imaju posebna pravila nasleđivanja i nisu bezuslovan primer tog pravila. Članovi trait-a uključuju se u koristeću klasu i prate njena pravila vidljivosti. as može napraviti alias, promeniti vidljivost metode ili oboje; dodavanje aliasa samo po sebi ne uklanja originalno ime.
<?php
class Base {
public function a() {}
protected function b() {}
private function c() {}
}
trait Logs {
public function log(string $message): void {}
}
class Child extends Base {
use Logs { log as protected writeLog; }
public function test(): void {
$this->a(); // OK
$this->b(); // OK
// $this->c(); // Not accessible: private to Base
$this->writeLog('ok'); // OK inside class, protected alias
}
}
$child = new Child();
$child->a();
// $child->b(); // Not accessible from outside
// $child->writeLog('x'); // Not accessible from outside
6Kako rade trait-ovi u PHP-u i kako se rešavaju sukobi kada više njih definiše istu metodu?
Trait-ovi omogućavaju horizontalnu ponovnu upotrebu koda: klasa uvozi njihove članove pomoću use, a trait se ne može samostalno instancirati. Metoda deklarisana u klasi ima prednost nad trait metodama, koje imaju prednost nad nasleđenim roditeljskim metodama. Ako više trait-ova nudi istu metodu i sukob nije već rešen metodom klase, birajte implementaciju pomoću insteadof. Koristite as za dodatno ime i po želji drugu vidljivost metode. Alias ne preimenuje niti uklanja originalnu metodu. Rešavanje kolizija i jasnoća dobijenog javnog interfejsa odvojene su odgovornosti.
<?php
trait JsonLogger {
public function log(string $message): void {
echo "json:$message\n";
}
}
trait FileLogger {
public function log(string $message): void {
echo "file:$message\n";
}
}
class Service {
use JsonLogger, FileLogger {
JsonLogger::log insteadof FileLogger;
FileLogger::log as logToFile;
JsonLogger::log as protected logJson;
}
}
$service = new Service();
$service->log('hello');
$service->logToFile('hello');
// $service->logJson('hello'); // not accessible from outside; protected alias
7Šta su PHP (Hypertext Preprocessor) nabrojivi tipovi (enums), uključujući čiste (pure) i podržane (backed) enume, i kako oni unapređuju modelovanje domena?
PHP nabrojivi tipovi (enums) definišu zatvoreni skup imenovanih slučajeva za određeni tip, kao što su statusi porudžbine ili korisničke uloge. Čisti enum-ovi sadrže samo imenovane slučajeve; podržani enum-ovi (backed enums) svakom slučaju dodeljuju jedinstvenu `string` ili `int` vrednost, što je korisno za skladištenje podataka, API (Application Programming Interface) komunikaciju ili interoperabilnost. Enum-ovi unapređuju modelovanje domena tako što zamenjuju nepouzdane sirove niske, cele brojeve ili konstante tipski bezbednim vrednostima koje same sebe dokumentuju. PHP enum-ovi takođe mogu imati metode i implementirati interfejse; svi enum-ovi podržavaju izlistavanje svojih slučajeva, a podržani enum-ovi podržavaju i konverziju u svoje skalarne vrednosti i iz njih.
enum OrderState
{
case Draft;
case Paid;
case Shipped;
}
enum PaymentStatus: string
{
case Pending = 'pending';
case Complete = 'complete';
case Failed = 'failed';
}
$status = PaymentStatus::tryFrom('complete');
var_dump($status === PaymentStatus::Complete);
8Kako skalarni, nullable, unijski, presečni i DNF tipovi izražavaju ugovore PHP interfejsa i kada se koriste?
PHP deklaracije opisuju prihvaćene i vraćene vrednosti. Skalarne deklaracije int, float, string i bool biraju kategoriju vrednosti; dozvoljenost kompatibilnih skalarnih konverzija zavisi i od strict_types i konteksta. ?T ili T|null dopušta null. Unije, dostupne od PHP 8.0, dopuštaju alternative poput int|string. Čisti preseci od PHP 8.1 zahtevaju da objekat zadovoljava sve navedene tipove klasa ili interfejsa, poput Cacheable&JsonSerializable; int&string nije validan presek. DNF tipovi (disjunktivna normalna forma), dostupni od PHP 8.2, kombinuju alternative i preseke u zagradama, na primer (Cacheable&JsonSerializable)|string. Birajte najuži razumljiv ugovor za ispravne ulaze, bez nepotrebnih alternativa.
<?php
interface Cacheable {}
class Report implements Cacheable, JsonSerializable {
public function jsonSerialize(): mixed { return []; }
}
function findName(int $id): ?string {
return $id > 0 ? 'Alice' : null;
}
function normalize(int|string $id): string {
return (string) $id;
}
function store(Cacheable&JsonSerializable $item): void {
// must satisfy both interfaces
}
function handle((Cacheable&JsonSerializable)|string $input): void {
// either an object satisfying both contracts, or a string
}
9Kako se razlikuju callable, Closure, prvoklasne callable vrednosti, pozivljivi objekti i callable nizovi u PHP-u?
callable opisuje pozivljive vrednosti: stringove sa imenima funkcija, Closure objekte, reference na dostupne metode i objekte sa __invoke. Closure je konkretan pozivljivi objekat nastao od anonimne funkcije ili imenovane funkcije/metode pretvorene u Closure. Sintaksa prvoklasnih callable vrednosti, poput strlen(...) ili $object->method(...), stvara Closure bez pozivanja cilja. [$object, 'method'] upućuje na dostupnu instancnu metodu; [ClassName::class, 'method'] u savremenom PHP-u zahteva odgovarajuću dostupnu statičku metodu. is_callable proverava pozivljivost u odgovarajućem opsegu. callable je širok ugovor, a Closure konkretan oblik predstavljanja.
class Greeter {
public function hello(string $name): string { return "Hello $name"; }
public function __invoke(string $name): string { return "Hi $name"; }
}
$g = new Greeter();
$closure = fn(string $name) => "Hey $name";
$arrayCallable = [$g, 'hello'];
$firstClass = $g->hello(...);
$invokable = $g;
foreach ([$closure, $arrayCallable, $firstClass, $invokable] as $cb) {
echo $cb('Sam'), PHP_EOL;
}
10Kako PHP (PHP: Hypertext Preprocessor) rukuje prekoračenjem celobrojnih vrednosti, preciznošću brojeva sa pokretnim zarezom i tačnim decimalnim proračunima?
PHP celi brojevi su veličine zavisne od platforme, sa granicama koje definišu konstante kao što je `PHP_INT_MAX`. PHP ne zadržava automatski nativne celobrojne rezultate kao cele brojeve proizvoljne preciznosti nakon prekoračenja; celobrojna aritmetika koja dovede do prekoračenja prelazi u `float` ili na drugi način gubi egzaktna celobrojna svojstva. PHP brojevi sa pokretnim zarezom su binarne floating-point vrednosti, pa su mnogi decimalni razlomci, kao što je `0.1`, samo približni. Za tačne decimalne ili novčane proračune treba izbegavati `float` i koristiti egzaktan pristup, kao što su celi brojevi u manjim novčanim jedinicama gde je to prikladno, ili alati za decimalne brojeve fiksne/proizvoljne preciznosti kao što je BCMath. Biblioteka GMP je korisna za celobrojnu aritmetiku proizvoljne preciznosti.
11Objasnite PHP reference (&), kopiranje pri upisu (copy-on-write) i kako reference mogu stvoriti neočekivane sporedne efekte.
PHP promenljive su interno predstavljene strukturama zval. Za vrednosti kao što su nizovi i stringovi, PHP koristi mehanizam kopiranja pri upisu (copy-on-write): dodela `$b = $a` obično deli istu osnovnu vrednost sve dok se jedna promenljiva ne izmeni, u kom trenutku je PHP razdvaja/kopira. Referenca kreirana pomoću `&` čini promenljive alijasima za isti kontejner, tako da promena jedne menja i drugu, što može zaobići ponašanje razdvajanja koje se očekuje od obične dodele vrednosti. Prosleđivanje po referenci omogućava funkciji da izmeni promenljivu pozivaoca. Reference mogu izazvati iznenađujuće sporedne efekte, posebno sa `foreach ($array as &$value)`: promenljiva petlje ostaje referenca na poslednji element nakon završetka petlje, pa njeno ponovno korišćenje kasnije može slučajno prepisati taj element osim ako ne pozovete `unset($value)`.
12Kako u jeziku PHP funkcionišu reference na objekte (handles), dodela vrednosti, clone, __clone, plitko kopiranje (shallow copy) i duboko kopiranje (deep copy)?
U PHP-u promenljiva objekta čuva referencu (handle) na objekat, a ne kompletnu kopiju objekta. Dodeljivanje `$b = $a` kopira samo referencu, pa obe promenljive pokazuju na isti objekat; menjanje objekta preko bilo koje od njih vidljivo je kroz drugu, bez upotrebe operatora `&`. Naredba `clone $a` kreira novu instancu objekta sa plitkom kopijom svojstava originalnog objekta. Ako je neko svojstvo i samo objekat, svojstvo kloniranog objekta i dalje pokazuje na isti ugnježdeni objekat, osim ako ga eksplicitno ne klonirate. Magična metoda `__clone()` se poziva na novom objektu nakon plitkog kopiranja i predstavlja mesto za resetovanje identifikatora (ID), oslobađanje resursa ili izvođenje dubokog kopiranja ugnježdenih objekata. Duboko kopiranje podrazumeva rekurzivno kopiranje ugnježdenih promenljivih objekata koji ne treba da budu deljeni.
class Address {
public function __construct(public string $city) {}
}
class User {
public function __construct(public string $name, public Address $address) {}
public function __clone() {
$this->address = clone $this->address; // make nested object independent
}
}
$u1 = new User('Ann', new Address('Paris'));
$u2 = $u1; // same object handle
$u2->name = 'Beth';
echo $u1->name, PHP_EOL;
$u3 = clone $u1; // new User object; __clone deep-clones address
$u3->address->city = 'Rome';
echo $u1->address->city, PHP_EOL;
echo $u3->address->city, PHP_EOL;
13Kako biste dijagnostikovali iscrpljivanje konekcija ka bazi podataka i projektovali upravljanje konekcijama za PHP-FPM (FastCGI Process Manager) i dugotrajne radne procese?
Da bih dijagnostikovao iscrpljivanje konekcija, uporedio bih ograničenja broja konekcija baze podataka i trenutne sesije sa ukupnom konkurentnošću u PHP-u: svaki aktivni PHP-FPM potproces može da drži sopstvenu konekciju ka bazi, a više grupa aplikacija, servera, cron zadataka, radnika u redovima čekanja, migracija i raspoređivanja višestruko uvećavaju potražnju. Pregledao bih prikaze sesija baze podataka, stanja konekcija, sesije neaktivne unutar transakcije (idle-in-transaction), spore upite, čekanja na zaključavanje, evidencije grešaka, metriku pulova i nedavne promene u saobraćaju ili broju radnika. Za PHP-FPM, potrebno je držati `max_children` i konkurentnost po serveru unutar budžeta baze podataka, koristiti kratkotrajne transakcije i konekcije vezane za opseg zahteva, odmah izvršiti `commit` ili `rollback` i izbegavati zadržavanje konekcija tokom sporih eksternih poziva. Perzistentne PDO konekcije mogu smanjiti režijske troškove povezivanja, ali mogu zadržati mnoge neaktivne konekcije po FPM procesu i sačuvati stanje sesije, pa zahtevaju oprez. Puling mehanizam ili proksi kao što je PgBouncer može multipleksirati mnoge PHP klijente na manji broj konekcija ka serveru baze kada je aplikacija kompatibilna. Dugotrajni radni procesi zahtevaju eksplicitno upravljanje životnim vekom konekcije: provere ispravnosti, ponovno povezivanje nakon isteka vremena neaktivnosti, ponovnih pokretanja, prebacivanja u slučaju otkaza ili račvanja, resetovanje stanja sesije i ponavljanje isključivo bezbednog ili idempotentnog posla.
Connection-budget example:
- 6 application servers
- 2 PHP-FPM pools on EACH server
- pm.max_children = 40 for EACH pool
- Assume at most 1 database connection per child
Potential request connections = 6 * 2 * 40 = 480.
If 40 were the TOTAL children per server across both pools, it would be 240.
Budget separately for queue workers, cron, deployments and administrators.
A limit of 300 database connections cannot accommodate 480 simultaneous connections.
14Kako biste u PHP-u upravljali replikama za čitanje, razdvajanjem čitanja i pisanja, kašnjenjem replikacije i prebacivanjem posle otkaza primarne baze?
Odvojite veze ka primarnoj bazi i replikama prema značenju operacije, ne samo tome da li SQL počinje sa SELECT: zaključavajuća čitanja, pisanja i transakcije kojima treba konzistentnost primarne baze moraju koristiti nju. Transakcije samo za čitanje mogu koristiti odgovarajuću repliku ako zahtevi konzistentnosti to dopuštaju. Asinhrona replikacija može kasniti. Laravel sticky posle pisanja usmerava dalja čitanja tekućeg zahteva na vezu za pisanje; fiksno vezivanje između zahteva samo ublažava problem i ne garantuje bezuslovno čitanje sopstvenih upisa. Za garanciju čitajte sa primarne baze ili čekajte da replika primeni potrebnu poziciju upisa, uz vremensko ograničenje i prelazak na primarnu bazu. Proksiji se razlikuju: RDS Proxy koristi posebne krajnje tačke za čitanje i pisanje i nije opšti automatski razdvajač svake SQL naredbe. Pri prebacivanju odbacite zastarele veze i povežite se ponovo uz ograničeno povećavanje pauza. Izgubljen commit odgovor ostavlja ishod nejasnim: ne ponavljajte ne-idempotentna pisanja naslepo; koristite ključeve idempotentnosti ili prvo utvrdite rezultat.
15Kako bezbedno postavljati migracije baze i promene šeme u PHP aplikacijama bez zastoja ili uz minimalan zastoj?
Koristite proširenje pa uklanjanje starih struktura, koje podržava staru i novu aplikaciju tokom postepenog postavljanja. Prvo dodajte kompatibilne strukture, zatim kod za obe reprezentacije. Ako je potrebno dvostruko pisanje, održavajte konzistentnost i projektujte nastavljivu dopunu istorijskih podataka ograničenom brzinom, bez prepisivanja novijih upisa. Proverite kopirane podatke i prebacite čitanja pre uklanjanja starih putanja koda. Online DDL (jezik za definisanje podataka) smanjuje određene rizike blokiranja, ali nije uvek bez zaključavanja: proverite zaključavanja konkretne baze i verzije, transakciona ograničenja, resurse i uticaj na replikaciju. PostgreSQL CREATE INDEX CONCURRENTLY, na primer, ima ograničenja i posle greške može ostaviti nevalidan indeks. Stare kolone uklonite tek po gašenju starih instanci i isteku potrebnog roka za povratak. Posle destruktivnog uklanjanja povratak na kod koji traži obrisane podatke više nije automatski bezbedan.
<?php
// Step 1: Migration (Expand) -> Add new column as nullable
Schema::table('users', function (Blueprint $table) {
$table->string('full_name')->nullable()->after('name');
});
// Step 2: Code deployment -> Write to both, read with fallback
class User extends Model {
public function setNameAttribute($value) {
$this->attributes['name'] = $value;
$this->attributes['full_name'] = $value; // Dual write
}
}
// Step 3: Background worker backfills existing rows in small batches
// Step 4: Code deployment -> Read/write exclusively from 'full_name'
// Step 5: Migration (Contract) -> Drop legacy 'name' column