PHP и Laravel дайджест новостей за октябрь 2024 года

Всем привет!
Это дайджест новостей от CutCode. Давайте посмотрим, что произошло за прошедший месяц в мире PHP и Laravel.

Давайте посмотрим, что произошло за прошедший месяц в мире PHP.

Новости PHP

PHP 8.4.0 RC3 доступен для тестирования

Перед финальным выпуском, который ожидается 21 ноября, остался последний релиз-кандидат.

Перед финальным выпуском, остался последний релиз-кандидат.

В день выхода PHP 8.4, 21 ноября, на канале CutCode мы с видными представителями PHP-сообщества обсудим главные фишки релиза, поговорим о PHP в целом и разыграем со зрителями PHP-слоника.

Вышли PHP 8.2.25 и PHP 8.3.13

Выпуски с исправлением ошибок вышли по расписанию.

Open Source Цех #1

Все, наверное, уже в курсе, что благодаря Валентину Удальцову, начиная с PHP 8.4, мы сможем вызывать метод инициируемого объекта с помощью ключевого слова new без скобок.

Валентин не остановился на реализации своего RFC и решил упростить нам работу, добавив новое правило в PHP CS Fixer, да еще и в прямом эфире.

Посмотрите запись трансляции, если пропустили.

Открыта программа раннего доступа PhpStorm 2024.3

В будущей версии PhpStorm появится поддержка вызова методов инициируемого объекта с помощью ключевого слова new без скобок, определение и преобразование циклов foreach в новые функции для работы с массивами: array_find(), array_find_key(), array_any() и array_all(), поддержка хуков свойств и многое другое.

PHP Russia 2024

2 и 3 декабря в Москве пройдёт конференция Highload, в рамках которой 16 докладов будут выделены под PHP Russia.

Список докладов уже опубликован на сайте конференции.

Пыхап № 1

8 ноября в Москве Валентин Удальцов собирает первый в истории митап от канала Пых, на котором выступят Андрей Клименко, Вадим Занфир и Сергей Жук.

Вход бесплатный, но по билетам, кто не сможет присоединиться, подключайтесь к трансляции.

Symfony исполнилось 19 лет 🎂

Спасибо сообществу за почти два десятилетия инноваций и вклада в открытый код. Пусть впереди команду Symfony ждет еще много лет развития!

«Своя игра» по PHP #3

30 октября пройдёт третий выпуск викторины «Своя игра» по PHP от CutCode. В этот раз на интеллектуальной арене встретятся Александр Черняев, Павел Бучнев и Сергей Предводителев.

Как обычно, пишите в комментариях свои предложения и замечания, а также кого вы хотели бы видеть на следующих играх. А, может, вы хотели бы посмотреть на суперфинал среди победителей прошедших игр? – напишите в комментариях.
Ядро PHP

Большинство новостей ядра PHP подробно освещаются в серии PHP Core Roundup от PHP Foundation, мы лишь быстро по ним пробежимся:

✅RFC: Change Directory class to behave like an opaque object

RFC, о котором мы говорили в прошлый раз принят единогласно. Класс Directory, вероятно, является первым экземпляром того, что мы сейчас называем «непрозрачным объектом». Непрозрачные объекты обычно являются результатом преобразования ресурсов в объекты, что в общем случае подразумевает, что они являются окончательными, не сериализуемыми, не инициализируемыми с помощью ключевого слова new, не могут быть приведены и не реализуют никаких методов. Однако, поскольку этот класс существует со времен PHP 4, ничего из этого формально не реализовано.

Начиная с PHP 8.5 класс Directory:

  • Будет окончательным.
  • Будет выбрасывать ошибку при инициализации с помощью ключевого слова new.
  • Будет предотвращено клонирование экземпляров класса Directory.
  • Будет запрещена сериализация с помощью doc-комментария @not-serializable к заглушке класса.
  • Будет запрещено создание динамических свойств для экземпляра класса Directory с помощью doc-комментария @strict-properties к заглушке класса.

📣RFC: Add get_declared_enums() function

Сейчас нет способа получить только объявленные перечисления, Juliette Reinders Folmer и Ayesh Karunaratne предлагают добавить новую функцию get_declared_enums().

