- Типизация аргументов и свойств в PHP 7: особенности работы с типами данных
- Используемые типы в PHP
- Стандартная типизация в PHP
- Неинициализированные типы в PHP
- Использование значений по умолчанию и конструкторов
- Наследование типов
- Возможные проблемы
- Заключение
- Типизация аргументов и свойств в PHP 7 и 8
- Типизация в PHP
- Типизация аргументов в функциях и методах
- Null и void в сигнатуре функции
- Преимущества указания типов в PHP
- Типизация свойств объекта в PHP 7.4
Типизация аргументов и свойств в PHP 7: особенности работы с типами данных
В первых версиях PHP типизация, то есть присвоение типов переменным, свойствам, аргументам и так далее, была динамическая. Это значит, что язык самостоятельно идентифицировал тип данных для того или иного элемента. С одной стороны это удобно тем, что не требуется тратить время на присвоение типов к каждому элементу, но с другой стороны может стать причиной появления дополнительных ошибок. В PHP 7 были добавлены типизированные свойства, что должно улучшить работу системы. Принятые изменения являются обратносовместимыми.
Используемые типы в PHP
В языке PHP предусмотрены следующие типы данных, которые можно использовать для присвоения типов переменных при написании кода:
- bool — используется для работы с логическими операциями и может принимать только два параметра: true или false (истина или ложь);
- float — используется для обозначения чисел с плавающей точкой, то есть всех нецелых чисел;
- int (integer) — присваивается только целым числам без точек (неважно положительным или отрицательным);
- string — преобразует значение в строку;
- mixed — способно принимать любое значение;
- callable — используется для обозначения функций;
- array — обозначение массива данных;
- iterable — используется для обозначения массивов или классов, реализованных через интерфейс Traversable, а также при переборе в цикле foreach;
- Self — присваивается для объекта, представляющего тот же класс. Доступно использование внутри классов;
- parent — присваивается родительскому классу. Тоже можно использовать внутри классов;
- null — несуществующий тип, присвоенный переменным без наследуемого типа данных.
Также в PHP можно передавать в качестве типа имя класса. В таком случае объект будет представлять данный класс или его производные классы. Еще в качестве аргумента можно передать имя интерфейса. Его можно передавать только для объектов и классов, использующих данный интерфейс.
Стандартная типизация в PHP
В PHP 7 добавлена возможность указывать типы аргументов функции и типы значений возвращаемого функцией, методом или замыканием элемента. Нужный тип указывается непосредственно оператором. Особенностью PHP является возможность не указывать тип элемента. В таком случае тип переменной будет установлен динамически в зависимости от значения или результат выполнения выражения, присваиваемого ей. Динамический тип может изменяться в течение всего скрипта, если этому способствуют условия его выполнения.
Вот пример стандартной типизации классов в PHP с пояснениями:
В стандартном PHP можно выполнить преобразование в другой тип, отличный от того, что был определен языком изначально. Делается это таким образом:
Преобразование еще возможно другим образом. Представим, что у нас имеется та же переменная $theVar с тем же значением, но только обозначенная как строка. Изменить ее снова на целочисленное можно простым прибавлением любого целого числа:
Аналогичным образом число можно преобразовать в строковое значение — добавив к нему данные, которые PHP по умолчанию посчитает строкой:
Прибавлять к числу строку, чтобы преобразовать все значение переменной в строку не очень удобно, поэтому иногда удобнее пользоваться присвоением по типу: $название_переменной = (желаемый_тип)$название_переменной.
Неинициализированные типы в PHP
Некоторые переменные не могут быть корректно инициализированы в PHP в силу каких-либо обстоятельств. Таким данным присваивается тип «состояния переменной»: неинициализированный. Если в скрипте есть хоть одна такая переменная, то он не сможет корректно сработать и, скорее всего, завершится ошибкой с текстом:
Fatal error: Uncaught Error: Typed property Foo::$theVar must not be accessed before initialization
Пример скрипта с неинициализированной переменной:
При выполнении данного скрипта возникнет ошибка, так как у переменной невозможно определить тип. В старых версиях PHP переменной $theVar был бы присвоен просто тип null, означающий, что у переменной нет типа. Однако типы можно обнулять, что делает невозможным корректно определить, было ли ранее установлено типизированное свойство обнуляемого типа или оно было забыто в ходе выполнения скрипта. Дабы исправить эту проблему в PHP 7 было добавлено «неинициализированное» состояние.
Исправить приведенный выше код можно установив неинициализированное значение после объекта:
В таком случае происходит проверка неинициализированного состояния только при чтении значения свойства, плюс, проверка типа будет выполняться и при записи в него. Это позволяет быть уверенным в том, что недопустимый тип не будет установлен в качестве значения свойства.
Учитывайте следующие моменты при работе с неинициализированными состояниями:
- Чтение значений из неинициализированный свойств невозможно, так как это всегда приводит к фатальной ошибке и завершения выполнения скрипта.
- Во избежании фатальных ошибок можно создать объект с неинициализированным свойством, даже если его тип не имеет значения NULL. Считать значение будет все-равно невозможно, но это не приведет к фатальным ошибкам.
- Также во избежании фатальных ошибок можно записать неинициализированное свойство перед чтением из основного скрипта.
- Если вам требуется сделать типизированное свойство неинициализированным, то используйте значение unset. При отключении нетипизированного свойства ему присваивается значение null.
Использование значений по умолчанию и конструкторов
Конструкторы в PHP помогает быстро и корректно инициализировать только созданный объект, присваивая ему как тип по умолчанию, так и пользовательский. Рассмотрим несколько примеров задания типов и аргументов в PHP.
Задание значений по умолчанию в скалярных типах:
В рассматриваемом классе задаются только пользовательские значения, присвоенные через знак “=”.
С типизацией функций и ее аргументов в PHP немного сложнее. Как вариант, можно попробовать задать такую типизацию:
В рассмотренном примере использовать тип null можно только в том случае, если тип действительно можно обнулить. Если это не так, то при компиляции кода может появиться ошибка.
Обратите внимание, что задать значения по умолчанию с object типами или классами невозможно. Для обхода данных ограничений как раз используется конструктор, который помогает корректно инициализировать тип у объекта. Пример использования конструктора в PHP:
Допускается указывать значения в неинициализированное свойство вне конструктора. Неинициализированная проверка не запускается до тех пор, пока из свойства ничего не читается, что помогает избежать ошибок с инициализацией и преждевременного прекращения выполнения кода.
Наследование типов
С помощью наследования можно создавать класс, который будет копировать основные параметры родительского класса, в том числе и тип, но будет считаться отдельным классом. Это полезно в тех случаях, когда нужно расширить функционал определенного элемента, но при этом важно сохранить какой-нибудь исходник с изначальными значениями.
Пример работы с наследованием:
В приведенном примере класс Bar наследует класс Foo, получая доступ ко всем методам и свойствам Foo, которые не являются приватными. Также наследуется и тип переменных в классе. Обратите внимание, что в новом классе изменен модификатор доступа изменен с private на public (допустимо также значение protected). В противном случае вы получите критическую ошибку. Рекомендуется по возможности избавляться от приватных свойств классов, особенно, если планируется использовать их в качестве родителей.
Возможные проблемы
У типизации в PHP есть важная проблема — он не строготипизируемый. Это значит, что корректность установленных типов проверяется непосредственно во время выполнения скрипта, а не компиляции кода. Это усложняет тестирование кода, так как нужно предусмотреть как можно больше сценариев поведения пользователя, чтобы скрипт не “посыпался” в самый ответственный момент.
Вот пара советов по решению возможных проблем с некорректной типизацией в PHP:
- Проверяйте код небольшими кусками в рантайме. Чем больше объем скрипта проходит проверку в режиме реального времени, тем больше нагрузка в процессе, следовательно, есть риск пропустить что-то важное.
- Потратьте дополнительное время на тестирование уже готового продукта. Даже если небольшие проверки не выявили никаких проблем с типизацией, реальное использование может обнажить проблемы в самый неподходящий момент. Рассмотрите несколько вариантов использования продукта пользователем, чтобы избежать хотя бы самых основных проблем.
Заключение
Тот факт, что в PHP можно не указывать типизацию аргументов и свойств с одной стороны позволяет сэкономить время, а с другой увеличивает риски появления ошибок. Рекомендуется использовать строгую типизацию, то есть по возможности прописывать типы в скрипте PHP вручную для тех компонентов, для которых это сделать возможно. Это даст следующие преимущества:
- снизит риск возникновения ошибок, связанных с типами;
- по заранее заданным типам проще составлять документацию к проекту;
- позволяет сделать проектирование функции более продуманным.
Как раз сложности с типизацией мешали PHP стать в один ряд с такими языками как Java и C#. В обновленном PHP 7 появились дополнительные возможности работы с типизацией, в том числе установка типов по умолчанию.
Типизация аргументов и свойств в PHP 7 и 8
С каждой версией язык php обрастает новыми возможностями, делая жизнь разработчиков проще. В этой статье мы обсудим возможности типизации в современных версиях PHP и преимущества использования типов.
Типизация в PHP
В языке PHP динамическая типизация. Это значит, что при инициализации переменной ей нельзя назначить тип. Тип переменной выставиться динамически в зависимости от значения (или результат выполнения выражения), которое ей присваивается. Он может меняться в процессе выполнения скрипта. Продемонстрирую на примере:
Для справки: есть языки программирования со статической типизацией. В них при инициализации переменной нужно обязательно указывать тип. В случае, если переменной будет присваиваться значение типа, отличного от указанного, то компилятор сообщит об ошибке.
Типизация аргументов в функциях и методах
В последних версиях PHP появился возможность указывать типы для аргументов функций и возвращаемого ею значения. Рассмотрим эти возможности на примере:
// Вызываем функцию myTestOperation(123, 'test', [4, 5, 6]);
Код содержит функцию с 3 аргументами различных типов. Типы указываются перед именем аргумента, а тип возращаемого значения после двоеточия.
Обратите внимание на строку «declare(strict_types=1);» Она включает строгую типизацию. Например, если при строгой типизации мы передадим в первый аргумент функции строку вместо числа, то получим ошибку:
TypeError: Argument 1 passed to myTestOperation() must be of the type int, string given
Если удалить строчку declare(strict_types=1) , PHP попытается преобразовать переданную строку в число. Если преобразовать невозможно, PHP выбросит исключение TypeError.
Типы аргументов и тип возвращаемого значения могу быть следующими:
- int — целочисленный тип;
- float — числа с плавающей точкой (десятичная дробь);
- string — строка;
- array — массив;
- callable — callback-функция;
- имя класса — в качестве аргумента должен передаваться экземпляр класса;
- интерфейс — в качестве аргумента должен передаваться класс, имплементирующий (реализующий) данный интерфейс.
Null и void в сигнатуре функции
В случае, если функция или метод класса не возвращают значение, после двоеточия пишется ключевое слово void.
В случае, если какие-то из аргументов функции мы захотим сделать необязательными, на нужно добавить знак ? перед именем типа. Это правило работает и для возвращаемого значения. Например, для аргумента ?int $myVar можно передать значение либо null, либо целое число. Чтобы явно не передавать null в качестве значения необязательного аргумента, обычно в сигнатуре присваивают значение по умолчанию, как показано в следующем примере:
function myTestOperation(int $firstParam, ?string $secondParam = null, ?array $thirdParam = null): ?float < var_dump($firstParam); // выведет int(6) var_dump($secondParam); // выведет NULL var_dump($thirdParam); // выведет NULL return null; // Можно венруть null, так как в сигнатуре ?float (либо десятичная дробь, либо null) >// Вызываем функцию // Явно передаем null во 2 и 3 аргументы myTestOperation(6, null, null); // Вызываем еще раз // При этом не передаем необязательные аргуметы, так как в сигнатуре присвоены дефолтные значения // При этом результат выполнения будет идентичен myTestOperation(6);
Преимущества указания типов в PHP
Использование типов и строгой типизации дает на следующие преимущества:
- снижает риск ошибок, связанными с типами;
- выполняет функцию документации — то есть мы видим: какого типа данные нужно передавать в качестве аргумента в функцию;
- заставляет вас правильно проектировать функцию, не позволяя принимать аргументы и возвращать значения разных типов.
Типизация свойств объекта в PHP 7.4
В PHP версии 7.4 появилась возможность указывать типы в классах.