Это не какие-то непонятные трюки. Это инструменты, поставляемые с современным PHP, и как только вы начнете их использовать, вы удивитесь, как вы жили без них.


1. Генераторы — обработка 1 миллиона строк с почти нулевой памятью

Вот сценарий, с которым сталкивался каждый бэкэнд-разработчик: вам нужно прочитать большой файл CSV, обработать его построчно и что-то сделать с данными. Инстинкт состоит в том, чтобы сначала загрузить все в массив.

Этот инстинкт убьет ваш сервер.

  Традиционный подход:

 function readCsv(string $path): array
{
    $rows = [];
    $handle = fopen($path, 'r');

    while ($row = fgetcsv($handle)) {
        $rows[] = $row; // everything lands in RAM
    }

    fclose($handle);
    return $rows;
}

// 1 million rows = ~460MB RAM 💀
foreach (readCsv('big.csv') as $row) {
    process($row);
}
 

  С генератором:

 function readCsv(string $path): Generator
{
    $handle = fopen($path, 'r');

    while ($row = fgetcsv($handle)) {
        yield $row; // one row at a time, nothing more
    }

    fclose($handle);
}

// 1 million rows = ~0.3MB RAM ✨
foreach (readCsv('big.csv') as $row) {
    process($row);
}
 

Единственная разница —   доходность   вместо  $rows[] =  . Но эффект драматичен.

  Реальный тест — 1 миллион строк, файл размером 42 МБ:

Традиционный Генератор
Время 6,5 с   2.67с
Память   462 МБ ~0,3 МБ
Результат идентичный идентичный

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

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

  За пределами CSV: Генераторы работают везде, где вы имеете дело с большими последовательностями: чтение результатов базы данных по частям, разбиение на страницы ответов API, создание бесконечных последовательностей или потоковая передача файлов журналов в режиме реального времени.


2. Именованные аргументы — никогда больше не перепутайте параметры

Быстрая викторина. Что делает этот код?

 $result = array_slice($items, 1, true, 3);
 

Вероятно, вам нужно открыть документы. Является ли   истинным   флагом  preserve_keys ? Является ли  3  длиной? Что что?

Это ошибка, ожидающая своего часа. И это случилось со всеми нами.

  Именованные аргументы полностью исправляют эту проблему:

 // ❌ Before — which true is which?
$result = array_slice($items, 1, 3, true);

// ✅ After — reads like documentation
$result = array_slice(
    array: $items,
    offset: 1,
    length: 3,
    preserve_keys: true
);
 

Но настоящая сила заключается не только в читабельности. Это   пропуск необязательных параметров в середине.

PHP имеет множество встроенных функций с необязательными параметрами, спрятанными в середине их сигнатур. При использовании позиционных аргументов вам придется указывать каждый параметр, пока не дойдете до нужного:

 // ❌ Before — had to repeat ENT_QUOTES and 'UTF-8' just to reach double_encode
$safe = htmlspecialchars($input, ENT_QUOTES, 'UTF-8', false);

// ✅ After — jump straight to what you care about
$safe = htmlspecialchars(string: $input, double_encode: false);
 

  Именованные аргументы + распространение = чистое внедрение зависимостей:

 $config = [
    'table'   => 'orders',
    'limit'   => 20,
    'cache'   => true,
    'orderBy' => 'total',
];

// spread a named array directly into a constructor
$query = new QueryBuilder(...$config);
 

И прежде чем вы спросите: нет, производительность не влияет на производительность. В нашем тесте, состоящем из 1 миллиона вызовов, именованные аргументы фактически были немного быстрее , чем позиционные (внутренний оптимизатор PHP 8 справляется с ними эффективно).


3. Fibers — основа ReactPHP и Swoole

Волокна — самая непонятая особенность в этом списке. Это не волшебный async/await. Они не будут автоматически заставлять ваш код работать параллельно. Но они являются тем примитивом, который делает возможными современные асинхронные фреймворки PHP.

Fiber — это функция, которая может   приостанавливать себя и передавать управление обратно вызывающей стороне , а затем возобновить ее позже именно с того места, где она остановилась.

 $fiber = new Fiber(function(): string {
    echo "[Fiber] starting" . PHP_EOL;

    $received = Fiber::suspend('first pause'); // pause + send value out
    echo "[Fiber] resumed, got: $received" . PHP_EOL;

    $received = Fiber::suspend('second pause');
    echo "[Fiber] resumed again, got: $received" . PHP_EOL;

    return 'done';
});

echo "[Main] starting fiber" . PHP_EOL;
$yielded = $fiber->start();                 // runs until first suspend
echo "[Main] fiber paused, sent: $yielded" . PHP_EOL;

$yielded = $fiber->resume('message one');   // resume, get next suspend value
echo "[Main] fiber paused again, sent: $yielded" . PHP_EOL;

$fiber->resume('message two');              // resume to completion
echo "[Main] fiber returned: " . $fiber->getReturn() . PHP_EOL;
 

Вывод:

 [Main] starting fiber
[Fiber] starting
[Main] fiber paused, sent: first pause
[Fiber] resumed, got: message one
[Main] fiber paused again, sent: second pause
[Fiber] resumed again, got: message two
[Main] fiber returned: done
 

