#Article #Типизация #php
#️⃣ Типизация PHP
Наверное буду кэпом, если скажу, что следование строгим типам и избежание неявных преобразований уменьшает магичность кода, что ведёт к стабильности и надёжности.
Поэтому в коде Spiral, Cycle и других наших продуктов мы используем строгую типизацию, где это возможно.
Вроде тема понятная, но... это же PHP. А значит без нюансов не обойтись 💩
⭕️ declare(strict_types=1);
Прям из доки: по умолчанию, PHP будет преобразовывать значения неправильного типа в ожидаемые. ...
Можно включить режим строгой типизации на уровне файла.
В этом режиме, тип значения должен строго соответствовать объявленному, иначе будет выброшено исключение
Единственным исключением из этого правила является передача значения типа
⚠️ На вызовы из внутренних функций, действие
Обратите внимание на предупреждение. Многие core-функции не следуют строгости типов.
Например,
А вот
Рефлексия тоже не следует строгости. Поэтому вместо
Оно, может, чуть медленнее, но зато надёжнее.
Теперь к костылям.
⭕️ Типизация переменных
С помощью несложного текучего костыля можно привязать тип к переменной.
Не делайте так.
⭕️ PHP 8.2
Типы
Зачем это надо? Для вариантности. Например, при расширении метода возвращаемое значение с
Завезли DNF (Disjunctive Normal Form).
Вот такого мутанта теперь можно будет встретить в коде:
ℹ️ Всегда явно указывайте тип nullable параметров (
ℹ️ Тип
ℹ️ Тип
Как же принять callable и записать в свойство? Например так:
Может у вас есть какие-то мысли, нюансы или лайфхаки вокруг типов PHP? Поделитесь в комментариях
#️⃣ Типизация PHP
Наверное буду кэпом, если скажу, что следование строгим типам и избежание неявных преобразований уменьшает магичность кода, что ведёт к стабильности и надёжности.
Поэтому в коде Spiral, Cycle и других наших продуктов мы используем строгую типизацию, где это возможно.
Вроде тема понятная, но... это же PHP. А значит без нюансов не обойтись 💩
⭕️ declare(strict_types=1);
Прям из доки: по умолчанию, PHP будет преобразовывать значения неправильного типа в ожидаемые. ...
Можно включить режим строгой типизации на уровне файла.
В этом режиме, тип значения должен строго соответствовать объявленному, иначе будет выброшено исключение
TypeError
.Единственным исключением из этого правила является передача значения типа
int
туда, где ожидается float
.⚠️ На вызовы из внутренних функций, действие
strict_types
не распространяется.Обратите внимание на предупреждение. Многие core-функции не следуют строгости типов.
Например,
array_map()
и array_filter()
сделают неявное приведение типов.print_r(array_map(fn(int $a, int $b) => $a + $b, [1, '10', 3], [4, 5, '1e2']));
---
Array (
[0] => 5
[1] => 15
[2] => 103
)
А вот
call_user_func()
будет ругаться на несоответствие типов.Рефлексия тоже не следует строгости. Поэтому вместо
newInstanceArgs
/newInstance
в фабрике контейнера у нас $instance = new $class(...$arguments);
.Оно, может, чуть медленнее, но зато надёжнее.
Теперь к костылям.
⭕️ Типизация переменных
С помощью несложного текучего костыля можно привязать тип к переменной.
function makeInt(int &$i): void
{
static $list = [];
$list[] = $obj = new class() {
public int $i;
};
$obj->i = &$i;
}
$int = 1;
makeInt($int);
$int = 42; // 42
$int = 'foo'; // Fatal error: Uncaught TypeError: Cannot assign string...
Не делайте так.
⭕️ PHP 8.2
Типы
null
, false
и true
теперь можно использовать автономно.Зачем это надо? Для вариантности. Например, при расширении метода возвращаемое значение с
bool
можно сузить до true
или false
, а nullable (?Foo
) — до null
. Таких кейсов в библиотеках не мало.Завезли DNF (Disjunctive Normal Form).
Вот такого мутанта теперь можно будет встретить в коде:
(Countable&Traversable)|array
ℹ️
Что интересно, nullable-сахар (?Foo
) был добавлен в PHP 7.1 ещё до Union Types.ℹ️ Всегда явно указывайте тип nullable параметров (
?Foo $foo = null
) а не полагайтесь только на значение null
по умолчанию (Foo $foo = null
).ℹ️ Тип
never
был добавлен в PHP 8.1. Но он вряд ли вам пригодится, если вы гоняете на RoadRunner.ℹ️ Тип
callable
существует только в сигнатурах функций и методов. Его нельзя указать для свойств, а значит и в Promoted properties не засунуть. А всё потому, что в разных контекстах callable
может быть разным.Как же принять callable и записать в свойство? Например так:
// A class declaration
private \Closure $callback;
public function __construct(callable $callback)
{
$this->callback = $callback(...);
}
Может у вас есть какие-то мысли, нюансы или лайфхаки вокруг типов PHP? Поделитесь в комментариях
#Article #Типизация #php
#️⃣ Variadic параметр
Вроде тут и сказать нечего. Ну поставил три точки в параметре и всё.
Ан нет. Приколов хватает. Погнали
⭕️ Что известно
Variadic параметр, он же "списки аргументов переменной длины", пользователем с помощью добавления (сюрприз!) многоточия. Примерно столько вы можете прочитать про variadic в документации. А ещё про то, что в него можно распаковывать итерируемые значения другим многоточием.
Variadic является опциональным параметром. Всегда. Т.е. вы можете ничего в него не передавать и ему с того норм.
> Кстати, многие путают понятия "аргумент" и "параметр". Параметр - то, что в определении функции, аргумент - значение, которое передаётся в функцию.
Зачем он нужен, если и так можно передать сколько угодно аргументов, а потом вынуть их с помощью
А затем, что в variadic умеет в типизацию! (про типы см. предыдущий пост).
Теперь только объекты
⭕️ Приколы с распаковкой
Распаковывая iterable в variadic, ключи могут быть разные. Если вы распаковываете список (ключи типа int), то их порядок будет сброшен. Т.е. распаковка массива
Ассоциативный массив останется ассоциативным массивом с теми же ключами.
Но не всегда можно смешивать целочисленные ключи со строковыми. При распаковке аргументов из массива, как и при передаче именованных+позиционных аргументов, позиционные аргументы должны идти перед именованными. Причём сортируется это по параметрам 💥
⭕️ Именованные аргументы (PHP 8.0)
Поиграем в игру. Есть сигнатура
(если нажать на один скрытый элемент — откроются все)
`["c" => 3]`
`Fatal Error`
`["foo" => 3]`
`Fatal Error`
`["c" => 3]`
`Syntax Error`
`[3, 4, "foo" => 5]`
`Fatal Error`
Сколько промахов?
Как итог: распаковка аргументов из массива даёт возможность передать любой сроковой ключ, именованные аргументы — нет.
⭕️ Хрень какая-то. Где оно надо то вообще?
- Когда нужна типизация на списке аргументов, при этом не нужно сохранять ключи. При этом сигнатура точно не будет дополняться. Например, в
- В атрибутах, которые конфижат непонятно что. В атрибуте
⭕️ Аннотации
Артефакты прошлого ещё долго будут встречаться в коде даже современном. Да, это про аннотации. Там variadic'и не поддерживаются. До недавнего времени они вообще фейлили парсер
⭕️ Поддержка IDE
Её нет. Пока нет известного мне способа рассказать IDE или Psalm'у, какие ключи можно передать в variadic. Можно описать тип только одного (каждого) передаваемого элемента. Если вам известно об этом больше - го в комменты.
#️⃣ Variadic параметр
Вроде тут и сказать нечего. Ну поставил три точки в параметре и всё.
Ан нет. Приколов хватает. Погнали
⭕️ Что известно
Variadic параметр, он же "списки аргументов переменной длины", пользователем с помощью добавления (сюрприз!) многоточия. Примерно столько вы можете прочитать про variadic в документации. А ещё про то, что в него можно распаковывать итерируемые значения другим многоточием.
function toArray(...$items): array { return $items; }
$a = ['foo', 'bar'];
toArray(...$a);
Variadic является опциональным параметром. Всегда. Т.е. вы можете ничего в него не передавать и ему с того норм.
> Кстати, многие путают понятия "аргумент" и "параметр". Параметр - то, что в определении функции, аргумент - значение, которое передаётся в функцию.
Зачем он нужен, если и так можно передать сколько угодно аргументов, а потом вынуть их с помощью
func_get_arg()
, как делали ещё наши предки? Кроме того, мы можем в конце списка параметров сунуть массив и даже распаковка не понадобится при вызове функции. function foo(Foo $a, array $myVariadic = []) {}
.А затем, что в variadic умеет в типизацию! (про типы см. предыдущий пост).
function foo(Foo $a, Bar ...$variadic) {}
Теперь только объекты
Bar
попадут в массив $variadic
. Ну это и так все знали.⭕️ Приколы с распаковкой
Распаковывая iterable в variadic, ключи могут быть разные. Если вы распаковываете список (ключи типа int), то их порядок будет сброшен. Т.е. распаковка массива
[3 => 'a', 2 => 'b', 1 => 'c']
приведёт к ['a', 'b', 'c']
в вариадике.Ассоциативный массив останется ассоциативным массивом с теми же ключами.
Но не всегда можно смешивать целочисленные ключи со строковыми. При распаковке аргументов из массива, как и при передаче именованных+позиционных аргументов, позиционные аргументы должны идти перед именованными. Причём сортируется это по параметрам 💥
⭕️ Именованные аргументы (PHP 8.0)
Поиграем в игру. Есть сигнатура
bar(int $a = 1, int $b = 2, int ...$c)
. Что попадёт в $c
?(если нажать на один скрытый элемент — откроются все)
bar(a: 1, c: 3)
- bar(c: [1, 2, 3])
- bar(foo: 3)
- bar(1, 2, a: 3)
- bar(c: 3, a: 1, b: 2)
- bar(foo-bar: 3)
- bar(1, 2, 3, 4, foo: 5)
- bar(b: 2, 3, 4, foo: 5)
- Как итог: распаковка аргументов из массива даёт возможность передать любой сроковой ключ, именованные аргументы — нет.
⭕️ Хрень какая-то. Где оно надо то вообще?
- Когда нужна типизация на списке аргументов, при этом не нужно сохранять ключи. При этом сигнатура точно не будет дополняться. Например, в
yiisoft/injector
в конструктор можно было бы передавать переменное число ContainerInterface
, а не один и только один (@samdark, я смотрю на тебя). Или вот пример, как заставить передать минимум один аргумент нужного типа: function setEncoders(Encoder $encoder, Encoder ...$encoders) {}
.- В атрибутах, которые конфижат непонятно что. В атрибуте
Column
компонента cycle/orm
можно передать любые именованные аргументы сверх тех, что предопределены заранее (спасибо, variadic). Например, определяем #[Column(type: 'bigint', unsigned: true)
. Здесь unsigned
попадёт в variadic и, если у вас MySQL, столбец будет unsigned.⭕️ Аннотации
Артефакты прошлого ещё долго будут встречаться в коде даже современном. Да, это про аннотации. Там variadic'и не поддерживаются. До недавнего времени они вообще фейлили парсер
doctrine/annotations
, однако там решили ограничиться просто фиксом "чтоб не падало". Поэтому (и не только) не пользуйтесь аннотациями, если есть возможность пользоваться атрибутами.⭕️ Поддержка IDE
Её нет. Пока нет известного мне способа рассказать IDE или Psalm'у, какие ключи можно передать в variadic. Можно описать тип только одного (каждого) передаваемого элемента. Если вам известно об этом больше - го в комменты.
#Article
Привет, привет всем любителям PHP! 🎉 Хочу поделиться свежей статьей под названием "Как раскрыть сверхспособности высокопроизводительных очередей для PHP-приложений и не сойти с ума!" 🚀
Знаете, я изучал тему очередей на PHP и обнаружил пару проблемок. Вроде бы все классно, но, честно говоря, PHP не слишком подходит для крутых инфраструктурных штуковин. Такой интерпретируемый характер и блокирующая модель IO могут сильно торомозить их работу Короче это тупо неэффективно. 🤔
И вот однажды один человек подумал, а почему бы не использовать в этом деле Golang? 🔥 Go умеет делать кучу дел одновременно (горутины), управляет ресурсами как настоящий профи, работает быстро как молния и еще и сокетами управляет на уровне близком к железу. Просто магия!
Итак, в моей статье я рассказываю о супер-мега-крутых очередях на RoadRunner (о плагине "Jobs"). 💪 Там PHP и Go нашли свою любовь и объединились в команду. Теперь PHP-разработчики могут наслаждаться всеми плюшками обоих языков и создавать высокопроизводительниые шедевры!
Но это еще не все, фартаны! Также, я раскрываю стратегии использования очередей. Мы будем погружаться в мир загадочных штуковин, как "Фанатик с запоздалыми задачами", "Король бесконечных повторений" и "Независимый ковбой с ретрайами". Это как сказка, только в мире программирования!
Так что, прямо сейчас отправляйтесь по ссылочке https://butschster.medium.com/unleashing-the-power-of-high-performance-queue-services-for-php-applications-dcc5c1426511! 😄
#PHP + #Go = Unstoppable! 💪
Жду ваших комментариев и обсуждений! 🙌
P.s. Мы как-то побенчили джобы разных реализаций. Оказалось, что очереди в отделении почты разгребаются быстрее, чем в Laravel.
Привет, привет всем любителям PHP! 🎉 Хочу поделиться свежей статьей под названием "Как раскрыть сверхспособности высокопроизводительных очередей для PHP-приложений и не сойти с ума!" 🚀
Знаете, я изучал тему очередей на PHP и обнаружил пару проблемок. Вроде бы все классно, но, честно говоря, PHP не слишком подходит для крутых инфраструктурных штуковин. Такой интерпретируемый характер и блокирующая модель IO могут сильно торомозить их работу Короче это тупо неэффективно. 🤔
И вот однажды один человек подумал, а почему бы не использовать в этом деле Golang? 🔥 Go умеет делать кучу дел одновременно (горутины), управляет ресурсами как настоящий профи, работает быстро как молния и еще и сокетами управляет на уровне близком к железу. Просто магия!
Итак, в моей статье я рассказываю о супер-мега-крутых очередях на RoadRunner (о плагине "Jobs"). 💪 Там PHP и Go нашли свою любовь и объединились в команду. Теперь PHP-разработчики могут наслаждаться всеми плюшками обоих языков и создавать высокопроизводительниые шедевры!
Но это еще не все, фартаны! Также, я раскрываю стратегии использования очередей. Мы будем погружаться в мир загадочных штуковин, как "Фанатик с запоздалыми задачами", "Король бесконечных повторений" и "Независимый ковбой с ретрайами". Это как сказка, только в мире программирования!
Так что, прямо сейчас отправляйтесь по ссылочке https://butschster.medium.com/unleashing-the-power-of-high-performance-queue-services-for-php-applications-dcc5c1426511! 😄
#PHP + #Go = Unstoppable! 💪
Жду ваших комментариев и обсуждений! 🙌
P.s. Мы как-то побенчили джобы разных реализаций. Оказалось, что очереди в отделении почты разгребаются быстрее, чем в Laravel.
Medium
Unleashing the Power of High Performance Queue Services for PHP applications.
In the modern era of PHP development, the efficient handling of asynchronous tasks and managing heavy loads has become a game-changer.
#Article
Написал длиннопост, который не влез в формат телеги и телеграфа.
⚠️ Алерт: там много англицизмов.
Читать на гитхабе: Cycle ORM до связей жадный
Надеюсь факт наличия сего писания сведёт к забвению и зарастанию тропы, ведущей к одним и тем же граблям, на которые я наступаю уже далеко не первый раз, но о которых постоянно забываю.
Написал длиннопост, который не влез в формат телеги и телеграфа.
⚠️ Алерт: там много англицизмов.
Читать на гитхабе: Cycle ORM до связей жадный
Надеюсь факт наличия сего писания сведёт к забвению и зарастанию тропы, ведущей к одним и тем же граблям, на которые я наступаю уже далеко не первый раз, но о которых постоянно забываю.
#Article #Типизация #php
Всем нравятся Constructor Property Promotion, не так ли?
☝️ эти фрагменты кода эквивалентны 👇
Но не эквивалентны этому:
Разница в наличии значений по умолчанию у свойств.
Теперь попробуем создать объекты из обоих вариантов через рефлексию без использования конструктора.
https://3v4l.org/0tLcM
Если у свойства нет значения по умолчанию, то оно будет неинициализированным.
Такой способ создания объектов в обход конструктора широко используется под капотом многих библиотек, не только тех, которые используют
И иногда это стоит учитывать при работе с классами, которые будут проходить через гидрацию или демаршализацию. У меня такое уже выстреливало 😳.
А если ты счастливый пользователь Cycle ORM, то я рекомендую вообще закрывать конструкторы сущностей (делать пустой приватный конструктор) и вместо этого писать фабрики.
Всем нравятся Constructor Property Promotion, не так ли?
final class Foo {
public function __construct(
public bool $bar = false,
public array $baz = [],
) { }
}
☝️ эти фрагменты кода эквивалентны 👇
final class Foo {
public bool $bar;
public array $baz;
public function __construct(
bool $bar = false,
array $baz = [],
) {
$this->bar = $bar;
$this->baz = $baz;
}
}
Но не эквивалентны этому:
final class Foo {
public bool $bar = false;
public array $baz = [];
public function __construct(
bool $bar = false,
array $baz = [],
) {
$this->bar = $bar;
$this->baz = $baz;
}
}
Разница в наличии значений по умолчанию у свойств.
Теперь попробуем создать объекты из обоих вариантов через рефлексию без использования конструктора.
https://3v4l.org/0tLcM
object(Foo)#3 (0) { ["bar"]=> uninitialized(bool) ["baz"]=> uninitialized(array) }
object(Foo)#3 (2) { ["bar"]=> bool(false) ["baz"]=> array(0) { } }
Если у свойства нет значения по умолчанию, то оно будет неинициализированным.
Такой способ создания объектов в обход конструктора широко используется под капотом многих библиотек, не только тех, которые используют
doctrine/instantiator
.И иногда это стоит учитывать при работе с классами, которые будут проходить через гидрацию или демаршализацию. У меня такое уже выстреливало 😳.
А если ты счастливый пользователь Cycle ORM, то я рекомендую вообще закрывать конструкторы сущностей (делать пустой приватный конструктор) и вместо этого писать фабрики.
Небольшой обзор всякой фигни вокруг PHP фреймворков и опенсорса с жиром и шутками ниже пояса 🚽
Notion
Telegraph (с поехавшими ссылками)
#Article #PHP #МыслиВслух
Notion
Telegraph (с поехавшими ссылками)
#Article #PHP #МыслиВслух
Please open Telegram to view this post
VIEW IN TELEGRAM
Telegraph
В мире PHP #0
Продвинутая рефлексия Symfony посмотрели на типун Удальцова и захотели такой же. Получился компонент типунфъ в сдержанном и консервативном стиле от дедов. Хочешь не хочешь, а эта херня получит распространение. Кирилл Несмеянов, кстати, подсуетился раньше…
Очередной упоротый обзор всякой фигни вокруг PHP с жиром и шутками 🚽
https://telegra.ph/V-mire-PHP-2-06-05
Зеркало на Notion, если телеграф опять все ссылки поломает.
⬆️ предыдущая часть
#Article #PHP #МыслиВслух
ℹ️ Если в конце статьи нет ссылки на следующую часть, то статья в телеге подгрузилась не полностью. В этом случае можно открыть в браузере.
https://telegra.ph/V-mire-PHP-2-06-05
Зеркало на Notion, если телеграф опять все ссылки поломает.
⬆️ предыдущая часть
#Article #PHP #МыслиВслух
ℹ️ Если в конце статьи нет ссылки на следующую часть, то статья в телеге подгрузилась не полностью. В этом случае можно открыть в браузере.
Please open Telegram to view this post
VIEW IN TELEGRAM
Telegraph
В мире PHP #1
⬅️ предыдущая часть Мутационное тестирование Напомню, что мутационное тестирование — это отличный способ убедиться в том, что написанные тесты – говно и ничего не тестируют даже при 100% покрытии. Infection Infection теперь поддерживает кастомные мутаторы.…
Новый выпуск "В мире PHP"
https://triangular-octopus-0f6.notion.site/PHP-2-d67a1f346b8541729fb3aa476e0fa086?pvs=4
⬆️ предыдущая часть
#Article #PHP #МыслиВслух
https://triangular-octopus-0f6.notion.site/PHP-2-d67a1f346b8541729fb3aa476e0fa086?pvs=4
⬆️ предыдущая часть
#Article #PHP #МыслиВслух
triangular-octopus-0f6 on Notion
В мире PHP #2 | Notion
Сорян, фартаны. Статейку чутка просрочил. Заработался, бывает. Представьте, что она опубликована месяц назад и баянов тут нет.
#Article
Кстати, а вставки кода в комментариях — тоже кучерявая тема в #PHP.
👉 Есть Java-like вариант: оборачивать тегами
Но мы, вроде, уже ушли от HTML среди PHP😏
👉 Есть Python-like вариант: делать отступы
Тут не очень очевидно, сколько пробелов от звёздочки должно быть: по логике 1+4, но не сходится с разметкой Tab-ов.
Это активно используется в Symfony и PhpStorm это даже рендерит. Кстати, кто знает, как отключить этот рендеринг для классов из vendor?
👉 Есть Markdown-like вариант:
Мне нравится markdown, но это не рендерится PhpStorm'ом. Однако, рендерится документором.
Python-like на документоре не проверял, может тоже рендерит.
Кстати, а вставки кода в комментариях — тоже кучерявая тема в #PHP.
👉 Есть Java-like вариант: оборачивать тегами
<code>
. Скуфы, мб, помнят.Но мы, вроде, уже ушли от HTML среди PHP
class CustomerDTO
{
/**
* @var non-empty-string Comment here
*
* <code>
* $x = foo()->bar();
* </code>
*/
public readonly string $name;
}
👉 Есть Python-like вариант: делать отступы
class CustomerDTO
{
/**
* @var non-empty-string Comment here
*
* $x = foo()->bar();
*/
public readonly string $name;
}
Тут не очень очевидно, сколько пробелов от звёздочки должно быть: по логике 1+4, но не сходится с разметкой Tab-ов.
Это активно используется в Symfony и PhpStorm это даже рендерит. Кстати, кто знает, как отключить этот рендеринг для классов из vendor?
👉 Есть Markdown-like вариант:
class CustomerDTO
{
/**
* @var non-empty-string Comment here
*
* ```php
* $x = foo()->bar();
* ```
*/
public readonly string $name;
}
Мне нравится markdown, но это не рендерится PhpStorm'ом. Однако, рендерится документором.
Python-like на документоре не проверял, может тоже рендерит.
Please open Telegram to view this post
VIEW IN TELEGRAM
Очередная токсик среда.
Как не хотелось не трогать сам PHP, но придётся, потому что моя жепь с этого немного подгорела на #RandomBeer в предыдущую пятницу.
PHP Lazy Objects #Article
Как не хотелось не трогать сам PHP, но придётся, потому что моя жепь с этого немного подгорела на #RandomBeer в предыдущую пятницу.
PHP Lazy Objects #Article
triangular-octopus-0f6 on Notion
PHP Lazy Objects | Notion
В PHP 8.4 добавили ленивые объекты и прокси (RFC , дока).
#Article
На канале Пых сегодня Валентин с Андреем по-pipes-дят, т.е. обсудят новый Pipe оператор.
А у меня для вас тоже что-то есть на тему: заметка pipes в бизнес-процессах.
Happy Pipes Day!
На канале Пых сегодня Валентин с Андреем по-pipes-дят, т.е. обсудят новый Pipe оператор.
А у меня для вас тоже что-то есть на тему: заметка pipes в бизнес-процессах.
Happy Pipes Day!
triangular-octopus-0f6 on Notion
Pipes в бизнес-процессах | Notion
В PHP 8.5 завезут пайпы (RFC) из функционального программирования.