Перейти к содержимому
Поиск
Php Разработка

PHP: полный мануал с примерами, функциями и ООП

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

Александр НекрасовИзменено:
Практический мануал PHP: от переменных и функций к объектной модели интернет-магазина
PHP 8.5.11149 глав4458 карточек справочника
Дерево руководства

Глава 135 · От запуска скрипта к эксплуатации сервера

OPcache: кеш PHP-кода, прогрев, обновление, preload и JIT

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

Что ускоряет OPcache: готовить код один раз, исполнять для каждого запроса

Интернет-магазин принимает много запросов, каждый использует одни и те же PHP-файлы. Перед исполнением движок разбирает исходник и готовит инструкции. OPcache позволяет сохранить эту подготовленную форму и использовать её снова. Если посетители разные, корзины всё равно вычисляются отдельно: кеш инструкций не хранит готовую корзину первого посетителя.

OPcache сохраняет подготовленный к исполнению PHP-код. При следующем запросе движок может взять готовые инструкции, но сами действия скрипта выполняются снова: SQL, HTTP-запросы, циклы и вывод не превращаются в готовый HTML-кеш. Экземпляр кеша связан с конкретным окружением PHP; консольный вызов не является удалённым управлением всеми PHP-FPM серверами.

Первые и следующие обращения
  1. Исходник calculation.php
  2. Первый раз: разбор → компиляция → кеш
  3. Следующий раз: готовые инструкции из кеша
  4. Каждый раз: выполнение с текущими данными
МеханизмЧто хранит и зачем
OPcacheПодготовленные инструкции PHP, чтобы не разбирать один исходник заново.
Кеш данныхНапример, результат дорогого запроса; его срок жизни и инвалидирование определяет приложение.
Кеш страницы/CDNГотовый HTTP-ответ или ресурс; важны пользователь, cookies и политика кеширования.
JITМашинный код выбранных участков; не заменяет остальные виды кеша.
PreloadПостоянные объявления классов/функций при старте процесса; отдельный lifecycle.
Узнать, разрешён ли кеш в этом окруженииСамостоятельный пример
<?php
declare(strict_types=1);

$configuration = opcache_get_configuration();
if ($configuration === false) throw new RuntimeException('Нет доступа к OPcache API');
echo $configuration['directives']['opcache.enable'] ? "Подготовленный код кешируется\n" : "Кеш отключён\n";
Ожидаемый результат
Подготовленный код кешируется

Массив configuration — диагностические данные текущего PHP. Эта строка не замеряет ускорение сайта; она только подтверждает настройку своей лаборатории.

PHP 8.5 и прежние версии: разные способы подключения

OPcache входит в PHP с 5.5; для 5.2–5.4 существовало отдельное PECL-расширение. Начиная с PHP 8.5 он всегда встроен в бинарник и загружен. Это не означает, что кеширование обязательно включено: opcache.enable и enable_cli продолжают управлять работой. В 8.5 строки zend_extension=opcache.so или zend_extension=php_opcache.dll вызывают предупреждение и должны быть удалены из старой конфигурации при миграции.

Для PHP до 8.5 действовал способ сборки shared extension с --enable-opcache, особенно при --disable-all, и загрузкой через zend_extension с путём к opcache.so/php_opcache.dll. Это Zend-расширение, поэтому обычное extension= не равнозначная замена. При совместном использовании с Xdebug OPcache загружается раньше. На странице installation официального manual ещё присутствует историческая инструкция; для 8.5 применяются более новые правила migration85.

Отделить присутствие API от включения CLIСамостоятельный пример
<?php
declare(strict_types=1);

var_dump(extension_loaded('Zend OPcache'));
var_dump(function_exists('opcache_get_status'));
echo ini_get('opcache.enable_cli') === '1' ? "CLI-кеш включён\n" : "CLI-кеш выключен\n";
Ожидаемый результат
bool(true)
bool(true)
CLI-кеш включён

В лаборатории enable_cli задан параметром запуска. В обычной установке default0; наличие функции в PHP8.5 не доказывает, что опкоды текущей команды кешируются.

Запустить собственный пример на Windows с отдельным кешемФрагмент или команда
php -d opcache.enable_cli=1 -d opcache.jit=disable -d opcache.jit_buffer_size=0 -d opcache.file_update_protection=0 -d opcache.cache_id=phpmanual-local -d opcache.file_cache=C:/php-lab/cache example.php

Для PHP8.5 сначала создают отдельную доступную C:/php-lab/cache и помещают скачанные файлы примера в свою учебную директорию. На PHP≤8.4 дополнительно требуется совместимо загруженный OPcache. Команда не изменяет серверный ini. На Unix cache_id не используется, выбирают отдельный CLI-процесс и свой путь file_cache.

Учебная file_update_protection=0 позволяет сразу кешировать созданные файлы. На работающем сайте это решение связывают с атомарной публикацией исходников. Не копируйте весь профиль лаборатории в production. Настройки памяти, числа файлов и проверки изменений выбираются по приложению; старое fast_shutdown=1 из исторических рекомендаций более не действует.

Прогрев, изменение кода и разные экземпляры кеша

После старта новый экземпляр памяти пустой. Первые запросы либо специальный прогрев наполняют его. При изменении исходников важно, когда PHP заметит новый код. validate_timestamps и revalidate_freq задают автоматическую проверку; при отключённой проверке обновление входит в процесс развёртывания.

Выполнить одно вычисление дважды после прогреваСамостоятельный пример
<?php
declare(strict_types=1);

$path = __DIR__ . '/calculation.php';
// Только наш учебный файл: исключаем защиту от совсем нового timestamp.
touch($path, time() - 60);

opcache_compile_file($path);
echo require $path, "\n";
echo require $path, "\n";
Ожидаемый результат
42
42

Готовый байткод используется снова, но require исполняет код каждый раз. Это не require_once и не мемоизация результата.

На Linux CLI не управляет in-memory кешем независимого PHP-FPM мастера. Несколько серверов за балансировщиком имеют свои экземпляры; несколько пулов не стоит автоматически считать независимыми кешами без знания процесса запуска. На Windows область разделения определяется также SAPI, пользователем и cache_id. Поэтому «opcache_reset вернул true в консоли» не равно «все посетители уже видят новый релиз».

