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

Новости

Вышли PHP 8.1.28, PHP 8.2.18 и PHP 8.3.6

❗️В этих выпусках исправлены уязвимости CVE-2024-1874, CVE-2024-2756 и CVE-2024-3096, в PHP 8.3.6 также исправлена уязвимость CVE-2024-2757, которые исправляют:

  • Обход функции proc_open в Windows с экранированием аргументов для bat- и cmd-файлов

  • Обход куки __Host / __Secure

  • Бесконечный цикл в функции mb_encode_mimeheader

  • Обработка функцией password_hash значения $password с нулевым символом.

❗️Версия PHP 8.3.5 была пропущена, пожалуйста, не полагайтесь этот тег.

Заявление PHP об уязвимости в glibc/iconv

Шумиха вокруг CVE-2024-2961, связанная с PHP, была крайне преувеличена. У многих сложилось впечатление, что уязвимость существует в самом языке и оказывает огромное влияние на PHP-разработчиков. Однако это не так.

Уязвимость может быть удаленно использована только в том случае, если приложение использует функции и потоковые фильтры модуля iconv с не валидированными кодировками, полученными из внешних источников.

Не стоит ожидать выпуска патча от PHP, поскольку glibc является динамически подключаемой библиотекой и не компилируется вместе с интерпретатором. Обновления glibc будет достаточно.

Все, что нужно знать о бэкдоре в XZ

Если вы не следили за этой историей, то вот вкратце, что произошло.

Некто под GitHub-аккаунтом @JiaT75 в течение 2 лет вносил свой вклад в библиетеку liblzma, создавая SSH-бэкдор незаметно для других сопровождающих. Он сделал более 700 коммитов, из которых лишь небольшое количество было вредоносным и спрятано в тестовых файлах.
Странное поведение было случайно обнаружено при проведении микробенчмаркинга утилиты xz.

Скорее всего, эта атака не является единичным случаем. По крайней мере, OpenJS Foundation уже сообщал о неудачных попытках захвата их проектов.

Объединение усилий для разработки стандартов кибербезопасности с открытым исходным кодом

PHP Foundation будет сотрудничать с фондами Apache, Eclipse, Rust и Python для создания стандартов для закона о киберустойчивости.

Это первый в мире закон, регулирующий индустрию программного обеспечения в целом. Он заставит некоторые проекты с открытым исходным кодом предоставлять политику кибербезопасности, добровольно сообщать об инцидентах и уязвимостях, а также сотрудничать с органами надзора за рынком.

Вышел PhpStorm 2024.1

Главные новинки этой версии:

  • Автодополнение строки целиком с помощью локального ИИ

  • Поддержка Symfony AssetMapper

  • Новый терминал

  • Улучшения для Pest

  • Поддержка PHPUnit 11.0

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

RFC: Property hooks

Хуки стали одним из самых больших и обсуждаемых RFC, в одном из предыдущих выпусках мы о них уже упоминали.

Они позволят разработчикам переопределять стандартное поведение "get" и "set" свойств объекта. Ларри Гарфилд и Илия Товилло вдохновлялись языками Kotlin, C# и Swift при разработке этого RFC.

RFC был принят подавляющим большинством голосов и мы с нетерпением ждем хуки в PHP 8.4.

?RFC: new MyClass()->method() without parentheses

RFC Валентина Удальцова, о котором мы говорили в конце прошлого года, перешел в стадию обсуждения.

У RFC положительные отзывы, ждем начала голосования, один голос у Валентина уже точно есть :)

На PHP-викторине, о которой мы поговорим чуть позже, Валентин также рассказал о своем RFC.

?RFC: array_find

Джошуа Рюсвег (Joshua Rüsweg) предлагает добавить новую функцию для поиска первого элемента, для которого callback-функция возвращает значение true.

?RFC: Casing of acronyms in class and method names

Тим Дюстерхус (Tim Düsterhus) предлагает пересмотреть предыдущее решение RFC по именованию классов. Вместо того, чтобы относиться к аббревиатурам как к обычным словам, сделать имена классов согласованными с PascalCase.

✅ RFC: Deprecate GET/POST sessions

RFC о котором мы говорили в прошлом выпуске принят.

Сейчас PHP поддерживает два способа распространения идентификатора токена сессии: с помощью файлов cookie и с помощью параметров GET или POST.

В PHP 8.4, если параметр session.use_only_cookies отключен, а параметр session.use_trans_sid – включен, будет выдаваться предупреждение об устаревании.

В PHP 9.0 распространение идентификатора токена сессии с помощью параметров GET или POST будет удалено.

✅ RFC: Release cycle update

Поддержка безопасности для версий PHP увеличена на один год. Таким образом, каждая версия PHP будет поддерживаться 4 года: 2 года исправлений ошибок и 2 года исправлений безопасности.

Изменения применяются немедленно ко всем поддерживаемым в настоящее время веткам, а ветка PHP 8.1 получит дополнительный год исправлений безопасности.

Релиз-менеджеры PHP 8.4