Перечисления также обнаруживаются с помощью функций class_exists() и get_declared_classes(), что также предлагается исправить в этом RFC.

📊RFC: Add persistent curl share handles

Сейчас модуль cURL не поддерживает постоянные дескрипторы совместного доступа cURL. Соединения передаются только между дескрипторами в рамках одного SAPI-запроса.

Eric Norris предлагает добавить новую функцию, curl_share_init_persistent(), которая сначала проверит глобальную память воркера SAPI на наличие существующего дескриптора совместного доступа, если таковой будет найден, то функция сразу же его вернет, в противном случае функция создаст новый, применит указанные опции ресурса, сохранит его в глобальной памяти и вернет его.

📣RFC: Change behaviour of array sort functions to return a copy of the sorted array

Сейчас в функции сортировки массивов, массив для сортировки передается по ссылке. Однако, начиная с PHP 5.6, эти функции всегда возвращали только значение true.

Gina Peter Banyard предлагает изменить возвращаемое значение функций sort(), rsort(), asort(), arsort(), ksort(), krsort(), natsort(), natcasesort(), usort(), uasort(), uksort(), array_multisort(), shuffle(), array_walk(), array_walk_recursive() и вместо true возвращать копию преобразованного массива.

Laravel дайджест

Обновления Laravel

11.26 Allows Unit & Backed Enums for registering named RateLimiter & RateLimited middleware

В прошлом дайджесте мы с вами видели интеграцию Enum всюду, теперь также затронут и RateLimit, и вместо строкового наименования RateLimit можно просто передавать Enum, и это также будет работать.

PHP и Laravel дайджест новостей за октябрь 2024 года

11.26 Add make:job-middleware artisan command

Двигаемся дальше к следующему pull request: добавлена новая artisan-команда, которая создает middleware для job. В целом, те же самые middleware, только для job, и они у нас складируются в директорию app/Jobs/Middleware.

<?php namespace {{ namespace }}; use Closure; class {{ class }} { /** * Process the queued job. * * @param \Closure(object): void $next */ public function handle(object $job, Closure $next): void { $next($job); } }

Если мы посмотрим на изменения в stub для job, то не увидим ничего принципиально нового. Единственное отличие — наличие типизации прямо в stub, причём даже в doc-блоке, что довольно необычно. Несмотря на это, Тейлор всё равно принял и смержил такой пулл-реквест.

11.26 Feat: factory generic in make:model command

Следующий пулл-реквест снова касается стабов: в них добавили дженерики как для моделей, так и для их фабрик (в тех случаях, где фабрики присутствуют).

<?php namespace App\Models; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; class Comment extends Model { /** @use HasFactory<\Database\Factories\CommentFactory> */ use HasFactory; }

11.26 Allow mass assignment with mutators when using model::guarded

Следующий pull request касается Eloquent модели: массовая гидрация модели, fillable, guarded. Если мы работаем с полями, которые соответствуют полям в таблице, то все работало нормально, но если мы взаимодействуем с мутаторами, у которых наименование не соответствует полям в таблице, то здесь уже проверки при наполнении не будут срабатывать. Этот pull request исправляет указанное поведение, и теперь с мутаторами также не будет проблем.

``` <?php namespace App\Models; use Illuminate\Database\Eloquent\Casts\Attribute; use Illuminate\Database\Eloquent\Model; class User extends Model { protected $guarded = []; // works protected $fillable = ['address']; // works protected $guarded = ['some_unrelated_column']; // doesn't work /** * Interact with the user's address. */ protected function address(): Attribute { return Attribute::make( get: fn (mixed $value, array $attributes) => new Address( $attributes['address_line_one'], $attributes['address_line_two'], ), set: fn (Address $value) => [ 'address_line_one' => $value->lineOne, 'address_line_two' => $value->lineTwo, ], ); } } $user = new User; $user->fill(['address' => new Address(...)]); ```

11.26 Add stop() method to Process and Pool

PR затрагивает класс Process для взаимодействия с процессорами через CLI. У нас был старт процессов, но при этом не было метода по остановке процессов.

$this->pool = Process::pool(function (Pool $pool) { $pool->path(base_path())->command('sleep 5'); $pool->path(base_path())->command('sleep 10'); })->start(); $this->pool->stop()