Для одного изменённого файла есть invalidate, для памяти всего экземпляра — reset, для preloaded-объявлений требуется restart процесса. Файловый кеш — отдельный второй уровень: reset его не удаляет, тогда как актуальный invalidate обрабатывает соответствующую файловую запись. Политика обновления должна быть согласована с конкретной версией и способом развёртывания, а не случайным публичным URL сброса.

Полная таблица настроек: назначение, версия и область изменения

INI_SYSTEM означает настройку при запуске сервера/процесса; ini_set в странице её не заменяет. INI_ALL разрешает runtime-изменение только в рамках специальных правил директивы. Значения в таблице — исходные значения документации, а не готовый универсальный production-профиль. В пакетах ОС они могут быть изменены.

Параметр и исходное значениеОбластьДля чего нужен
opcache.enable = 1ALLВключение кеширования и оптимизации. Через ini_set можно выключить, но нельзя включить обратно в уже запущенном скрипте.
opcache.enable_cli = 0SYSTEMОтдельное разрешение CLI; оно не управляет PHP-FPM. В 7.1.2–7.1.6 стандартное значение было 1.
opcache.memory_consumption = 128SYSTEMМиБ общей памяти для OPcache, минимум 8. При JIT дополнительно нужен его буфер; подбор зависит от кодовой базы.
opcache.interned_strings_buffer = 8SYSTEMМиБ для повторно используемых строк. С 8.4 максимум 32767 на 64 бит и 4095 на 32 бит; раньше 4095 везде.
opcache.max_accelerated_files = 10000SYSTEMПредельное число ключей. Диапазон 200…1000000, вне его значение ограничивается; фактическая таблица округляется до подходящего простого размера.
opcache.max_wasted_percentage = 5SYSTEMДопустимая доля wasted памяти перед планированием restart при недостатке свободной. Верхний предел 50, это не процент memory_limit скрипта.
opcache.use_cwd = 1SYSTEMДобавлять рабочую директорию к ключу скрипта. Отключение может столкнуть одинаковые относительные имена в разных каталогах.
opcache.validate_timestamps = 1ALLПроверять изменение исходных файлов. При 0 обновления требуют согласованного invalidate/reset или перезапуска нужного процесса.
opcache.revalidate_freq = 2ALLИнтервал проверки mtime в секундах; 0 проверяет каждый запрос. Не действует при validate_timestamps=0.
opcache.revalidate_path = 0ALLПовторно разрешать пути поиска. При 0 уже закешированное имя из include_path может скрыть другой одноимённый файл в пути поиска.
opcache.save_comments = 1SYSTEMСохранять doc comments. Их удаление может сломать аннотации, Reflection и зависящие от них библиотеки; это не автоматическая рекомендация оптимизации.
opcache.fast_shutdown = 0, удаленаSYSTEMУдалена с 7.2: вариант механизма встроен в движок. Не добавлять в современный ini по старой статье.
opcache.enable_file_override = 0SYSTEMРазрешить проверкам file_exists/is_file/is_readable использовать кеш скриптов. Без проверки timestamps возможны устаревшие ответы.
opcache.optimization_level = 0x7FFEBFFFSYSTEMБитовая маска проходов оптимизатора. До 7.3 стандарт был 0x7FFFBFFF; небезопасные биты меняют предположения о поведении кода.
opcache.inherited_hack = 1, удаленаSYSTEMИсторическая игнорируемая настройка; удалена в 7.3.
opcache.dups_fix = 0ALLСтарый специальный обход Cannot redeclare class. Причину повторного объявления сначала исправляют в загрузке кода.
opcache.blacklist_filename = пустоSYSTEMФайл перечня исходников, которые не следует ускорять; строки поддерживают префиксы/маски, ; начинает комментарий.
opcache.max_file_size = 0SYSTEMМаксимальный размер исходного файла для кеша в байтах; 0 не вводит ограничение. Это не максимальный HTTP upload.
opcache.consistency_checks = 0, удаленаALLИсторическая проверка checksum каждые N запросов. Отключена с 8.1.18/8.2.5, удалена в 8.3; отладочный расход не ускорение.
opcache.force_restart_timeout = 180SYSTEMСекунды ожидания запланированного restart; после предела возможна остановка процессов, удерживающих блокировки. Не поддерживается Windows; события журналируются при verbosity≥2.
opcache.error_log = пустоSYSTEMПуть журнала OPcache; пустое значение направляет записи в stderr, часто журнал веб-сервера.
opcache.log_verbosity_level = 1SYSTEM0 — fatal, 1 — errors, 2 — warnings, 3 — информация, 4 — отладка. Подробный журнал требует контроля объёма.
opcache.record_warnings = 0SYSTEMС 8.0 сохранять compile-time warnings и повторять при загрузке из кеша. Важно для одинаковой диагностики холодного и прогретого кода.
opcache.preferred_memory_model = пустоSYSTEMАвтовыбор модели общей памяти обычно правильный; специальные значения mmap/shm/posix/win32 зависят от системы.
opcache.protect_memory = 0SYSTEMЗащита shared memory от неожиданных записей для внутренней отладки. Не прикладная проверка безопасности загружаемых файлов.
opcache.mmap_base = nullSYSTEMТолько Windows: базовый адрес отображения для решения конфликтов reattach. Не универсальная настройка, которую копируют с чужого компьютера.
opcache.restrict_api = пустоSYSTEMРазрешённый префикс пути скриптов, вызывающих API. Пусто — без такого ограничения; не заменяет авторизацию HTTP-доступа.
opcache.file_update_protection = 2ALLНе кешировать совсем недавно изменённые файлы, чтобы не схватить неполное обновление. 0 допустим при гарантированно атомарной публикации файлов.
opcache.huge_code_pages = 0SYSTEMПеренос text segment бинарника PHP в huge pages. Нужна поддержка ОС; это не тот же сегмент, что memory_consumption. Linux с 7.0, FreeBSD с 7.4; при отказе remap warning.
opcache.lockfile_path = /tmpSYSTEMАбсолютная директория shared lockfiles на Unix-подобной системе. Не произвольный файл приложения.
opcache.opt_debug_level = 0SYSTEMС 7.1 дампы инструкций оптимизатора: 0x10000 до оптимизации, 0x20000 после. Для диагностики, не публичного вывода страницы.
opcache.file_cache = null / пустоSYSTEMСуществующая доступная директория второго уровня на диске. Пустое значение отключает этот уровень; защищайте доступ к подготовленному коду.
opcache.file_cache_only = 0SYSTEM1 использует файловый кеш вместо общей памяти. До 8.1 при выключении режима с заполненным кешем требовалась его ручная очистка.
opcache.file_cache_consistency_checks = 1SYSTEMПроверять контрольную сумму при загрузке подготовленного файла. Это отдельная настройка, не удалённая consistency_checks.
opcache.file_cache_fallback = 1SYSTEMТолько Windows: при неудачном подключении общей памяти переходить к file_cache_only для процесса. Требует заданного file_cache; отключение может помешать старту.
opcache.validate_permission = 0SYSTEMПроверять права кешированного файла относительно текущего пользователя; с 7.0.14.
opcache.validate_root = 0SYSTEMЗащищать имена в chroot от столкновений и доступа к файлам вне корня; включают для chroot-окружений. С 7.0.14.
opcache.preload = пустоSYSTEMС 7.4 путь startup-скрипта для постоянных объявлений. На Windows preload не поддерживается; код выполняется при запуске сервера.
opcache.preload_user = пустоSYSTEMПользователь запуска preload в Unix. Root по умолчанию запрещён; с 8.3 для CLI/phpdbg отдельное значение не требуется лишь ради root-старта.
opcache.cache_id = пустоSYSTEMWindows с 7.4: один пользователь, SAPI и cache_id делят экземпляр. Для изолированных приложений задают разные ID; в IIS возможен APP_POOL_ID.
opcache.jit = disableALLС 8.0 выбор режима JIT; до 8.4 default tracing. disable запрещает последующее включение в runtime, off оставляет такую возможность.
opcache.jit_buffer_size = 64MSYSTEMБайты/суффикс размера для машинного кода. 0 отключает JIT; до 8.4 default был 0. Сам ненулевой размер не отменяет jit=disable.
opcache.jit_debug = 0ALLБитовая маска отладочного вывода JIT из ZEND_JIT_DEBUG_* в zend_jit.h конкретной версии.
opcache.jit_bisect_limit = 0ALLОстановить компиляцию после N функций для поиска проблемного места. Работает с trigger 0 или 1, не универсальный лимит tracing.
opcache.jit_prof_threshold = 0.005ALLДля первого profiling-запроса доля вызовов, выше которой функция hot. 0.005 соответствует 0.5% всех вызовов.
opcache.jit_max_root_traces = 1024SYSTEMПредел корневых трасс; после достижения новые корневые трассы не компилируются.
opcache.jit_max_side_traces = 128SYSTEMПредел боковых трасс одной root trace, то есть альтернативных путей от неё.
opcache.jit_max_exit_counters = 8192SYSTEMОбщий предел счётчиков выходов для боковых трасс всех корневых путей.
opcache.jit_hot_loop = 61SYSTEMИтерации до hot loop. В 8.0–8.4 default64, с 8.5 —61. Рекомендуемое простое число снижает совпадения с периодом циклов.
opcache.jit_hot_func = 127SYSTEMВызовы до hot function; общие правила диапазона 0…255 описаны ниже.
opcache.jit_hot_return = 8SYSTEMВозвраты до hot return; 0 отключает трассировку соответствующего вида.
opcache.jit_hot_side_exit = 8ALL в 8.5.11Выходы до hot side exit. Таблица manual указывает SYSTEM, но tagged PHP 8.5.11 регистрирует ALL; проверяйте свою сборку.
opcache.jit_blacklist_root_trace = 16ALLСколько попыток компиляции root trace допускается до внесения её в blacklist.
opcache.jit_blacklist_side_trace = 8ALLАналогичный предел попыток для боковой трассы.
opcache.jit_max_loop_unrolls = 8ALLПопытки развёртки цикла в боковой трассе, чтобы достигнуть root trace и замкнуть внешний цикл.
opcache.jit_max_recursive_calls = 2ALLПредел развёрнутых рекурсивных вызовов при компиляции, не лимит глубины рекурсии вашего PHP-кода.
opcache.jit_max_recursive_returns = 2ALLПредел развёртки рекурсивных возвратов, не количество разрешённых return в программе.
opcache.jit_max_polymorphic_calls = 2ALLПопытки встроить динамические/методные вызовы с разными целями; выше предела считаются megamorphic и не встраиваются.
opcache.jit_max_trace_length = 1024ALL, tagged 8.5.11Дополнительная настройка реализации: предел длины трассы. Допускается диапазон от4 до ZEND_JIT_TRACE_MAX_LENGTH; не выбирайте значение вне диапазона своей версии.

