To pytania z PHP, które najczęściej pojawiają się na rozmowach kwalifikacyjnych, z krótkimi odpowiedziami poprawnymi dla PHP 8.3 i kodem tam, gdzie kod najlepiej to pokazuje. Zanim naciśniesz Run, przewidź wynik każdego bloku: to właśnie sprawdza rekruter.
Pytania rekrutacyjne z PHP dla początkujących
Czym jest PHP i jak działa?
PHP to język skryptowy po stronie serwera. Serwer WWW (Nginx albo Apache) przekazuje żądanie do PHP (zwykle PHP-FPM), PHP wykonuje skrypt od góry, a wszystko, co wypisze, staje się odpowiedzią, zwykle HTML. Każde żądanie zaczyna się od zera: zmienne nie przetrwają między żądaniami, chyba że zapiszesz je w sesji, bazie danych lub cache. PHP uruchamia też skrypty w wierszu poleceń (php script.php).
Czym różni się == od ===?
== porównuje po żonglowaniu typami, === porównuje wartość i typ bez konwersji. Rozgrzewka powyżej pokazuje == w działaniu: stringi liczbowe są porównywane jak liczby, więc '10' == '1e1' daje true. Od PHP 8 liczba porównywana ze stringiem nieliczbowym jest porównywana jak string, więc 0 == 'a' daje false (w PHP 7 było true). Klikaj komórki, aby porównać oba operatory:
| $a == $b | true | false | 0 | 1 | "0" | "1" | "" | null | [] | "a" | "10" | "1e1" |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| true | ||||||||||||
| false | ||||||||||||
| 0 | ||||||||||||
| 1 | ||||||||||||
| "0" | ||||||||||||
| "1" | ||||||||||||
| "" | ||||||||||||
| null | ||||||||||||
| [] | ||||||||||||
| "a" | ||||||||||||
| "10" | ||||||||||||
| "1e1" |
Domyślnie używaj ===. in_array() i array_search() używają ==, chyba że przekażesz true jako trzeci argument.
Czym różni się echo od print?
Oba wypisują stringi. echo przyjmuje kilka argumentów (echo $a, $b;) i nic nie zwraca; print przyjmuje jeden i zwraca 1, więc można go użyć w wyrażeniu. Żadne nie jest funkcją, więc nawiasy są opcjonalne. W praktyce wszyscy używają echo.
Pojedyncze czy podwójne cudzysłowy?
Stringi w podwójnych cudzysłowach interpolują zmienne i sekwencje ucieczki: "Hi $name\n". Stringi w pojedynczych cudzysłowach są dosłowne, z wyjątkiem \' i \\: 'Hi $name\n' wypisuje dokładnie te znaki. Różnica w szybkości jest pomijalna, więc wybór dotyczy czytelności: podwójne, gdy interpolujesz, pojedyncze, gdy nie.
Jakie typy danych ma PHP?
Cztery typy skalarne (int, float, string, bool), dwa złożone (array i object) i dwa specjalne (null i resource). callable, iterable, mixed i void istnieją tylko jako deklaracje typów, nigdy jako typ wartości. gettype() i get_debug_type() podają typ wartości w czasie działania, a var_dump() wypisuje go razem z wartością.
Czym różnią się isset, empty i is_null?
isset($x) daje true, gdy zmienna istnieje i nie jest null. empty($x) daje true, gdy zmienna nie istnieje albo jest traktowana jak fałsz (0, '0', '', [], false, null). is_null($x) daje true dla null, ale ostrzega przy niezdefiniowanej zmiennej. isset i empty nigdy nie ostrzegają, dlatego używa się ich na kluczach tablic.
| $x | isset | empty | is_null |
|---|---|---|---|
| null | |||
| false | |||
| 0 | |||
| "0" | |||
| "" | |||
| " " | |||
| [] | |||
| "a" | |||
| 0.0 |
Wiersz, w którym ludzie się mylą, to "0": string zero jest pusty, a " " (spacja) nie. Pełną tabelę ma strona o isset.
Czym różni się include od require?
Oba wstawiają i wykonują inny plik. Jeśli pliku brakuje, include zgłasza ostrzeżenie i skrypt działa dalej, a require rzuca Error („Failed opening required”), który zatrzymuje skrypt, chyba że coś go złapie. Warianty _once (include_once, require_once) pomijają plik, który został już wczytany, co zapobiega błędom „cannot redeclare function”. Używaj require (lub require_once) dla plików, bez których skrypt nie może działać, czyli prawie wszystkich; autoloader Composera sprawia, że większość ręcznych dołączeń jest niepotrzebna.
Czym różni się GET od POST?
GET wysyła dane w query stringu adresu URL ($_GET), więc są widoczne, można je zapisać w zakładce i mają ograniczoną długość; używaj go do odczytu, jak wyszukiwanie i stronicowanie. POST wysyła dane w treści żądania ($_POST); używaj go do wszystkiego, co zmienia stan, jak logowanie, zapisywanie czy płatności. Żądanie GET nigdy nie powinno zmieniać danych, bo linki są wczytywane z wyprzedzeniem i odwiedzane przez roboty.
Czym jest operator null coalescing?
$a ?? $b zwraca $a, jeśli istnieje i nie jest null, w przeciwnym razie $b, bez ostrzeżenia dla niezdefiniowanej zmiennej lub klucza: $page = $_GET['page'] ?? 1;. ??= przypisuje tylko wtedy, gdy lewa strona to null albo jej brakuje. Różni się od ?:, który wraca do wartości zastępczej przy każdej wartości traktowanej jak fałsz, więc 0 ?: 5 daje 5, a 0 ?? 5 daje 0.
Pytania rekrutacyjne z PHP na poziomie średnim
Co się dzieje przy pętli foreach przez referencję?
foreach ($arr as &$v) pozwala modyfikować elementy w miejscu, ale $v po pętli pozostaje referencją do ostatniego elementu. Ponowne użycie tej nazwy w drugiej pętli nadpisuje wtedy ten element:
Ostatni element to 20, a nie 30: w drugiej pętli $v (nadal $arr[2]) dostaje kolejno 10, potem 20, a potem samego siebie. Usuń // przed unset($v) i uruchom ponownie. Referencje szczegółowo opisuje strona o foreach.
Czym różni się klasa abstrakcyjna od interfejsu? A czym jest trait?
Interfejs to kontrakt: sygnatury metod (i stałe) bez implementacji, a klasa może implementować wiele interfejsów. Klasa abstrakcyjna może zawierać właściwości, konstruktory i gotowe metody oraz metody abstrakcyjne, które muszą napisać jej potomkowie; klasa rozszerza tylko jedną. Trait to zestaw metod kopiowanych do klasy przez use, służący do wielokrotnego użycia kodu w klasach bez wspólnego rodzica.
Krótka odpowiedź, którą lubią rekruterzy: interfejsu używaj, by powiedzieć, co coś potrafi, klasy abstrakcyjnej, by współdzielić to, jak to robi, a traitu, gdy niepowiązane klasy potrzebują tych samych kilku metod.
Czym różni się self od static?
self odnosi się do klasy, w której napisano kod. static odnosi się do klasy, która faktycznie została wywołana w czasie działania; nazywa się to późnym wiązaniem statycznym. Ma to znaczenie w metodach rodzica, które tworzą lub opisują „ten rodzaj obiektu”:
User::createStatic() buduje User. User::createSelf() buduje Model, co łamie typ zwracany static, więc PHP rzuca TypeError. Dlatego metody fabryczne używają new static().
Czym są metody magiczne?
Metodami, które PHP wywołuje za ciebie; wszystkie zaczynają się od dwóch podkreśleń. Najczęstsze: __construct() przy tworzeniu obiektu, __destruct() przy jego niszczeniu, __toString(), gdy jest używany jako string, __get() i __set() dla niedostępnych właściwości, __call() i __callStatic() dla brakujących metod, __invoke(), gdy obiekt jest wywoływany jak funkcja, oraz __clone() po clone.
Czym różni się array_merge() od operatora +?
array_merge() dopisuje: klucze liczbowe są numerowane od nowa, a późniejsze klucze tekstowe nadpisują wcześniejsze. + to suma: zachowuje lewą tablicę i dodaje tylko brakujące klucze, więc przy powtórzonym kluczu wygrywa pierwsza wartość.
+ to właściwe narzędzie dla wartości domyślnych: $options + ['timeout' => 30] zachowuje to, co przekazał wywołujący, i uzupełnia resztę.
Co robi declare(strict_types=1)?
Bez tego PHP konwertuje argumenty skalarne na zadeklarowany typ parametru, gdy to możliwe: przekazanie "5" do parametru int daje 5. Z declare(strict_types=1); jako pierwszą instrukcją pliku wywołania z tego pliku muszą przekazywać dokładny typ, inaczej rzucany jest TypeError (int przekazany do parametru float to jedyne dozwolone poszerzenie).
Usuń linię declare i uruchom ponownie: drugie wywołanie zwraca 10 zamiast rzucać wyjątek.
Czym różni się sesja od ciasteczka?
Ciasteczko jest przechowywane w przeglądarce i wysyłane z każdym żądaniem, więc odwiedzający może je odczytać i zmienić. Sesja przechowuje dane na serwerze i trzyma w ciasteczku tylko losowy identyfikator (PHPSESSID). Wszystko, czemu trzeba ufać, jak identyfikator zalogowanego użytkownika, umieszczaj w $_SESSION; po zalogowaniu wywołaj session_regenerate_id(true). Kod ma strona o sesjach i ciasteczkach.
Jak zapobiegać SQL injection?
Nigdy nie buduj SQL przez sklejanie danych od użytkownika. Używaj zapytań przygotowanych, aby wartości były wysyłane osobno od zapytania:
<?php
$stmt = $pdo->prepare('SELECT * FROM users WHERE email = ?');
$stmt->execute([$email]);
$user = $stmt->fetch();
Symbole zastępcze działają tylko dla wartości. Nazwy tabel, nazwy kolumn i ASC/DESC trzeba sprawdzić z listą dozwolonych, zanim trafią do SQL. Zobacz PDO.
Jak zapobiegać XSS?
Escapuj wyjście, nie wejście: każdą wartość wypisywaną do HTML otaczaj przez htmlspecialchars($value, ENT_QUOTES, 'UTF-8') albo używaj silnika szablonów, który robi to automatycznie (Twig, Blade). Wewnątrz <script> używaj json_encode() z JSON_HEX_TAG. Jako drugą warstwę dodaj nagłówek Content-Security-Policy.
Jak przechowywać hasła?
Przez password_hash($password, PASSWORD_DEFAULT), a sprawdzać przez password_verify($password, $hash). Używają bcrypt (lub Argon2id) z losową solą zapisaną wewnątrz hasha i celowym kosztem. md5() i sha1() nie nadają się do haseł, bo są szybkie i bez soli. Przy logowaniu używaj password_needs_rehash(), aby aktualizować stare hashe.
Zaawansowane pytania rekrutacyjne z PHP
Czym są generatory i kiedy ich używać?
Funkcja zawierająca yield zwraca Generator, który wytwarza wartości po jednej, tylko na żądanie. Pozwala przejść po dużym lub nieskończonym ciągu bez budowania tablicy w pamięci:
Typowe zastosowania to czytanie dużego pliku linia po linii, strumieniowanie wierszy z bazy danych i leniwe potoki. Generatory obsługują też klucze (yield $key => $value), delegację (yield from) i przyjmowanie wartości przez send().
Czym są domknięcia i co robi use?
Funkcja anonimowa (function () { ... }) to obiekt Closure. Nie widzi zmiennych z zewnątrz, chyba że wymienisz je w use, które kopiuje wartość w chwili tworzenia domknięcia; use (&$x) przechwytuje zamiast tego przez referencję. Funkcje strzałkowe (fn ($x) => $x * $factor) przechwytują zmienne z zewnątrz automatycznie, przez wartość, i zawierają jedno wyrażenie.
Czym różnią się abstract, final i readonly?
abstract oznacza klasę, której nie można utworzyć, albo metodę, którą musi zaimplementować potomek. final zabrania nadpisywania metody lub rozszerzania klasy. readonly (PHP 8.1) sprawia, że właściwość można zapisać tylko raz, z wnętrza klasy, zwykle w konstruktorze; PHP 8.2 dodało klasy readonly, w których każda właściwość jest readonly.
Czym jest Composer i czym jest PSR-4?
Composer to menedżer zależności PHP: composer.json wymienia pakiety, composer.lock przypina dokładne wersje, a vendor/autoload.php wczytuje wszystko. PSR-4 to standard autoloadingu, który przypisuje prefiks przestrzeni nazw do folderu, więc App\Mail\Mailer leży w src/Mail/Mailer.php i jest wczytywany przy pierwszym użyciu. Wyjaśnij install (z pliku lock) vs update (nowe wersje, nowy plik lock).
Co zmieniło się w PHP 8?
Najważniejsze funkcje: w 8.0 argumenty nazwane, match, operator nullsafe ?->, typy unijne, promocja właściwości w konstruktorze, atrybuty, str_contains() i rozsądniejsze porównywanie stringów z liczbami; w 8.1 enumy, właściwości readonly, składnia first-class callable (strlen(...)) i fibers; w 8.2 klasy readonly i samodzielne typy null/true/false; w 8.3 typowane stałe klas, atrybut #[\Override] i json_validate(); w 8.4 property hooks, asymetryczna widoczność (public private(set)), array_find() i new Foo()->method() bez otaczających nawiasów. Wiele funkcji rzuca też teraz TypeError lub ValueError zamiast zwracać false z ostrzeżeniem.
Jak PHP zarządza pamięcią?
Każda wartość ma licznik referencji; gdy spada do zera, pamięć jest natychmiast zwalniana. Obiekty, które odwołują się do siebie nawzajem w cyklu, nigdy nie osiągają zera, więc zbieracz cykli uruchamia się, gdy zapełni się jego bufor potencjalnych korzeni cykli (domyślnie 10 000 wpisów), i je zwalnia (gc_collect_cycles() uruchamia go na żądanie). Tablice i stringi działają w trybie copy-on-write: przypisanie $b = $a współdzieli dane, dopóki któraś zmienna nie zostanie zmodyfikowana. Ponieważ każde żądanie zaczyna się i kończy czysto, wycieki pamięci mają znaczenie głównie w długo działających workerach i konsumentach kolejek.
Jak zaimplementowane są tablice w PHP?
Tablica w PHP to uporządkowana mapa mieszająca: zachowuje kolejność wstawiania i przypisuje wartości do kluczy int lub string, dlatego ten sam typ służy jako lista, słownik i stos. Wyszukiwanie po kluczu (isset($arr[$key])) zajmuje średnio stały czas, a in_array() przegląda każdą wartość. Klucze tekstowe wyglądające jak liczby całkowite są konwertowane: $a['1'] i $a[1] to ten sam element.
Typowe zadania z kodowania na rozmowie
Krótkie ćwiczenia często zadawane na żywo. Każda funkcja to jedna z typowych odpowiedzi; uruchom je, dodaj własne dane wejściowe (w tym puste) i bądź gotów wyjaśnić złożoność czasową, bo zwykle to właśnie sprawdza rekruter.
Najczęściej zadawane pytania
Jak przygotować się do rozmowy kwalifikacyjnej na programistę PHP?
Upewnij się, że potrafisz wyjaśnić podstawy i napisać do nich kod bez sprawdzania: porównania i typy, tablice i ich funkcje, OOP (interfejsy, klasy abstrakcyjne, traity, static), wyjątki i bezpieczeństwo (zapytania przygotowane, htmlspecialchars, password_hash). Potem poznaj jeden framework (Laravel albo Symfony) i Composera na tyle dobrze, by omówić zbudowany przez siebie projekt.
Jaką wersję PHP znać na rozmowę kwalifikacyjną?
PHP 8. Rekruterzy oczekują znajomości funkcji dodanych w wersjach od 8.0 do 8.3: argumentów nazwanych, match, operatora nullsafe ?->, typów unijnych, promocji właściwości w konstruktorze, enumów, właściwości readonly i składni first-class callable. Zachowanie z PHP 7 wspominaj tylko tam, gdzie się zmieniło, na przykład 0 == "a".
Jakie pytania z PHP zadaje się juniorom?
Głównie o podstawy języka: == vs ===, echo vs print, pojedyncze vs podwójne cudzysłowy, isset vs empty, include vs require, GET vs POST, sesje vs ciasteczka oraz proste zadania na tablicach, jak odwracanie, filtrowanie czy liczenie wartości. Spodziewaj się co najmniej jednego krótkiego zadania z kodowania.
Jakie pytania z PHP zadaje się doświadczonym programistom?
O projektowanie i działanie pod spodem: interfejsy vs klasy abstrakcyjne, traity, późne wiązanie statyczne, referencje, generatory i pamięć, wyjątki i obsługa błędów, SQL injection i transakcje, cache, autoloading Composera oraz o to, jak zbudowałbyś lub przetestował daną funkcję. Od odpowiedzi oczekuje się kompromisów, a nie definicji.