Дослідження внутрішніх механізмів Symfony
Ми вже досить довго використовуємо Symfony для розробки потужного застосунку, але більша частина коду, що виконується цим застосунком, походить від Symfony. Кілька сотень рядків коду в порівнянні з тисячами рядків коду.
Мені подобається розуміти, як все працює за лаштунками. І мене завжди захоплювали інструменти, які допомагають мені зрозуміти, як все працює. Перший раз, коли я використовував покроковий налагоджувач або вперше дослідив ptrace — це магічні спогади.
Хочете краще зрозуміти, як працює Symfony? Настав час розібратися в тому, як Symfony змушує ваш застосунок працювати. Замість того щоб описувати, як Symfony обробляє HTTP-запит з теоретичної точки зору, що було б досить нудно, ми збираємося використовувати Blackfire, щоб отримати певні візуальні уявлення і використовувати це для дослідження певних, більш просунутих, тем.
Опанування внутрішніх механізмів Symfony за допомогою Blackfire
Ви вже знаєте, що всі HTTP-запити обслуговуються однією точкою входу: файлом public/index.php. Але що відбувається далі? Як викликаються контролери?
Відпрофілюймо головну сторінку англійською мовою в продакшн використовуючи Blackfire, за допомогою розширення браузера Blackfire:
1
$ symfony remote:openАбо безпосередньо через командний рядок:
1
$ blackfire curl `symfony cloud:env:url --pipe --primary`en/Перейдіть у режим перегляду "Timeline" результатів профілювання, ви маєте побачити щось схоже на наступне:
 
    Наведіть курсор на кольорові смужки на часовій шкалі, щоб отримати додаткову інформацію про кожен виклик; ви дізнаєтеся багато нового про те, як працює Symfony:
- Основною точкою входу є public/index.php;
- Метод Kernel::handle()обробляє запит;
- Він викликає HttpKernel, який оголошує кілька подій;
- Першою подією є RequestEvent;
- Викликається метод ControllerResolver::getController(), щоб визначити, який контролер слід викликати для вхідної URL-адреси;
- Викликається метод ControllerResolver::getArguments(), щоб визначити, які аргументи передати контролеру (викликається перетворювач параметрів);
- Викликається метод ConferenceController::index(), і більша частина нашого коду виконується цим викликом;
- Метод ConferenceRepository::findAll()отримує всі конференції з бази даних (зверніть увагу на підключення до бази даних за допомогоюPDO::__construct());
- Метод Twig\Environment::render()відмальовує шаблон;
- Оголошуються ResponseEventіFinishRequestEvent, але це виглядає так, неначе насправді жодних слухачів не зареєстровано, оскільки вони, здається, дуже швидко виконуються.
Часова шкала — це чудовий спосіб зрозуміти, як працює певний код; що дуже корисно, коли ви отримуєте проект, що розроблений кимось іншим.
Тепер відпрофілюйте ту саму сторінку з локального комп'ютера в середовищі розробки:
1
$ blackfire curl `symfony var:export SYMFONY_PROJECT_DEFAULT_ROUTE_URL`en/Відкрийте результати профілювання. Ви маєте бути перенаправлені в режим перегляду графа викликів, оскільки запит був достатньо швидким і часова шкала була б абсолютно порожньою:
 
    Ви розумієте, що відбувається? HTTP-кеш увімкнено, і тому ми профілюємо шар HTTP-кешу Symfony. Оскільки сторінка знаходиться в кеші, HttpCache\Store::RestoResponse() отримує HTTP-відповідь з кешу, а контролер ніколи не викликається.
Вимкніть шар кешу в public/index.php, як ми робили це на попередньому кроці, і повторіть спробу. Ви можете відразу побачити, що результат профілювання виглядає зовсім по-іншому:
 
    Основні відмінності полягають у наступному:
- TerminateEvent, якої не було видно в продакшн, займає великий відсоток часу виконання; придивившись ближче, ви можете побачити, що ця подія відповідає за зберігання даних профілювальника Symfony, зібраних під час запиту;
- Під викликом ConferenceController::index()зверніть увагу на методSubRequestHandler::handle(), який відмальовує ESI (ось чому ми маємо два викликиProfiler::saveProfile()— один для основного запиту, і ще один для ESI).
Ознайомтеся з часовою шкалою, щоб дізнатися більше; перемкніться в режим перегляду графа викликів, щоб мати інше представлення тих самих даних.
Як ми тільки що з’ясували, код, що виконується в середовищі розробки й у продакшн — дуже відрізняється. Середовище розробки працює повільніше, оскільки профілювальник Symfony намагається зібрати багато даних, щоб полегшити складнощі налагодження. Ось чому вам завжди слід профілювати тільки у продакшн середовищі, навіть локально.
Кілька цікавих експериментів: відпрофілюйте сторінку помилки, відпрофілюйте сторінку / (яка є перенаправленням) або ресурс API. Кожен результат профілювання розповість вам трохи більше про те, як працює Symfony, які класи/методи викликаються, що виконується повільно, а що — швидко.
Використання налагоджувального додатка Blackfire
За замовчуванням Blackfire видаляє всі виклики методів, які не є достатньо значущими, щоб уникнути значних навантажень і об'ємних графіків. При використанні Blackfire, у ролі інструменту налагодження, краще зберігати всі виклики. Це забезпечується налагоджувальним додатком.
У командному рядку використовуйте прапорець --debug:
1 2
$ blackfire --debug curl `symfony var:export SYMFONY_PROJECT_DEFAULT_ROUTE_URL`en/
$ blackfire --debug curl `symfony cloud:env:url --pipe --primary`en/У продакшн ви побачите, наприклад, завантаження файлу з назвою .env.local.php:
 
    Звідки він береться? Platform.sh виконує деякі оптимізації під час розгортання застосунку Symfony, як-от оптимізацію автозавантажувача Composer (--optimize-autoloader --apcu-autoloader --classmap-authoritative). Він також оптимізує змінні середовища, що визначені у файлі .env (щоб уникнути розбору файлу для кожного запиту), генеруючи файл .env.local.php:
1
$ symfony run composer dump-env prodBlackfire — це дуже потужний інструмент, який допомагає зрозуміти, як виконується код на PHP. Поліпшення продуктивності є тільки одним зі способів використання профілювальника.
Використання покрокового налагоджувача за допомогою Xdebug
Часові шкали і графи викликів Blackfire дозволяють розробникам візуалізувати, які файли/функції/методи виконуються механізмом PHP, щоб краще зрозуміти кодову базу проекту.
Іншим способом стежити за виконанням коду є використання покрокового налагоджувача, як-от Xdebug. Покроковий налагоджувач дозволяє розробникам інтерактивно проходити по коду проекту PHP для налагодження потоку управління і вивчення структур даних. Це дуже корисно для налагодження неочікуваної поведінки, і замінює поширений метод налагодження "var_dump()/exit()".
По-перше, встановіть розширення PHP xdebug. Переконайтеся, що воно встановлене, виконавши наступну команду:
1
$ symfony php -vВи маєте побачити Xdebug виводі:
1 2 3 4 5 6
PHP 8.0.1 (cli) (built: Jan 13 2021 08:22:35) ( NTS )
Copyright (c) The PHP Group
Zend Engine v4.0.1, Copyright (c) Zend Technologies
    with Zend OPcache v8.0.1, Copyright (c), by Zend Technologies
    with Xdebug v3.0.2, Copyright (c) 2002-2021, by Derick Rethans
    with blackfire v1.49.0~linux-x64-non_zts80, https://blackfire.io, by BlackfireВи також можете перевірити чи увімкнено Xdebug для PHP-FPM, зайшовши в браузер і натиснувши на посилання "View phpinfo()" наводячи курсор на логотип Symfony на панелі інструментів веб-налагодження:
 
    Тепер увімкніть режим Xdebug debug:
1 2 3
[xdebug]
xdebug.mode=debug
xdebug.start_with_request=yesЗа замовчуванням Xdebug відправляє дані на порт 9003 локального хоста.
Ініціювання Xdebug може бути здійснене різними способами, але найпростішим є використання Xdebug із вашої IDE.  У цьому розділі ми будемо використовувати Visual Studio Code, щоб продемонструвати, як це працює. Встановіть розширення PHP Debug, запустивши функцію "Quick Open" (Ctrl+P), вставте наступну команду й натисніть клавішу Enter:
1
ext install felixfbecker.php-debugСтворіть наступний файл конфігурації:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
{
    "version": "0.2.0",
    "configurations": [
        {
            "name": "Listen for XDebug",
            "type": "php",
            "request": "launch",
            "port": 9003
        },
        {
            "name": "Launch currently open script",
            "type": "php",
            "request": "launch",
            "program": "${file}",
            "cwd": "${fileDirname}",
            "port": 9003
        }
    ]
}З Visual Studio Code, перебуваючи в каталозі вашого проекту, перейдіть до налагоджувача й натисніть зелену кнопку відтворення з написом "Listen for Xdebug":
 
    Якщо ви перейдете у браузер і оновите сторінку, IDE має автоматично взяти фокус на себе, тобто сеанс налагодження готовий. За замовчуванням все є точкою зупинки, тому виконання зупиняється на першій інструкції. Потім ви маєте перевірити поточні змінні, перейти через код, увійти в код, ...
Під час налагодження ви можете зняти прапорець "Everything" і явно встановити точки зупинки у своєму коді.
Якщо ви новачок у покроковому налагодженні, прочитайте навчальний посібник із Visual Studio Code, де все пояснюється візуально.