Фактическая ёмкость max_accelerated_files выбирается из 223, 463, 983, 1979, 3907, 7963, 16229, 32531, 65407, 130987, 262237, 524521, 1048793: ближайшее не меньше заданного значения после ограничения допустимого диапазона. Поэтому число в статусе может отличаться от ini. При нехватке памяти рост числа ключей сам по себе не поможет.

jit_hot_loop, jit_hot_func, jit_hot_return и jit_hot_side_exit принимают 0…255. Вне диапазона используется стандартное значение; 0 выключает соответствующий вид трассировки. Список настроек JIT зависит от поддержки в сборке. Порог компиляции не является таймаутом или ограничением пользовательского цикла.

При validate_timestamps=0 отдельные проверки времени всё ещё могут происходить во время компиляции из-за file_update_protection/max_file_size. Это не означает автоматическую актуализацию всего кеша. Подготовьте воспроизводимый план deploy до отключения проверок. Параметры shared memory и huge pages имеют разные роли: общий сегмент может пытаться использовать huge pages и без копирования text segment директивой huge_code_pages.

Проходы оптимизатора и их битовая маска

Оптимизатор преобразует PHP-инструкции перед сохранением. Маска optimization_level задаёт проходы, а не уровень качества JPEG и не права доступа. Стандартная комбинация сохраняет поведение обычного кода; особые небезопасные предположения полезны только для внутренней диагностики.