По традиции, PHP 8.4 будут сопровождать два «новичка» релиз-менеджера: Саки Такамачи (Saki Takamachi), core-разработчик, поддерживаемая PHP Foundation, и Кельвин Бакли (Calvin Buckley), разработчик нескольких модулей PECL.

Им будет помогать ветеран релиз-менеджер PHP 8.3 Эрик Манн (Eric Mann).

На канале CutCode прошла вторая викторина «Своя игра» по PHP

Валентин Удальцов, Алексей Гагарин и Петр Мязин ответили на каверзные вопросы по PHP.

Обязательно посмотрите, если пропустили. А также пишите свои пожелания, замечания и вопросы для следующих игр - ссылка на форму.

Состоялся релиз onFriday

Сервис по деплою приложений в один клик с нулевым временем простоя и контролем результата. Возможности аналогичные Envoyer, плюс есть дополнительные фичи. Подходит для деплоя приложений на PHP и Go.

Laravel дайджест

Обновления Laravel

11.2. Add fluent helper

https://github.com/laravel/framework/pull/50848

PR на тему Fluent объекта. Что за Fluent такой? Это дополнительный сахар. Там где у нас коллекции не справляются с сахаром, на помощь приходит Fluent объект. Давайте взглянем на примеры. Скажем, у нас есть коллекция. 

Есть метод Get. Элемент массива с Fluent мы можем просто указывать как свойство объекта и получать также доступ:

$data = [
    'user' => [
        'name' => 'Philo',
        'address' => [
            'city' => 'Amsterdam',
            'country' => 'Netherlands',
        ]  
    ],
    'posts' => [
        [
            'title' => 'Post 1',                
        ],
        [
            'title' => 'Post 2',               
        ]
    ]
];

❌ collect($data)->get('user');
✅ fluent($data)->user

Далее у нас в коллекциях через метод Get не поддерживается вложенность через метод DataGet во Fluent объекте. Теперь такая возможность есть, через точку можем обращаться к вложенным элементам:

❌ collect($data)->get('user')['name'];
✅ fluent($data)->get('user.name');

Также есть возможность Fluent объект трансформировать в коллекции:

❌ collect(collect($data)->get('posts'))->pluck('title');
✅ fluent($data)->collect('posts')->pluck('title');

И присутствует интересный метод Scope, который у нас под капотом содержит метод get, но возвращает новый instance fluent-объекта. И также нам демонстрирует пример, как удобно с помощью fluent-helper сделать JSON-Encode. 

❌ json_encode(collect($data)->get('user')['address']);
✅ fluent($data)->scope('user.address')->toJson();

11.2. Add a new helper for context 

https://github.com/laravel/framework/pull/50878

Следующий PR затрагивает context. У нас уже был context фасад. И пришло время  для helper для функции context:

context(['user' => auth()->user()]); // Add user information to the context
$context = context(); // Retrieve the context object
$user = context('user'); // Retrieve user information from the context

11.2. Add default value for get and getHidden on Context 

https://github.com/laravel/framework/pull/50824

Еще один PR по Context . У нас были методы-геттеры для получения элементов из контекста get и get-hidden. Но при этом мы не могли с вами указать дефолтное значение. Если такого ключа в контексте нет, то мы получаем с вами null:

$isUser = Context::get('is_user'); //  If is not has in array return null forever
$isUser = Context::getHidden('is_user'); //  If is not has in array return null forever

Не совсем привычно для гетеров в Laravel, так как всегда присутствует вторым параметром дефолтное значение, и теперь оно есть и в Context:

$isUser = Context::get('is_user', false); // If is not has in array return false
$isUser = Context::getHidden('is_user', false); // If is not has in array return false

11.2. Trim invisible characters

https://github.com/laravel/framework/pull/50832

PR затрагивает Middlewar trim strings. До этого была проблема, невидимые Unicode символы Trimstrings не чистил. PR решает эту проблему. 

11.2. Do not wipe database if it does not exists 

https://github.com/laravel/framework/pull/50838

Следующий PR по консольным командам. Проблема заключается в следующем. Если вы сделаете Migrate fresh и у вас еще не создана база данных, помощник вам подскажет создать её либо нет. И если вы отвечаете нет, то сработает исключение и вы получите ошибку. 

Теперь мы не будем получать исключения, а команда просто не будет выполнена. 

11.2. Str trim methods 

https://github.com/laravel/framework/pull/50822

Следующий PR добавляет новый сахар в класс по работе со строками. Раньше был trim(), теперь есть ltrim() и rtrim().

11.3. Add Component: Multiline Text Input

https://github.com/laravel/prompts/pull/88

Новая фича в Laravel Prompts. Обожаю этот пакет, и теперь появился элемент формы textarea() с помощью которого можем в консоли пользоваться текстовым блоком.

11.3. Add pull methods to Context 

https://github.com/laravel/framework/pull/50904

Этот PR добавляет в Context новый метод pull и pullHidden. Как и во всех pull в Laravel мы забираем значение из указанного элемента и после удаляем его из Context. 

11.3. Add session hasAny method 

https://github.com/laravel/framework/pull/50897

