Увійти Реєстрація
Блог Серії
Кар'єра
Вакансії Компанії
Навчання
Документація Співбесіди Тестування Відео
Екосистема
Пакети Ресурси Проєкти Інструменти Події
Інше
Про нас Реклама

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()) зручно інкапсулюють логіку відображення.

Докладніше в документації: Валідація enum

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', що інколи несподівано.

Жодна з функцій не змінює початковий масив - усі повертають новий.

Докладніше в документації: array_map

Прочитати - ще не значить знати

20 питань, по одному на екран, ~15 хв. Після завершення - розбір кожної помилки з посиланням на питання.