Бит / проходПреобразование
0 / PASS_1Локальные упрощения соседних инструкций.
1 / PASS_2Исторический проход объединён с PASS_1.
2 / PASS_3Упрощение переходов.
3 / PASS_4Оптимизация вызовов.
4 / PASS_5Преобразования по графу потока управления.
5 / PASS_6Анализ потока данных.
6 / PASS_7Использовать граф вызовов для SSA-оптимизаций.
7 / PASS_8Распространение известных констант по достижимым веткам.
8 / PASS_9Оптимизация временных переменных.
9 / PASS_10Удаление пустых NOP.
10 / PASS_11Уплотнение литералов.
11 / PASS_12Расчёт размера стека вызова заранее.
12 / PASS_13Удаление неиспользуемых переменных.
13 / PASS_14Удаление недостижимого кода.
14 / PASS_15Подстановка объявлений констант без сохранения всех runtime-условий; unsafe, по умолчанию выключен.
15 / PASS_16Встраивание тривиальных функций в рамках оптимизации вызовов.
16 / специальный флагИгнорировать перегрузку операторов; unsafe и по умолчанию выключен.

Например, агрессивная подстановка константы может проигнорировать порядок её объявления. Предположение об отсутствии перегрузки арифметики некорректно для GMP/BCMath-объектов с do_operation. Не включайте все биты в надежде получить «максимальную скорость»: исключённые биты исключены по смысловой причине. Типовая оптимизация проекта начинается с измерений реальных узких мест.

JIT: режимы, пороги и реальная польза

JIT переводит подходящие участки в машинный код. OPcache без JIT всё равно полезен: он исключает повторную подготовку PHP-исходников. Для страницы, которая большую часть времени ждёт SQL или API, JIT не устраняет это ожидание. Больше шансов на выигрыш у длительных вычислительных участков; измерять надо реальную нагрузку после прогрева.

Режим jitПоведение
disableПолностью выключен, включить в runtime нельзя; default с PHP 8.4.
offСейчас выключен, runtime-включение разрешено при подходящем буфере/сборке.
tracing / onСобирать и компилировать горячие пути; CRTO1254.
functionКомпилировать функции; CRTO1205.

Продвинутая числовая запись CRTO содержит четыре независимые цифры. C:0 без специальных CPU-оптимизаций,1 использует AVX при доступности. R:0 без распределения регистров,1 локальное,2 глобальное. T:0 все функции при загрузке,1 при первом вызове,2 профиль первого запроса с последующей компиляцией горячих функций,3 профилирование горячих функций в ходе работы,4 не используется,5 tracing. O:0 без JIT,1 обычные VM-handlers,2 их встраивание,3 вывод типов,4 граф вызовов,5 оптимизация всего скрипта.

Нулевой jit_buffer_size отключает JIT, ненулевой резервирует память под машинный код. С PHP 8.4 ошибка инициализации включённого JIT прерывает старт fatal error: сначала проверяют профиль на отдельном экземпляре. Для расследования есть jit_debug, jit_bisect_limit и opcache_jit_blacklist. Примеры этой главы не выдают обычное CLI-исполнение за доказательство ускорения JIT.

Preload: чем постоянные объявления отличаются от обычного кеша

С PHP 7.4 startup-скрипт может подготовить функции, классы, интерфейсы и трейты в постоянную память. Они доступны следующим запросам без обычной загрузки объявления. Глобальные константы таким же образом не становятся preloaded. Выигрыш оплачивается базовым потреблением памяти и более строгим обновлением: старые preloaded-объявления убирает restart процесса, а не замена файла на диске.

На Windows preload не поддерживается. Для обычной короткой CLI-команды польза мала: процесс сразу завершится. Исключение в документации — отдельные задачи FFI-библиотек. Практический смысл в долгоживущем серверном процессе, где одни объявления используются многими запросами. Не обязательно загружать весь vendor: измеряют реально нужную часть и базовую память.

Указать startup-скрипт для подходящего Unix-сервераФрагмент или команда
opcache.preload=/srv/shop/preload.php
opcache.preload_user=www-data

Фрагмент ini для сервера, где существует пользователь www-data и указанный доверенный скрипт. Это пример контекста, а не команда для текущей Windows. Изменение требует перезапуска обслуживающего PHP-процесса.

Подготовить известные файлы без выполнения их телПример для указанного окружения
<?php
declare(strict_types=1);

// preload.php: выполняется при старте поддерживаемого Unix SAPI.
$files = [__DIR__ . '/src/Price.php', __DIR__ . '/src/Cart.php'];
foreach ($files as $path) {
    if (!opcache_compile_file($path)) throw new RuntimeException('Preload не завершён');
}

Два пути выбирает приложение. Файлы должны содержать пригодные для preload объявления и быть доступны startup-пользователю. Здесь проверен синтаксис; Windows preload не выдаётся за реально запущенный.

Startup-файл сам исполняется. Если он использует include/require, тела подключённых файлов тоже исполняются и цепочки их include обрабатываются. Это позволяет условные объявления, но также запускает побочные действия. opcache_compile_file не выполняет тело и даёт больше свободы порядка компиляции, в том числе файлов с наследованием. Для include базовый класс должен быть определён до потомка; условные объявления требуют исполнения нужной ветки.

Если последующий запрос включает уже preloaded-файл, его основное тело всё равно может исполниться, а постоянные символы не объявляются заново. include_once не делает факт preloading равнозначным прошлому обычному include этого запроса. Иногда повторное включение нужно именно ради глобальных констант или runtime-инициализации — поэтому файлы объявлений и побочные действия стоит разделять осмысленно.

При старте FPM от root выполняйте preload с подходящим непривилегированным пользователем. Явное preload_user=root разрешает root-исполнение, но это не рекомендуемый универсальный профиль. С 8.3 правила CLI/phpdbg допускают root-start без этой отдельной настройки; это исключение не следует переносить на серверную модель. Зависимости и объём постоянной памяти проверяют на отдельном окружении перед выпуском.

Полный список функций OPcache по задачам

