Давайте посмотрим, что произошло за прошедший месяц в мире 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 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
https://github.com/laravel/framework/pull/52935
В прошлом дайджесте мы с вами видели интеграцию Enum
всюду, теперь также затронут и RateLimit
, и вместо строкового наименования RateLimit
можно просто передавать Enum
, и это также будет работать.
11.26 Add make:job-middleware artisan command
https://github.com/laravel/framework/pull/52965
Двигаемся дальше к следующему 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
https://github.com/laravel/framework/pull/52855
Следующий пулл-реквест снова касается стабов: в них добавили дженерики как для моделей, так и для их фабрик (в тех случаях, где фабрики присутствуют).
<?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
https://github.com/laravel/framework/pull/52962
Следующий pull request касается Eloquent модели: массовая гидрация модели, fillable, guarded. Если мы работаем с полями, которые соответствуют полям в таблице, то все работало нормально, но если мы взаимодействуем с мутаторами, у которых наименование не соответствует полям в таблице, то здесь уже проверки при наполнении не будут срабатывать. Этот pull request исправляет указанное поведение, и теперь с мутаторами также не будет проблем.
```
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
https://github.com/laravel/framework/pull/52959
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
https://github.com/laravel/framework/pull/53022
В этом 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
https://github.com/laravel/framework/pull/53035
Снова helper по работе со строками Str. Был метод Str::contain()
, а с этим pull request добавили Str::doesntContain()
. Но я думаю, объяснять, что он из себя представляет, не имеет никакого смысла.
11.27 Str: Add extension support for Str::inlineMarkdown()
https://github.com/laravel/framework/pull/53033
Двигаемся к следующему pull request. Снова класс по работе со строками. Еще недавно у нас появилась возможность добавлять extension для markdown. Были методы Str::inlineMarkdown()
и str()->inlineMarkdown()
, где extension не поддерживались, и соответственно, этим pull request добавлена такая поддержка. Фича, которая появилась давно, но ее забыли добавить в соседние методы, и постепенно с новыми релизами такие моменты закрываются.
11.27 Utilise Illuminate\Support\php_binary()
https://github.com/laravel/framework/pull/53008
Следующий pull request. Когда-то была добавлена функция для поиска PHP-бинарника. Соответственно, в команде по установке API еще использовался класс от Symfony, и соответственно, в данном PR поменяли на функцию из Laravel.
11.27 feat: narrow types for throw_if and throw_unless
https://github.com/laravel/framework/pull/53005
Для большинства, я думаю, не важный PR, но для меня лично интересный, так как функцию throw_if
проапгрейдили в рамках док-блока. До этого статический анализ в том же самом PHP ругался о том, что если мы используем эту функцию, то все, что у нас двигается дальше, это уже unreachable statement из-за неправильного док-блока. Теперь этот момент исправлен.
11.27 Improve Schema::hasTable() performance
https://github.com/laravel/framework/pull/53006
Следующий pull request затрагивает schema и метод hasTable
. В данном случае улучшена производительность, оптимизированы запросы (стали более легковесными), и благодаря этому метод работет быстрее. Отличный pull request.
11.27 Add methods to the HTTP kernel to append middleware relative to other middleware
https://github.com/laravel/framework/pull/52897
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
https://github.com/laravel/framework/pull/52887
Следующий pull request затрагивает artisan команду queue: work
, добавляя новый флаг --json
, который меняет output в формат JSON, что в итоге будет выглядеть следующим образом:
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
}
Более правильный стандартизированный путь. За этот pull request лайк!
11.28 feat: add useful defaultLocale and defaultCurrency helpers to Number facade
https://github.com/laravel/framework/pull/53101
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
https://github.com/laravel/framework/pull/53104
Следующий 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
https://github.com/laravel/framework/pull/53096
Следующий 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
https://github.com/laravel/framework/pull/53080
Следующий 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
https://github.com/laravel/framework/pull/53122
Снова атрибуты, и на этот раз интересный лично для меня атрибут, чтобы в рамках модели мы сразу могли указать, в какую коллекцию мы будем маппить результат с записями. До этого тоже не составляло труда это делать через метод newCollection, который нужно было перезаписать, но всегда у меня возникали сложности, как он точно там называется, что именно и в каком виде должен возвращать, так как в Laravel иногда в этом бывает каша.
#[CollectedBy(PostCollection::class)]
class Post
{
// ...
}
Теперь же все будет просто: атрибут, указываем какая коллекция и дальше с ней взаимодействуем.
11.28 Add Tailwind, "composer run dev"
https://github.com/laravel/laravel/pull/6463
Двигаемся дальше по апдейту 11.28, но это уже не репозиторий Laravel/Framework
, это уже Laravel/Laravel
, то есть сам скелет.
И здесь Тейлор добавил новый скрипт в Composer, называется он Dev, с помощью него мы можем теперь запускать composer run dev
, и он будет выполнять все необходимые нам команды для Dev разработки. А именно сразу запускать виртуальный сервер (php artisan serve
), очереди (php artisan pail
) и Vite в режиме Dev. Как видим output одной командой, мы все запустили и погнали работать!
11.29 Add directive @bool to Blade
https://github.com/laravel/framework/pull/53179
Первый 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="#">Actiona>
<a class="dropdown-item" href="#">Another actiona>
<a class="dropdown-item" href="#">Something else herea>
div>
div>
11.29 Add waitUntil method to Process
https://github.com/laravel/framework/pull/53236
У нас уже был процесс с новым методом stop, также появился новый метод waitUntil
.
Если мы имеем дело с long-running процессом, мы с вами также можем динамически указать сколько времени мы можем ожидать его завершения.
11.29 Add helper method to determine stray request prevention state
https://github.com/laravel/framework/pull/53232
И напоследок по релизу 11.29 и дайджесту за октябрь — это pull request, который затрагивает HTTP-клиент и добавляет метод preventingStrayRequests
. На самом деле, мне кажется, он уже был. Но как бы там ни было, видим, что он добавлен в 11 версию. И тем самым он предостерегает нас, чтобы не было запросов к каким-либо внешним API. Это будет полезно на уровне тестов, чтобы мы были уверены, что у нас все запросы фейковые, а не реальные.
Напоминаю вам, что 30 октября выходит викторина "Своя игра", уже третий выпуск. Всех вас обязательно ждем в прямом эфире.
Видео-версия дайджеста: