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

Senior: питання на співбесіді з теми «ООП»

Питання з реальних співбесід з відповідями: Laravel і PHP, бази даних, JavaScript і фронтенд, Git, Docker, API, безпека й архітектура. Тими самими темами, що й тести.

4 питання

Успадкування - найсильніший зв'язок між класами: нащадок залежить від усіх деталей предка, зокрема захищених. Будь-яка зміна в базовому класі може тихо зламати десяток нащадків - це називають проблемою крихкого базового класу.

Типові симптоми:

  • Ієрархія росте вглиб: Report → PdfReport → ColoredPdfReport → ... Нова комбінація ознак - новий клас.
  • Нащадок перевизначає метод, щоб «вимкнути» поведінку предка, яка йому не потрібна.
  • Базовий клас обростає protected-хелперами, бо так зручніше нащадкам.

Композиція - об'єкт отримує інші об'єкти і делегує їм роботу:

final class ReportExporter
{
    public function __construct(
        private Formatter $formatter,   // PDF, CSV, HTML
        private Storage $storage,       // диск, S3
    ) {}

    public function export(Report $report): string
    {
        return $this->storage->put($this->formatter->format($report));
    }
}

Тепер формат і сховище комбінуються вільно, кожне тестується окремо, і змінити одне можна, не чіпаючи іншого.

Успадкування доречне, коли є справжнє «є різновидом» і спільний контракт стабільний: власні винятки від RuntimeException, моделі від Model. Корисна звичка - робити класи final за замовчуванням: тоді успадкування стає свідомим рішенням, а не випадковістю.

Докладніше в документації: Успадкування об'єктів

readonly-властивість (PHP 8.1) можна присвоїти лише один раз і лише всередині класу - зазвичай у конструкторі. Далі будь-яка спроба змінити її кидає Error. readonly class (PHP 8.2) робить такими всі властивості класу.

final readonly class Money
{
    public function __construct(
        public int $amount,
        public string $currency,
    ) {}
}

Що варто знати:

  • readonly захищає саму властивість, а не вміст об'єкта в ній: якщо там лежить змінюваний об'єкт, його поля змінювати можна.
  • Властивість мусить мати тип, і значення за замовчуванням у неї бути не може.

Змінені копії («withers»). Незмінний об'єкт не редагують, а створюють новий з іншим значенням:

public function withAmount(int $amount): static
{
    return new static($amount, $this->currency);
}

Через clone раніше так не виходило: копія мала ту саму вже ініціалізовану властивість. З PHP 8.3 readonly-властивості можна переприсвоїти в __clone(), а PHP 8.5 додав clone з переліком нових значень - clone($this, ['amount' => $amount]), без ручного конструктора.

Навіщо це все: незмінні value objects (гроші, діапазони дат, адреси) можна спокійно передавати куди завгодно - ніхто не змінить їх «під ногами» в іншому місці коду.

Докладніше в документації: Readonly-властивості

clone $obj створює новий об'єкт з тими самими значеннями властивостей. Але копія поверхнева: якщо властивість містить інший об'єкт, копіюється лише посилання на нього - обидва об'єкти ділять той самий вкладений об'єкт.

final class Order
{
    public function __construct(public DateTime $createdAt, public array $items) {}
}

$a = new Order(new DateTime('2026-01-01'), ['book']);
$b = clone $a;

$b->items[] = 'pen';                 // масив скопійовано: $a->items не змінився
$b->createdAt->modify('+1 day');     // об'єкт спільний: $a->createdAt теж змінився!

Масиви копіюються (за значенням), об'єкти всередині - ні.

__clone() викликається на копії після клонування - там роблять глибоку копію вкладених об'єктів:

public function __clone(): void
{
    $this->createdAt = clone $this->createdAt;
}

Де це стає проблемою:

  • «Незмінні» об'єкти з змінюваними частинами. Wither, що робить clone $this і змінює одне поле, ділить усі вкладені змінювані об'єкти з оригіналом.
  • Ресурси й з'єднання. Клон об'єкта з відкритим файлом чи з'єднанням з базою ділить той самий ресурс; закриття в одному ламає інший.
  • Ідентичність: клон Eloquent-моделі зберігає той самий первинний ключ. Для копії запису в базі є replicate().

Як уникнути проблем: робити вкладені значення незмінними (DateTimeImmutable, readonly value objects) - тоді поверхнева копія безпечна. З PHP 8.3 readonly-властивості можна переприсвоїти в __clone(), а PHP 8.5 додав clone($obj, ['prop' => $value]) для «зміненої копії» без ручного __clone.

Докладніше в документації: Клонування об'єктів

Property hooks (PHP 8.4) дозволяють прив'язати логіку до читання й запису властивості - без геттерів і сеттерів і без магічних __get/__set.

final class User
{
    public string $email {
        set (string $value) {
            if (! filter_var($value, FILTER_VALIDATE_EMAIL)) {
                throw new InvalidArgumentException('Некоректний email');
            }
            $this->email = mb_strtolower($value);
        }
    }

    public string $displayName {
        get => trim("{$this->firstName} {$this->lastName}");   // віртуальна властивість
    }

    public function __construct(public string $firstName, public string $lastName, string $email)
    {
        $this->email = $email;
    }
}

$user->email = 'Olia@Example.com';   // спрацює set-хук
echo $user->displayName;            // спрацює get-хук

Властивість, що має лише get-хук і не звертається до власного значення, - віртуальна: місця для неї не виділяється.

Асиметрична видимість - різні модифікатори для читання й запису:

final class Order
{
    public private(set) string $status = 'new';   // читати - всім, змінювати - лише класу

    public function ship(): void
    {
        $this->status = 'shipped';
    }
}

Скорочення: private(set) string $status означає public private(set).

Що це змінює:

  • Публічна властивість більше не означає «будь-хто може записати що завгодно» - валідацію й нормалізацію можна додати пізніше, не змінюючи API на методи.
  • Відпадає шаблонний код геттерів для «читається всім, змінюється лише всередині».
  • Хуки можна оголошувати в інтерфейсах: public string $name { get; }.

Обмеження: хуки й readonly не поєднуються, а посилання на властивість з set-хуком (&$obj->prop) заборонене.

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