ЗадачаAPI
Подготовить код без выполненияopcache_compile_file
Проверить подготовленность в памяти/на дискеopcache_is_script_cached · opcache_is_script_cached_in_file_cache
Посмотреть настройки и состояниеopcache_get_configuration · opcache_get_status
Обновить один файл или память экземпляраopcache_invalidate · opcache_reset
Диагностировать одну tracing-JIT функциюopcache_jit_blacklist

OPcache не предлагает классы для вашей корзины или платежа. Его функции относятся к подготовке и эксплуатации PHP-кода, а не к бизнес-данным. Подробные карточки содержат различия return, области действия и версии.

Практический маршрут: после deploy видно старую страницу

ШагЧто отличаем
1. Проверить обслуживающий узелЗапрос действительно попал в новый релиз и нужный PHP-процесс? Балансировщик может вести на разные версии.
2. Разделить уровни кешаЭто HTML/CDN/browser кеш, данные приложения либо PHP-байткод? Очистка одного не очищает все.
3. Увидеть настройки нужного SAPIvalidate_timestamps/revalidate_freq/file_cache/preload определяют жизненный цикл. CLI-конфигурация может отличаться.
4. Обновить контролируемоДействовать выбранной схемой развёртывания: invalidate, reset или restart для соответствующего случая.
5. Проверить результат снаружиУбедиться, что нужные узлы отвечают новым релизом; отдельный bool из административного скрипта недостаточен.

Мониторинг должен отличать нормальный холодный старт от постоянных промахов и нехватки памяти. Счётчики oom_restarts/hash_restarts/manual_restarts, заполнение памяти и число ключей помогают выбрать, какой предел исследовать. Нельзя из одного процента hit rate заключить, что приложение быстро: медленный SQL, внешний HTTP и тяжёлые циклы остаются. Их разбирает раздел измерения производительности.

Примеры не включают JIT ради красивого числа ускорения и не сбрасывают кеш опубликованного сайта. Чтобы измерить эффект, сравнивают одинаковую нагрузку, прогретость, версии и ресурсы. Сначала устраняют причину задержки, затем выбирают соответствующий вид кеша.

Функции и методы этой темы

Откройте карточку, чтобы увидеть договор функции, примеры, ограничения и связанные инструменты.

opcache_compile_fileСкомпилировать файл для прогрева без исполнения его тела.

PHP 5.5.5+ · Zend OPcache

opcache_compile_file(string $filename): bool

Скомпилировать файл для прогрева без исполнения его тела.

OPcache сохраняет подготовленный к исполнению PHP-код. При следующем запросе движок может взять готовые инструкции, но сами действия скрипта выполняются снова: SQL, HTTP-запросы, циклы и вывод не превращаются в готовый HTML-кеш. Экземпляр кеша связан с конкретным окружением PHP; консольный вызов не является удалённым управлением всеми PHP-FPM серверами.

Часть вызоваКонтракт
filenameПуть к доверенному PHP-скрипту.
Результатtrue — компиляция успешна; false — неудача.
Побочные действияТело скрипта не исполняется; параметры кеша всё равно определяют возможность сохранения.

В обычном запросе файл компилируется перед выполнением. При прогреве можно заранее подготовить известные файлы приложения, не исполняя их основное тело. Это полезно после старта нового пула работников; прогревать следует доверенный список, а не путь из GET.

Подготовить файл и затем отдельно получить его результатСамостоятельный пример
<?php
declare(strict_types=1);

$path = __DIR__ . '/calculation.php';
// Только наш учебный файл: исключаем защиту от совсем нового timestamp.
touch($path, time() - 60);

var_dump(opcache_compile_file($path));
$answer = require $path;
echo $answer, "\n";
Ожидаемый результат
bool(true)
42

calculation.php содержит return 40+2. Вызов compile не возвращает 42: он сообщает успешность компиляции. Значение появляется только после require.

Увидеть границу между компиляцией и выполнениемСамостоятельный пример
<?php
declare(strict_types=1);

$path = __DIR__ . '/message.php';
touch($path, time() - 60);
opcache_compile_file($path);
echo "После компиляции\n";
$value = require $path;
echo $value, "\n";
Ожидаемый результат
После компиляции
Файл выполнен
42

message.php печатает строку только во время require. Эта граница важна для файлов с побочными действиями: прогрев не должен отправлять письма или создавать заказы.

Прогреть два известных файла и проверить памятьСамостоятельный пример
<?php
declare(strict_types=1);

$files = [__DIR__ . '/calculation.php', __DIR__ . '/message.php'];
foreach ($files as $path) {
    touch($path, time() - 60);
    if (!opcache_compile_file($path)) throw new RuntimeException('Ошибка прогрева');
    echo basename($path), ': ', opcache_is_script_cached($path) ? 'в памяти' : 'не в памяти', "\n";
}
Ожидаемый результат
calculation.php: в памяти
message.php: в памяти

Успешная компиляция и фактическая запись в память — разные наблюдения. Параметры ограничения размера, blacklist, свежести файла и памяти влияют на кеширование.

Ошибка загрузки или компиляции может сопровождаться E_WARNING; не подавляйте её глобально. Успех компиляции не гарантирует отсутствие runtime-ошибок будущего вызова и не заменяет тестирование. В режиме startup-preload эта функция участвует в постоянной загрузке объявлений; это отдельный жизненный цикл, разобранный в главе. Не компилируйте чужой PHP как способ «проверки безопасности»: это доверенный исходный код приложения.

Рядом по задаче: opcache_is_script_cached; opcache_is_script_cached_in_file_cache; opcache_invalidate.

opcache_is_script_cachedПроверить наличие скрипта именно в кеше памяти.

PHP 5.5.11+ · Zend OPcache

opcache_is_script_cached(string $filename): bool

Проверить наличие скрипта именно в кеше памяти.

OPcache сохраняет подготовленный к исполнению PHP-код. При следующем запросе движок может взять готовые инструкции, но сами действия скрипта выполняются снова: SQL, HTTP-запросы, циклы и вывод не превращаются в готовый HTML-кеш. Экземпляр кеша связан с конкретным окружением PHP; консольный вызов не является удалённым управлением всеми PHP-FPM серверами.

Часть вызоваКонтракт
filenameПуть к скрипту.
Результатtrue — есть в текущей памяти OPcache, иначе false.