С этим pull request такой метод появился, он появился и в рамках процесса pool, и просто одиночного процесса. И помимо этого, также в метод stop добавили возможность передавать сигнал:

/** * Stop the process if it is still running. * * @param float $timeout * @param int|null $signal * * @return int|null */ public function stop(float $timeout = 10, ?int $signal = null) { return $this->process->stop($timeout, $signal); }

11.27 feat: introduce option to change default Number currency

В этом pull request нас ждет класс Number, где появился статический метод currency. Такие pull request появляются довольно часто - когда через статический метод в определенных, так называемых, классах-утилитах в Laravel добавляются методы, меняющие дефолтное поведение. Как и здесь, через статический метод указываем дефолтную валюту.

/** * The current default currency. * * @var string */ protected static $currency = 'USD';

11.27 feat: add Str::doesntContain() method and supporting tests

Снова helper по работе со строками Str. Был метод Str::contain(), а с этим pull request добавили Str::doesntContain(). Но я думаю, объяснять, что он из себя представляет, не имеет никакого смысла.

11.27 Str: Add extension support for Str::inlineMarkdown()

Двигаемся к следующему pull request. Снова класс по работе со строками. Еще недавно у нас появилась возможность добавлять extension для markdown. Были методы Str::inlineMarkdown() и str()->inlineMarkdown(), где extension не поддерживались, и соответственно, этим pull request добавлена такая поддержка. Фича, которая появилась давно, но ее забыли добавить в соседние методы, и постепенно с новыми релизами такие моменты закрываются.

11.27 Utilise Illuminate\Support\php_binary()

Следующий pull request. Когда-то была добавлена функция для поиска PHP-бинарника. Соответственно, в команде по установке API еще использовался класс от Symfony, и соответственно, в данном PR поменяли на функцию из Laravel.

11.27 feat: narrow types for throw_if and throw_unless

Для большинства, я думаю, не важный PR, но для меня лично интересный, так как функцию throw_if проапгрейдили в рамках док-блока. До этого статический анализ в том же самом PHP ругался о том, что если мы используем эту функцию, то все, что у нас двигается дальше, это уже unreachable statement из-за неправильного док-блока. Теперь этот момент исправлен.

11.27 Improve Schema::hasTable() performance

Следующий pull request затрагивает schema и метод hasTable. В данном случае улучшена производительность, оптимизированы запросы (стали более легковесными), и благодаря этому метод работет быстрее. Отличный pull request.

11.27 Add methods to the HTTP kernel to append middleware relative to other middleware

PR затрагивает middleware - добавлены новые методы. Раньше через Kernek могли добавлять с вами middleware, но не могли добавлять в рамках приоритета. Теперь нам добавили несколько методов и можно добавлять middleware в определенном месте. Выглядит как полезная фича.

$kernel->addToMiddlewarePriorityAfter( \Illuminate\Routing\Middleware\ValidateSignature::class, [ \Illuminate\Cookie\Middleware\EncryptCookies::class, \Illuminate\Contracts\Auth\Middleware\AuthenticatesRequests::class, ], ); $kernel->addToMiddlewarePriorityBefore( \Illuminate\Routing\Middleware\ValidateSignature::class, [ \Illuminate\Cookie\Middleware\EncryptCookies::class, \Illuminate\Contracts\Auth\Middleware\AuthenticatesRequests::class, ], );

11.27 Add --json flag to queue:work command for structured logging

Следующий pull request затрагивает artisan команду queue: work, добавляя новый флаг --json, который меняет output в формат JSON, что в итоге будет выглядеть следующим образом:

Более правильный стандартизированный путь. За этот pull request лайк!