В объект сессий добавили метод hasAny, чтобы не плодить условия или и указать все в массиве через новый метод:

До:

if (session()->has('first_name') || session()->has('last_name')) {
    // do something...
}

После

if (session()->hasAny(['first_name', 'last_name'])) {
    // do something...
}

11.4. Introduces Exceptions facade 

https://github.com/laravel/framework/pull/50704

PR от Nuno Maduro, новый фасад exceptions, который даст нам новые возможности в тестировании exception. В примерах показано, как в тестах мы можем переключить exceptions в fake и далее в тестах проверять, вызваны ли исключения, сколько раз вызваны, и так далее. 

11.4. Allowing Usage of Livewire Wire Boolean Style Directives

https://github.com/laravel/framework/pull/51007

Следующий pull request, для фанатов livewire. Теперь в Blade-директивы можно будет отправлять параметры в boolean стиле. До этого при генерации HTML у нас добавлялся value: 

{{-- Using like this --}}
<x-fetch wire:poll />
{{-- Generates this HTML --}}
<div ... wire:poll="wire:poll" />

Теперь же у нас если нет значения, то и при рендере его не будет:

<x-fetch wire:poll />
<div ... wire:poll />

 11.4. afterQuery hook

https://github.com/laravel/framework/pull/50587

Следующий pull request добавляет новый метод в queryBuilder — autoquery, который исходя из названия будет вызван после того, как будет вызван запрос.

В pull request показан пример, где есть scope и мы после запроса итерируем продукты и устанавливаем определенный атрибут. 

public function scopeWithIsFavoriteOf($query, ?User $user = null) : void
{
    if ($user === null) {
        return $query;
    }
    $query->addSelect([
    // 'is_favorite' =&gt; some query ...
]);

}
$products = Product::withIsFavoriteOf(auth()->user())->get();
if (auth()->user() === null) {
$products->each->setAttribute('is_favorite', false);
}

Теперь же с помощью этого метода мы прямо в scope можем указать что у нас за логика будет после вызова запроса и здесь же ее и выполнить:

public function scopeWithIsFavoriteOf($query, ?User $user = null) : void
{
    if ($user === null) {
        $query->afterQuery(fn ($products) => $products->each->setAttribute('is_favorite', false));
        return;
    }
    $query->addSelect([
        // 'is_favorite' => some query ...
    ]);
}
Product::withIsFavoriteOf(auth()->user())->get();

11.4. Add RequiredIfDeclined validation rule 

https://github.com/laravel/framework/pull/51030

Следующий pull request добавляет правила валидации requiredIfDeclined. У нас уже был requiredIfAccepted, теперь появился его “брат” - если у нас checkbox не выбран. 

11.4. Adds support for enums on mapInto collection method 

https://github.com/laravel/framework/pull/51027

В следующем pull request речь пойдет методе mapInto для коллекций. Теперь мы можем мапить в enum данные:

public function store(Request $request)
{
    $request->validate([
        'features' => ['array'],
        'features.*' => [new Enum(Feature::class)],
    ]);
$features = $request
    -&gt;collect('features')
    -&gt;mapInto(Feature::class);

if ($features-&gt;contains(Feature::DarkMode)) {
    // ?
}

}

11.4. Adds Reversible Forms to Prompts

https://github.com/laravel/prompts/pull/118

В Laravel Prompts добавилась новая фича. До этого мы могли через Form Builder, через Helper Form, строить формы и получать значение из всех элементов формы только после сабмита:

$responses = form()
    ->text('What is your name?')
    ->select('What is your favourite language?', ['PHP', 'JS'])
    ->submit();

Но бывают кейсы, когда скажем нам в select внутри нужно получить результат из предыдущего поля. До этого такой возможности не было, теперь появился метод add, где мы с вами будем иметь доступ к предыдущим ответам и на основе их, если необходимо, кастомизировать следующий элемент формы:

$responses = form()
    ->text('What is your name?')
    ->select('What is your favourite language?', ['PHP', 'JS'])
    ->add(fn ($responses) => note("Your name is {$responses[0]} and your language is {$responses[1]}"))
    ->submit();

11.5. Supercharge Blade 

https://github.com/laravel/framework/pull/51143

PR, который затрагивает Blade, и ускоряет его работу. Теперь Blade будет работать в 15-25 раз быстрее. 

11.5. Blade Component Loop Speed Improvement 

https://github.com/laravel/framework/pull/51158

PR оптимизирующий loop внутри Blade - скорость увеличена на 7%. 

11.5. Ability to generate URL's with query params 

https://github.com/laravel/framework/pull/51075

PullRequest, который затрагивает URL-генератор. Появился новый метод Query, с помощью которого можно удобно строить URL вместе с Query-параметрами:

// http://localhost/products?sort=-name
url()->query('products', ['sort' => '-name']);
// http://localhost/products?columns[0]=name&columns[1]=price&columns[2]=quantity
url()->query('products', ['columns' => ['name', 'price', 'quantity']]);

Много возможностей, как видим из примеров. Крутая фича для URL генератора.

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