Эта функция отвечает на конкретный вопрос: есть ли подготовленный скрипт в памяти текущего OPcache. Она не смотрит в HTTP/CDN-кеш и не подтверждает наличие записи в файловом OPcache. Полезна при проверке прогрева или объяснении, почему нужный файл постоянно компилируется.

Сопоставить состояние до и после прогреваСамостоятельный пример
<?php
declare(strict_types=1);

$path = __DIR__ . '/calculation.php';
// Только наш учебный файл: исключаем защиту от совсем нового timestamp.
touch($path, time() - 60);

var_dump(opcache_is_script_cached($path));
opcache_compile_file($path);
var_dump(opcache_is_script_cached($path));
Ожидаемый результат
bool(false)
bool(true)

Путь относится к отдельной учебной директории, поэтому раньше файла в этом кеше не было. В работающем сервисе исходное состояние может уже быть true.

Проверить только нужный приложению файлСамостоятельный пример
<?php
declare(strict_types=1);

$path = __DIR__ . '/calculation.php';
// Только наш учебный файл: исключаем защиту от совсем нового timestamp.
touch($path, time() - 60);

opcache_compile_file($path);
$ready = opcache_is_script_cached($path);
echo $ready ? "Расчёт подготовлен\n" : "Нужно проверить прогрев\n";
echo require $path, "\n";
Ожидаемый результат
Расчёт подготовлен
42

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

false бывает при выключенном кеше или отсутствии подходящей записи, а не только при отсутствии файла на диске. Не используйте эту функцию вместо is_file/is_readable и не делайте бизнес-логику зависимой от прогретости. Путь и разрешение include_path/cwd должны соответствовать реальному приложению. Ограничение opcache.restrict_api может запретить диагностический вызов.

Рядом по задаче: opcache_compile_file; opcache_is_script_cached_in_file_cache; opcache_get_status.

opcache_is_script_cached_in_file_cacheПроверить подготовленную запись скрипта в файловом кеше.

PHP 8.5+ · Zend OPcache

opcache_is_script_cached_in_file_cache(string $filename): bool

Проверить подготовленную запись скрипта в файловом кеше.

OPcache сохраняет подготовленный к исполнению PHP-код. При следующем запросе движок может взять готовые инструкции, но сами действия скрипта выполняются снова: SQL, HTTP-запросы, циклы и вывод не превращаются в готовый HTML-кеш. Экземпляр кеша связан с конкретным окружением PHP; консольный вызов не является удалённым управлением всеми PHP-FPM серверами.

Часть вызоваКонтракт
filenameПуть исходного PHP-скрипта.
Результатtrue — запись обнаружена в file cache, иначе false.
ВерсияДобавлена в PHP 8.5; проверка памяти вынесена в другую функцию.

У OPcache есть второй уровень — файлы с подготовленными инструкциями на диске. Они могут пережить завершение процесса, а кеш памяти — нет. Начиная с PHP 8.5 эту часть можно проверить отдельной функцией. Не путайте её с проверкой существования исходного .php файла.

Проверить два уровня отдельноСамостоятельный пример
<?php
declare(strict_types=1);

$path = __DIR__ . '/calculation.php';
// Только наш учебный файл: исключаем защиту от совсем нового timestamp.
touch($path, time() - 60);

opcache_compile_file($path);
echo opcache_is_script_cached($path) ? "В памяти\n" : "Не в памяти\n";
echo opcache_is_script_cached_in_file_cache($path) ? "На диске\n" : "Не на диске\n";
Ожидаемый результат
В памяти
На диске

В нашей отдельной лаборатории задана доступная директория opcache.file_cache. При стандартном пустом значении второй результат был бы отрицательным, даже если память прогрета.

Проверить удаление конкретной файловой записиСамостоятельный пример
<?php
declare(strict_types=1);

$path = __DIR__ . '/calculation.php';
// Только наш учебный файл: исключаем защиту от совсем нового timestamp.
touch($path, time() - 60);

opcache_compile_file($path);
var_dump(opcache_is_script_cached_in_file_cache($path));
opcache_invalidate($path, true);
var_dump(opcache_is_script_cached_in_file_cache($path));
Ожидаемый результат
bool(true)
bool(false)

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

Функция возвращает только bool, не путь внутреннего файла и не объём кеша. Имена внутренних каталогов и форматы файлов — детали реализации; не записывайте туда свой PHP-код вручную. Общий opcache_get_status описывает память, а не этот второй уровень. Для версий до 8.5 проверяйте function_exists либо используйте операционные средства наблюдения своей версии.

Рядом по задаче: opcache_is_script_cached; opcache_compile_file; opcache_invalidate.

opcache_invalidateИнвалидировать подготовленный код одного файла.

PHP 5.5+ · Zend OPcache

opcache_invalidate(string $filename, bool $force = false): bool

Инвалидировать подготовленный код одного файла.

OPcache сохраняет подготовленный к исполнению PHP-код. При следующем запросе движок может взять готовые инструкции, но сами действия скрипта выполняются снова: SQL, HTTP-запросы, циклы и вывод не превращаются в готовый HTML-кеш. Экземпляр кеша связан с конкретным окружением PHP; консольный вызов не является удалённым управлением всеми PHP-FPM серверами.

Часть вызоваКонтракт
filenameПуть изменяемого файла приложения.
forcefalse сверяет время изменения; true принудительно инвалидирует.
Результатtrue при выполненной операции или отсутствии необходимости; false при недоступном кеше.

При развёртывании изменился один шаблон. opcache_invalidate делает его кешированную версию непригодной для следующей загрузки, не сбрасывая автоматически всё приложение. Это не подмена уже исполняющегося кода внутри текущего запроса.

Отметить один учебный файл для повторной компиляцииСамостоятельный пример
<?php
declare(strict_types=1);

$path = __DIR__ . '/calculation.php';
// Только наш учебный файл: исключаем защиту от совсем нового timestamp.
touch($path, time() - 60);

opcache_compile_file($path);
var_dump(opcache_is_script_cached($path));
var_dump(opcache_invalidate($path, true));
var_dump(opcache_is_script_cached($path));
Ожидаемый результат
bool(true)
bool(true)
bool(false)

force=true действует независимо от сравнения времени изменения. Повторная загрузка этого файла будет готовить актуальный код. Другие файлы не перечислялись и не сбрасывались.

