Pragmatism in the real world

Step-debugging linked composer dependencies with PhpStorm

One project I’m working on has multiple separate parts in different git repositories that are brought into the main project using linked composer directories. I needed to get step debugging working in PhpStorm and this is the approach I took.

Directory layout

My project is laid out on disk like this:

├── main-app/
│   ├── public/
│   ├── src/
│   ├── tests/
│   ├── vendor/
│   └── composer.json
└── plugin-one/
    ├── src/
    └── tests/

As you can see I have main-app and plugin-one which are the two sets of source code that I need to work on.

Linked directories in composer

We need to set up ./plugin-one/ as a composer dependency of ./main-app/ such that it appears under ./main-app/vendor/client/plugin-one/, but set-up so that we can edit the original files and have main-app pick up the changes.

This is done by setting up a repository in our composer.json that points to the relevant path using a symlink. These are know as Path repositories and look like this in ./main-app/composer.json:

  "repositories": [
      "type": "path",
      "url": "../plugin-one"
      "options": {
        "symlink": true

I can then set my require like this:

  "require": {
    "client/plugin-one": "@dev",

The net result is that ./main-app/vendor/client/plugin-one is a symlink to ./plugin-one

Setting up PhpStorm

We create a PhpStorm project for ./main-app and then add plug-one to it as a “content root”:

  • Go to Preferences -> Directories
  • Click “+ Add Content Root” add add {full path}/plugin-one
  • Click “Apply” and then “OK” to close the Preferences dialog

We now have the same source code in the project twice (once in ./plugin-one and once in ./main-app/vendor/client/plugin-one), so we need to excluded the vendor directory:

  • In the Project tree view, find the main-app/vendor/client/plugin-one folder and select it
  • Right click -> Mark Directory As -> Excluded

The folder will now be coloured orange, so we know it’s excluded.

Set-up for debugging

PhpStorm now knows about our source code, so next we need to map it to the server. I’m using Docker for this project, but it’s the for any remote-like project.

  • Go to Preferences -> Languages & Frameworks -> PHP -> Servers
  • Add a new entry:
    • Name: {dev-hostname-for-main-app}
    • Host: {dev-hostname-for-main-app}
    • Port: 8888 (or whatever)
    • Debugger: Xdebug (of course!)
    • [✓] Use path mappings
    • In the File/Directory list set up these mappings:
      • {full path on local disk}/main-app => /var/www/html/main-app
      • {full path on local disk}/plugin-one => /var/www/html/plugin-one

The key bit for this article is that path mapping section. The "/var/www/html" is whatever the correct path is on your Docker/Vagrant/whatever host.

Finally, click the menu item Run -> Start Listening for PHP Debug Connections. Note that if this is already selected, then the menu item does not exist and the menu item Stop Listening for PHP Debug Connections is in its place.

Start debugging

You can now set breakpoints in plugin-one/src/whatever.php and PhpStorm will stop on them.

If you are debugging a website using a browser then get the relevant Xdebug extension for Chrome or Firefox.

If it’s an API, then you’ll need to set a cookie to enable Xdebug for the request:

curl --cookie 'XDEBUG_SESSION=PHPSTORM; path=/' "http://{dev-hostname-for-main-app}:8888/foo/bar"

Translate to your API client of choice!


This worked for me, hopefully it’ll work for you too if you have a similar situation!