Sucessful Job { "timestamp": "2024-09-23T13:47:37.347077+00:00", "level": "info", "job": "App\\Jobs\\NotificarJob", "id": "9af01732-609a-4418-86f3-904f928fd8cc", "uuid": "88098a41-4a1e-4ea6-a4f0-58511f91e915", "connection": "rabbitmq", "queue": "default", "status": "success", "result": "deleted", "attempts": 1, "duration": 0.014354 } Failed Job { "timestamp": "2024-09-23T13:44:57.393616+00:00", "level": "warning", "job": "App\\Jobs\\TestJob", "id": "12c2916c-6a04-40d7-b0d8-c48897c425a0", "uuid": "7e719912-f6c2-4018-a1c2-c7254992f182", "connection": "rabbitmq", "queue": "default", "status": "failed", "result": "deleted", "attempts": 3, "exception": "App\\Exceptions\\TestException", "message": "I have failed you.", "duration": 0.001601 } Job Starting { "timestamp": "2024-09-23T13:48:37.593466+00:00", "level": "info", "job": "App\\Jobs\\ConsultarEnvioDocumentoAReguladorJob", "id": "e600e9dd-474b-4e18-ad5e-a3cadd3b04c9", "uuid": "e76cd7c8-1754-48e7-9b45-5a7ea8664d53", "connection": "rabbitmq", "queue": "regulador", "status": "starting", "attempts": 2 }

11.28 feat: add useful defaultLocale and defaultCurrency helpers to Number facade

Pull request затрагивает снова класс Number. До этого мы с вами статически видели как задается дефолтная $locale и $currency но метода для получения дефолтного значения не было и вот с этим pull request он добавлен. Кстати, что вы скажете о нейминге? У нас и сеттер без префикса set и геттер без префикса get и тем самым глядя вот на такое сразу и не поймешь это у нас геттер или все-таки здесь есть какие-то параметры и это сеттер. В общем иногда возникают вопросы.

Number::defaultLocale() // returns default locale Number::defaultCurrency() // returns default currency

11.28 Feat: remove HasFactory in model when not required

Следующий pull request. На самом деле, этот pull request мне кажется, я хотел лично сделать уже на протяжении где-то года, но никак не доходили руки и было лень. О чем он? У нас, если мы создаем модель через artisan-команду, то даже если мы не указываем, что она у нас с фабрикой, у нас все еще присутствовал trait HasFactory. Соответственно, после этого pull request этот момент исправлен. У нас trait будет присутствовать только если указан флаг, что нам необходимы также и фабрики.