Не требовать сброса неизменённого файлаСамостоятельный пример
<?php
declare(strict_types=1);

$path = __DIR__ . '/calculation.php';
// Только наш учебный файл: исключаем защиту от совсем нового timestamp.
touch($path, time() - 60);

opcache_compile_file($path);
var_dump(opcache_invalidate($path, false));
var_dump(opcache_is_script_cached($path));
Ожидаемый результат
bool(true)
bool(true)

При force=false файл не новее кеша, поэтому остаётся пригодным. true не значит «обязательно что-то удалили»: он также означает, что менять было нечего.

В PHP 8.5.11 при настроенном file cache инвалидируется и его соответствующая запись. Возврат false возможен при выключенном/недоступном кеше; opcache.restrict_api тоже ограничивает вызов. Пользовательский URL не должен управлять путём сброса. Используйте перечень файлов своего релиза и исполняйте операцию в нужном SAPI/экземпляре; успешный CLI-вызов не доказывает обновление другого сервера.

Рядом по задаче: opcache_compile_file; opcache_reset; opcache_is_script_cached.

opcache_get_configurationПолучить настройки, версию и список исключений OPcache.

PHP 5.5+ · Zend OPcache

opcache_get_configuration(): array|false

Получить настройки, версию и список исключений OPcache.

OPcache сохраняет подготовленный к исполнению PHP-код. При следующем запросе движок может взять готовые инструкции, но сами действия скрипта выполняются снова: SQL, HTTP-запросы, циклы и вывод не превращаются в готовый HTML-кеш. Экземпляр кеша связан с конкретным окружением PHP; консольный вызов не является удалённым управлением всеми PHP-FPM серверами.

Часть вызоваКонтракт
АргументыНет.
РезультатАссоциативный массив directives/version/blacklist либо false.
Ограничениеrestrict_api может выдать warning и запретить доступ.

Конфигурация отвечает «как кеш настроен», статус — «что с ним происходит сейчас». opcache_get_configuration возвращает массив directives, version и blacklist; объём свободной памяти и счётчики запросов получают другой функцией.

Прочитать конкретные настройки без публичного дампаСамостоятельный пример
<?php
declare(strict_types=1);

$configuration = opcache_get_configuration();
if ($configuration === false) throw new RuntimeException('API недоступно');
$settings = $configuration['directives'];
echo $settings['opcache.enable'] ? "Кеш разрешён\n" : "Кеш отключён\n";
echo $settings['opcache.enable_cli'] ? "CLI разрешён\n" : "CLI отключён\n";
Ожидаемый результат
Кеш разрешён
CLI разрешён

Так настроен только учебный процесс. Серверный запрос может вернуть другую конфигурацию и даже другой набор параметров версии.

Сформировать понятный вывод о проверке обновленийСамостоятельный пример
<?php
declare(strict_types=1);

$configuration = opcache_get_configuration();
if ($configuration === false) throw new RuntimeException('Нет конфигурации');
$automatic = $configuration['directives']['opcache.validate_timestamps'];
echo $automatic ? "Изменения файлов проверяются\n" : "Обновление требует действий deploy\n";
var_dump(is_array($configuration['blacklist']));
Ожидаемый результат
Изменения файлов проверяются
bool(true)

Это диагностика стратегии обновления, не универсальный выбор production-настроек. Даже при automatic=true важны интервал и другие кеши приложения.

При запрете opcache.restrict_api будет E_WARNING и данные не вернутся. Не выкладывайте весь массив в общедоступный endpoint: там могут быть пути и конфигурация сервера. Список полей зависит от версии/платформы; обращайтесь к известным ключам, а необязательные проверяйте. ini_set не умеет менять все перечисленные настройки: у INI_SYSTEM нужен перезапуск нужного процесса после изменения конфигурации.

Рядом по задаче: opcache_get_status; opcache_is_script_cached.

opcache_get_statusПосмотреть состояние памяти, статистику и при необходимости скрипты.

PHP 5.5+ · Zend OPcache

opcache_get_status(bool $include_scripts = true): array|false

Посмотреть состояние памяти, статистику и при необходимости скрипты.

OPcache сохраняет подготовленный к исполнению PHP-код. При следующем запросе движок может взять готовые инструкции, но сами действия скрипта выполняются снова: SQL, HTTP-запросы, циклы и вывод не превращаются в готовый HTML-кеш. Экземпляр кеша связан с конкретным окружением PHP; консольный вызов не является удалённым управлением всеми PHP-FPM серверами.

Часть вызоваКонтракт
include_scriptstrue добавляет массив scripts; false ограничивается общими данными.
РезультатСтатус in-memory экземпляра или false. Файловый кеш здесь не перечисляется.
Ошибкиrestrict_api может вызвать E_WARNING и отказ.

Для внутреннего мониторинга нужно видеть заполнение памяти, попадания и промахи, ожидающий restart. opcache_get_status(false) не включает большой перечень всех файлов и поэтому подходит для общего обзора. Данные относятся к текущему экземпляру памяти, не ко всем серверам и не к file cache.

Прочитать общий статус без списка путейСамостоятельный пример
<?php
declare(strict_types=1);

$status = opcache_get_status(false);
if ($status === false) throw new RuntimeException('Нет статуса памяти');
echo $status['opcache_enabled'] ? "Работает\n" : "Отключён\n";
echo array_key_exists('scripts', $status) ? "Со списком файлов\n" : "Без списка файлов\n";
echo $status['memory_usage']['free_memory'] > 0 ? "Есть свободная память\n" : "Память закончилась\n";
Ожидаемый результат
Работает
Без списка файлов
Есть свободная память

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

Найти известный прогретый скрипт в подробном статусеСамостоятельный пример
<?php
declare(strict_types=1);

$path = __DIR__ . '/calculation.php';
// Только наш учебный файл: исключаем защиту от совсем нового timestamp.
touch($path, time() - 60);

opcache_compile_file($path);
$status = opcache_get_status(true);
if ($status === false) throw new RuntimeException('Нет статуса');
$script = $status['scripts'][realpath($path)] ?? null;
echo is_array($script) ? "Учебный файл найден\n" : "Записи нет\n";
echo isset($script['revalidate']) ? "Есть время следующей проверки\n" : "Нет поля revalidate\n";
Ожидаемый результат
Учебный файл найден
Есть время следующей проверки

