Оригинал: PHP Fibers: How PHP is Finally Warming Up to Asynchronous Programming
Перевод для канала Мы ж программист
Ах, PHP. В течение многих лет он был основным языком для веб-разработки, на нем работали все – от крошечных сайтов-блогов до гигантских платформ вроде Facebook (по крайней мере, на заре своего существования). Но, несмотря на весь свой рост, PHP всегда отличался несколько консервативным подходом к асинхронному программированию – до сих пор. С появлением PHP Fibers в PHP 8.1 (и столь необходимой доработкой в PHP 8.4), кажется, PHP наконец-то принимает радости параллелизма.
Итак, давайте подробно рассмотрим PHP Fibers: что это такое, как они работают и почему вы можете ухмыляться, внедряя их в свою кодовую базу.
Что такое файберы и почему они должны вас волновать?
Думайте о файберах как о легких тредах (threads) – они позволяют приостанавливать и возобновлять работу функций без блокировки всего приложения. Представьте, что вы стоите в очереди в кофейне и заказываете латте. Пока вы ждете, вместо того чтобы просто стоять и ничего не делать, вы можете приостановить заказ и проверить электронную почту или пообщаться с другом. Когда ваш латте будет готов, вы вернетесь к процессу заказа, не пропустив ни одного мгновения.
В коде файберы позволяют вашему приложению продолжать выполнять полезную работу в ожидании завершения трудоемких задач. В отличие от традиционного синхронного PHP-кода, где все выполняется в одну строку (одна команда за другой, без пропусков вперед), файберы позволяют вам перескакивать с одной задачи на другую – идеальный вариант для приложений реального времени, которые не могут позволить себе задерживаться.
Важное замечание о версиях: файберы были введены в PHP 8.1, но они полностью функциональны и более стабильны в PHP 8.4. Так что если вы все еще работаете на PHP 7.x или 8.0, возможно, сейчас самое время обновиться.
Почему именно файберы? Разве в PHP раньше не было асинхронных возможностей?
Вы можете подумать: «Но подождите! Разве у нас не было таких опций, как pcntl_fork и ReactPHP для параллелизма?». И вы будете правы. Но эти методы либо сложны, либо требуют дополнительных расширений и библиотек. Кроме того, они не позволяют писать асинхронный код так естественно, как это делают файберы.
С файберами вам не нужно танцевать с бубном или устанавливать дополнительные расширения. Просто напишите свой код, добавьте немного файберов, и все готово.
Начало работы с файберами в PHP
Теперь давайте поиграем с кодом и узнаем, как работают файберы. Во-первых, давайте проверим, готова ли ваша версия PHP к революции:
php -v
Если вы видите PHP 8.1 или выше, все готово! Если нет, что ж, пора поговорить с тем, кто управляет вашим сервером, или, в крайнем случае, готовиться к великой миграции.
Базовая анатомия файбера
Красота файберов PHP заключается в их простоте. Вот как создать файбер и запустить его в работу:
$fiber = new Fiber(function() {
echo "Fiber started…\n";
Fiber::suspend(); // Pause the fiber
echo "Fiber resumed…\n";
});
echo "Fiber starting…\n";
$fiber->start(); // Start the fiber
echo "Main script running…\n";
$fiber->resume(); // Resume the fiber
Вот что происходит:
- Мы создаем новый Fiber с функцией, которая делает две вещи: печатает сообщение, приостанавливается, затем печатает другое сообщение.
- Мы запускаем файбер, он начинает выполняться до тех пор, пока не достигнет Fiber::suspend().
- Файбер «приостанавливает» себя на Fiber::suspend(), позволяя основному скрипту продолжать выполнение.
- Позже мы вызываем $fiber->resume(), что подхватывает файбер с того места, где он остановился, и завершает работу.
И вуаля! Вы только что создали асинхронный код в PHP.
Практический пример: Одновременная выборка данных с помощью файберов
Предположим, вы хотите получать данные из нескольких API, не дожидаясь завершения каждого запроса, прежде чем переходить к следующему. Fiber-подход позволяет запускать каждый запрос, приостанавливать его, а затем возобновлять позже, позволяя вашему основному скрипту выполнять другие задачи в это время.
<?php
// Function to create a fiber that simulates fetching data from an API
function fetchData(string $url, int $delay) {
return new Fiber(function() use ($url, $delay) {
echo "Starting request to $url...\n";
sleep($delay); // Simulate network delay
Fiber::suspend("Data from $url"); // Pause fiber and return simulated data
});
}
// Initialize fibers for each "API request"
$fiber1 = fetchData('https://jsonplaceholder.typicode.com/posts/1', 2);
$fiber2 = fetchData('https://jsonplaceholder.typicode.com/posts/2', 3);
// Start fibers (this initiates each "request" and pauses at Fiber::suspend)
$fiber1->start();
$fiber2->start();
echo "Performing other tasks while waiting for data...\n";
// Collect data once fibers have completed their simulated requests
$data1 = $fiber1->resume();
$data2 = $fiber2->resume();
echo "$data1\n";
echo "$data2\n";
echo "All data fetched successfully.\n";
Объяснение:
- Создание файберов: Функция
fetchData
создает файбер для каждого «API-запроса». Каждый файбер имитирует задержку, приостанавливается и возвращает сообщение, как только «данные» будут готовы. - Запуск файберов:
start()
инициирует каждый файбер, который затем приостанавливается после запуска «запроса», позволяя другому коду работать, пока он «ждет». - Неблокирующее выполнение: После запуска обоих файберов основной скрипт приступает к выполнению других задач (на что указывает оператор
echo
). - Возобновление работы: Когда вы готовы получить результаты, вы вызываете
resume()
на каждом файбере, чтобы получить данные. Приостановленные данные изFiber::suspend()
возвращаются при вызовеresume()
, завершая «запросы».
Сначала это может показаться немного странным, но представьте себе возможности… Вы можете работать над другими частями вашего приложения (например, рендерингом страницы или обработкой пользовательского ввода), пока данные собираются в фоновом режиме.
Реальный пример использования: Асинхронная обработка изображений
Давайте сделаем еще один шаг вперед. Представьте, что у вас есть приложение, в которое пользователи могут загружать изображения, и вы хотите применить к ним несколько фильтров. Обработка каждого фильтра может занять некоторое время, поэтому давайте воспользуемся файберами для обработки всего этого в фоновом режиме:
<?php
function applyFilter($image, $filter) {
return new Fiber(function() use ($image, $filter) {
echo "Applying $filter to $image...\n";
sleep(2); // Simulate filter processing delay
Fiber::suspend("$filter applied to $image."); // Suspend fiber and provide completion message
});
}
$image = 'user-uploaded-image.jpg';
$filters = ['grayscale', 'blur', 'sharpen'];
// Create a fiber for each filter
$fibers = array_map(fn($filter) => applyFilter($image, $filter), $filters);
// Start all fibers
foreach ($fibers as $fiber) {
$fiber->start();
}
echo "Processing other tasks while filters are being applied...\n";
// Resume each fiber to complete processing and collect results
foreach ($fibers as $fiber) {
if ($fiber->isSuspended()) {
$result = $fiber->resume();
echo "$result\n";
}
}
echo "All filters have been applied.\n";
В этом примере используются файберы для одновременного применения трех различных фильтров к изображению. Пока фильтры применяются, сценарий может двигаться дальше и «обрабатывать другие задачи». Когда мы будем готовы, мы возобновим работу каждого файбера, чтобы завершить процесс фильтрации.
PHP 8.4 и последующие версии: Что дальше для файберов?
Если файберы в PHP 8.1 были захватывающим началом, то PHP 8.4 предлагает несколько улучшений, которые делают их более стабильными и эффективными для использования в производстве. В версии 8.4 исправлены некоторые проблемы, связанные с управлением памятью и обработкой параллелизма, что делает файберы более предсказуемыми и надежными.
Если вы погружаетесь в асинхронное программирование на PHP, использование PHP 8.4 обеспечит более плавный опыт. Стоит отметить, что файберы являются низкоуровневой функцией – если вы создаете сложные приложения, подумайте об использовании библиотеки более высокого уровня (например, ReactPHP), которая может абстрагировать некоторые детали и предложить дополнительные инструменты для управления параллелизмом.
Предостережение
Файберы – мощный инструмент, но с большой силой приходит и большая ответственность. Легко сойти с ума от файберов и начать приостанавливать и возобновлять все подряд. Но чрезмерное использование может сделать ваш код сложным для чтения и отладки, поэтому используйте файберы с умом. И помните: использование файберов – это как жонглирование горящими мечами: захватывающе, но лучше всего, если вы хорошо знаете основы.
Заключение
PHP Fibers – это отличное дополнение для разработчиков PHP, которые давно хотели найти более простой способ работы с асинхронным программированием. Они предоставляют понятный, управляемый способ работы с параллелизмом, без сложностей потоков или внешних библиотек. От асинхронных вызовов API до фоновой обработки, файберы открывают мир возможностей в PHP, делая его более современным, мощным и просто немного более увлекательным.
Так что, если вы работаете с PHP 8.1 или выше, попробуйте использовать файберы в своем следующем проекте. Возможно, они станут вашей новой любимой функцией!