PHP Fart Time
1.72K subscribers
75 photos
3 videos
2 files
162 links
Привет, фартаны!

Дурачимся, пилим OpenSource и рассказываем про пердовые технологии в php.

Авторы контента: @roxblnfk и @butschster
Download Telegram
#Article #Типизация #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 в документации. А ещё про то, что в него можно распаковывать итерируемые значения другим многоточием.

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) - `["c" => 3]`
bar(c: [1, 2, 3]) - `Fatal Error`
bar(foo: 3) - `["foo" => 3]`
bar(1, 2, a: 3) - `Fatal Error`
bar(c: 3, a: 1, b: 2) - `["c" => 3]`
bar(foo-bar: 3) - `Syntax Error`
bar(1, 2, 3, 4, foo: 5) - `[3, 4, "foo" => 5]`
bar(b: 2, 3, 4, foo: 5) - `Fatal Error`
Сколько промахов?
Как итог: распаковка аргументов из массива даёт возможность передать любой сроковой ключ, именованные аргументы — нет.

⭕️ Хрень какая-то. Где оно надо то вообще?

- Когда нужна типизация на списке аргументов, при этом не нужно сохранять ключи. При этом сигнатура точно не будет дополняться. Например, в 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.
#Article

Написал длиннопост, который не влез в формат телеги и телеграфа.
⚠️ Алерт: там много англицизмов.
Читать на гитхабе: Cycle ORM до связей жадный

Надеюсь факт наличия сего писания сведёт к забвению и зарастанию тропы, ведущей к одним и тем же граблям, на которые я наступаю уже далеко не первый раз, но о которых постоянно забываю.
#Article #Типизация #php

Всем нравятся 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 с жиром и шутками 🚽
https://telegra.ph/V-mire-PHP-2-06-05

Зеркало на Notion, если телеграф опять все ссылки поломает.

⬆️ предыдущая часть

#Article #PHP #МыслиВслух

ℹ️ Если в конце статьи нет ссылки на следующую часть, то статья в телеге подгрузилась не полностью. В этом случае можно открыть в браузере.
Please open Telegram to view this post
VIEW IN TELEGRAM
#Article

Кстати, а вставки кода в комментариях — тоже кучерявая тема в #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
#Article
На канале Пых сегодня Валентин с Андреем по-pipes-дят, т.е. обсудят новый Pipe оператор.
А у меня для вас тоже что-то есть на тему: заметка pipes в бизнес-процессах.

Happy Pipes Day!