PHP: Junior
20 питань · ~15 хв · Версія v3.0
Увійдіть, щоб продовжити
Синтаксис, типи, масиви, рядки, базові функції.
- За спробу
- 20
- У пулі
- 100
- Проходжень
- 47
- Середній бал
- 86%
- Пройшли на 70%+
- 94%
Питання для підготовки
32 питанняBacked enum - перелік зі скалярним значенням, прив'язаним до кожного кейса:
enum Status: string
{
case Draft = 'draft';
case Published = 'published';
}
Інтеграція з Laravel:
// каст у моделі - атрибут стає об'єктом enum
protected $casts = ['status' => Status::class];
// валідація
$request->validate(['status' => [Rule::enum(Status::class)]]);
// Route Model Binding теж резолвить enum з URL
Enum робить «магічні рядки» типобезпечними, а методи на enum (label(), color()) зручно інкапсулюють логіку відображення.
PHP дозволяє оголошувати типи аргументів, повернення та властивостей - і перевіряє їх у рантаймі.
class VacancyService
{
public function __construct(
private VacancyRepository $repository,
) {
}
public function publish(int $id, ?string $comment = null): Vacancy
{
// ...
}
}
Що дає: помилка ловиться в момент виклику, а не через три шари; редактор підказує методи; статичний аналіз бачить невідповідності до запуску.
declare(strict_types=1) змінює поведінку перевірки. Без нього PHP приводить типи мовчки:
function repeat(int $times): string { /* ... */ }
repeat('5'); // без strict_types: '5' стане 5, викликається нормально
repeat('5'); // зі strict_types: TypeError
Саме мовчазне приведення небезпечне: 'abc' перетвориться на 0, а '5 котів' - на 5, і помилка проявиться далеко від місця, де виникла.
Важлива деталь: директива діє на файл, де вона написана, і стосується викликів з нього, а не в нього. Тому її ставлять у кожен файл, першим рядком після <?php.
Корисні типи PHP 8:
?string- рядок абоnull.int|string- обʼєднання типів.never- функція не повертає керування (кидає виняток чи завершує процес).
У Laravel-проєктах declare(strict_types=1) зазвичай вимагається стилем коду й перевіряється Pint.
Інтерфейс - це лише контракт: перелік публічних методів, які клас зобов'язується мати. Реалізації в ньому немає (крім констант), і клас може реалізувати скільки завгодно інтерфейсів.
Абстрактний клас - це недобудований клас: у ньому можуть бути властивості, готові методи й абстрактні методи, які мають дописати нащадки. Успадкувати можна лише один клас.
interface PaymentGateway
{
public function charge(Money $amount): Receipt;
}
abstract class HttpGateway implements PaymentGateway
{
public function __construct(protected HttpClient $http) {}
protected function post(string $path, array $data): array
{
return $this->http->post($this->baseUrl() . $path, $data);
}
abstract protected function baseUrl(): string;
}
Коли що:
- Інтерфейс - коли потрібна взаємозамінність: код залежить від
PaymentGateway, а конкретний шлюз підставляється ззовні. - Абстрактний клас - коли кілька реалізацій ділять спільний код і стан.
Часто вони йдуть разом, як у прикладі: залежать від інтерфейсу, а абстрактний клас лише прибирає повтор між реалізаціями.
Це модифікатори видимості - хто може звертатися до властивості чи методу:
public- будь-хто, зокрема код поза класом.protected- сам клас і його нащадки.private- лише сам клас, де член оголошено. Нащадок його не бачить.
class Account
{
public function __construct(private int $balance = 0) {}
public function deposit(int $amount): void
{
$this->guard($amount);
$this->balance += $amount;
}
protected function guard(int $amount): void
{
if ($amount <= 0) {
throw new InvalidArgumentException('Сума має бути додатною');
}
}
}
Тут баланс не можна змінити напряму ззовні - лише через deposit(), який перевіряє суму. У цьому й сенс інкапсуляції: клас сам стежить за тим, щоб його стан лишався коректним.
Правило за замовчуванням: усе private, доки не з'явиться причина відкрити більше. Відкриту властивість потім важко закрити - на неї вже покладається чужий код.
У PHP 8.4 з'явилася асиметрична видимість: public private(set) int $balance - читати можна звідусіль, а змінювати лише всередині класу.
Усі три обходять масив і викликають функцію для кожного елемента, але повертають різне:
array_map- новий масив тієї ж довжини, кожен елемент перетворено.array_filter- лише ті елементи, для яких функція повернулаtrue. Ключі зберігаються.array_reduce- одне значення, накопичене з усіх елементів.
$prices = [100, 250, 40];
array_map(fn (int $p): int => $p * 2, $prices); // [200, 500, 80]
array_filter($prices, fn (int $p): bool => $p > 50); // [0 => 100, 1 => 250]
array_reduce($prices, fn (int $sum, int $p): int => $sum + $p, 0); // 390
Пастки, про які питають:
- Порядок аргументів різний: у
array_mapспершу функція, уarray_filter- масив. Плутають постійно. - Після
array_filterключі «дірчасті» ([0, 2, 5]), іjson_encodeперетворить такий масив на об'єкт. Допомагаєarray_values(). array_filterбез функції прибирає всі «порожні» значення - разом із0і'0', що інколи несподівано.
Жодна з функцій не змінює початковий масив - усі повертають новий.
Спершу почитати
Прочитати - ще не значить знати
20 питань, по одному на екран, ~15 хв. Після завершення - розбір кожної помилки з посиланням на питання.