PHP 7.0 — Революция производительности
PHP 7.0 (декабрь 2015) стал одним из самых значительных релизов в истории языка. Движок Zend Engine 3 обеспечил двукратный прирост производительности по сравнению с PHP 5.6 и значительное снижение потребления памяти.
Скалярные type hints
До PHP 7.0 type hints поддерживали только классы, интерфейсы, массивы и callable. Теперь доступны скалярные типы.
<?php
declare(strict_types=1);
// Скалярные type hints: int, float, string, bool
function calculateDiscount(float $price, int $percentage): float
{
return $price * ($percentage / 100);
}
echo calculateDiscount(100.0, 15); // 15.0
// Без strict_types PHP приводит типы автоматически:
// calculateDiscount("100", "15") — сработает в coercive mode
// С strict_types=1 будет TypeError:
// calculateDiscount("100", "15"); // TypeError
Важно:
declare(strict_types=1)влияет только на файл, в котором объявлен. Вызовы из других файлов без этой директивы работают в coercive mode.
Return type declarations
<?php
declare(strict_types=1);
function sum(int $a, int $b): int
{
return $a + $b;
}
function getUser(int $id): array
{
return ['id' => $id, 'name' => 'John'];
}
// Возврат неправильного типа вызывает TypeError
function broken(): int
{
return "hello"; // TypeError в strict mode
}
Оператор spaceship <=>
Оператор комбинированного сравнения возвращает -1, 0 или 1.
<?php
// Сравнение чисел
echo 1 <=> 2; // -1
echo 2 <=> 2; // 0
echo 3 <=> 2; // 1
// Удобно для сортировки
$users = [
['name' => 'Charlie', 'age' => 30],
['name' => 'Alice', 'age' => 25],
['name' => 'Bob', 'age' => 35],
];
// Сортировка по возрасту
usort($users, fn($a, $b) => $a['age'] <=> $b['age']);
// Сортировка по нескольким полям
usort($users, function ($a, $b) {
return $a['age'] <=> $b['age']
?: $a['name'] <=> $b['name'];
});
Null coalescing operator ??
<?php
// До PHP 7.0
$username = isset($_GET['user']) ? $_GET['user'] : 'guest';
// PHP 7.0+
$username = $_GET['user'] ?? 'guest';
// Цепочка — берёт первое не-null значение
$config = $customConfig ?? $envConfig ?? $defaultConfig ?? 'fallback';
// Работает с массивами и свойствами объектов
$city = $user['address']['city'] ?? 'Unknown';
$name = $order?->customer?->name ?? 'Anonymous'; // в сочетании с ?-> (PHP 8.0)
Отличие от
?:: оператор??проверяет наnull, а?:проверяет на falsy (0, '', false, null, []).
Анонимные классы
<?php
// Полезно для одноразовых реализаций интерфейсов
interface Logger
{
public function log(string $message): void;
}
function processData(Logger $logger): void
{
$logger->log('Processing started');
// ...
}
// Анонимный класс вместо создания отдельного файла
processData(new class implements Logger {
public function log(string $message): void
{
echo "[LOG] $message\n";
}
});
// Анонимные классы могут наследовать и использовать трейты
$obj = new class('value') extends ParentClass implements Interface1 {
use SomeTrait;
public function __construct(private string $value) {}
};
Группировка use declarations
<?php
// До PHP 7.0 — каждый use отдельно
use App\Models\User;
use App\Models\Order;
use App\Models\Product;
// PHP 7.0+ — группировка
use App\Models\{User, Order, Product};
// Можно группировать разные типы
use App\Services\{
UserService,
OrderService,
};
use function App\Helpers\{format_date, format_money};
use const App\Config\{DB_HOST, DB_PORT};
Обработка ошибок: Throwable, Error, TypeError
<?php
// PHP 7.0 ввёл иерархию: Throwable -> Error | Exception
try {
// Фатальные ошибки теперь перехватываемые
$result = undefinedFunction();
} catch (\Error $e) {
echo "Error: " . $e->getMessage();
}
// TypeError при нарушении типов
try {
function add(int $a, int $b): int { return $a + $b; }
add("hello", "world"); // TypeError в strict mode
} catch (\TypeError $e) {
echo "Type error: " . $e->getMessage();
}
// Перехват всего через Throwable
try {
// ...
} catch (\Throwable $e) {
// Ловит и Error, и Exception
echo $e->getMessage();
}
PHP 7.1 — Уточнение типовой системы
PHP 7.1 (декабрь 2016) расширил систему типов nullable-типами и void.
Nullable types
<?php
declare(strict_types=1);
// Знак ? перед типом означает "тип ИЛИ null"
function findUser(int $id): ?User
{
$user = UserRepository::find($id);
return $user; // User или null
}
// Nullable параметр
function setNickname(?string $nickname): void
{
// $nickname может быть string или null
}
setNickname('Neo'); // OK
setNickname(null); // OK
setNickname(); // Ошибка! null и отсутствие аргумента — разные вещи
Подвох для экзамена:
?string $paramиstring $param = null— не одно и то же. Первый ТРЕБУЕТ аргумент (string или null), второй допускает пропуск аргумента.
Void return type
<?php
function sendEmail(string $to, string $subject): void
{
// Отправляем email
mail($to, $subject, '...');
// return; — можно (без значения)
// return null; — ОШИБКА! void не допускает даже null
}
// void нельзя использовать как тип параметра
// function broken(void $param) {} // Parse error
Iterable pseudo-type
<?php
// iterable принимает array или Traversable
function processItems(iterable $items): void
{
foreach ($items as $item) {
echo $item . "\n";
}
}
processItems([1, 2, 3]); // array — OK
processItems(new ArrayIterator([4, 5, 6])); // Traversable — OK
// В return type
function getItems(): iterable
{
yield 'first';
yield 'second';
// Генератор реализует Traversable
}
Видимость констант класса
<?php
class Config
{
// До PHP 7.1 — все константы были public
public const VERSION = '1.0.0';
protected const DB_HOST = 'localhost';
private const DB_PASSWORD = 'secret';
public function getDbHost(): string
{
return self::DB_HOST;
}
}
echo Config::VERSION; // OK
// echo Config::DB_HOST; // Fatal error: Cannot access protected constant
// echo Config::DB_PASSWORD; // Fatal error: Cannot access private constant
Multi-catch exceptions
<?php
try {
$data = json_decode($input, true, 512, JSON_THROW_ON_ERROR);
$db->query($sql);
} catch (JsonException | PDOException $e) {
// Один блок catch для нескольких типов исключений
logger()->error('Data processing failed', [
'exception' => $e::class,
'message' => $e->getMessage(),
]);
} catch (RuntimeException $e) {
// Отдельная обработка
logger()->critical('Runtime error', ['error' => $e->getMessage()]);
}
Symmetric array destructuring
<?php
// До PHP 7.1 — list()
list($first, $second) = [1, 2];
// PHP 7.1+ — короткий синтаксис []
[$first, $second] = [1, 2];
// С ключами (тоже новинка 7.1!)
['name' => $name, 'age' => $age] = ['name' => 'Alice', 'age' => 30];
echo "$name is $age years old"; // Alice is 30 years old
// В foreach
$users = [
['name' => 'Alice', 'role' => 'admin'],
['name' => 'Bob', 'role' => 'user'],
];
foreach ($users as ['name' => $name, 'role' => $role]) {
echo "$name ($role)\n";
}
// Вложенная деструктуризация
[[$a, $b], [$c, $d]] = [[1, 2], [3, 4]];
Отрицательные строковые смещения
<?php
$str = 'Hello World';
echo $str[-1]; // d (последний символ)
echo $str[-5]; // W
// Работает и в функциях
echo substr($str, -5); // World
echo strpos($str, 'o', -6); // 7 (поиск с конца)
Сводная таблица: PHP 7.0 vs 7.1
| Возможность | PHP 7.0 | PHP 7.1 |
|---|---|---|
| Скалярные type hints | int, float, string, bool |
-- |
| Return types | Да | + void |
| Nullable types | -- | ?int, ?string |
?? (null coalescing) |
Да | -- |
<=> (spaceship) |
Да | -- |
| Анонимные классы | Да | -- |
iterable |
-- | Да |
| Multi-catch | -- | catch (A | B $e) |
| Видимость констант | -- | public/protected/private const |
Array destructuring [] |
-- | Да + ключи |
| Отрицательные смещения строк | -- | Да |
Группировка use |
Да | -- |
Throwable / Error |
Да | -- |
Типичные вопросы на экзамене
- Что вернёт
1 <=> 1? Ответ:0 - Можно ли использовать
voidкак тип параметра? Нет, только как return type. - Разница между
??и?:???проверяет на null,?:на falsy. - Что произойдёт при
return nullв void-функции? Fatal error. ?string $xvsstring $x = null? Первый требует аргумент, второй допускает пропуск.
Практические задания
Задание 1: Spaceship сортировка
Отсортируйте массив пользователей сначала по роли (admin > editor > viewer), затем по имени.
Задание 2: Null coalescing
Напишите функцию getConfig(string $key): mixed, которая ищет значение сначала в $_ENV, потом в файле конфигурации, потом возвращает default.
Задание 3: Nullable types
Реализуйте UserRepository с методом findByEmail(?string $email): ?User, который обрабатывает null-email корректно.