С PHP 8.3 revalidate — Unix timestamp следующей проверки времени изменения по revalidate_freq. Он не является временем исполнения бизнес-логики.

memory_usage различает занятую, свободную и wasted память; последняя относится к неиспользуемым областям после изменений, а не обязательно к утечке переменных приложения. opcache_statistics показывает hits/misses, количество скриптов и причины перезапусков. Hit rate оценивайте после прогрева и за сопоставимый период: один процент без нагрузки и числа запросов мало объясняет. restart_pending/restart_in_progress показывают состояние перехода; cache_full помогает найти ограничения. jit описывает отдельный механизм, а не само наличие OPcache.

Рядом по задаче: opcache_get_configuration; opcache_is_script_cached; opcache_reset.

opcache_resetЗапросить сброс всего кеша памяти текущего экземпляра.

PHP 5.5+ · Zend OPcache

opcache_reset(): bool

Запросить сброс всего кеша памяти текущего экземпляра.

OPcache сохраняет подготовленный к исполнению PHP-код. При следующем запросе движок может взять готовые инструкции, но сами действия скрипта выполняются снова: SQL, HTTP-запросы, циклы и вывод не превращаются в готовый HTML-кеш. Экземпляр кеша связан с конкретным окружением PHP; консольный вызов не является удалённым управлением всеми PHP-FPM серверами.

Часть вызоваКонтракт
АргументыНет.
Результатtrue при принятом сбросе, false при недоступности операции.
ГраницаНе очищает файловый кеш и не заменяет restart процесса для preload.

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

Запросить сброс только изолированной лабораторииСамостоятельный пример
<?php
declare(strict_types=1);

if (PHP_SAPI !== 'cli' || !str_starts_with((string) ini_get('opcache.cache_id'), 'phpmanual-')) {
    throw new RuntimeException('Пример запускается только в собственной CLI-лаборатории');
}
$accepted = opcache_reset();
echo $accepted ? "Запрос сброса принят\n" : "Запрос не принят\n";
Ожидаемый результат
Запрос сброса принят

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

Убедиться, что reset не очищает второй уровеньСамостоятельный пример
<?php
declare(strict_types=1);

$path = __DIR__ . '/calculation.php';
// Только наш учебный файл: исключаем защиту от совсем нового timestamp.
touch($path, time() - 60);

if (PHP_SAPI !== 'cli' || !str_starts_with((string) ini_get('opcache.cache_id'), 'phpmanual-')) {
    throw new RuntimeException('Только своя CLI-лаборатория');
}
opcache_compile_file($path);
opcache_reset();
var_dump(opcache_is_script_cached_in_file_cache($path));
Ожидаемый результат
bool(true)

Reset затронул память, но не удалил записанный файл второго уровня. Для одного изменённого пути существует opcache_invalidate; preloaded-код имеет свой процессный жизненный цикл.

Документация описывает false при недоступном/выключенном кеше и при невозможности сброса в текущем состоянии. Реализация PHP 8.5.11 планирует restart; успешный return не следует превращать в доказательство моментально пустого scripts. Не делайте публичный reset.php без контролируемого административного доступа. CLI, PHP-FPM, Apache, отдельные Windows cache_id и разные узлы имеют разные области кеширования — план обновления должен обращаться к нужному окружению.

Рядом по задаче: opcache_invalidate; opcache_get_status; opcache_is_script_cached_in_file_cache.

opcache_jit_blacklistИсключить функцию из дальнейшей tracing-JIT компиляции.

PHP 8.4+ · Zend OPcache

opcache_jit_blacklist(Closure $closure): void

Исключить функцию из дальнейшей tracing-JIT компиляции.

OPcache сохраняет подготовленный к исполнению PHP-код. При следующем запросе движок может взять готовые инструкции, но сами действия скрипта выполняются снова: SQL, HTTP-запросы, циклы и вывод не превращаются в готовый HTML-кеш. Экземпляр кеша связан с конкретным окружением PHP; консольный вызов не является удалённым управлением всеми PHP-FPM серверами.

Часть вызоваКонтракт
closureКонкретная функция как first-class callable или анонимная Closure.
Результатvoid. Функция продолжает исполняться обычным способом.
ОграничениеНе отменяет ранее скомпилированные участки; PHP 8.4+.

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

Передать функцию как значение и всё равно вызвать еёСамостоятельный пример
<?php
declare(strict_types=1);

function addDelivery(int $items, int $delivery): int { return $items + $delivery; }
opcache_jit_blacklist(addDelivery(...));
echo addDelivery(1000, 200), "\n";
Ожидаемый результат
1200

addDelivery(...) без аргументов здесь — синтаксис first-class callable, создающий Closure. Сам вызов с числами находится на следующей строке. JIT в нашей лаборатории выключен: проверены доступность API и обычное исполнение, не машинная трасса.

Исключить конкретную анонимную функциюСамостоятельный пример
<?php
declare(strict_types=1);

$normalize = static fn(int $value): int => max(0, $value);
opcache_jit_blacklist($normalize);
echo $normalize(-4), "\n";
Ожидаемый результат
0

Анонимная функция уже является Closure. Исключается именно она; остальные вычисления программы не становятся частью этого запрета. Возвращаемого значения у blacklist нет.

API добавлен в PHP 8.4 и относится к tracing JIT. Уже скомпилированные участки он не отменяет, поэтому настройку применяют до первого выполнения нужной функции. Для строкового имени потребуется получить Closure, а не передать строку напрямую. Blacklist — инструмент локализации проблем компилятора, не универсальная оптимизация приложения. Чтобы утверждать эффект на трассы, нужен отдельный запуск с включённым поддерживаемым JIT и его диагностикой.

Рядом по задаче: opcache_get_status; opcache_get_configuration.

Источники и обсуждения

Код и руководство с собой

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

Код не исполняется на сайте. Скачайте нужный пример и запускайте его в своём локальном окружении.

Материалы сверены 2026-10-12. Примеры и объяснения написаны для этого руководства; источники и обсуждения доступны в каждой главе.