Обратите внимание на двустороннюю связь. Это то, что отличает волокна от генераторов — данные текут как внутрь, так и наружу.  suspend()  отправляет значение вызывающему объекту;  resume($value)  отправляет значение обратно в Fiber.

  Ключевая идея: Волокна сами по себе не обеспечивают параллелизма. Они обеспечивают совместную многозадачность — каждое волокно добровольно передает управление в известной точке (ожидание ввода-вывода, сетевой вызов, запрос к базе данных). Затем планировщик может запустить другое волокно, пока первое ждёт.

Этот планировщик — это то, что предоставляют ReactPHP, Swoole и цикл событий Revolt. PHP 8.1 предоставил нам примитив; фреймворки построили на его основе асинхронный режим.

Если вы пишете код приложения, вы, вероятно, не будете создавать волокна напрямую. Но их понимание покажет вам , почему   amphp   и  ReactPHP  работают именно так — и почему асинхронный код PHP фундаментально отличается от однопоточного цикла событий JavaScript.


4. Первоклассный вызываемый синтаксис — функции как значения, наконец-то чистые

PHP всегда позволял передавать функции как значения. Но синтаксис был настолько болезненным, что большинство людей вместо этого просто писали встроенные замыкания.

 // before PHP 8.1 — verbose wrappers everywhere
$fn = Closure::fromCallable('strlen');
$lengths = array_map(Closure::fromCallable('strtoupper'), $words);

// or inline closures that wrap a single function call
$lengths = array_map(function($w) { return strlen($w); }, $words);
 

В PHP 8.1 появился чистый синтаксис:  functionName(...)  создает первоклассное замыкание из любого вызываемого объекта — встроенных функций, статических методов, методов экземпляра — всех из них.

 // ✅ PHP 8.1+
$fn     = strlen(...);
$upper  = strtoupper(...);
$method = $obj->format(...);
$static = Formatter::clean(...);

$lengths = array_map(strlen(...), $words);
 

Вот где он действительно хорош —   шаблоны конвейера:

 function pipeline(mixed $value, callable ...$fns): mixed
{
    return array_reduce($fns, fn($carry, $fn) => $fn($carry), $value);
}

$result = pipeline(
    "  Hello, World! This is PHP.  ",
    trim(...),
    strtolower(...),
    fn($s) => str_replace([',', '!', '.'], '', $s),
    fn($s) => explode(' ', $s),
    array_filter(...),
    array_values(...),
);

// ['hello', 'world', 'this', 'is', 'php']
 

Или   цепочки проверки , где каждое правило является просто вызываемым:

 function validate(mixed $value, callable ...$rules): array
{
    return array_filter(array_map(fn($rule) => $rule($value), $rules));
}

$errors = validate(
    $password,
    fn($v) => strlen($v) < 8    ? 'Minimum 8 characters' : null,
    fn($v) => !preg_match('/\d/', $v) ? 'Must contain a number' : null,
    fn($v) => !preg_match('/[A-Z]/', $v) ? 'Must contain uppercase' : null,
);
 

Чистый, компонуемый, тестируемый — каждое правило можно независимо протестировать, а добавление нового — это однострочный процесс.


5. Недооцененные встроенные функции, которые вы продолжаете переопределять

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

   array_is_list()  — PHP 8.1+

 // before: manual check
function isList(array $arr): bool {
    return $arr === [] || array_keys($arr) === range(0, count($arr) - 1);
}

// now
array_is_list([1, 2, 3]);         // true
array_is_list(['a' => 1, 'b' => 2]); // false
array_is_list([0 => 'a', 1 => 'b']); // true
 

Важно, когда вы решаете, следует ли кодировать JSON как  []  или  {}  .

   str_contains()  ,  str_starts_with()  ,  str_ends_with()  — PHP 8.0+

 // before
if (strpos($haystack, $needle) !== false) { ... }

// now
if (str_contains($haystack, $needle)) { ... }
if (str_starts_with($url, 'https://')) { ... }
if (str_ends_with($file, '.php')) { ... }
 

Больше никаких сравнений  !== false . Больше не нужно случайно считать  0  ложным.

   array_any()  и  array_all()  — PHP 8.5+

 // before: manual loops or array_filter tricks
$hasAdmin = count(array_filter($users, fn($u) => $u->role === 'admin')) > 0;

// PHP 8.5
$hasAdmin = array_any($users, fn($u) => $u->role === 'admin');
$allActive = array_all($users, fn($u) => $u->isActive());
 

Собираем это вместе

Ни одна из этих функций не требует новой инфраструктуры или серьезного рефакторинга. Они доступны в PHP 8.0–8.1+, которые уже работают в большинстве производственных сред.

Схема для всех пяти одинакова:   меньше шаблонов, более ясные намерения, лучшее использование ресурсов. Генераторы держат память под контролем. Именованные аргументы делают подписи самодокументируемыми. Волокна питают асинхронную экосистему. Первоклассные вызываемые объекты делают функциональную композицию чистой. А встроенные убирают ручные переопределения.

Начните с того, что решает проблему, с которой вы уже столкнулись. Как только он щелкнет, все остальное произойдет само собой.


Все тесты проводились с использованием интерфейса командной строки PHP 8.3 в Ubuntu. Исходный код каждого примера доступен на GitHub . `