Még mindig sok fejlesztőt látok PHP-s körökben debugger nélkül fejleszteni. Minden tiszteletem azoké, akik képesek hatékonyan dolgozni print_r()
és var_dump()
függvényekkel. Tudom, hogy sokaknak nehézséget okoz beállítani az Xdebug-ot pláne Docker segítségével konténerizált fejlesztői környezetben. Ebben a bejegyzésben szeretnék segíteni és megmutatni azt, mennyire egyszerű is egy ilyen beállítás.
Mire lesz szükségünk
Első körben szükség lesz egy olyan szerkesztő programra, amivel lehet PHP-t debuggolni. Erre számtalan megoldás létezik én most a VSCode programmal fogom bemutatni, amihez szükség lesz a Felix Becker által készített PHP Debug (felixfbecker.php-debug) kiegészítőre is.
A hiba, amibe mindenki belefut
Mielőtt nekilátunk fontos megértsünk egy dolgot, amit általában mindenki elhibáz az elején. Amikor debuggolsz a szerkesztőprogramod lesz a szerver, a webszerver pedig a kliens.
Mivan, mivan, mivan?
Igen, jól hallottad. A szerkesztő program fogja megnyitni a fejlesztő gépén az alapértelmezetten 9000-es portot és ott hallgatózik a bejövő kérésekre, majd a PHP futtató, azon belül az Xdebug próbál meg majd rácsatlakozni. Ez akkor, amikor XAMPP/MAMP egyikét használod nem probléma, mert az IDE és a webszerver is ugyanazon a gépen fut. Amint a kód egy távoli gépen vagy egy Docker konténerben fut, problémát fog jelenteni.
Tehát az egy dolog, hogy a fejlesztő eléri a távoli gépet, arra is szükség lesz, hogy a webszerver is el tudja érni a fejlesztő gépét. Ez viszont már korántsem olyan magától értetődő. Ezt majd tesztelni is fogjuk.
Minta alkalmazás
Készítettem egy pici minta alkalmazást, amivel csak azt fogod megtanulni, hogy hogyan lehet működésre bírni az Xdebug-ot. Ha ez már zökkenőmentesen megy, akkor érdemes egy már létező alkalmazás tesztelésébe kezdened.
Dockerfile
A hivatalos PHP image-et, annak is az apache-os verzióját használjuk. Ez az image 4 stage-ből áll. Az első a base, ami az alkalmazás futtatásához szükséges csomagokat tartalmazza. Ezután jön az Xdebug-base, ami az Xdebug kiegészítőt. Végezetül jön az Xdebug és a név nélküli alap stage, ami az alkalmazás kódját tartalmazza. Röviden a lényege ennek a felépítésnek, hogy a ritkábban változó nagyobb dolgokat igyekszünk előre rakni a Dockerfile-ban, hogy azokat gyorstárból tudja venni a builder. Ekkor az alkalmazásunk kódjának módosítása esetén csak az utolsó stage-et és azon belül is azt az egy fájlt kell csak beletennie a buildernek az image-be.
Jelen bejegyzés szempontjából csak az Xdebug-base stage az érdekes. Ennek első sorában telepítjük az ncat programot, mivel szükségünk lesz arra, hogy teszteljük, hogy a PHP és benne az Xdebug eléri-e az IDE-nket. Utána feltelepítjük az Xdebug aktuális verzióját, engedélyezzük azt, majd a szükséges beállításokat tartalmazó xdebug.ini
fájlt bemásoljuk a megfelelő helyre. A conf.d
könyvtár az, ahonnan a php értelmező felolvassa az összes ini fájlt amit talál.
FROM php:apache as base
#Install needed package
#RUN apt-get update && apt-get install -y ...
#RUN docker-php-ext-install ...
#RUN pecl install ...
#RUN composer install
#...
FROM base as xdebug-base
RUN apt-get update && apt-get install -y ncat
RUN pecl install xdebug \
&& docker-php-ext-enable xdebug
COPY xdebug.ini /usr/local/etc/php/conf.d
FROM xdebug-base as xdebug
COPY index.php /var/www/html/index.php
FROM base
COPY index.php /var/www/html/index.php
Xdebug.ini
Ez az a pont, ahol könnyű hibázni. Ugyanis számos felhasználó esetet támogat az Xdebug. Én most egy fapados, Docker konténer számára kidolgozott megoldást mutatok, ahol az Xdebug mindig elindul és mindig megpróbál felcsatlakozni a szerkesztőre. Ne aggódj, ha az Xdebug kiegészítő be van töltve a script futása lassabb lesz akár engedélyezve van, akár nem. Az állandó engedélyezés maga nem jelent már jelentős lassulást. Ha nem akarod ezt a lassulást, akkor egyszerűen ne kapcsold be ezt a kiegészítőt és használd az alapértelmezett stage-et. Fontos, hogy a remote_connect_back
beállítás false
értéken legyen, mert az Xdebug csak ebben az esetben veszi figyelembe a remote_host
beállítást. A remote_connect_back
, bár alapértelmezetten ki van kapcsolva, az egyértelműség kedvéért bekerült a beállítások közé.
xdebug.remote_enable = on
xdebug.remote_autostart = on
xdebug.remote_connect_back = false
xdebug.remote_host = "host.docker.internal"
Végezetül a remote_host
beállítás értékéről kell ejteni pár szót. A fenti megoldás ugyanis csak Windows és OSX rendszereken működik Linuxon nem. A Docker Desktop for Windows/OSX ugyanis sok-sok kényelmi funkción túl megoldja nekünk azt, hogy a host.docker.internal
domain név a tényleges host ip címére legyen feloldva.
Ennek megoldásához Linuxon meg kell néznned, hogy a docker0 interface-nek mi az ip címe és azt kell beállítanod. Ezt az ifconfig parancs kimenetéből tudod kideríteni, de nagy valószínűséggel 172.17.0.1.
Ezután nem kell mást tenned Linuxon, mint a konténernél beállítani a következő környezeti változót (environment): XDEBUG_CONFIG="remote_host=172.17.0.1"
lásd még következő fejezet
Composer fájlok
A docker-compose.yml
fájl viszonylag egyszerű. Olyan sok mindent nincs mit magyarázni rajta.
version: "2.4"
services:
web:
build: .
Az override fájl már érdekesebb. Ez a fájl nincs is a repoban, csak egy .dev kiterjesztésű, mivel ezt a fájlt módosíthatjuk az igényeinknek megfelelően. Ez a fájl lesz az ami minden környezetben más lesz. Más lehet az egyes fejlesztőknél és más a CI/CD-nél is.
version: "2.4"
services:
web:
ports:
- 80:80
# Settings for Xdebug
build:
target: xdebug
# Settings for Linux
# environment:
# XDEBUG_CONFIG: "remote_host=172.17.0.1"
Ha Linuxon vagy, akkor az environment két sorának elejéről kell kiszedned a megjegyzésre szolgáló #
jelet. Ha nem szeretnéd, hogy az Xdebug lassítsa a rendszerünket akkor pedig a build két sorát kell kikommentezni. Akkor pedig, ha nem a 80-as portra szeretnénd kitenni az alkalmazásodat, akkor értelemszerűen a ports résznél tudod módosítani azt. Ne felejtsd el módosítás előtt leállítani és újra elindítani a rendszered.
VSCode launch.json
A mintaalkalmazás repojában van még egy fájl a rejtett .vscode
könyvtárban. Ahhoz, hogy ez a fájl ténylegesen működjünk szükséged lesz a korábban említett Xdebug kiegészítőre.
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Listen for Xdebug",
"type": "php",
"request": "launch",
"port": 9000,
"pathMappings": {
"/var/www/html": "${workspaceRoot}"
},
"stopOnEntry": true,
"log": true
}
]
}
Ha mindent jól csináltál, akkor a Debbuger tab-on lesz egy “Listen for Xdebug” task, amit elindítva megjelenik a debugger bar. Ezek értelmezéséhez lásd az ábrát alant:
Nézzük akkor az opciókat. A name
részt tetszőlegesen átírhatod, lehet mondjuk röviden Xdebug. A type
és request
értékeket ne módosítsd. A port
értelemszerűen az a port ahol hallgatózni fog az IDE. A pathMappings
beállítás fontos, hisz más útvonalon helyezkednek el a fájlok a konténeren belül, mint kívül. Erre figyelj majd, ha már létező projektbe próbálod meg bevezetni az Xdebug-ot. Itt természetesen több útvonalat is megadhatsz, ha esetleg a konténerben nem ugyanolyan hierarchiába kerülnek be a fájlok.
A stopOnEntry
beállítást true
-ra állítottam be, mivel most az alap működést teszteljük és biztos akartam benne lenni, hogy megáll a debugger. Ha a pathMapping
értéket elrontotod, akkor hiába helyezel el töréspontokat (breakpoints) nem fog megállni a program futása. Ezért azt javaslom, hogy első alkalommal mindig legyen bekapcsolva ez a beállítás, amit értelemszerűen a későbbiekben kikapcsolhatsz. Legyen ez az első amit bekapcsolsz, amikor arra gyanakszol, hogy nem működik az Xdebug.
Itt kell még megemlíteni, hogy az Xdebug alapbeállításban minden nem várt eseménynél (Notices, Warnings, Errors, Exceptions) megáll. Ezt a debug Tab alján található Breakpoints résznél tudod kikapcsolni. A képen látható Everything opció elől kell kivenni a pipát.
Műxik?
Amennyiben minden jól megy, akkor a böngészőbe behozva az alkalmazást a debugger az index.php
első sorában meg fog állni. Ugyanígy megáll majd, ha az index.php
-t parancssorból futtatod:
docker-compose exec web php index.php
Ha nem megy, két helyen lehet probléma. Az egyik az, hogy az Xdebug nem éri el az IDE-t. Nézd meg, hogy fut-e a “Listen for Xdebug” task. Vizsgáld meg a “DEBUG CONSOLE” ablakát, hogy van-e hibaüzenet. Ezután a fejlesztői gépeden nézd meg, hogy a 9000-s port elérhető-e. Ehhez persze kell az ncat, vagy azzal egyenértékű program a fejlesztői gépen is:
nc -vz localhost 9000
Ennek hatására a “Listen for Xdebug” task össze fog omlani, le fog állni. Csak nyugodtan, indítsd újra. Most nézzük meg, hogy a konténeren belülről elérhető-e az IDE:
docker-compose exec web nc -vz docker.host.internal 9000
Figyelj oda, hogy a docker.host.internal
csak OSX-en és Windows-on működik Linuxon ki kell derítened, hogy mi az. Ha a fentiek stimmelnek és nem kapcsolódik az Xdebug, akkor nézd meg, hogy jól van-e beállítva a php:
docker-compose exec web php -i | grep remote_host
Ha a fentiek közül minden jó és mégsincs kapcsolat, akkor keress meg, hogy bővíthessem ezt a leírást!
Ha megy a kapcsolódás és a debugger, de nem látszik melyik fájlban van a végrehajtás, akkor a pathMappings beállítást kell módosítanod és újraindítani a “Listen for Xdebug” taskot.
Remélhetőleg ezek után már könnyebben debuggolsz és hatékonyabban keresed a hibát a jövőben… vagyis inkább a programodban.
Ha érdekel, hogy hogyan építs fel és vezess be Docker konténerekre alapuló fejlesztőkörnyezetet, akkor iratkozz be a Konténerizált Fejlesztői Környezet kialakítása című képzésemre.