php declare strict types
Строгая типизация в PHP
По умолчанию PHP работает в нестрого-типизированном режиме, если вы хотите работать в строгом режиме, нужно кое-что добавить
Все чаще стал замечать, что много пользователей приходят на сайт с поисковым запросом «строгая типизация php», поэтому я решил немного уделить этому времени и написать небольшой пост о том, как включить режим строгой типизации в вашем проекте.
Немного информации
Строгая типизация была введена в PHP 7, благодаря ей появилась возможность указывать типы аргументов функций(методов) и тип возвращаемого значения.
По умолчанию PHP 7, работая в нестрого типизированном режиме, будет стараться преобразовать значения другого типа в ожидаемый скалярный тип, если это возможно. Например, функцию, ожидающую строку, можно по-прежнему вызывать с целочисленным аргументом, поскольку целое число можно преобразовать в строку, как показано в следующем примере:
Без строгой типизации PHP преобразовал целое число 33 в строку «33», что и следовало ожидать.
Введение таких подсказок скалярного типа и включение строгих требований позволит писать более правильные и самодокументированные программы PHP. Это также дает вам больше контроля над вашим кодом и может облегчить его чтение. Тем более что, в последней версии PHP 7.4. ввели типизированные свойства, что делает строгую типизацию еще строже :).
Как включить строгую типизацию в PHP?
В PHP 7 нам завезли директиву declare(strict_types=1);, которая как раз и включает строгий режим. В строгом режиме будет принята только переменная точного, заданного типа, или будет выброшен TypeError. Единственное исключение из этого правила состоит в том, что функция, ожидающая float, может давать целое число.
Итак, переделываем наш пример выше под строгую типизацию:
и ожидаемо получаем исключение TypeError. Единственное исключение из правила строгой типизации состоит в том, что функция, ожидающая float, может давать целое число:
Директива declare(strict_types=1) должна быть вставлена в первой строке вашего кода, даже перед пространствами имен, соответственно после открытия Вам нужно будет объявить declare(strict_types=1); в верхней части каждого файла, где вы намереваетесь использовать строгую типизацию.
Строгая типизация определяется только для объявлений скалярного типа, поэтому не будет работать в PHP менее 7 версии, поскольку объявления скалярного типа были добавлены именно в этой версии.
Веб-разработчик со стажем программирования более 9 лет, всегда в процессе учебы и созидания.
declare(strict_types=1)
В прошлом выпуске Пятиминутки PHP я рассказал, как обновлял кодовую базу с помощью утилиты Rector. Одно из направлений – простановка типов. Типы в свойствах классов, типы в параметрах функций, типы возвращаемых значений.
По умолчанию PHP пытается привести значения несоответствующих типов к скалярному типу, если это возможно. Например, если в функцию передается целое число ( int ), а тип аргумента объявлен как строка ( string ), в итоге функция получит преобразованное в строку ( string ) значение.
Два важных замечания.
Во-вторых, строгая типизация применима только к скалярным типам и работает только в PHP 7.0 и выше. Равно как и сами объявления скалярных типов добавлены в этой версии.
Именно таким путём я и пошел, из глубины. С одним отличием — не стал растягивать удовольствие на месяцы, я плотно засел на весь день.
Короче, ручное приведение к string во всех этих примерах, по сути, это ничего не поменяло, программа и раньше работала верно, но потрудиться пришлось изрядно.
И теперь у меня смешанные ощущения. На что я потратил целый день? Было ли это обновление действительно полезной работой или работой ради работы? Возможно, это хорошее вложение в код на долгосрочную перспективу.
Резюмируя: в прошлом выпуске, я однозначно рекомендовал попробовать Rector. В этом выпуске я однозначно рекомендую использовать declare(strict_types=1) в новом коде, но не факт, что есть смысл трогать старый код, особенно код уровня приложения.
What do strict types do in PHP?
I’ve seen the following new line in PHP 7, but nobody really explains what it means. I’ve googled it and all they talk about is will you be enabling it or not like a poll type of thing.
What does it do? How does it affect my code? Should I do it?
Some explanation would be nice.
2 Answers 2
With PHP 7 we now have added Scalar types. Specifically: int, float, string, and bool.
By adding scalar type hints and enabling strict requirements, it is hoped that more correct and self-documenting PHP programs can be written. It also gives you more control over your code and can make the code easier to read.
By default, scalar type-declarations are non-strict, which means they will attempt to change the original type to match the type specified by the type-declaration. In other words, if you pass a string that starts with a number into a function that requires a float, it will grab the number from the beginning and remove everything else. Passing a float into a function that requires an int will become int(1).
By default, PHP will cast values of the wrong type into the expected scalar type if possible. For example, a function that is given an integer for a parameter that expects a string will get a variable of type string.
Strict types disabled (eval):
It is possible to enable strict mode on a per-file basis. In strict mode, only a variable of exact type of the type declaration will be accepted, or a TypeError will be thrown. The only exception to this rule is that an integer may be given to a function expecting a float. Function calls from within internal functions will not be affected by the strict_types declaration.
To enable strict mode, the declare statement is used with the strict_types declaration:
Php declare strict types
(PHP 4, PHP 5, PHP 7, PHP 8)
Конструкция declare используется для установки директив исполнения для блока кода. Синтаксис declare аналогичен с синтаксисом других конструкций управления выполнением:
Поскольку директивы обрабатываются при компиляции файла, то только символьные данные могут использоваться как значения директивы. Нельзя использовать переменные и константы. Пример:
// Правильно:
declare( ticks = 1 );
// Недопустимо:
const TICK_VALUE = 1 ;
declare( ticks = TICK_VALUE );
?>
Конструкция declare также может быть использована в глобальной области видимости, влияя на весь следующий за ней код (однако если файл с declare был включён, то она не будет действовать на родительский файл).
// можно так:
declare( ticks = 1 ) <
// прочие действия
>
// или так:
declare( ticks = 1 );
// прочие действия
?>
Не все выражения подсчитываются. Обычно, условные выражения и выражения аргументов не подсчитываются.
Пример #1 Пример использования тика
// Функция, исполняемая при каждом тике
function tick_handler ()
<
echo «Вызывается tick_handler()\n» ;
>
register_tick_function ( ‘tick_handler’ ); // вызывает событие тика
$a = 1 ; // вызывает событие тика
Кодировка
Пример #2 Определение кодировки для скрипта.
User Contributed Notes 15 notes
It’s amazing how many people didn’t grasp the concept here. Note the wording in the documentation. It states that the tick handler is called every n native execution cycles. That means native instructions, not including system calls (i’m guessing). This can give you a very good idea if you need to optimize a particular part of your script, since you can measure quite effectively how many native instructions are in your actual code.
A good profiler would take that into account, and force you, the developer, to include calls to the profiler as you’re entering and leaving every function. That way you’d be able to keep an eye on how many cycles it took each function to complete. Independent of time.
That is extremely powerful, and not to be underestimated. A good solution would allow aggregate stats, so the total time in a function would be counted, including inside called functions.
A few important things to note for anyone using this in conjunction with signal handlers:
If anyone is trying to optionally use either pcntl_async_signals() when available (PHP >= 7.1) or ticks for older versions, this is not possible. at least not in a way that does NOT enable ticks for newer PHP versions. This is because there is simply no way to conditionally declare ticks. For example, the following will «work» but not in the way you might expect:
if ( function_exists ( ‘pcntl_async_signals’ )) <
pcntl_async_signals ( true );
> else <
declare( ticks = 1 );
>
?>
While signal handlers will work with this for old and new version, ticks WILL be enabled even in the case where pcntl_async_signals exists, simply because the declare statement exists. So the above is functionally equivalent to:
if ( function_exists ( ‘pcntl_async_signals’ )) pcntl_async_signals ( true );
declare( ticks = 1 );
?>
Another thing to be aware of is that the scoping of this declaration changed a bit from PHP 5.6 to 7.x. actually it was corrected apparently as noted here:
This can cause some very confusing behavior. One example is with the pear/System_Daemon module. With PHP 5.6 that will work with a SIGTERM handler even if the script using it doesn’t itself use declare(ticks=1), but does not work in PHP 7 unless the script itself has the declaration. Not only does the handler not get called, but the signal does nothing at all, and the script doesn’t exit.
A side note regarding ticks that’s annoyed me for some time: As if there wasn’t enough confusion around all this, the Internet is full of false rumors that ticks were deprecated and are being removed, and I believe they all started here:
Despite a very obscure author’s note at the very end of the page saying he got that wrong (that even I just noticed), the first very prominent sentence of the article still says this, and that page is near the top of any Google search.
Сравнение систем типов PHP7 и Hack
Установка
Получите следующий результат:
Некоторые примеры
Рассмотрим простой код:
Его выполнение в PHP7 вернет:
PHP с радостью исполняет код. Hack же возвращает ошибку:
Hack не позволяет нам иметь дефолтный аргумент со значением null, т.к. не смешивает понятия «необязательный аргумент» с «обязательный аргументом, который позволяет иметь дефолтное значение» (Подробнее об этом читайте в книге Hack and HHVM). Язык предлагает вам сделать аргумент nullable :
Давайте попробуем что-нибудь посложнее. Что произойдет, если мы смешаем типизации в PHP? Обратите внимание, что определение strict-режима в верхней части файла не имеет никакого эффекта в HHVM.
Для logger.php включился strict-режим, но PHP позволяет передать int в него из nonstrict-файла. HHVM в подобном случае выбрасывает исключение. Что произойдет, если мы переведем add.php в режим строгой типизации:
Так-то лучше. Strict-режим действует только в тех файлах, где он указан, даже если декларирующий функцию файл подразумевает иное. А что произойдет, если мы вызовем non-strict функцию, из strict-функции? Для реализации я поставил следующие значения для файлов:
В Hack соотношение обратное. Если HHVM выполняет main.php в нестрогом режиме, и логгер написан на Hack (c hh тегом в верхней части файла), мы все равно получим ошибку типа несмотря на то, что вызываемый файл не написан на Hack.
Другим интересным отличием между системами типов Hack и PHP является аннотация float. Возьмем пример:
Если есть еще какие-то другие различия, которые я упустил, пожалуйста, дайте знать в комментариях ниже. Я бы очень хотел глубже изучить эту тему.
Заключение
В то время, как Hack поддерживает множество фишек, PHP7 не поддерживает типы nullable, mixed, void возвращаемые значения, коллекции, async и т. д. Но все же меня сильно радует безопасность и читаемость, которая достигается новым strict-режимом в PHP7.
После написания пары небольших проектов на Hack я понял, что не сами типы делают Hack приятным, а та самая обратная связь, которую создает язык между разработчиком и машиной. Наличие интеграции проверки типов для Hack в моем редакторе означает, что вся кодовая база анализируется за доли секунды после того, как я сохраню файл. Сразу же отсекаются глупые или неочевидные ошибки, которые я допустил. Я все чаще стал ловить себя на том, что не задумываюсь о возвращаемых значениях функций, просто пишу код по наитию, предполагая очевидные варианты. Если будет ошибка, редактор немедленно сообщит мне. Пара небольших правок и можно двигаться дальше.
PHP также всегда способствовал тесной обратной связи между машиной и разработчиком. Сохраните файл, обновите страницу в браузере, повторите до достижения результата. Быстро и удобно. Но проверка типов в Hack делает это даже быстрее. Я с нетерпением жду появления аналогичного функционала в IDE/редакторах для строгой типизации в новом PHP.