PhpStorm, Docker and Xdebug 3 on PHP 8.1 in 2022 [Tutorial Part 3]
Tags: php, phpstorm, xdebug, docker
Published: 2022-06-24 08:22:02

<p>This article appeared first on <a href='https://www.pascallandau.com/'>https://www.pascallandau.com/</a> at <a href='https://www.pascallandau.com/blog/phpstorm-docker-xdebug-3-php-8-1-in-2022/'>PhpStorm, Docker and Xdebug 3 on PHP 8.1 in 2022 [Tutorial Part 3]</a></p>
<hr />

In this part of the tutorial series on developing PHP on Docker we  will **setup our local development environment to be used by PhpStorm and Xdebug**. We will also  ensure that we can run **PHPUnit tests from the command line as well as from PhpStorm** and throw  the tool `strace` into the mix for debugging long running processes.


{% youtube bZ1MiynqT98 %}


All code samples are publicly available in my [Docker PHP Tutorial repository on Github](https://github.com/paslandau/docker-php-tutorial/). You find the branch for this tutorial at  [part-4-2-phpstorm-docker-xdebug-3-php-8-1-in-2022](https://github.com/paslandau/docker-php-tutorial/tree/part-4-2-phpstorm-docker-xdebug-3-php-8-1-in-2022)

**All published parts of the Docker PHP Tutorial** are collected under a dedicated page at [Docker PHP Tutorial](https://www.pascallandau.com/docker-php-tutorial/). The previous part was [Docker from scratch for PHP 8.1 Applications in 2022](https://www.pascallandau.com/blog/docker-from-scratch-for-php-applications-in-2022/) and the following one is [Run Laravel 9 on Docker in 2022](https://www.pascallandau.com/blog/run-laravel-9-docker-in-2022/).

If you want to follow along, please subscribe to the [RSS feed](https://www.pascallandau.com/feed.xml) or [via email](https://www.pascallandau.com/blog/#newsletter) to get **automatic notifications** when the next part comes out :)

<!-- generated -->
<a id='table-of-contents'> </a>
<!-- /generated -->

## Table of contents

<!-- toc -->

- [Introduction](#introduction)
- [Install Tools](#install-tools)
    - [Install composer](#install-composer)
    - [Install Xdebug](#install-xdebug)
    - [Install PHPUnit](#install-phpunit)
    - [Install SSH](#install-ssh)
- [Setup PhpStorm](#setup-phpstorm)
    - [SSH Configuration](#ssh-configuration)
    - [PHP Interpreter](#php-interpreter)
    - [PHPUnit](#phpunit)
    - [Debugging](#debugging)
        - [Debug code executed via PhpStorm](#debug-code-executed-via-phpstorm)
        - [Debug code executed via php-fpm, cli or from a worker](#debug-code-executed-via-php-fpm-cli-or-from-a-worker)
            - [php-fpm](#php-fpm)
            - [cli](#cli)
            - [php-workers](#php-workers)
- [strace](#strace)
- [Wrapping up](#wrapping-up)

<!-- /toc -->

<!-- generated -->
<a id='introduction'> </a>
<!-- /generated -->

## Introduction

This article is mostly an update of [Setting up PhpStorm with Xdebug for local development on Docker](https://www.pascallandau.com/blog/setup-phpstorm-with-xdebug-on-docker/) but will also cover the "remaining cases" of **debugging php-fpm** and **php worker processes**. 

We will still rely on an **always-running docker setup** that we connect to via an SSH Configuration instead of using the  [built-in docker-compose capabilities](https://www.pascallandau.com/blog/setup-phpstorm-with-xdebug-on-docker/#run-debug-a-php-script-on-docker-server) as I feel it's closer to what we do in CI / production. However, we will **not use SSH keys**  any longer but simply authenticate via password. This reduces complexity and removes any  pesky warnings regarding "SSH keys being exposed in a repository".

<!-- generated -->
<a id='install-tools'> </a>
<!-- /generated -->

## Install Tools

<!-- generated -->
<a id='install-composer'> </a>
<!-- /generated -->

### Install composer [Composer](https://getcomposer.org/) is installed by pulling  [the official composer docker image](https://hub.docker.com/_/composer) and simply "copying" the  composer executable over to the base php image. In addition, composer needs the extensions `mbstring` and `phar`

```
# File: .docker/images/php/base/Dockerfile

ARG ALPINE_VERSION
ARG COMPOSER_VERSION
FROM composer:${COMPOSER_VERSION} as composer
FROM alpine:${ALPINE_VERSION} as base

# ...

RUN apk add --update --no-cache  \
        php-mbstring~=${TARGET_PHP_VERSION} \
        php-phar~=${TARGET_PHP_VERSION} \

# ...

COPY --from=composer /usr/bin/composer /usr/local/bin/composer
```

Because we want our build to be deterministic, we "pin" the composer version by adding a  `COMPOSER_VERSION` variable to the `.docker/.env` file 

```dotenv
COMPOSER_VERSION=2.2.5
```

and using it in `.docker/docker-compose/docker-compose-php-base.yml`:

```yaml
services:
  php-base:
    build:
      args:
        - COMPOSER_VERSION=${COMPOSER_VERSION?}
```

<!-- generated -->
<a id='install-xdebug'> </a>
<!-- /generated -->

### Install Xdebug Install the extension via `apk` (only for the `local` target):

```Dockerfile
# File: .docker/images/php/base/Dockerfile

FROM base as local

RUN apk add --no-cache --update \
        php-xdebug~=${TARGET_PHP_VERSION} \
    # ensure that xdebug is not enabled by default
    && rm -f /etc/php8/conf.d/00_xdebug.ini
```

We also don't want to enable `xdebug` immediately but only when we need it (due to the decrease  in performance when the extension is enabled), hence we remove the default config file and  disable the extension in the application `.ini` file

```
# File: .docker/images/php/base/conf.d/zz-app-local.ini

; Note:
; Remove the comment ; to enable debugging
;zend_extension=xdebug
xdebug.client_host=host.docker.internal
xdebug.start_with_request=yes
xdebug.mode=debug
```

See [Fix Xdebug on PhpStorm when run from a Docker container](https://www.pascallandau.com/blog/setup-phpstorm-with-xdebug-on-docker/#fix-xdebug-on-phpstorm-when-run-from-a-docker) for an explanation of the `xdebug.client_host=host.docker.internal` setting (previously called `xdebug.remote_host` in xdebug < 3). This will still work out of the box for Docker Desktop, but for Linux users we need to add the  [`host-gateway` magic reference](https://github.com/docker/for-linux/issues/264#issuecomment-965465879)
**to all PHP containers** (we can't add it to the php base image because this is a runtime setting):

```yaml
services:
  service:
    extra_hosts:
      - host.docker.internal:host-gateway
```

Finally, we need to add  [the environment variable `PHP_IDE_CONFIG`](https://www.jetbrains.com/help/phpstorm/debugging-a-php-cli-script.html)
**to all PHP containers**. The variable is defined as `PHP_IDE_CONFIG=serverName=dofroscra`, where  "dofroscra" is the name of the server that we will configure later for debugging. Because we  need the same value in multiple places, the variable is configured in `.docker/.env`:

```
PHP_IDE_CONFIG=serverName=dofroscra
```

And then added in `.docker/docker-compose/docker-compose.local.yml`

```yaml
services:
  php-fpm:
    environment:
      - PHP_IDE_CONFIG=${PHP_IDE_CONFIG?}
  
  php-worker:
    environment:
      - PHP_IDE_CONFIG=${PHP_IDE_CONFIG?}
  
  application:
    environment:
      - PHP_IDE_CONFIG=${PHP_IDE_CONFIG?}
```

<!-- generated -->
<a id='install-phpunit'> </a>
<!-- /generated -->

### Install PHPUnit PHPUnit will be installed via `composer` but will not be "baked into the image" for local  development. Thus, we must run `composer require` **in the container**. To make this more  convenient a make target for running arbitrary composer commands is added in  `.make/01-00-application-setup.mk`:

```makefile
.PHONY: composer
composer: ## Run composer commands. Specify the command e.g. via ARGS="install"
	$(EXECUTE_IN_APPLICATION_CONTAINER) composer $(ARGS);
```

This allows me to run `make composer ARGS="install"` from the host system to execute `composer  install` in the container. In consequence, `composer` will use the PHP version and extensions of  the `application` container to install the dependencies, yet I will still see the installed files locally because the codebase is configured as a volume for the container.

Before installing phpunit, we must add the required extensions `dom` and `xml` to the container

```
# File: .docker/images/php/base/Dockerfile

# ...

RUN apk add --update --no-cache  \
        php-dom~=${TARGET_PHP_VERSION} \
        php-xml~=${TARGET_PHP_VERSION} \
```

as well as rebuild and restart the docker setup via 

```
make docker-build
make docker-down
make docker-up
```

Now we can add phpunit via

```
make composer ARGS='require "phpunit/phpunit"'
```

which will create a `composer.json` file and setup up the `vendor/` directory:

```
$ make composer ARGS='require "phpunit/phpunit"'
Using version ^9.5 for phpunit/phpunit
./composer.json has been created
Running composer update phpunit/phpunit
Loading composer repositories with package information
Updating dependencies
...
```

**CAUTION**: If you **run into the following permission error** at this step, you are likely using  Linux and  haven't set the `APP_USER_ID` and `APP_GROUP_ID` variables as described in the previous article under  [Solving permission issues](https://www.pascallandau.com/blog/docker-from-scratch-for-php-applications-in-2022/#solving-permission-issues).

```text
make composer ARGS='req phpunit/phpunit' ENV=local TAG=latest DOCKER_REGISTRY=docker.io DOCKER_NAMESPACE=dofroscra APP_USER_NAME=application APP_GROUP_NAME=application docker-compose -p dofroscra_local --env-file ./.docker/.env -f ./.docker/docker-compose/docker-compose.yml -f ./.docker/docker-compose/docker-compose.local.yml exec -T --user application application composer req phpunit/phpunit
./composer.json is not writable.
make: *** [.make/01-00-application-setup.mk:14: composer] Error 1
```


I have also added 

- a minimal `phpunit.xml` config file
- a test case at `tests/SomeTest.php` 
- and a new Makefile for "anything related to qa" at `.make/01-02-application-qa.mk`:

```Makefile
##@ [Application: QA]

.PHONY: test
test: ## Run the test suite 
	$(EXECUTE_IN_WORKER_CONTAINER) vendor/bin/phpunit -c phpunit.xml
```

So I can run tests simply via `make test`

```
$ make test
ENV=local TAG=latest DOCKER_REGISTRY=docker.io DOCKER_NAMESPACE=dofroscra APP_USER_NAME=application APP_GROUP_NAME=application docker-compose -p dofroscra_local --env-file ./.docker/.env -f ./.docker/docker-compose/docker-compose.yml -f ./.docker/docker-compose/docker-compose.local.yml exec -T --user application php-worker vendor/bin/phpunit
PHPUnit 9.5.13 by Sebastian Bergmann and contributors.

.                                                                   1 / 1 (100%)

Time: 00:00.324, Memory: 4.00 MB

OK (1 test, 1 assertion)
```

<!-- generated -->
<a id='install-ssh'> </a>
<!-- /generated -->

### Install SSH

We will execute commands from PhpStorm via ssh in the `application` container. As mentioned, we  won't use a key file for authentication but will instead simply use a password that is  configured via the `APP_SSH_PASSWORD` variable in `.docker/.env` and passed to the image in  `.docker/docker-compose/docker-compose.local.yml`. In addition, we map port `2222` from the  host system to port `22` of the application container and make sure that the codebase is shared as a volume between host and container

```yaml
  application:
    build:
      args:
        - APP_SSH_PASSWORD=${APP_SSH_PASSWORD?}
    volumes:
      - ${APP_CODE_PATH_HOST?}:${APP_CODE_PATH_CONTAINER?}
    ports:
      - "${APPLICATION_SSH_HOST_PORT:-2222}:22"
```

The container already contains `openssh` and sets the password

```Dockerfile
ARG BASE_IMAGE
FROM ${BASE_IMAGE} as base

FROM base as local

RUN apk add --no-cache --update \
        openssh

ARG APP_SSH_PASSWORD
RUN echo "$APP_USER_NAME:$APP_SSH_PASSWORD" | chpasswd 2>&1

# Required to start sshd, otherwise the container will error out on startup with the message
# "sshd: no hostkeys available -- exiting."
# @see https://stackoverflow.com/a/65348102/413531 
RUN ssh-keygen -A

# we use SSH deployment configuration in PhpStorm for local development
EXPOSE 22

CMD ["/usr/sbin/sshd", "-D"]
```

<!-- generated -->
<a id='setup-phpstorm'> </a>
<!-- /generated -->

## Setup PhpStorm

We will configure a remote PHP interpreter that uses an SSH connection to run commands in the  `application` container. Before,  [we have been using an `SFTP Deployment configuration`](https://www.pascallandau.com/blog/setup-phpstorm-with-xdebug-on-docker/#configure-the-deployment-configuration) , which was kinda confusing ("What is SFTP doing here?"), so we will use an  [SSH Configuration](https://www.jetbrains.com/help/phpstorm/create-ssh-configurations.html)  instead and configure the path mappings in the **Cli Interpreter** interface

<!-- generated -->
<a id='ssh-configuration'> </a>
<!-- /generated -->

### SSH Configuration

At `File | Settings | Tools | SSH Configurations` create a new SSH Configuration named  "Docker PHP Tutorial" with the following settings 

- Host: 127.0.0.1
- Port: see `APPLICATION_SSH_HOST_PORT` in `.docker/docker-compose/docker-compose.local.yml`
- User name: see `APP_USER_NAME` in `.make/.env`
- Authentication type: Password
- Password: see `APP_SSH_PASSWORD` in `.docker/.env`

[![PhpStorm SSH Configuration](https://www.pascallandau.com/img/phpstorm-docker-xdebug-3-php-8-1-in-2022/phpstorm-ssh-configuration.PNG "PhpStorm SSH Configuration")](https://www.pascallandau.com/img/phpstorm-docker-xdebug-3-php-8-1-in-2022/phpstorm-ssh-configuration.PNG)

<!-- generated -->
<a id='php-interpreter'> </a>
<!-- /generated -->

### PHP Interpreter

At `File | Settings | PHP` add a new PHP CLI interpreter that uses the new SSH Configuration

[![PhpStorm new CLI interpreter](https://www.pascallandau.com/img/phpstorm-docker-xdebug-3-php-8-1-in-2022/phpstorm-cli-interpreter.PNG "PhpStorm new CLI interpreter")](https://www.pascallandau.com/img/phpstorm-docker-xdebug-3-php-8-1-in-2022/phpstorm-cli-interpreter.PNG)

In addition, we define the **path to the xdebug extension** because it is disabled by default but PhpStorm can enable it automatically if required. You can find the path in the `application` container via

```
root:/var/www/app# php -i | grep extension_dir
extension_dir => /usr/lib/php8/modules => /usr/lib/php8/modules
root:/var/www/app# ll /usr/lib/php8/modules | grep xdebug
-rwxr-xr-x    1 root     root        303936 Jan  9 00:21 xdebug.so
```

We still need to  [Fix Xdebug on PhpStorm when run from a Docker container](https://www.pascallandau.com/blog/setup-phpstorm-with-xdebug-on-docker/#fix-xdebug-on-phpstorm-when-run-from-a-docker-container) by adding a custom PHP option for `xdebug.client_host=host.docker.internal`. That's the same value we use in `.docker/images/php/base/conf.d/zz-app-local.ini`.

[![PhpStorm Xdebug settings for the CLI interpreter](https://www.pascallandau.com/img/phpstorm-docker-xdebug-3-php-8-1-in-2022/phpstorm-cli-interpreter-xdebug.PNG "PhpStorm Xdebug settings for the CLI interpreter")](https://www.pascallandau.com/img/phpstorm-docker-xdebug-3-php-8-1-in-2022/phpstorm-cli-interpreter-xdebug.PNG)

In the interpreter overview we must now configure the **path mappings** so that PhpStorm knows "which local file belongs to which remote one". The remote folder is defined in `.docker/.env` via

```dotenv
APP_CODE_PATH_CONTAINER=/var/www/app
```

[![PhpStorm path mappings for the CLI interpreter](https://www.pascallandau.com/img/phpstorm-docker-xdebug-3-php-8-1-in-2022/phpstorm-cli-interpreter-path-mappings.PNG "PhpStorm path mappings for the CLI interpreter")](https://www.pascallandau.com/img/phpstorm-docker-xdebug-3-php-8-1-in-2022/phpstorm-cli-interpreter-path-mappings.PNG)

Afterwards we can set a breakpoint e.g. in `setup.php` and start debugging:

[![PhpStorm debugging breakpoint](https://www.pascallandau.com/img/phpstorm-docker-xdebug-3-php-8-1-in-2022/phpstorm-xdebug-breakpoint.PNG "PhpStorm debugging breakpoint")](https://www.pascallandau.com/img/phpstorm-docker-xdebug-3-php-8-1-in-2022/phpstorm-xdebug-breakpoint.PNG)

The screenshot shows that PhpStorm adds the Xdebug extension that we defined previously.

<!-- generated -->
<a id='phpunit'> </a>
<!-- /generated -->

### PHPUnit

`phpunit` is configured via `File | Settings | PHP | Test Frameworks`. First, we select the interpreter that we just added

[![Set up phpunit in PhpStorm](https://www.pascallandau.com/img/phpstorm-docker-xdebug-3-php-8-1-in-2022/phpstorm-phpunit-interpreter.PNG "Set up phpunit in PhpStorm")](https://www.pascallandau.com/img/phpstorm-docker-xdebug-3-php-8-1-in-2022/phpstorm-phpunit-interpreter.PNG)

Then, we add the paths to the composer autoload script and the `phpunit.xml` configuration file.

[![phpunit settings in PhpStorm](https://www.pascallandau.com/img/phpstorm-docker-xdebug-3-php-8-1-in-2022/phpstorm-phpunit-settings.PNG "phpunit settings in PhpStorm")](https://www.pascallandau.com/img/phpstorm-docker-xdebug-3-php-8-1-in-2022/phpstorm-phpunit-settings.PNG)

PhpStorm will now execute tests using the PHP interpreter in the `application` container

[![Run a phpunit test in PhpStorm](https://www.pascallandau.com/img/phpstorm-docker-xdebug-3-php-8-1-in-2022/phpstorm-run-phpunit-test.PNG "Run a phpunit test in PhpStorm")](https://www.pascallandau.com/img/phpstorm-docker-xdebug-3-php-8-1-in-2022/phpstorm-run-phpunit-test.PNG)

<!-- generated -->
<a id='debugging'> </a>
<!-- /generated -->

### Debugging

First of all, if you haven't already please also take a look at the  [official xdebug documentation](https://xdebug.org/docs/step_debug). Derick is doing a great job  at explaining xdebug in detail including some helpful videos like [Xdebug 3: Xdebug with Docker and PhpStorm in 5 minutes](https://www.youtube.com/watch?v=4opFac50Vwo)

<!-- generated -->
<a id='debug-code-executed-via-phpstorm'> </a>
<!-- /generated -->

#### Debug code executed via PhpStorm

This should already work out of the box. Simply set a break point, right-click on a file and choose  "Debug '...'"

[![PhpStorm debugging breakpoint](https://www.pascallandau.com/img/phpstorm-docker-xdebug-3-php-8-1-in-2022/phpstorm-xdebug-breakpoint.PNG "PhpStorm debugging breakpoint")](https://www.pascallandau.com/img/phpstorm-docker-xdebug-3-php-8-1-in-2022/phpstorm-xdebug-breakpoint.PNG)

<!-- generated -->
<a id='debug-code-executed-via-php-fpm-cli-or-from-a-worker'> </a>
<!-- /generated -->

#### Debug code executed via php-fpm, cli or from a worker

For code that is executed "directly" by a container without PhpStorm, we first need to enable  `xdebug` in the container by removing the `;` in front of the extension in  `/etc/php8/conf.d/zz-app-local.ini`

```
; Note:
; Remove the comment ; to enable debugging
zend_extension=xdebug
```

To make this a little more convenient, we use dedicated make recipes for those actions in `.make/01-01-application-commands.mk`

```Makefile
.PHONY: execute-in-container
execute-in-container: ## Execute a command in a container. E.g. via "make execute-in-container DOCKER_SERVICE_NAME=php-fpm COMMAND="echo 'hello'"
	@$(if $(DOCKER_SERVICE_NAME),,$(error DOCKER_SERVICE_NAME is undefined))
	@$(if $(COMMAND),,$(error COMMAND is undefined))
	$(EXECUTE_IN_CONTAINER) $(COMMAND);

.PHONY: enable-xdebug
enable-xdebug: ## Enable xdebug in the given container specified by "DOCKER_SERVICE_NAME". E.g. "make enable-xdebug DOCKER_SERVICE_NAME=php-fpm"
	"$(MAKE)" execute-in-container APP_USER_NAME="root" DOCKER_SERVICE_NAME=$(DOCKER_SERVICE_NAME) COMMAND="sed -i 's/.*zend_extension=xdebug/zend_extension=xdebug/' '/etc/php8/conf.d/zz-app-local.ini'"

.PHONY: disable-xdebug
disable-xdebug: ## Disable xdebug in the given container specified by "DOCKER_SERVICE_NAME". E.g. "make enable-xdebug DOCKER_SERVICE_NAME=php-fpm"
	"$(MAKE)" execute-in-container APP_USER_NAME="root" DOCKER_SERVICE_NAME=$(DOCKER_SERVICE_NAME) COMMAND="sed -i 's/.*zend_extension=xdebug/;zend_extension=xdebug/' '/etc/php8/conf.d/zz-app-local.ini'"
```

To capture incoming requests, we need to make PhpStorm listen for PHP Debug connections via `Run | Start Listening for PHP Debug Connections`. 

[![PhpStorm: Start Listening for PHP Debug Connections](https://www.pascallandau.com/img/phpstorm-docker-xdebug-3-php-8-1-in-2022/phpstorm-start-listening-for-debug-connections.PNG "PhpStorm: Start Listening for PHP Debug Connections")](https://www.pascallandau.com/img/phpstorm-docker-xdebug-3-php-8-1-in-2022/phpstorm-start-listening-for-debug-connections.PNG)

The corresponding ports are configured at `File | Settings | PHP | Debug`. In Xdebug < 3 the  default port was `9000` and in [Xdebug 3 it is `9003`](https://xdebug.org/docs/all_settings#client_port) 

[![PhpStorm: configure xdebug ports](https://www.pascallandau.com/img/phpstorm-docker-xdebug-3-php-8-1-in-2022/phpstorm-xdebug-ports.PNG "PhpStorm: configure xdebug ports")](https://www.pascallandau.com/img/phpstorm-docker-xdebug-3-php-8-1-in-2022/phpstorm-xdebug-ports.PNG)

Finally, we need to add a server via `File | Settings | PHP | Servers`

[![PhpStorm: configure a server](https://www.pascallandau.com/img/phpstorm-docker-xdebug-3-php-8-1-in-2022/phpstorm-server.PNG "PhpStorm: configure a server")](https://www.pascallandau.com/img/phpstorm-docker-xdebug-3-php-8-1-in-2022/phpstorm-server.PNG)

The name of the server must match the value of the `serverName` key in the environment variable  `PHP_IDE_CONFIG` that we configured previously as `serverName=dofroscra`.

<!-- generated -->
<a id='php-fpm'> </a>
<!-- /generated -->

##### php-fpm

For `php-fpm` we must [restart the `php-fpm` process without restarting the container](https://stackoverflow.com/a/43076457) after we have activated `xdebug` via

```
kill -USR2 1
```

Since this is a pain to remember, we add a make target in `.make/01-01-application-commands.mk`

```Makefile
# @see https://stackoverflow.com/a/43076457
.PHONY: restart-php-fpm
restart-php-fpm: ## Restart the php-fpm service
	"$(MAKE)" execute-in-container DOCKER_SERVICE_NAME=$(DOCKER_SERVICE_NAME_PHP_FPM) COMMAND="kill -USR2 1"
```

So we can now simply run

```
make enable-xdebug DOCKER_SERVICE_NAME=php-fpm
make restart-php-fpm
```

Setting a breakpoint in `public/index.php` and opening [http://127.0.0.1/](http://127.0.0.1/) in a browser or via `curl http://127.0.0.1/` will halt the execution as expected.

[![PhpStorm debugging breakpoint for php-fpm](https://www.pascallandau.com/img/phpstorm-docker-xdebug-3-php-8-1-in-2022/phpstorm-xdebug-breakpoint-php-fpm.PNG "PhpStorm debugging breakpoint for php-fpm")](https://www.pascallandau.com/img/phpstorm-docker-xdebug-3-php-8-1-in-2022/phpstorm-xdebug-breakpoint-php-fpm.PNG)

<!-- generated -->
<a id='cli'> </a>
<!-- /generated -->

##### cli

Instead of triggering a PHP script via HTTP request, we can also run CLI scripts - think of the  `make setup-db` target for instance. To debug such invocations, we need to follow the same steps as before:
 
- enable the `xdebug` extension in the `application` container
- "Listening for PHP Debug Connections" from PhpStorm

Running the following make targets will trigger a breakpoint in `setup.php`:

```
make enable-xdebug DOCKER_SERVICE_NAME=application
make setup-db
```

[![PhpStorm debugging breakpoint for cli](https://www.pascallandau.com/img/phpstorm-docker-xdebug-3-php-8-1-in-2022/phpstorm-xdebug-breakpoint-cli.PNG "PhpStorm debugging breakpoint for cli")](https://www.pascallandau.com/img/phpstorm-docker-xdebug-3-php-8-1-in-2022/phpstorm-xdebug-breakpoint-cli.PNG)

<!-- generated -->
<a id='php-workers'> </a>
<!-- /generated -->

##### php-workers

And finally the same thing for long running PHP processes (aka workers). Just as before:

- enable the `xdebug` extension in the `php-worker` container
- "Listening for PHP Debug Connections" from PhpStorm
- restart the php workers  

Running the following make targets will trigger a breakpoint in `worker.php`:

```
make enable-xdebug DOCKER_SERVICE_NAME=php-worker
make restart-workers
```

[![PhpStorm debugging breakpoint for php-workers](https://www.pascallandau.com/img/phpstorm-docker-xdebug-3-php-8-1-in-2022/phpstorm-xdebug-breakpoint-php-worker.PNG "PhpStorm debugging breakpoint for php-workers")](https://www.pascallandau.com/img/phpstorm-docker-xdebug-3-php-8-1-in-2022/phpstorm-xdebug-breakpoint-php-worker.PNG)

<!-- generated -->
<a id='strace'> </a>
<!-- /generated -->

## strace

[strace](https://strace.io/) is a great tool for debugging long running processes that I've  adopted after reading [What is PHP doing?](https://derickrethans.nl/what-is-php-doing.html). I've added it to the php base image:

```Dockerfile
RUN apk add --update --no-cache \
        strace
```

You can attach to any running process via `sudo strace -p $processId` - BUT that doesn't work  out of the box on docker and will fail with the error message

```
strace: attach: ptrace(PTRACE_SEIZE, 1): Operation not permitted
```

This is caused by a security measure from docker and  [can be circumvented](https://stackoverflow.com/a/46676868) by adding

```yaml
services:
  service:
    cap_add:
      - "SYS_PTRACE"
    security_opt:
      - "seccomp=unconfined"
```

in `.docker/docker-compose/docker-compose.local.yml` **to all PHP containers**. After  rebuilding and restarting the docker setup, you can now e.g. log in the `php-worker` container and run `strace` on a php worker process:

```
application:/var/www/app# ps aux
PID   USER     TIME  COMMAND
    1 applicat  0:00 {supervisord} /usr/bin/python3 /usr/bin/supervisord
    7 applicat  0:00 php /var/www/app/worker.php
    8 applicat  0:00 php /var/www/app/worker.php
    9 applicat  0:00 php /var/www/app/worker.php
   10 applicat  0:00 php /var/www/app/worker.php
   11 applicat  0:00 bash
   20 applicat  0:00 ps aux
application:/var/www/app# sudo strace -p 7
strace: Process 7 attached
restart_syscall(<... resuming interrupted read ...>) = 0
poll([{fd=4, events=POLLIN|POLLPRI|POLLERR|POLLHUP}], 1, 0) = 0 (Timeout)
sendto(4, "*2\r\n$4\r\nRPOP\r\n$5\r\nqueue\r\n", 25, MSG_DONTWAIT, NULL, 0) = 25
poll([{fd=4, events=POLLIN|POLLPRI|POLLERR|POLLHUP}], 1, 0) = 1 ([{fd=4, revents=POLLIN}])
recvfrom(4, "$", 1, MSG_PEEK, NULL, NULL) = 1
```

<!-- generated -->
<a id='wrapping-up'> </a>
<!-- /generated -->

## Wrapping up

Congratulations, you made it! If some things are not completely clear by now, don't hesitate to leave a comment. Apart from that, you should now have a fully configured development setup that works with PhpStorm as your IDE.

In the next part of this tutorial, we will  [use a fresh installation of Laravel on top of our setup](https://www.pascallandau.com/blog/run-laravel-9-docker-in-2022/).

Please subscribe to the [RSS feed](https://www.pascallandau.com/feed.xml) or [via email](https://www.pascallandau.com/blog/#newsletter) to get automatic notifications when this next part comes out :)