Priprema za PHP intervju

Pitanja za PHP Backend Developer intervju

15 često postavljanih pitanja za PHP intervju. Pitanja pokrivaju različite nivoe, a odgovore možete vežbati naglas u našem treneru za intervjue.

Započni PHP AI intervjuKreditna kartica nije potrebna. Dostupna je 1 besplatna sesija.
Vežba tehničkog intervjua na engleskomRežim u kome kandidati kojima engleski nije maternji mogu da vežbaju tehničke intervjue.

Pitanja za početni nivo

1Kako tipski sistem jezika PHP (PHP: Hypertext Preprocessor) razlikuje slabo i strogo tipiziranje i šta se menja kada se omogući declare(strict_types=1)?

PHP podrazumevano koristi slabu/koercitivnu proveru tipova za deklaracije skalarnih tipova: kada funkcija deklariše skalarne tipove parametara ili povratne vrednosti, PHP može konvertovati kompatibilne vrednosti (kao što su numerički stringovi) u brojeve umesto da ih odbije. Direktiva `declare(strict_types=1)` čini proveru skalarnih tipova strogom za taj fajl, tako da se nekompatibilne skalarne vrednosti ne konvertuju automatski i dolazi do izbacivanja greške `TypeError`. Ova direktiva važi na nivou pojedinačnog fajla: za argumente korisnički definisanih funkcija, fajl pozivaoca određuje striktnost; za povratne vrednosti (`return`), fajl u kome je funkcija definisana kontroliše striktnost povratne vrednosti. To ne čini PHP globalno strogim niti onemogućava sve konverzije u jeziku.

<?php
// lib.php
function takesInt(int $x): int {
    return $x;
}

// weak.php, no strict_types
echo takesInt('123'); // coerces string to int

// strict.php
declare(strict_types=1);
echo takesInt('123'); // TypeError
Pokušajte da odgovorite na ovo pitanje sa AI trenerom

2Objasnite razliku između == i === u jeziku PHP (PHP: Hypertext Preprocessor), uključujući zamke sa numeričkim stringovima, nizovima, objektima i funkcijom in_array().

`==` vrši labavo poređenje (loose comparison), pri čemu PHP može automatski konvertovati tipove pre poređenja; `===` vrši striktno poređenje, zahtevajući i istu vrednost i isti tip bez prinudne konverzije (coercion). Numerički stringovi predstavljaju čestu zamku jer se u labavim poređenjima mogu tretirati kao brojevi. Nizovi i objekti imaju svoja pravila poređenja: nizovi se porede po ključevima i vrednostima, dok `==` za objekte poredi objekte iste klase po njihovim svojstvima, a `===` za objekte zahteva potpuno istu instancu. Funkcija `in_array()` podrazumevano koristi labavo poređenje, pa prosledite `true` kao treći argument kada tipovi moraju da se poklapaju.

<?php
var_dump(123 == '123');      // loose: numeric string is converted
var_dump(123 === '123');     // strict: int !== string

$values = [0, 1, 2];
var_dump(in_array('0', $values));       // loose
var_dump(in_array('0', $values, true)); // strict
Pokušajte da odgovorite na ovo pitanje sa AI trenerom

3Kako se mixed, void i never razlikuju kao PHP (PHP: Hypertext Preprocessor) povratni tipovi i koje garancije pružaju?

Kao povratni tipovi, `mixed`, `void` i `never` daju različite garancije. `mixed` označava da funkcija može vratiti bilo koju vrednost, uključujući `null`. `void` znači da funkcija nije namenjena da vrati smislenu vrednost; može koristiti `return;`, ali ne može vratiti izraz. `never` označava da funkcija ne završava normalnim povratkom pozivaocu: uvek mora baciti izuzetak, pozvati `exit` ili na drugi način prekinuti tok izvršavanja. `never` pruža jaču garanciju od `void` jer se izvršavanje ne može nastaviti nakon regularnog poziva funkcije sa tipom `never`.

<?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';
Pokušajte da odgovorite na ovo pitanje sa AI trenerom

4Šta je `iterable` u PHP-u i u kakvoj je vezi sa `array`, `Traversable`, `Iterator` i `Generator`?

