Зачем использовать declare (strict_types=1)

Источник: «Using `declare(strict_types=1)` for More Robust PHP Code»
Прочитайте о том, как вы можете использовать declare(strict_types=1) для повышения безопасности типов PHP-кода. Мы обсудим, как сделать код более надёжным и избежать принудительного приведения типов, что может привести к ошибкам.

Введение

Если вы пишете на PHP, вы, вероятно, видели выражение declare(strict_types=1) в начале некоторых PHP файлов.

Когда я впервые увидел это выражение, я понятия не имел, что оно делает. Я предположил, что это какой-то комментарий или, может быть, старый синтаксис PHP, который был до меня, но я ошибался (очень ошибался)!

В этой статье мы расскажем, что такое declare(strict_types=1) и как это может помочь вам повысить безопасность типов PHP-кода.

Что такое declare(strict_types=1)?

declare(strict_types=1) — это выражение, обеспечивающий строгую типизацию в приложениях PHP.

Когда вы используете это выражение, PHP будет выполнять строгую проверку типов параметров функций и возвращаемых типов. Это означает, что если ваша функция ожидает определённый тип параметра или возвращаемого значения, PHP выдаст ошибку, если используется неправильный тип.

Возьмём простой пример, в котором не используется declare(strict_types=1):

function add(int $a, int $b): int
{
return $a + $b;
}

Теперь предположим, что мы вызвали эту функцию со строковыми параметрами:

echo add('1', '2');

// Вывод:
// 3

PHP с радостью конвертирует строковые параметры в целочисленные и вернёт результат 3.

В некоторых случаях такое поведение может вас полностью устраивать. Но это может иметь некоторые непредвиденные последствия, которые могут вызывать ошибки в вашем приложении.

Однако давайте представим, что в этом примере мы используем declare(strict_types=1). Мы могли бы сделать это, добавив следующее выражение в начало файла:

declare(strict_types=1);

function add(int $a, int $b): int
{
return $a + $b;
}

Теперь, если мы вызовем функцию add со строковыми параметрами, PHP выдаст ошибку:

echo add('1', '2');

// Output:
// Fatal error: Uncaught TypeError: Argument 1 passed to add() must be of the type int, string given

Как мы видим, PHP выдал ошибку, потому что функция add ожидала передачи целых чисел, но вместо этого получила строки.

Точно так же PHP выдаст ошибку, если включена строгая проверка типов, и мы пытаемся вернуть неверный тип данных из метода. Предположим, что наша функция add теперь принимает числа с плавающей точкой вместо целых чисел и у нас не включена строгая проверка типов:

function add(float $a, float $b): int
{
return $a + $b;
}

Мы могли бы вызывать функцию следующим образом:

echo add(1.25, 2.25);

// Вывод:
// 3

Вы заметили проблему в выводе?

Ответ, который мы должны были получить равен 3.5. Однако поскольку мы определили возвращаемый тип как int, мы преобразовали float (которое должно быть возвращено) в целое число и потеряли точность. Как вы можете представить, это может вызвать некоторые проблемы в других частях нашего приложения, где мы используем этот результат, и может потребоваться точность.

Исправим эту проблему, используя declare(strict_types=1):

declare(strict_types=1);

function add(float $a, float $b): int
{
return $a + $b;
}

Затем, мы могли бы вызвать функцию следующим образом:

echo add(1.25, 2.25);

// Вывод:
// Fatal error: Uncaught TypeError: add(): Return value must be of type int, float returned

Как мы видим, включив строгую проверку типов, мы можем обнаружить, что функция возвращает неправильный тип данных. Это здорово, потому сто может указать на возможную ошибку в коде, о которой мы не знали. Затем мы могли бы предпринять необходимые шаги, чтобы:

Нужно ли использовать declare(strict_types=1)

Я считаю хорошей идеей использовать declare(strict_types=1) во всех PHP-файлах. Раньше я думал, что просто иметь подсказки типов и возвращаемые типы было достаточно, чтобы гарантировать передачу правильных типов данных, но с тех пор я передумал. Я чувствую себя намного увереннее, когда использую declare(strict_types=1) и обнаружил несколько ошибок в результате его использования (в частности, при добавлении к старым кодовым базам).

Узнав об этом я взял за привычку использовать его в каждом новом PHP файле, который я создаю. Фактически, я обновил все шаблоны в моей настройке PHPStorm, чтобы они автоматически выключались в начало каждого файла, который я создаю. Например, вот шаблон, который используется при создании нового PHP класса:

<?php

declare(strict_types=1);

#parse("PHP File Header.php")

#if (${NAMESPACE})
namespace ${NAMESPACE};

#end
class ${NAME} {

}

Это действительно удобно, потому что побуждает меня продолжать использовать declare(strict_types=1) без необходимости вносить какие-либо изменения вручную после создания файла (о чём я определённо забыл бы сделать!).

Для моих Laravel читателей, вы также можете опубликовать шаблоны, которые используются для создания PHP файлов при выполнении команд Artisan, таких, как php artisan make:controller. Публикуя шаблоны, вы можете отредактировать их и добавить declare(strict_types=1) вверху. Это означает, что файлы, которые вы создаёте с помощью команд Artisan, будут создаваться с уже включённой более строгой безопасностью типов.

Конечно, если вы собираетесь добавить более строгую проверку типов к своим существующим файлам, я настоятельно рекомендую сначала установить качественный набор тестов. Ваш PHP-код мог разрешать неверную передачу типов данных без каких-либо ошибок. Но, включив строгую проверку типов, ваш код станет гораздо менее щадящим и может начать выдавать ошибки. Это может привести к неожиданному сбою приложения для пользователей.

Также вы можете обнаружить, что необходимо провести рефакторинг части кода, чтобы сделать его совместимым с declare(strict_types=1). Хотя я бы не считал это чем-то плохим. Вместо этого я бы рассматривал это как возможность улучшить качество вашего кода.

Для упрощения процесса добавления в код declare(strict_types=1), вы можете использовать такой инструмент, как PHPStan, который может выявить несоответствия типов.

Заключение

Надеюсь эта статья дала вам обзор того, что такое declare(strict_types=1) и как это может помочь улучшить безопасность типов PHP-кода.

Похожие статьи

Дополнительные материалы

Предыдущая Статья

Новое в Symfony 6.3 — Улучшения HttpClient

Следующая Статья

Настройка CSP в Laravel и Vite