Debugging PHP Code in PhpStorm (mit Xdebug)

Mon, Jan 10, 2022 4 Minuten zum Lesen

PHP kann in verschiedenen Kontexten ausgeführt werden. Beispielsweise als Kommandozeilenapplikation (CLI) oder als Apache Modul. Je nach Kontext sind andere Schritte zu setzen falls man den ausgeführten PHP-Code debuggen möchte.

In diesem Blog Post betrachten wir das Szenario einer PHP-Applikation die eine REST-Schnittstelle bereistellt. Der PHP Code wird von PHP-FPM in einem Docker Container ausgeführt.

Ursprünglich handelte es sich bei PHP-FPM um ein eigenständiges Projetk. Seit PHP Version 5.3.3 ist es aber offizieller Bestandteil des PHP-Projekts. Die Webseite des ursprünglichen PHP-FPM Projekts is übrigens noch hier zu finden.

Beginnen wir mit unserer Docker Umgebung. Speichere die folgenden Dateien in ein beliebiges Arbeitsverzeichnis.

docker-compose

Datei /tmp/src/docker-compose.yml:

version: '3.3'
services:
  nginx:
      build: ./nginx
      
      ports:
          - 80:80

  fpm:
      build: ./fpm

nginx Docker Container

Datei /tmp/src/nginx/Dockerfile:

FROM nginx:1.21.5-alpine
COPY dockerfiles/etc/nginx/conf.d/default.conf /etc/nginx/conf.d/default.conf

Datei /tmp/src/nginx/dockerfiles/etc/nginx/conf.d/default.conf:

server { 
  listen 80; 
  server_name localhost; 
  root /var/www/html; 

  location ~ \.php$ { 
    fastcgi_pass fpm:9000;
    fastcgi_param  SCRIPT_FILENAME $document_root$fastcgi_script_name;
    include fastcgi_params;
  }
}

fpm (PHP) Container

Datei /tmp/src/fpm/Dockerfile:

FROM php:8.1.1-fpm-alpine3.15

COPY dockerfiles/var/www/html/api.php /var/www/html/api.php

############################################################
# install xdebug begin

RUN apk add --no-cache --virtual .build-deps $PHPIZE_DEPS \
    && pecl install xdebug \
    && docker-php-ext-enable xdebug \
    && apk del -f .build-deps \
    # Prepare log file for xdebug.
    && touch /tmp/xdebug.log && chmod 0666 /tmp/xdebug.log

RUN echo "xdebug.start_with_request=yes" >> /usr/local/etc/php/conf.d/xdebug.ini \
    && echo "xdebug.mode=debug" >> /usr/local/etc/php/conf.d/xdebug.ini \
    && echo "xdebug.log=/tmp/xdebug.log" >> /usr/local/etc/php/conf.d/xdebug.ini \
    && echo "xdebug.discover_client_host=1" >> /usr/local/etc/php/conf.d/xdebug.ini \
    && echo "xdebug.client_port=9000" >> /usr/local/etc/php/conf.d/xdebug.ini

# install xdebug end
############################################################

Zu guter letzt implementieren wir noch eine einfache HTTP REST-API in PHP. Datei /tmp/src/fpm/dockerfiles/var/www/html/api.php:

<?php

// Just a simple REST-like API implmemented in plain PHP

if (!empty($_GET['name'])) {

	$name = $_GET['name'];
	$price = get_price($name);

	if (empty($price)) {
		response(401, NULL);
	} else {
		response(200, $price);
	}

} else {
	response(400, "Invalid Request", NULL);
}

function response($status, $data)
{
	header("Content-Type:application/json");
	header("HTTP/1.1 " . $status);
	$json_response = json_encode($data);
	echo $json_response;
}

function get_price($name)
{
	$products = [
		"book" => 200,
		"pen" => 100,
		"pencil" => 50
	];

	foreach ($products as $product => $price) {
		if ($product == $name) {
			return $price;
			break;
		}
	}
}

Eure Dateistruktur sollte nun aussehen wie folgt:

/tmp/src/fpm/dockerfiles/var/www/html/api.php
/tmp/src/fpm/Dockerfile

/tmp/src/nginx/dockerfiles/etc/nginx/conf.d/default.conf
/tmp/src/nginx/Dockerfile

/tmp/src/docker-compose.yml

Wechsle nun ins Arbeitsverzeichnis /tmp/src und starte die Docker Container mit folgendem Kommando:

user@pc:/tmp/src$ docker-compose up --build

Mit dem Programm https://curl.se/ kann man nun die REST-API testen. Falls man https://curl.se/ nicht installiert hat kann man statt dessen auch Insomnia oder Postman verwenden. Aus eigener Erfahrung kann ich euch aber die Verwendung von curl empfehlen. Erst wenn es darum geht sich eine Template-Bibliothek mit Dutzenden REST-Requests anzulegen und zu organisieren haben grafische Programme wie Insomnia oder Postman handfeste Vorteile gegenüber einer reinen CLI-Applikation. Wenn es aber um das Debugging oder Troubleshooting eines konkrete Requests geht greife ich aber so gut wie immer zu https://curl.se/, da es oft wichtige Details des HTTP-Protokolls (HTTP Reply Header, …) nicht irgendwo in einem Untermenü einer grafischen Benutzeroberfläche versteckt. Nun aber zum eigentlichen Test der REST-API. Dazu öffnet man eine neue Shell und führt folgendes Kommando aus:

user@pc:/tmp/src$ curl -v 'http://localhost/api.php?name=book'

* Connected to localhost (127.0.0.1) port 80 (#0)

> GET /api.php?name=book HTTP/1.1
> Host: localhost
> User-Agent: curl/7.74.0
> Accept: */*


< HTTP/1.1 200 OK
< Server: nginx/1.21.5
< Date: Tue, 18 Jan 2022 14:34:28 GMT
< Content-Type: application/json
< Transfer-Encoding: chunked
< Connection: keep-alive
< X-Powered-By: PHP/8.1.1
 
200
user@pc:/tmp/src$

Wenn alles klappt schickt die RES-API den Wert 200 zurück. Soweit so gut. Nun wollen wir den PHP-Quellcode in PhpStorm debuggen. Damit das klappt haben wir im fpm Docker Container bereits xdebug installiert und konfiguriert. xdebug ist eines der beiden Programme die man aktuell zum Debuggen von PHP verwenden kann. Das andere Programm ist der Zend Debugger.

Öffne den Pfad /tmp/src/fpm/dockerfiles/var/www/html in PhpStorm. Klicke auf das Symbol “Start Listening for PHP Debug Connections”. Du findest dieses Symbol in der rechten oberen Ecke des PhpStorm Fensters:

Aktivierung des PHP Debuggers.

Aktivierung des PHP Debuggers.

Danach platziere einen Breakpoint an einer Stelle des Programmcodes der sicher ausgeführt wird:

Setzen eines Breakpoints.

Setzen eines Breakpoints.

Falls du nicht sicher bist wo du einen Breakpoint im Quellcode setzen musst um einen Request an die REST-API zu debuggen kannst du folgende Option aktivieren:

  1. File -> Settings -> PHP -> Debug
  2. Im Abschnitt “External connections” aktiviere die Option “Break at first line in PHP scripts”.

Zu guter letzt müssen wir noch ein “Path Mapping” definieren. Navigiere dazu zum entsprechenden Menüpunkt in den Settings (File -> Settings -> PHP -> Servers) und fülle die Details aus wie im Screenshot ersichtlich:

Anlegen eines Path Mappings.

Anlegen eines Path Mappings.

Dieses Path Mapping kann theoretisch beim ersten Request auch automatisch von PhpStorm erstellt werden. In diesem Fall öffnet PhpStorm automatisch das Fenster “Incoming Connection From Xdebug”. Dort gäbe es auch die Möglichkeit ein Path Mapping eines Deployments zu verwenden (“Import mappings from deployment”). Diese Option scheint aktuell aber nicht zu funktionieren. Es gibt diesbezüglich zumindest einen offenen Bug.

Sobald wir nun einen weitere Request absetzen wird der Debugger in PhpStorm aktiv und man kann Schritt für Schritt den Programmcode ausführen und den Inhalt von Variablen inspizieren:

Debugging.

Debugging.

Hinweis zu Netcup (Werbung)

Der deutsche Hoster Netcup bietet unter anderem günstige und zugleich leistungsstarke Webhosting Pakete, KVM-basierte Root Server und dezidierte Server an. Mit unseren Gutscheincodes kannst du noch mehr Geld sparen (6€ bei deiner ersten Bestellung, 30% Rabatt auf alle KVM-basierten Root Server, ...).