U PHP-u, `iterable` je deklaracija tipa koja označava „`array` ili objekat koji implementira `Traversable`“. `Traversable` je osnovni marker interfejs za objekte koji se mogu koristiti u `foreach` petlji; korisnički kod obično implementira `Iterator` ili `IteratorAggregate`, koji proširuju `Traversable`. `Generator` je objekat koji vraća generatorska funkcija korišćenjem ključne reči `yield`; on implementira `Iterator`, pa samim tim predstavlja i `Traversable` i prihvata se svuda gde se zahteva `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());
Pokušajte da odgovorite na ovo pitanje sa AI trenerom

5Koja su pravila vidljivosti za public, protected i private u PHP-u (Hypertext Preprocessor) i kako ona funkcionišu u kombinaciji sa nasleđivanjem i trejtovima?

U PHP-u, `public` članovi su dostupni odsvuda, `protected` članovi su dostupni iz klase koja ih deklariše i njenih potklasa, dok su `private` članovi dostupni isključivo iz klase koja ih je deklarisala. Prilikom nasleđivanja, potklase mogu koristiti nasleđene `public` i `protected` članove, ali `private` članovi roditeljske klase nisu dostupni kao nasleđeni API. Prilikom redefinisanja (overriding) metoda, klasa naslednica generalno ne može smanjiti vidljivost; na primer, `public` metoda roditeljske klase mora ostati `public`. Trejti (traits) se komponuju unutar klase koja ih koristi, a članovi trejta prate standardna PHP pravila vidljivosti. Vidljivost metoda iz trejta se takođe može prilagoditi ili joj se može dodeliti alijas pomoću ključne reči `as` prilikom uvoza trejta.

<?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
Pokušajte da odgovorite na ovo pitanje sa AI trenerom

6Kako funkcionišu traits u jeziku PHP (PHP: Hypertext Preprocessor) i kako se razrešavaju konflikti kada više njih definiše istu metodu?

Traits predstavljaju PHP mehanizam za horizontalno ponovno korišćenje koda: klasa može koristiti jedan ili više trait-ova, pri čemu se njihovi članovi uključuju u samu klasu. Traits nisu samostalne klase i ne mogu se direktno instancirati. Ako više trait-ova pruža metode sa istim nazivom, PHP zahteva eksplicitno razrešavanje konflikta. Operator `insteadof` bira koja se metoda iz trait-a koristi za konfliktni naziv. Operator `as` omogućava kreiranje alijasa za metodu iz trait-a, a može i promeniti vidljivost uvezene metode. Metode definisane u samoj klasi imaju prednost nad metodama iz trait-ova, dok metode iz trait-ova imaju prednost nad nasleđenim metodama roditeljske klase.

<?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
Pokušajte da odgovorite na ovo pitanje sa AI trenerom

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);
Pokušajte da odgovorite na ovo pitanje sa AI trenerom

Pitanja za srednji nivo

8Kako skalarni, nulabilni (nullable), unijski (union), presečni (intersection) i DNF (Disjunctive Normal Form) tipovi izražavaju API (Application Programming Interface) ugovore u PHP-u i kada koji od njih treba koristiti?

PHP deklaracije tipova opisuju vrednosti koje API prihvata ili vraća. Skalarni tipovi kao što su `int`, `float`, `string` i `bool` koriste se kada se očekuje tačno jedna primitivna vrsta podataka. Nulabilni tipovi, zapisani kao `?T` ili `T|null`, dozvoljavaju ili vrednost tipa `T` ili `null`. Unijski tipovi kao što je `A|B` dozvoljavaju jednu od nekoliko alternativa. Presečni tipovi kao što je `A&B` zahtevaju da vrednost zadovolji sve navedene tipove, najčešće više interfejsa. DNF tipovi kombinuju unije i preseke, na primer `(A&B)|C`, radi izražavanja složenijih ugovora. Svaki tip treba koristiti onda kada odgovara stvarnom obećanju API-ja, izbegavajući nepotrebno široke ili zbunjujuće tipove.

<?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
}
Pokušajte da odgovorite na ovo pitanje sa AI trenerom

9Koja je razlika između `callable`, `Closure`, first-class callable izraza, pozivih objekata i callable nizova u PHP-u?

`callable` je širok tip/ugovor za bilo koju vrednost koju PHP može da pozove, kao što je string sa nazivom funkcije, `Closure`, pozivi objekat ili niz koji referencira metodu poput `[$object, 'method']` ili `[ClassName::class, 'method']`. `Closure` je konkretan objekat anonimne funkcije i sam po sebi je callable. Sintaksa za first-class callable, kao što su `$obj->method(...)` ili `strlen(...)`, kreira `Closure` iz pozivog izraza. Pozivi objekat je callable zato što definiše metodu `__invoke()`. Callable nizovi predstavljaju tradicionalni oblik referenciranja metode pomoću dva elementa. Funkcija `is_callable()` se može koristiti za proveru mogućnosti poziva tokom izvršavanja.

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;
}
Pokušajte da odgovorite na ovo pitanje sa AI trenerom

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.

var_dump(0.1 + 0.2 === 0.3);
printf('%.17f\n', 0.1 + 0.2);
Pokušajte da odgovorite na ovo pitanje sa AI trenerom

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)`.

$a = [1, 2];
$b = $a;      // shared until modification
$b[0] = 99;   // separates; $a is unchanged
var_dump($a, $b);

