PHP Fart Time
1.77K subscribers
87 photos
4 videos
2 files
179 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? Поделитесь в комментариях
🔥9
#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. Можно описать тип только одного (каждого) передаваемого элемента. Если вам известно об этом больше - го в комменты.
🔥5🤯2
#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.
🔥2🤯2💩1
#Article

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

Надеюсь факт наличия сего писания сведёт к забвению и зарастанию тропы, ведущей к одним и тем же граблям, на которые я наступаю уже далеко не первый раз, но о которых постоянно забываю.
🔥6
#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, то я рекомендую вообще закрывать конструкторы сущностей (делать пустой приватный конструктор) и вместо этого писать фабрики.
🔥9🤔6
Очередной упоротый обзор всякой фигни вокруг PHP с жиром и шутками 🚽
https://telegra.ph/V-mire-PHP-2-06-05

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

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

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

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

Я уже ранее публиковал заметку про любопытное отличие Promoted Properties от обычных свойств в кишках #PHP.

Promoted Properties от того и Promoted, что очень неплохо продвигались в плане маркетинга.


Например, вот такая портянка:
class CustomerDTO
{
public string $name;

public string $email;

public DateTimeImmutable $birth_date;

public function __construct(
string $name,
string $email,
DateTimeImmutable $birth_date
) {
$this->name = $name;
$this->email = $email;
$this->birth_date = $birth_date;
}
}


превращается в такую:

class CustomerDTO
{
public function __construct(
public string $name,
public string $email,
public DateTimeImmutable $birth_date,
) {}
}


Вау! Круто!
Но не раскрыта тема комментариев.
Если вы не пишете комментарии в коде, то вам, в прочем, без разницы 😑 но мне вот приходится 😫

Как правило, если требуется указать более точный "псалмовый" тип, то он перемещается из аннотации @var в аннотацию @param над конструктором, поэтому иногда получается такое говно, в котором нужный тебе параметр пойти найди.
А т.к. я обычно пишу непонятные тулзы, которые хер пойми как работают, то в комментариях часто нужен не только текст, но и вставки кода, дополнительные аннотации типа @internal, @note, @since, @see, @link.

И хорошего решения тут нет.

👉 Писать портянку под @property — тем всратее, чем больше комментариев и параметров.

👉 Писать типы и комментарии непосредственно над параметром — всрато.

class CustomerDTO
{
public function __construct(
/**
* @var non-empty-string Comment here
* Example here
*/
public readonly string $name,
//...
) {}
}


👉 Не использовать Promoted Properties и дублировать комментарий — всрато, но есть исключения (если свойство публичное, а конструктор internal, то в конструкторе можно обойтись только типом).
И даже если сделаешь красиво, то всё-равно потом придёт какой-нибудь умник с ректором или CS фиксером и запромоутит разом все параметры 🚽
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
🤔8🔥2
Очередная токсик среда.

Как не хотелось не трогать сам PHP, но придётся, потому что моя жепь с этого немного подгорела на #RandomBeer в предыдущую пятницу.

PHP Lazy Objects #Article
137
#Article
На канале Пых сегодня Валентин с Андреем по-pipes-дят, т.е. обсудят новый Pipe оператор.
А у меня для вас тоже что-то есть на тему: заметка pipes в бизнес-процессах.

Happy Pipes Day!
15💩76