741 lines
22 KiB
Markdown
741 lines
22 KiB
Markdown
# Xdebug – Installation, Konfiguration, Nutzung
|
||
|
||
Stand: Xdebug 3.x (Modus-Konfiguration ab Xdebug 3, Port **9003**)
|
||
|
||
---
|
||
|
||
## Die wichtigste Antwort vorweg
|
||
|
||
**Nein – man trägt nichts in die einzelnen PHP-Dateien ein.**
|
||
|
||
Xdebug wird **einmal** in der `php.ini` konfiguriert. Danach ist es für *jedes* PHP-Skript
|
||
aktiv, das über diesen Webserver bzw. diese CLI läuft. Kein `require`, kein `include`,
|
||
keine Zeile im Quellcode.
|
||
|
||
```
|
||
php.ini ändern → Apache neu starten → gilt ab sofort überall
|
||
```
|
||
|
||
Das `ini_set(...)` aus dem Gesprächsverlauf war ausdrücklich der **Notfallweg** für
|
||
Systeme, auf denen man keine INI-Datei anfassen darf. Wer Zugriff auf die `php.ini` hat,
|
||
braucht es nicht.
|
||
|
||
Im Code gibt es nur zwei *optionale* Dinge, die man punktuell einsetzen kann:
|
||
|
||
```php
|
||
xdebug_break(); // setzt einen Breakpoint ohne IDE-Klick
|
||
xdebug_start_trace('/tmp/xy'); // startet ein Aufrufprotokoll ab hier
|
||
```
|
||
|
||
Beides ist Zusatzkomfort, keine Voraussetzung.
|
||
|
||
---
|
||
|
||
## 0. Versionsmatrix
|
||
|
||
| PHP | Xdebug | Konfigurationsstil |
|
||
|---|---|---|
|
||
| 7.0 – 7.1 | 2.9.x | `xdebug.remote_*`, Port 9000 |
|
||
| 7.2 – 7.4 | 3.1.x | `xdebug.mode`, Port 9003 |
|
||
| 8.0 – 8.2 | 3.2.x | `xdebug.mode`, Port 9003 |
|
||
| 8.3+ | 3.3+ / 3.5+ | `xdebug.mode`, Port 9003 |
|
||
|
||
> Xdebug 3.5 setzt mindestens PHP 8.3 voraus. Bei älterem PHP die Version explizit pinnen:
|
||
> `pecl install xdebug-3.2.2`
|
||
|
||
Verzeichnisnamen wie `no-debug-non-zts-20220829` kodieren die PHP-API-Version:
|
||
`20220829` = PHP 8.2 · `20230831` = PHP 8.3 · `20240924` = PHP 8.4
|
||
|
||
---
|
||
|
||
## 1. Installation unter XAMPP (macOS)
|
||
|
||
Xdebug wird für macOS **nicht** vorkompiliert ausgeliefert – es muss gebaut werden.
|
||
|
||
### 1.1 Vorbereitung
|
||
|
||
```bash
|
||
xcode-select --install # Compiler / Command Line Tools
|
||
brew install autoconf # phpize benötigt autoconf
|
||
```
|
||
|
||
Auf Apple Silicon zusätzlich:
|
||
|
||
```bash
|
||
softwareupdate --install-rosetta --agree-to-license
|
||
```
|
||
|
||
### 1.2 Architektur-Falle (Apple Silicon)
|
||
|
||
XAMPP für macOS ist **x86_64** und läuft unter Rosetta 2. Ein nativer Build auf einem
|
||
M-Mac erzeugt eine arm64-`.so`, die PHP nicht laden kann:
|
||
|
||
```
|
||
dlopen(...) (mach-o file, but is an incompatible architecture
|
||
(have 'arm64', need 'x86_64'))
|
||
```
|
||
|
||
`arch -x86_64` **vor** `pecl` reicht nicht – `pecl` ist ein PHP-Skript, dessen
|
||
Kindprozesse (`phpize`, `configure`, `clang`) die Rosetta-Umgebung nicht zuverlässig
|
||
erben. Deshalb eine komplette x86_64-Shell öffnen:
|
||
|
||
```bash
|
||
arch -x86_64 /bin/zsh
|
||
arch # muss "i386" ausgeben
|
||
```
|
||
|
||
### 1.3 Build via pecl
|
||
|
||
```bash
|
||
# Alte Reste entfernen, sonst wird der falsche Build wiederverwendet
|
||
sudo rm -f /Applications/XAMPP/xamppfiles/lib/php/extensions/no-debug-non-zts-*/xdebug.so
|
||
rm -rf /tmp/pear
|
||
|
||
# PEAR-Registry abmelden (sonst: "already installed ... install failed")
|
||
sudo /Applications/XAMPP/xamppfiles/bin/pecl uninstall xdebug
|
||
|
||
# Build (in der Rosetta-Shell!)
|
||
sudo CFLAGS="-arch x86_64" LDFLAGS="-arch x86_64" CXXFLAGS="-arch x86_64" \
|
||
/Applications/XAMPP/xamppfiles/bin/pecl install xdebug-3.2.2
|
||
```
|
||
|
||
`pecl` gibt am Ende die Zeile `Installing '/Applications/XAMPP/.../xdebug.so'` aus –
|
||
**das** ist der Pfad für die `php.ini`.
|
||
|
||
### 1.4 Ergebnis prüfen
|
||
|
||
```bash
|
||
file /Applications/XAMPP/xamppfiles/lib/php/extensions/no-debug-non-zts-20220829/xdebug.so
|
||
# muss "x86_64" enthalten
|
||
```
|
||
|
||
### 1.5 Manueller Build (falls pecl scheitert)
|
||
|
||
Gibt mehr Kontrolle, weil jeder Schritt einzeln sichtbar ist:
|
||
|
||
```bash
|
||
cd /tmp && curl -O https://xdebug.org/files/xdebug-3.2.2.tgz
|
||
tar -xzf xdebug-3.2.2.tgz && cd xdebug-3.2.2
|
||
|
||
/Applications/XAMPP/xamppfiles/bin/phpize
|
||
./configure --with-php-config=/Applications/XAMPP/xamppfiles/bin/php-config \
|
||
CFLAGS="-arch x86_64" LDFLAGS="-arch x86_64"
|
||
make
|
||
file modules/xdebug.so # VOR dem Kopieren prüfen
|
||
sudo cp modules/xdebug.so \
|
||
/Applications/XAMPP/xamppfiles/lib/php/extensions/no-debug-non-zts-20220829/
|
||
```
|
||
|
||
### 1.6 Alternative ohne Compile-Schmerz
|
||
|
||
Wer nicht an XAMPP gebunden ist:
|
||
|
||
```bash
|
||
brew install php # natives arm64-PHP
|
||
pecl install xdebug # läuft ohne Rosetta durch
|
||
php -S localhost:8000 # eingebauter Server statt Apache
|
||
```
|
||
|
||
---
|
||
|
||
## 2. Installation unter XAMPP (Windows)
|
||
|
||
Deutlich einfacher – es gibt fertige DLLs.
|
||
|
||
### 2.1 Passende DLL ermitteln
|
||
|
||
XAMPP kompiliert PHP als **Thread Safe** (Apache mit mod_php), also wird eine TS-DLL
|
||
benötigt. Statt zu raten, den Xdebug-Wizard nutzen:
|
||
|
||
**https://xdebug.org/wizard**
|
||
|
||
1. `phpinfo()`-Ausgabe erzeugen – im Browser (`http://localhost/info.php`) oder per
|
||
`C:\xampp\php\php.exe -i > info.txt`
|
||
2. **Kompletten** Output kopieren (Strg+A / Strg+C), nicht nur die sichtbare Tabelle
|
||
3. Ins Textfeld einfügen → *Analyse my phpinfo() output*
|
||
4. Der Wizard nennt exakt die passende Datei, z. B.
|
||
`php_xdebug-3.4.x-8.2-vs16-x86_64.dll`
|
||
|
||
> Alternativ manuell unter https://xdebug.org/download – dann Thread Safety und
|
||
> VS/VC-Compilerversion selbst aus `phpinfo()` ablesen.
|
||
|
||
Nach dem Test: **`info.php` wieder löschen.**
|
||
|
||
### 2.2 Installieren
|
||
|
||
DLL kopieren nach `C:\xampp\php\ext\php_xdebug.dll`, dann in `C:\xampp\php\php.ini`
|
||
(ganz unten steht bereits ein auskommentierter `[XDebug]`-Block):
|
||
|
||
```ini
|
||
[XDebug]
|
||
zend_extension = xdebug
|
||
xdebug.mode = develop,debug
|
||
xdebug.start_with_request = trigger
|
||
xdebug.client_port = 9003
|
||
xdebug.client_host = 127.0.0.1
|
||
```
|
||
|
||
### 2.3 Prüfen
|
||
|
||
Apache über das Control Panel **Stop/Start** (nicht nur „Config"), dann:
|
||
|
||
```
|
||
C:\xampp\php\php.exe -v
|
||
```
|
||
|
||
Die Ausgabe muss `with Xdebug v3.x.x` enthalten.
|
||
|
||
---
|
||
|
||
## 3. Installation unter Debian
|
||
|
||
Der bequemste Fall – Paketverwaltung erledigt alles.
|
||
|
||
### 3.1 Über apt
|
||
|
||
```bash
|
||
sudo apt install php-xdebug
|
||
# oder versionsspezifisch, z. B. bei Sury-Repository:
|
||
sudo apt install php8.4-xdebug
|
||
```
|
||
|
||
### 3.2 Über pecl (wenn kein Paket verfügbar)
|
||
|
||
```bash
|
||
sudo apt install php-pear php-dev autoconf
|
||
sudo pecl install xdebug
|
||
```
|
||
|
||
### 3.3 Konfiguration – hier gilt `conf.d`
|
||
|
||
Debian nutzt ein Scan-Verzeichnis. **Nicht** die zentrale `php.ini` anfassen, sondern
|
||
eine eigene Datei anlegen:
|
||
|
||
```
|
||
/etc/php/8.4/apache2/conf.d/99-xdebug-local.ini
|
||
```
|
||
|
||
```ini
|
||
xdebug.mode = develop,debug
|
||
xdebug.start_with_request = trigger
|
||
xdebug.client_port = 9003
|
||
xdebug.discover_client_host = 1
|
||
xdebug.var_display_max_depth = 10
|
||
xdebug.var_display_max_data = 1024
|
||
xdebug.var_display_max_children = 256
|
||
```
|
||
|
||
**Warum `99-`?** Die Dateien werden alphabetisch geladen, die letzte gewinnt. Xdebug
|
||
bringt typischerweise `20-xdebug.ini` mit – `99-` läuft danach und überschreibt.
|
||
Außerdem überlebt die eigene Datei ein `apt upgrade`, während Änderungen an der
|
||
zentralen `php.ini` einen Konfliktdialog auslösen.
|
||
|
||
### 3.4 SAPI-Trennung beachten
|
||
|
||
Debian hat getrennte Bäume:
|
||
|
||
```
|
||
/etc/php/8.4/apache2/conf.d/
|
||
/etc/php/8.4/fpm/conf.d/
|
||
/etc/php/8.4/cli/conf.d/
|
||
```
|
||
|
||
Eine Änderung in `cli/` wirkt **nicht** auf den Webserver. Meist sind es Symlinks nach
|
||
`/etc/php/8.4/mods-available/` – dort anlegen und mit `phpenmod` aktivieren, dann gilt
|
||
sie für beide.
|
||
|
||
```bash
|
||
sudo systemctl restart apache2
|
||
```
|
||
|
||
---
|
||
|
||
## 4. Wo wird was eingetragen?
|
||
|
||
### 4.1 Die entscheidende Frage zuerst
|
||
|
||
```bash
|
||
php -i | grep -E "Loaded Configuration|Scan this dir"
|
||
```
|
||
|
||
| Ergebnis bei *Scan this dir* | Vorgehen |
|
||
|---|---|
|
||
| ein Verzeichnispfad | eigene Datei mit hoher Nummer, z. B. `99-xdebug-local.ini` |
|
||
| `(none)` | direkt in die `php.ini` (Loaded Configuration File) |
|
||
|
||
**XAMPP → immer `php.ini`** (Windows, macOS, Linux). XAMPP liefert bewusst eine
|
||
einzige, selbst verwaltete Datei ohne `conf.d`.
|
||
|
||
**Debian / RHEL / Sury / Docker-Images → `conf.d` mit `99-…`**
|
||
|
||
> CLI und Browser können unterschiedliche INIs laden. Was Apache tatsächlich nutzt,
|
||
> zeigt nur `phpinfo()` im Browser.
|
||
|
||
### 4.2 Referenz-Konfiguration
|
||
|
||
```ini
|
||
zend_extension = xdebug.so ; NICHT "extension="!
|
||
|
||
; Was Xdebug tut
|
||
xdebug.mode = develop,debug ; develop = schöneres var_dump + Stack-Traces
|
||
; debug = Breakpoints
|
||
; trace = Aufrufprotokoll in Datei
|
||
; profile = Performance-Analyse
|
||
|
||
; Wann Xdebug aktiv wird
|
||
xdebug.start_with_request = trigger ; nur bei ?XDEBUG_TRIGGER=1 – hält Overhead klein
|
||
|
||
; Verbindung zur IDE
|
||
xdebug.client_port = 9003
|
||
xdebug.client_host = 127.0.0.1
|
||
xdebug.discover_client_host = 1 ; praktisch bei Remote / Lima / Docker
|
||
|
||
; Anzeige-Limits (Defaults sind zu knapp!)
|
||
xdebug.var_display_max_depth = 10 ; Default 3
|
||
xdebug.var_display_max_children = 256 ; Default 128
|
||
xdebug.var_display_max_data = 1024 ; Default 512
|
||
```
|
||
|
||
Die drei Anzeige-Limits sind der Grund, warum `var_dump` oft `...` statt der
|
||
eigentlichen Daten zeigt.
|
||
|
||
### 4.3 Nur wenn keine INI-Datei erreichbar ist
|
||
|
||
**`.htaccess`** (nur bei mod_php, nicht FPM; erfordert `AllowOverride Options`):
|
||
|
||
```apache
|
||
php_value xdebug.var_display_max_depth 10
|
||
php_flag xdebug.collect_return 1
|
||
```
|
||
|
||
**Im Skript** – funktioniert nur für `PHP_INI_ALL`-Direktiven:
|
||
|
||
```php
|
||
ini_set('xdebug.var_display_max_depth', '10');
|
||
ini_set('xdebug.var_display_max_data', '1024');
|
||
```
|
||
|
||
Sinnvoll dann **einmal zentral** im Bootstrap, nicht in jeder Datei:
|
||
|
||
```php
|
||
require __DIR__ . '/vendor/autoload.php';
|
||
|
||
if (($_ENV['APP_ENV'] ?? 'prod') === 'dev') {
|
||
ini_set('xdebug.var_display_max_depth', '10');
|
||
ini_set('xdebug.var_display_max_data', '1024');
|
||
}
|
||
```
|
||
|
||
Zwei Fallstricke:
|
||
- Die Werte wirken erst auf `var_dump`-Aufrufe **nach** dem `ini_set()`
|
||
- Gilt nur für den einen Request (nicht für separate Ajax-/Cron-Skripte)
|
||
|
||
`xdebug.mode` ist `PHP_INI_SYSTEM` und lässt sich zur Laufzeit **nicht** setzen.
|
||
|
||
**Auf der CLI ganz ohne Datei:**
|
||
|
||
```bash
|
||
php -d xdebug.mode=develop,trace -d xdebug.start_with_request=yes script.php
|
||
```
|
||
|
||
---
|
||
|
||
## 5. Schritt für Schritt: Debuggen unter XAMPP (macOS/Windows)
|
||
|
||
Ausgangslage: Xdebug ist installiert, `php -v` zeigt „with Xdebug".
|
||
|
||
### Schritt 1 – Konfiguration eintragen (einmalig)
|
||
|
||
`php.ini` öffnen. Pfad zur *richtigen* Datei ermitteln:
|
||
|
||
```bash
|
||
# macOS
|
||
/Applications/XAMPP/xamppfiles/bin/php -i | grep "Loaded Configuration"
|
||
```
|
||
```
|
||
:: Windows
|
||
C:\xampp\php\php.exe -i | findstr "Loaded Configuration"
|
||
```
|
||
|
||
Am Ende der Datei eintragen (jede Direktive auf einer **eigenen Zeile**):
|
||
|
||
```ini
|
||
[XDebug]
|
||
zend_extension = /Applications/XAMPP/xamppfiles/lib/php/extensions/no-debug-non-zts-20220829/xdebug.so
|
||
xdebug.mode = develop,debug
|
||
xdebug.start_with_request = trigger
|
||
xdebug.client_port = 9003
|
||
xdebug.client_host = 127.0.0.1
|
||
xdebug.var_display_max_depth = 10
|
||
xdebug.var_display_max_data = 1024
|
||
xdebug.var_display_max_children = 256
|
||
```
|
||
|
||
Unter Windows genügt `zend_extension = xdebug`, weil die DLL in `ext\` liegt.
|
||
|
||
### Schritt 2 – Apache neu starten
|
||
|
||
```bash
|
||
sudo /Applications/XAMPP/xamppfiles/xampp restart
|
||
```
|
||
|
||
Windows: XAMPP Control Panel → Apache **Stop**, dann **Start**.
|
||
|
||
### Schritt 3 – Kontrollieren
|
||
|
||
Datei `htdocs/info.php` mit `<?php phpinfo();` anlegen, `http://localhost/info.php`
|
||
aufrufen. Es muss ein eigener **Xdebug-Abschnitt** erscheinen. Danach Datei löschen.
|
||
|
||
Ab hier ist Xdebug für **alle** Skripte unter `htdocs/` aktiv – ohne weitere Eintragung.
|
||
|
||
### Schritt 4 – Erster Test ohne IDE
|
||
|
||
Beliebige Datei, z. B. `htdocs/test.php`:
|
||
|
||
```php
|
||
<?php
|
||
$daten = ['id' => 7, 'name' => 'Müller', 'rollen' => ['admin', 'user']];
|
||
var_dump($daten);
|
||
```
|
||
|
||
Aufrufen. Erwartetes Ergebnis: farbige, eingerückte Ausgabe mit Datei- und
|
||
Zeilenangabe – **ohne** dass `<pre>` nötig wäre. Das ist der `develop`-Modus.
|
||
|
||
Ebenso: einen absichtlichen Fehler einbauen (`$x->foo();`) – statt der nackten
|
||
PHP-Meldung kommt ein tabellarischer Stack-Trace mit allen Aufrufen und Parametern.
|
||
|
||
### Schritt 5 – Breakpoint-Debugging vorbereiten
|
||
|
||
**Grundprinzip verstehen:** PHP verbindet sich **zur IDE**, nicht umgekehrt. Die IDE
|
||
lauscht auf Port 9003, PHP klopft an. Der Listener muss also **vor** dem Seitenaufruf
|
||
laufen – sonst passiert schlicht nichts.
|
||
|
||
VS Code: Extension „PHP Debug" (xdebug.php-debug) installieren, dann im Projektordner
|
||
`.vscode/launch.json` anlegen:
|
||
|
||
```json
|
||
{
|
||
"version": "0.2.0",
|
||
"configurations": [
|
||
{ "name": "Listen for Xdebug", "type": "php", "request": "launch", "port": 9003 }
|
||
]
|
||
}
|
||
```
|
||
|
||
`pathMappings` ist **nicht** nötig, weil Code und IDE auf derselben Maschine liegen.
|
||
|
||
### Schritt 6 – Breakpoint setzen und Session starten
|
||
|
||
1. In `test.php` links neben die Zeilennummer klicken → roter Punkt erscheint
|
||
2. **F5** drücken → unten wird die Statusleiste orange, VS Code lauscht jetzt
|
||
3. Im Browser aufrufen: `http://localhost/test.php?XDEBUG_TRIGGER=1`
|
||
4. Die Seite bleibt hängen, VS Code springt in den Vordergrund und markiert die Zeile
|
||
|
||
### Schritt 7 – Was man jetzt sieht
|
||
|
||
| Bereich in VS Code | Inhalt |
|
||
|---|---|
|
||
| **Variables** (links oben) | alle Variablen im aktuellen Scope, aufklappbar |
|
||
| **Watch** | eigene Ausdrücke: `$daten['name']`, `count($items)`, Methodenaufrufe |
|
||
| **Call Stack** | wer diese Funktion aufgerufen hat, mit Parametern – Klick springt dorthin |
|
||
| **Breakpoints** | Liste aller gesetzten Haltepunkte |
|
||
| **Debug Console** | beliebigen PHP-Code im aktuellen Kontext ausführen |
|
||
|
||
Steuerung: **F10** Step Over (nächste Zeile) · **F11** Step Into (in Funktion hinein) ·
|
||
**Shift+F11** Step Out · **F5** Continue (bis zum nächsten Breakpoint)
|
||
|
||
### Schritt 8 – Trigger bequemer machen
|
||
|
||
Der Query-Parameter `?XDEBUG_TRIGGER=1` funktioniert nicht bei POST-Formularen oder
|
||
Ajax. Lösung: Browser-Extension **Xdebug Helper** (Chrome/Firefox) – ein Klick auf den
|
||
Käfer setzt ein Cookie, danach ist jeder Request auf der Domain getriggert.
|
||
|
||
CLI-Äquivalent:
|
||
|
||
```bash
|
||
XDEBUG_TRIGGER=1 /Applications/XAMPP/xamppfiles/bin/php script.php
|
||
```
|
||
|
||
### Schritt 9 – Session beenden
|
||
|
||
**Shift+F5** in VS Code stoppt den Listener. Die Seite im Browser läuft dann normal
|
||
weiter. Ohne laufenden Listener werden Breakpoints einfach ignoriert – es entsteht kein
|
||
Fehler.
|
||
|
||
---
|
||
|
||
## 6. Schritt für Schritt: Debuggen unter Debian
|
||
|
||
Zwei Fälle: Code liegt lokal auf demselben Rechner, oder auf einem entfernten Server.
|
||
|
||
### Schritt 1 – Installieren und konfigurieren
|
||
|
||
```bash
|
||
sudo apt install php-xdebug
|
||
sudo nano /etc/php/8.4/apache2/conf.d/99-xdebug-local.ini
|
||
```
|
||
|
||
```ini
|
||
xdebug.mode = develop,debug
|
||
xdebug.start_with_request = trigger
|
||
xdebug.client_port = 9003
|
||
xdebug.discover_client_host = 1
|
||
xdebug.var_display_max_depth = 10
|
||
xdebug.var_display_max_data = 1024
|
||
xdebug.var_display_max_children = 256
|
||
```
|
||
|
||
```bash
|
||
sudo systemctl restart apache2
|
||
```
|
||
|
||
Für CLI-Skripte dieselbe Datei zusätzlich unter `/etc/php/8.4/cli/conf.d/` ablegen –
|
||
oder einmal in `mods-available/` und mit `phpenmod xdebug-local` für beide aktivieren.
|
||
|
||
### Schritt 2 – Kontrollieren
|
||
|
||
```bash
|
||
php -v # CLI: "with Xdebug v3.x.x"
|
||
php -i | grep xdebug.mode
|
||
```
|
||
|
||
Für den Webserver: `phpinfo()` im Browser aufrufen – nur das zeigt, was Apache lädt.
|
||
Danach die Datei löschen.
|
||
|
||
### Schritt 3 – Client-Position klären
|
||
|
||
Der Debug-Client läuft auf **deiner** Maschine, nicht auf dem Server.
|
||
|
||
| Situation | Vorgehen |
|
||
|---|---|
|
||
| Code liegt lokal | nichts weiter nötig, Listener einfach starten |
|
||
| Code auf entferntem Server | SSH-Reverse-Tunnel |
|
||
|
||
```bash
|
||
ssh -R 9003:localhost:9003 user@server
|
||
```
|
||
|
||
Damit verbindet sich Xdebug auf dem Server nach `localhost:9003` – und landet über den
|
||
Tunnel bei dir. Der Tunnel muss offen bleiben, solange du debuggst.
|
||
|
||
### Schritt 4 – Pfade zuordnen (nur bei Remote)
|
||
|
||
Die IDE muss wissen, welche lokale Datei welchem Serverpfad entspricht.
|
||
|
||
VS Code, `.vscode/launch.json`:
|
||
|
||
```json
|
||
{
|
||
"version": "0.2.0",
|
||
"configurations": [
|
||
{
|
||
"name": "Listen for Xdebug",
|
||
"type": "php",
|
||
"request": "launch",
|
||
"port": 9003,
|
||
"pathMappings": {
|
||
"/var/www/projekt": "${workspaceFolder}"
|
||
}
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
Links steht immer der **Serverpfad**, rechts der lokale. Stimmt das nicht, hält der
|
||
Debugger zwar an, öffnet aber keine Datei – der häufigste Frustpunkt bei Remote-Setups.
|
||
|
||
### Schritt 5 – Ohne VS Code: vdebug in Vim/Neovim
|
||
|
||
Spricht DBGp direkt, braucht nur Vim mit `+python3` – keine Node-Abhängigkeit.
|
||
|
||
`.vimrc`:
|
||
|
||
```vim
|
||
let g:vdebug_options = {
|
||
\ 'port': 9003,
|
||
\ 'break_on_open': 0,
|
||
\ 'path_maps': {'/var/www/projekt': '/home/sven/projekt'},
|
||
\}
|
||
```
|
||
|
||
Bedienung: **F5** Listener starten · **F10** Breakpoint setzen/entfernen ·
|
||
**F2** Step Over · **F3** Step Into · **F4** Step Out · **F6** beenden
|
||
|
||
Sobald die Session steht, teilt sich das Fenster: Quellcode, Variablen im aktuellen
|
||
Scope, Call-Stack und ein Watch-Bereich, in dem sich beliebige Ausdrücke auswerten
|
||
lassen.
|
||
|
||
Alternativen: **nvim-dap** (moderneres UI, braucht Node + `vscode-php-debug`) und
|
||
**dbgp-tools** von Derick Rethans (reiner CLI-Client, via Composer).
|
||
|
||
### Schritt 6 – Request triggern
|
||
|
||
```bash
|
||
# Webseite
|
||
http://server/projekt/index.php?XDEBUG_TRIGGER=1
|
||
|
||
# CLI-Skript auf dem Server
|
||
XDEBUG_TRIGGER=1 php artisan irgendwas
|
||
```
|
||
|
||
### Schritt 7 – Wenn keine IDE möglich ist: Trace-Dateien
|
||
|
||
Auf gesperrten Systemen oft der stärkste Hebel – ein Step-Debugger im Nachhinein.
|
||
|
||
```ini
|
||
xdebug.mode = develop,trace
|
||
xdebug.start_with_request = trigger
|
||
xdebug.output_dir = /tmp
|
||
xdebug.trace_format = 0
|
||
xdebug.collect_params = 4 ; volle Argumentwerte
|
||
xdebug.collect_return = 1
|
||
```
|
||
|
||
Ein Request mit `?XDEBUG_TRIGGER=1` schreibt `/tmp/trace.*.xt` – eine vollständige,
|
||
eingerückte Aufrufliste mit allen Parametern und Rückgabewerten:
|
||
|
||
```bash
|
||
ls -t /tmp/trace.*.xt | head -1 | xargs less
|
||
```
|
||
|
||
Gezielt nur einen Abschnitt protokollieren:
|
||
|
||
```php
|
||
xdebug_start_trace('/tmp/meintrace');
|
||
// verdächtiger Abschnitt
|
||
xdebug_stop_trace();
|
||
```
|
||
|
||
Für die Frage „wer ruft das mit welchen Werten auf" meist schneller als ein Breakpoint.
|
||
|
||
---
|
||
|
||
## 7. Was man wo sieht – Überblick
|
||
|
||
| Modus | Ausgabeort | Zeigt |
|
||
|---|---|---|
|
||
| `develop` | Browser / STDOUT | farbiges `var_dump`, Stack-Traces bei Fehlern |
|
||
| `trace` | Datei in `xdebug.output_dir` | kompletter Aufrufbaum mit Parametern |
|
||
| `debug` | IDE / Vim | Variablen, Call-Stack, Watch, schrittweise Ausführung |
|
||
| `profile` | `cachegrind.out.*` in `output_dir` | Laufzeit pro Funktion (qcachegrind) |
|
||
|
||
Zusätzlich im Code nutzbar (alle Modi):
|
||
|
||
```php
|
||
xdebug_info(); // Konfigurations-Übersicht im Browser
|
||
xdebug_break(); // Breakpoint aus dem Code heraus
|
||
var_dump(xdebug_get_function_stack()); // Wer hat mich aufgerufen?
|
||
error_log(xdebug_print_function_stack()); // Stack ins Logfile
|
||
echo xdebug_call_line();
|
||
```
|
||
|
||
### Conditional Breakpoints
|
||
|
||
Rechtsklick auf den Breakpoint → Bedingung eintragen, z. B. `$id === 4711`. Damit hält
|
||
man gezielt bei einem Datensatz in einer Schleife über 10.000 Zeilen an. Einer der
|
||
größten praktischen Vorteile gegenüber `var_dump`.
|
||
|
||
### Profiling
|
||
|
||
```ini
|
||
xdebug.mode = profile
|
||
xdebug.output_dir = /tmp
|
||
```
|
||
|
||
Auswertung mit **qcachegrind** (`brew install qcachegrind`, `apt install kcachegrind`).
|
||
|
||
> Nicht dauerhaft anlassen: bremst massiv und füllt `/tmp`.
|
||
|
||
---
|
||
|
||
## 8. Alternative & Ergänzung: Symfony VarDumper
|
||
|
||
Unabhängig von Xdebug, rein per Composer – nützlich auf Systemen, wo keine Extension
|
||
installiert werden darf.
|
||
|
||
```bash
|
||
composer require --dev symfony/var-dumper
|
||
```
|
||
|
||
```php
|
||
dump($row); // weiter im Code
|
||
dd($row, $other); // dump and die
|
||
```
|
||
|
||
Ausgabe im Browser aufklappbar, auf der CLI eingefärbt, kürzt rekursive Strukturen.
|
||
|
||
**Dump-Server** – fängt Ausgaben in einem separaten Terminal ab, statt sie in die
|
||
Response zu schreiben. Praktisch bei API-Endpunkten, wo HTML im Output stört:
|
||
|
||
```bash
|
||
vendor/bin/var-dump-server
|
||
# dazu: VAR_DUMPER_FORMAT=server
|
||
```
|
||
|
||
| PHP | symfony/var-dumper |
|
||
|---|---|
|
||
| 7.1 | `^4.4` |
|
||
| 7.2 – 7.4 | `^5.4` |
|
||
| 8.0 – 8.1 | `^6.4` |
|
||
| 8.2+ | `^7` |
|
||
|
||
**Absolutes Minimum**, wenn gar nichts geht (Ajax, Redirects, Cronjobs):
|
||
|
||
```php
|
||
error_log(var_export($row, true));
|
||
```
|
||
|
||
```bash
|
||
tail -f /var/log/apache2/error.log
|
||
```
|
||
|
||
### Ohne Xdebug: `<pre>` nicht vergessen
|
||
|
||
`var_dump()` gibt Plaintext aus, der Browser kollabiert Whitespace. Deshalb:
|
||
|
||
```php
|
||
function dbg(...$vars): void {
|
||
echo '<pre style="text-align:left">';
|
||
var_dump(...$vars);
|
||
echo '</pre>';
|
||
}
|
||
```
|
||
|
||
`var_dump()` ist variadisch – mehrere Werte auf einmal sind erlaubt. Praktischer Trick
|
||
zur Orientierung: `var_dump(__LINE__, $row);`
|
||
|
||
---
|
||
|
||
## 9. Stolperfallen-Kurzliste
|
||
|
||
| Symptom | Ursache / Lösung |
|
||
|---|---|
|
||
| Extension wird ignoriert, keine Meldung | `extension=` statt `zend_extension=` |
|
||
| `incompatible architecture (have 'arm64', need 'x86_64')` | Build ohne Rosetta-Shell – `arch -x86_64 /bin/zsh`, dann neu bauen |
|
||
| `already installed ... install failed` | `pecl uninstall xdebug` oder `pecl install -f` |
|
||
| `Failed loading ... (no such file)` beim `pecl`-Aufruf | harmlos: PHP liest die INI, die `.so` wurde gerade gelöscht |
|
||
| `pecl` schreibt in falsche PHP-Installation | XAMPP-eigenes `pecl` aus `.../xamppfiles/bin/` verwenden |
|
||
| CLI zeigt Xdebug, Browser nicht | Apache lädt eine andere `php.ini` → Pfad aus `phpinfo()` nehmen |
|
||
| Direktiven wirken nicht | alles in einer Zeile statt untereinander |
|
||
| `<datum>` im Pfad | Platzhalter! echten Namen per `php -i \| grep extension_dir` ermitteln |
|
||
| `var_dump` zeigt `...` statt Daten | `var_display_max_depth` / `_data` / `_children` erhöhen |
|
||
| Breakpoint wird nie erreicht | Listener nicht gestartet, oder Trigger fehlt |
|
||
| Debugger hält an, öffnet aber keine Datei | `pathMappings` / `path_maps` falsch (Serverpfad links) |
|
||
| Port-Verbindung schlägt fehl | Xdebug 2 nutzt 9000, Xdebug 3 nutzt 9003 |
|
||
| `autoconf: command not found` | `brew install autoconf` |
|
||
| `Cannot find php-config` | falsches `phpize`/`php-config` – das aus dem Ziel-PHP nehmen |
|
||
|
||
---
|
||
|
||
## 10. Randnotiz: `var_dump` mit Barewords
|
||
|
||
```php
|
||
var_dump("Test", ffff); // ohne $
|
||
```
|
||
|
||
| PHP | Verhalten |
|
||
|---|---|
|
||
| ≤ 7.1 | `Notice: Use of undefined constant ffff` → gibt `string(4) "ffff"` aus |
|
||
| 7.2 – 7.4 | `Deprecated`, sonst wie oben |
|
||
| 8.0+ | **fataler** `Error: Undefined constant "ffff"` |
|