Типизация: аргументы и возвращаемые значения
Зачем типизация
Типы — контракт между функцией и вызывающим кодом.
Плюсы:
- Ошибки ловятся раньше — до выполнения.
- Документация — видно, что принимает и что возвращает.
- IDE — автодополнение, рефакторинг.
- Рефакторинг безопаснее — типы проверяются.
Минус: чуть больше кода.
Объявление типов
Параметры:
function greet(string $name): string
{
return "Привет, $name";
}
Возвращаемый тип после :.
Примитивные типы
function f(
int $a,
float $b,
string $c,
bool $d,
array $e,
object $f,
mixed $g,
): void {}
mixed — любой тип (PHP 8.0+).
null — только если параметр опциональный:
function f(?string $name): void {}
Возвращаемые типы
function getName(): string { return 'Иван'; }
function getId(): int { return 1; }
function isActive(): bool { return true; }
function getItems(): array { return []; }
function getUser(): User { return new User(); }
function getMaybe(): ?string { return null; }
void — ничего:
function log(string $msg): void
{
echo $msg;
}
never (PHP 8.1+):
function fail(string $msg): never
{
throw new Exception($msg);
}
Функция никогда не возвращает управление — прерывает через throw, exit.
Strict types
По умолчанию PHP делает неявные приведения:
function sum(int $a, int $b): int { return $a + $b; }
sum('2', '3'); // 5 — строки превратились в int
sum(2.5, 3.7); // 5 — float → int (с потерей)
Со strict_types=1:
<?php
declare(strict_types=1);
function sum(int $a, int $b): int { return $a + $b; }
sum('2', '3'); // TypeError
sum(2, 3); // 5
Правило: всегда declare(strict_types=1).
Работает для вызовов из того же файла. Для вызовов извне — по настройке того файла.
Union types
PHP 8.0+:
function formatId(int|string $id): string
{
return (string) $id;
}
Несколько вариантов:
function process(int|float|string $value): void {}
С null:
function f(int|string|null $value): void {}
Типичный пример — цена:
function setPrice(int|float $price): void { }
Nullable
?type — короткая запись type|null:
function greet(?string $name): string
{
return $name !== null ? "Привет, $name" : 'Привет';
}
Со значением по умолчанию:
function greet(?string $name = null): string
{
return $name !== null ? "Привет, $name" : 'Привет';
}
Пересечение типов
PHP 8.1+:
function f(Countable&ArrayAccess $collection): void { }
Объект должен реализовать оба интерфейса.
DNF types
PHP 8.2+ — disjunctive normal form:
function f((A&B)|C $value): void { }
Комбинация union и intersection.
Классы и интерфейсы
function save(User $user): void { }
function process(LoggerInterface $logger): void { }
Строгая типизация — IDE, статанализ.
self, static, parent
class Query
{
public function where(string $f): static
{
return $this;
}
public function duplicate(): self
{
return clone $this;
}
}
self — тип текущего класса.
static — тип вызывающего класса (для наследников).
callable
function apply(callable $fn, mixed $value): mixed
{
return $fn($value);
}
apply('strlen', 'Hello'); // 5
apply(fn($x) => $x * 2, 5); // 10
apply([$obj, 'method'], $arg); // метод
Быстрее и точнее — Closure:
function apply(Closure $fn, mixed $value): mixed
{
return $fn($value);
}
iterable
function process(iterable $items): void
{
foreach ($items as $item) { }
}
Принимает массив или Traversable.
object
function f(object $obj): void { }
Любой объект.
Обычно лучше указать класс или интерфейс.
array
function sum(array $numbers): int
{
return array_sum($numbers);
}
Для типизированных массивов — PHPDoc:
/**
* @param int[] $numbers
* @return int
*/
function sum(array $numbers): int
{
return array_sum($numbers);
}
Для объектов:
/**
* @param User[] $users
* @return string[]
*/
function getNames(array $users): array
{
return array_map(fn(User $u) => $u->name, $users);
}
PHPDoc — читает IDE и анализаторы (PHPStan, Psalm).
mixed
PHP 8.0+ — любой тип:
function debug(mixed $value): void
{
var_dump($value);
}
Эквивалент array|bool|callable|int|float|null|object|resource|string.
Не злоупотребляй — теряется типизация.
never
PHP 8.1+:
function fail(string $msg): never
{
throw new RuntimeException($msg);
}
function loop(): never
{
while (true) { }
}
Функция не возвращает управление.
Ковариантность / контравариантность
Возврат — ковариантный:
class ParentClass
{
public function get(): ParentType { }
}
class ChildClass extends ParentClass
{
public function get(): ChildType { } // уже (ChildType — потомок ParentType)
}
Параметры — контравариантные:
class ParentClass
{
public function set(ChildType $x): void { }
}
class ChildClass extends ParentClass
{
public function set(ParentType $x): void { } // шире
}
Правило: возврат можно сузить, параметры расширить.
Типы в свойствах
class User
{
public string $name;
public ?string $email = null;
public int $age = 0;
public array $roles = [];
}
С PHP 8.1:
class User
{
public readonly string $id;
}
Типы в константах
PHP 8.3+:
class Config
{
const int MAX_ATTEMPTS = 5;
const string APP_NAME = 'MyApp';
}
Проверка типов во время выполнения
function f(mixed $x): void
{
if (!is_int($x)) {
throw new InvalidArgumentException('Ожидается int');
}
}
Для сложных случаев — is_object, instanceof:
if ($x instanceof User) {
echo $x->name;
}
Type juggling
PHP сам приводит типы в некоторых случаях:
// Арифметика
'5' + 3; // 8
'5' . 3; // '53' (конкатенация)
// Сравнение
'5' == 5; // true
'5' === 5; // false
Со strict_types — при вызовах функций не приводит.
Практика
<?php
declare(strict_types=1);
final class Money
{
public function __construct(
public readonly int $amount,
public readonly string $currency,
) {}
public function add(Money $other): static
{
if ($this->currency !== $other->currency) {
throw new InvalidArgumentException('Валюты разные');
}
return new static($this->amount + $other->amount, $this->currency);
}
public function format(): string
{
return number_format($this->amount / 100, 2) . ' ' . $this->currency;
}
}
function total(array $items): Money
{
$sum = new Money(0, 'RUB');
foreach ($items as $item) {
$sum = $sum->add($item);
}
return $sum;
}
$sum = total([
new Money(10000, 'RUB'),
new Money(25000, 'RUB'),
]);
echo $sum->format(); // 350.00 RUB
Итог
- Типы — обязательны для функций, методов, свойств.
declare(strict_types=1)— всегда.- Union —
int|string. - Nullable —
?stringилиstring|null. void— ничего,never— не возвращает.mixed— любой (осторожно).- PHPDoc — для коллекций.
- Ковариантность — возврат сужается, параметры расширяются.
Дальше — strict_types, union types, nullable.