TANSOT

czyli dziennik wojen z oprogramowaniem. Więcej »

Xdebug

12 wrz 2021 511 słów

Ten wpis oryginalnie pojawił się tutaj w czerwcu 2020 r., a we wrześniu został rozszerzony i zaktualizowany dla nowej wersji Xdebuga.

Odrobina teorii

Integracja debuggera z IDE opiera się o model klient-serwer. PhpStorm nasłuchuje na porcie TCP (domyślnie 9000 lub 9003), a debugger inicjuje połączenie i wysyła do IDE komunikaty zawierające obecny stan wykonania skryptu.

Schemat. Proces PHP łączy się po porcie 9003 z PhpStormem

Let’s get this party started

Pierwszy krok to zainstalowanie i skonfigurowanie rozszerzenia Xdebug. Pod Ubuntu:

$ sudo apt install php-xdebug

Konfiguracja globalna w pliku php.ini

W pliku php.ini powinno znaleźć się coś mniej więcej takiego:

; Xdebug 2
[xdebug]
zend_extension="xdebug.so"
xdebug.remote_enable=1
xdebug.remote_autostart=1
xdebug.remote_host=127.0.0.1
xdebug.remote_port=9000
; Xdebug 3
[xdebug]
zend_extension="xdebug.so"
xdebug.mode=debug
xdebug.client_host=127.0.0.1
xdebug.client_port=9003
xdebug.start_with_request=yes

Aby sprawdzić poprawność instalacji, można wykonać polecenie:

$ php --version

Powinna pojawić się linijka

with Xdebug v3.0.4, Copyright (c) 2002-2021, by Derick Rethans

(oczywiście numer wersji może się różnić).

Tymczasowo, przy każdym uruchomieniu PHP

Zamiast umieszczać powyższą konfigugrację w php.ini, można podać wspomniane flagi jako argumenty do polecenia php, np. tak:

# Xdebug 2
$ php -dzend_extension=xdebug.so -dxdebug.remote_enable=1 -dxdebug.remote_autostart=1 -dxdebug.remote_host=127.0.0.1 -dxdebug.remote_port=9000 -S 127.0.0.1:8000 -t public/
# Xdebug 3
$ php -dzend_extension=xdebug.so -dxdebug.mode=debug -dxdebug.client_host=127.0.0.1 -dxdebug.client_port=9003 -dxdebug.start_with_request=yes -S 127.0.0.1:8000 -t public/
Uwaga

Ten sposób nie zadziała z php artisan serve! W przypadku Laravela serwer developerski można odpalić np. tak:

# Xdebug 2
$ php -dzend_extension=xdebug.so -dxdebug.remote_enable=1 -dxdebug.remote_autostart=1 -dxdebug.remote_host=127.0.0.1 -dxdebug.remote_port=9000 -S 127.0.0.1:8000 server.php
# Xdebug 3
$ php -dzend_extension=xdebug.so -dxdebug.mode=debug -dxdebug.client_host=127.0.0.1 -dxdebug.client_port=9003 -dxdebug.start_with_request=yes -S 127.0.0.1:8000 server.php

Teraz kluczową sprawą jest włączenie nasłuchiwania połączeń od debuggera. W tym celu z menu Run wybieramy „Start listening for PHP Debug Connections”.

Docker?

W przypadku Dockera proces będzie wyglądał różnie w zależności od konfiguracji kontenerów. To, co napiszę dalej, zakłada następujący i zdaje się najpopularniejszy setup:

  • kontener z PHP jest oparty o oficjalne obrazy,
  • kontener PHP ma własny Dockerfile,
  • „frontowym” serwerem HTTP jest nginx i działa w osobnym kontenerze,
  • do połączenia kontenerów użyty jest docker-compose.

Oczywiście w innym przypadku konfiguracja Xdebuga jest możliwa, ale wtedy potrzebne będzie dostosowanie konfiguracji.

W pliku docker-compose.yml, dla kontenera z PHP dodajemy następujące zmienne środowiskowe:

environment:
  XDEBUG_CONFIG: idekey=PHPSTORM
  PHP_IDE_CONFIG: serverName=moj-serwer

W pliku Dockerfile dla kontenera PHP dodajemy:

RUN pecl install xdebug \
    && docker-php-ext-enable xdebug \
    && echo "xdebug.mode=debug" >> /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini \
    && echo "xdebug.discover_client_host=1" >> /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini \
    && echo "xdebug.start_with_request=yes" >> /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini \

Przebudowujemy kontener z PHP:

$ docker-compose build moj-kontener-php

Teraz czas na konfigurację w PhpStormie. Najpierw dodajemy interpreter, jeśli jeszcze tego nie zrobiliśmy w „Setting → PHP”:

Konfiguracja interpretera

Po dodaniu sugeruję w opcjach zaznaczyć „Connect to existing container (‘docker-compose exec’)”, będzie kapkę szybciej. Potem przechodzimy do „Settings → PHP → Servers”, dodajemy serwer ikonką „+” i ustawiamy:

Konfiguracja serwera

Ostatnia rzecz, to włączenie w konfiguracji nginx-a nagłówka X-Forwarded-For:

server {
    # ...
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    # ...
}

i restart lub przebudowanie kontenera.

Frequently Asked Questions

Czy to spowalnia program?

Spowalnia, ale jest to zauważalne głównie dla programów, które są CPU-bound (tj. spędzają znaczną część czasu na wykonywaniu kodu). Jeśli program spędza większość czasu na czekaniu na wyniki z sieci (np. odpowiedź bazy danych), to nie będzie to aż tak zauważalne.

Co to jeszcze potrafi?

Oprócz debugowania, Xdebug może zostać użyty do profilowania aplikacji i zbierania informacji o pokryciu kodu.