php artisan make:model Post <?php namespace App\Models; use Illuminate\Database\Eloquent\Model; class Post extends Model { // } php artisan make:model Comment -f <?php namespace App\Models; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; class Comment extends Model { /** @use HasFactory<\Database\Factories\CommentFactory> */ use HasFactory; }

11.28 Add Illuminate\Support\enum_value to resolve BackedEnum or UnitEnum to scalar

Следующий pull request. И снова сахар, в этот раз helper, функция enum_value, которая у нас автоматически резолвит, независимо от того, у нас BackedEnum, либо UnitEnum, приводит его к скалярному значению. Если мы заглянем под капот в саму функцию, то увидим здесь проверку. Если у нас сразу строка, то мы ее возвращаем, либо мы с вами трансформим: если это бэки, то обращаемся к свойству value, если юнит, то к свойству name.

if (! function_exists('Illuminate\Support\enum_value')) { /** * Return a scalar value for the given value that might be an enum. * * @internal * * @template TValue * @template TDefault * * @param TValue $value * @param TDefault|callable(TValue): TDefault $default * @return ($value is empty ? TDefault : mixed) */ function enum_value($value, $default = null) { if (is_string($value) && empty($value)) { return $value; } return transform($value, fn ($value) => match (true) { $value instanceof \BackedEnum => $value->value, $value instanceof \UnitEnum => $value->name, default => $value, }, $default); } }

Вот такой сахар, которым, собственно, переполнен Laravel, и в дальнейшем, я думаю, есть огромное количество возможностей для pull request, чтобы там, где под капотом Laravel выполнял данные проверки, переделать их на этот helper. Собственно, в данном pull request, где автор нашел, он уже переделал.

11.28 Introduce RouteParameter attribute

Следующий pull request из Laravel 11.28 и у нас снова тема атрибутов. Как я вам говорил уже ранее, скоро атрибутов в Laravel будет огромное множество и мы практически с каждым релизом начинаем их замечать. Данный атрибут у нас называется RouteParameter и мы благодаря ему можем сразу получить из реквеста из роута определенный параметр. Скажем у нас форм реквесты и в rules до этого нам бы пришлось обращаться к реквесту, к роуту и получать параметр Post. Теперь же мы можем просто использовать атрибут и зарезолвить параметр из роут реквеста.

class UpdatePost extends FormRequest { public function authorize(#[CurrentUser] User $user, #[RouteParameter('post')] Post $post): bool { return $post->user_id === $user->id; } public function rules(#[RouteParameter('post')] Post $post): array { return [ 'slug' => ['required', 'string', Rule::unique(Post::class, 'slug')->ignore($post->id); // ... ]; } }

Взглянем на то, что происходит под капотом: видим обращение из контейнера к реквесту, далее как раз route и получение параметра из роута:

/** * Resolve the route parameter. * * @param self $attribute * @param \Illuminate\Contracts\Container\Container $container * @return mixed */ public static function resolve(self $attribute, Container $container) { return $container->make('request')->route($attribute->parameter); }

11.28 Add CollectedBy attribute

Снова атрибуты, и на этот раз интересный лично для меня атрибут, чтобы в рамках модели мы сразу могли указать, в какую коллекцию мы будем маппить результат с записями. До этого тоже не составляло труда это делать через метод newCollection, который нужно было перезаписать, но всегда у меня возникали сложности, как он точно там называется, что именно и в каком виде должен возвращать, так как в Laravel иногда в этом бывает каша.

#[CollectedBy(PostCollection::class)] class Post { // ... }

Теперь же все будет просто: атрибут, указываем какая коллекция и дальше с ней взаимодействуем.

11.28 Add Tailwind, "composer run dev"

Двигаемся дальше по апдейту 11.28, но это уже не репозиторий Laravel/Framework, это уже Laravel/Laravel, то есть сам скелет.

PHP и Laravel дайджест новостей за октябрь 2024 года

И здесь Тейлор добавил новый скрипт в Composer, называется он Dev, с помощью него мы можем теперь запускать composer run dev, и он будет выполнять все необходимые нам команды для Dev разработки. А именно сразу запускать виртуальный сервер (php artisan serve), очереди (php artisan pail) и Vite в режиме Dev. Как видим output одной командой, мы все запустили и погнали работать!

11.29 Add directive @bool to Blade

Первый pull request в Laravel 11.29: это новая директива @bool для Blade. В чем ее особенность? Если мы в Blade взаимодействуем с AlpineJS либо просто с JS и пытаемся где-то вывести булево значение, то нам всегда приходилось это делать с проверкой: если это true, то мы выводим строкой true, либо, соответственно, строкой false. Эта директива все сделает за нас и будет делать echo true либо echo false. Ничего особенного, просто очередной помощник.

JS <script> let config = { isActive: @bool($isActive), hasAccess: @bool($hasAccess) }; </script> Alpine <div x-data="{ isActive: @bool($isActive) }"> <button :class="{ 'active': isActive }">Toggle </button> </div> Bootstrap: <div class="dropdown"> <button class="btn btn-secondary dropdown-toggle" type="button" id="dropdownMenuButton" data-toggle="dropdown" aria-haspopup="@bool($hasPopup)" aria-expanded="@bool($isExpanded)"> Dropdown button </button> <div class="dropdown-menu" aria-labelledby="dropdownMenuButton"> <a class="dropdown-item" href="#">Action</a> <a class="dropdown-item" href="#">Another action</a> <a class="dropdown-item" href="#">Something else here</a> </div> </div>

11.29 Add waitUntil method to Process

У нас уже был процесс с новым методом stop, также появился новый метод waitUntil.

Если мы имеем дело с long-running процессом, мы с вами также можем динамически указать сколько времени мы можем ожидать его завершения.

11.29 Add helper method to determine stray request prevention state

И напоследок по релизу 11.29 и дайджесту за октябрь — это pull request, который затрагивает HTTP-клиент и добавляет метод preventingStrayRequests. На самом деле, мне кажется, он уже был. Но как бы там ни было, видим, что он добавлен в 11 версию. И тем самым он предостерегает нас, чтобы не было запросов к каким-либо внешним API. Это будет полезно на уровне тестов, чтобы мы были уверены, что у нас все запросы фейковые, а не реальные.

Напоминаю вам, что 30 октября выходит викторина от CutCode "Своя игра", уже третий выпуск. Всех вас обязательно ждем в прямом эфире.

Видео-версия дайджеста:

Начать дискуссию