`DateTime` змінює сам себе на кожному `modify()`/`add()`, тому одна змінна тихо міняється в усіх місцях, де на неї посилаються; `DateTimeImmutable` має той самий API, але повертає новий екземпляр. Час зберігають як UTC-мітку, а пояс користувача застосовують лише на виводі через `setTimezone()` і завжди за ідентифікатором зони, а не за зсувом.
PHP для Junior: питання на співбесіду
17 питань рівня Junior з теми Core PHP з розгорнутими відповідями, порадами та перевіркою.
Оператор == порівнює значення з приведенням типів, === порівнює і значення, і тип без жодних перетворень.
Інтерфейс — це публічний контракт без стану, і їх можна реалізувати скільки завгодно; абстрактний клас — частково готова реалізація зі станом, конструктором і protected-методами, але успадкувати можна лише один.
Рядок у PHP - це послідовність байтів без інформації про кодування, тому `strlen` рахує байти, а кирилиця в UTF-8 займає по два байти на літеру. Символи рахують і ріжуть `mb_`-функції (`mb_strlen`, `mb_substr`, `mb_strtolower`), а регулярні вирази переводить у режим символів модифікатор `u`.
Float у PHP - це двійковий double за IEEE 754, у якому 0.1, 0.2 чи 19.99 не мають точного представлення, тому суми накопичують похибку. Гроші тримають у цілих копійках (int) або в рядках з DECIMAL/numeric у базі, рахують через bcmath чи `BcMath\Number` з PHP 8.4, а валюту й правило округлення вважають частиною значення.
Enum (PHP 8.1+) — тип із фіксованим переліком case-ів; pure enum не має скалярного значення, backed enum привʼязаний до int або string і вміє from()/tryFrom().
Prepared statement - це запит із плейсхолдерами (`?` або `:name`), у якому дані передаються окремо від тексту SQL, тому вони ніколи не парсяться як команда. Захист працює саме через це розділення, а не через екранування лапок; у PDO MySQL за замовчуванням увімкнена емуляція, тому перевіряйте `PDO::ATTR_EMULATE_PREPARES` і завжди задавайте `charset` у DSN.
readonly дозволяє записати типізовану властивість рівно один раз зі скоупу класу; з PHP 8.2 модифікатор можна поставити на весь клас.
Впровадження залежностей означає, що клас отримує готові співпрацівники ззовні (найчастіше через конструктор), а не створює їх сам. Контейнер це фабрика з реєстром, яка вміє зібрати граф обʼєктів за типами параметрів конструктора і підставити реалізацію там, де оголошено інтерфейс.
З PHP 7 усе, що можна кинути, реалізує інтерфейс `Throwable`, під яким дві незалежні гілки: `Exception` (очікувані ситуації рантайму) і `Error` (помилки програміста: `TypeError`, `ValueError`, `DivisionByZeroError`). `finally` виконується завжди, окрім `exit()` і фатального завершення, а warnings лишилися для ситуацій, які PHP історично вміє пережити, і перетворюються на винятки через `set_error_handler`.
Хешувати треба `password_hash()` і перевіряти `password_verify()`: функція сама генерує криптостійку сіль і кладе її разом з алгоритмом і вартістю в один рядок. Окрема колонка для солі не потрібна, а параметри піднімають через `password_needs_rehash()` під час успішного логіну.
Замикання — це обʼєкт класу Closure; `function` захоплює зовнішні змінні лише явно через `use` (за значенням або за посиланням через `&`), а стрілочна функція `fn` (PHP 7.4+) захоплює їх автоматично й завжди за значенням.
У cookie браузер тримає лише ідентифікатор (типово `PHPSESSID`), а самі дані `$_SESSION` лежать на сервері: у файлах `session.save_path`, у Redis або у власному `SessionHandlerInterface`. Cookie сесії має бути `HttpOnly`, `Secure` і `SameSite`, а API замість сесії краще авторизувати токеном у заголовку `Authorization`, бо cookie шлеться браузером автоматично й тягне за собою CSRF і прив'язку до сервера.
Composer реєструє свій завантажувач через `spl_autoload_register`, і при першій згадці класу PSR-4 перетворює його повне імʼя на шлях: найдовший збіг префікса замінюється базовою текою, решта імені стає підтеками й файлом. Classmap натомість заздалегідь сканує теки й будує таблицю «клас - файл», а `dump-autoload -o` перетворює PSR-4-правила на таку саму таблицю, щоб прибрати пошук по файловій системі.
Магічні методи - це хуки, які движок кличе замість помилки: `__get`/`__set`/`__call` підміняють доступ до неоголошених членів класу, `__invoke` робить обʼєкт callable, `__toString` дає рядкове представлення, `__construct` і `__destruct` тримають життєвий цикл. За `__get`/`__set`/`__call` платять автодоповненням, типами й рефакторингом, тому їх лишають для проксі та ORM, а в звичайному коді оголошують властивості й методи явно.
Трейт — механізм горизонтального повторного використання коду: набір методів і властивостей, який компілятор копіює в клас.
Масив передається за значенням, але фізична копія не створюється: PHP збільшує лічильник посилань на внутрішню структуру і копіює її лише при першому записі (copy-on-write). Обʼєкт copy-on-write не має - копіюється лише ідентифікатор, тому зміни властивостей видно з обох боків.