MidТеория3 min

Система типов

Type hints, union/intersection, readonly, nullable

Система типов ООП

Type Hints для свойств и методов

<?php
declare(strict_types=1);

class UserProfile
{
    public string $name;
    public int $age;
    public ?string $bio = null;        // Nullable
    public array $hobbies = [];
    public bool $active = true;

    public function setAge(int $age): void
    {
        if ($age < 0 || $age > 150) {
            throw new InvalidArgumentException("Invalid age: {$age}");
        }
        $this->age = $age;
    }
}

Union Types (PHP 8.0+)

<?php
declare(strict_types=1);

class ApiResponse
{
    // Property can be int or string
    public int|string $id;

    // Method parameters
    public function setData(array|string $data): void
    {
        $this->data = is_string($data)
            ? json_decode($data, true, 512, JSON_THROW_ON_ERROR)
            : $data;
    }

    // Return type
    public function findUser(int $id): User|null  // Same as ?User
    {
        return $this->repository->find($id);
    }

    // false as type
    public function search(string $query): array|false
    {
        return $this->doSearch($query) ?: false;
    }
}

// Special union types
function process(int|float|string $input): int|float
{
    return match(true) {
        is_int($input) => $input * 2,
        is_float($input) => round($input, 2),
        is_string($input) => (int)$input,
    };
}

Intersection Types (PHP 8.1+)

<?php
declare(strict_types=1);

interface Countable
{
    public function count(): int;
}

interface Serializable
{
    public function serialize(): string;
}

// Parameter MUST implement BOTH interfaces
function process(Countable&Serializable $data): string
{
    return "Count: {$data->count()}, Serialized: {$data->serialize()}";
}

// DNF Types — Disjunctive Normal Form (PHP 8.2+)
// Combine union and intersection
function handle((Countable&Serializable)|null $data): void
{
    // $data implements both OR is null
    if ($data === null) {
        return;
    }
    echo $data->count();
}

Запомни: Intersection types (A&B) — параметр должен реализовать ВСЕ типы. Union types (A|B) — параметр может быть ЛЮБЫМ из типов. DNF types ((A&B)|null) — комбинация обоих (PHP 8.2+).

Readonly свойства (PHP 8.1+)

<?php
declare(strict_types=1);

class Invoice
{
    public function __construct(
        public readonly string $number,
        public readonly float $amount,
        public readonly DateTimeImmutable $createdAt,
    ) {
    }
}

$invoice = new Invoice('INV-001', 150.00, new DateTimeImmutable());
echo $invoice->number;  // INV-001

// Cannot modify readonly property after initialization
// $invoice->number = 'INV-002';  // Error!

// Readonly properties CAN only be set in constructor or declaration
class Settings
{
    public readonly string $theme;

    public function __construct(string $theme)
    {
        $this->theme = $theme;  // OK — first assignment
        // $this->theme = 'dark';  // Error — already initialized!
    }
}

Readonly классы (PHP 8.2+)

<?php
declare(strict_types=1);

// ALL properties are implicitly readonly
readonly class Money
{
    public function __construct(
        public float $amount,
        public string $currency,
    ) {
    }

    public function add(Money $other): self
    {
        if ($this->currency !== $other->currency) {
            throw new InvalidArgumentException('Currency mismatch');
        }
        return new self($this->amount + $other->amount, $this->currency);
    }
}

$a = new Money(10.00, 'USD');
$b = new Money(5.00, 'USD');
$c = $a->add($b);
echo $c->amount;  // 15.0

// Cannot add non-readonly properties to readonly class
// readonly class Broken {
//     public string $mutable;  // Error! Must be readonly in readonly class
// }

Ловушка экзамена: В readonly классе ВСЕ свойства автоматически readonly. Нельзя объявить не-readonly свойство. readonly работает только с типизированными свойствами. Нельзя объявить readonly свойство без типа.

Nullable типы

<?php
declare(strict_types=1);

class UserService
{
    // ?Type syntax (PHP 7.1+)
    public function find(int $id): ?User
    {
        return $id > 0 ? new User($id) : null;
    }

    // Same as int|null (PHP 8.0+)
    public function getAge(int $userId): int|null
    {
        return $this->find($userId)?->age;
    }

    // Nullable parameter
    public function setName(?string $name): void
    {
        // $name can be string or null
    }

    // Default null (implicitly nullable before PHP 8.4)
    public function setEmail(string $email = null): void
    {
        // Deprecated implicit nullable in PHP 8.4!
        // Use: ?string $email = null
    }
}

Ловушка экзамена: В PHP 8.4 неявная nullable (string $param = null) помечена как deprecated. Используйте явную: ?string $param = null. На экзамене могут проверить знание этого изменения.

Nullsafe оператор (PHP 8.0+)

<?php
declare(strict_types=1);

class Address
{
    public function __construct(
        public readonly string $city,
        public readonly ?string $zip = null,
    ) {
    }
}

class User
{
    public function __construct(
        public readonly string $name,
        public readonly ?Address $address = null,
    ) {
    }
}

$user = new User('Alice');

// Without nullsafe: verbose null checking
$zip = $user->address !== null ? $user->address->zip : null;

// With nullsafe operator
$zip = $user->address?->zip;  // null (short-circuits)

// Chaining
$result = $user->address?->zip?->trim();  // null

// With method calls
$length = $user->address?->city?->length();  // Error! string has no length()
// But:
$upper = $user->address?->city;
$upper = $upper !== null ? strtoupper($upper) : null;

Вопросы с экзамена ZCE

Проверь себя

5 из 11

Что выведет следующий код? ```php function getReduced(int $x) { $x--; return $x; } function getIncreased(int $x): void { $x++; return $x; } $x = 0; $x = getReduced($x); $x = getIncreased($x); echo $x; ```

Какое допустимое тело функции с типом возврата `void`? ```php function test(): void // ??? ```

Выберите все правильные варианты

Что отобразится при выполнении следующего кода? ```php function a(): void { echo 'Hello'; } function b(): void { echo 'World'; return void; } a(); b(); ```

Что произойдёт, если использовать `fwrite` для записи в файл, открытый только для чтения?

Можно ли изменить readonly свойство после конструктора?