$c =& $a;     // alias/reference
$c[0] = 42;   // modifies $a too
var_dump($a, $c);
Pokušajte da odgovorite na ovo pitanje sa AI trenerom

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;
Pokušajte da odgovorite na ovo pitanje sa AI trenerom

Pitanja za napredni nivo

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.

Example:
- 6 application servers
- 40 PHP-FPM max_children per server
- 2 separate FPM pools that can hit the database

Potential concurrent PHP request DB connections = 6 * 40 * 2 = 480

If PostgreSQL max_connections is 300 and queues/cron/admin tools also connect,
connection exhaustion is expected during traffic spikes.
Pokušajte da odgovorite na ovo pitanje sa AI trenerom

14Kako biste upravljali replikama za čitanje, razdvajanjem čitanja i pisanja, kašnjenjem replikacije i prebacivanjem u slučaju otkaza primarne baze podataka u PHP (Hypertext Preprocessor) aplikacijama?

Upravljanje replikama za čitanje i oporavkom od otkaza (failover) u PHP-u zahteva razdvajanje saobraćaja za čitanje i pisanje, ublažavanje kašnjenja replikacije i upravljanje prekidima konekcija tokom prebacivanja. Na nivou aplikacije, konfiguracija baze podataka ili posrednički sloj (middleware) usmerava upite za čitanje (`SELECT`) na replike za čitanje, a upite za pisanje (`INSERT`, `UPDATE`, `DELETE`) na primarnu instancu (što se alternativno može rešiti posrednicima baza podataka kao što su ProxySQL ili AWS RDS Proxy). Pošto je replikacija asinhrona, kašnjenje replikacije može dovesti do čitanja zastarelih podataka. Da bi se garantovala konzistentnost 'čitanja sopstvenih upisa' (read-your-writes), aplikacija treba da usmeri čitanja unutar aktivne transakcije na primarnu bazu i privremeno veže naredna čitanja za primarnu instancu tokom korisničke sesije ili životnog ciklusa zahteva neposredno nakon upisa. Tokom prebacivanja usled otkaza primarne baze, savremeni klasteri promovišu repliku i ažuriraju DNS/proksi krajnje tačke. U PHP-u, sloj za povezivanje mora presretati gubitak konekcije i greške vezane za režim samo za čitanje (read-only), zatvarati zastarele PDO sokete i primenjivati kontrolisane ponovne pokušaje sa eksponencijalnim odlaganjem (exponential backoff).

<?php
// config/database.php
return [
    'mysql' => [
        'read' => [
            'host' => [env('DB_REPLICA_HOST_1'), env('DB_REPLICA_HOST_2')],
        ],
        'write' => [
            'host' => [env('DB_PRIMARY_HOST')],
        ],
        'sticky'    => true,
        'driver'    => 'mysql',
        'database'  => env('DB_DATABASE'),
        'username'  => env('DB_USERNAME'),
        'password'  => env('DB_PASSWORD'),
        'charset'   => 'utf8mb4',
        'collation' => 'utf8mb4_unicode_ci',
    ],
];
Pokušajte da odgovorite na ovo pitanje sa AI trenerom

15Kako se migracije baza podataka i izmene šeme bezbedno primenjuju sa nultim ili minimalnim zastojem u PHP (PHP: Hypertext Preprocessor) aplikacijama?

Primena migracija baze podataka bez zastoja u PHP aplikacijama zahteva razdvajanje evolucije šeme od puštanja aplikativnog koda u rad primenom obrasca Expand and Contract (paralelno izvršavanje) uz neblokirajuće online DDL prakse. U fazi proširenja (Expand), primenjuju se izmene šeme kompatibilne unazad (npr. dodavanje kolona koje prihvataju null vrednosti, nove tabele ili konkurentno kreiranje indeksa pomoću alata kao što su pt-online-schema-change, gh-ost ili izvorni online DDL u MySQL/PostgreSQL bazama). Aplikacija se zatim postavlja tako da podržava dvostruko upisivanje (upisivanje i u staru i u novu strukturu šeme) dok čita iz bilo koje od njih. Istorijski podaci se potom asinhrono naknadno popunjavaju u kontrolisanim pozadinskim grupama kako bi se sprečilo zaključavanje tabela ili izazivanje kašnjenja u replikaciji. U fazi skupljanja (Contract), kada se potvrdi konzistentnost podataka i čitanje u potpunosti prebaci na novu strukturu, novo postavljanje koda uklanja reference ka staroj šemi. Na kraju, završna migracija čišćenja uklanja zastarele kolone ili tabele. Bezbedan povratak na prethodnu verziju (rollback) zahteva da svako međustanje šeme ostane potpuno kompatibilno i sa prethodnom i sa dolaznom verzijom aplikacije tokom postepenih izdanja (rolling releases).

<?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
Pokušajte da odgovorite na ovo pitanje sa AI trenerom