One session for backend logic and the build pipeline
In Hyva theme projects, a bug rarely lives cleanly on one side: is a Tailwind class generated wrong, or does the watcher simply never see it? PhpStorm can bundle an Xdebug session and a Node process into a single Compound configuration so both sides pause in sync.
Table of Contents
- 1. Why separate debugging breaks down on Hyva projects
- 2. The core idea: Compound configurations bundle sessions
- 3. Setting the Xdebug entry point correctly inside the container
- 4. Starting the watcher's Node process with the inspector flag
- 5. Creating the Compound configuration in the PhpStorm UI
- 6. Coordinating breakpoints across language boundaries
- 7. Practical example: tracking down a missing Tailwind class
- 8. Performance pitfalls of running two debuggers at once
- 9. When it is not worth the effort and what the alternatives are
- 10. Summary
- 11. FAQ
1. Why separate debugging breaks down on Hyva projects
A typical Hyva setup runs two processes in parallel: the Magento PHP backend rendering ViewModels and phtml templates, and a Node-based Tailwind watcher that regenerates CSS classes on every change. When a layout bug appears, it is often unclear which side is responsible: does the PHP layer produce a wrong class, or does the Tailwind scanner simply fail to detect the pattern and purge the class away.
Debugging both sides separately means constantly switching between an Xdebug session and a separate Node inspector connection in a Chrome DevTools window. The timing relationship between PHP output and Node processing gets lost, because both breakpoints can never be observed at the same moment. This is exactly where a shared debug session in PhpStorm brings both worlds into one window. For teams onboarding onto an existing Hyva project, this approach saves considerable ramp-up time, because the relationship between backend and build pipeline becomes directly visible instead of having to be painstakingly reconstructed from two separate tools.
2. The core idea: Compound configurations bundle sessions
Alongside individual Run/Debug configurations, PhpStorm offers a Compound type. A Compound configuration starts several stored configurations with a single click and keeps them active for the whole session. In our case, two child configurations are combined: a PHP Remote Debug configuration for the Docker container and an Attach to Node.js/Chrome configuration for the running watcher process.
Each child configuration keeps its own breakpoints, watches, and variable views, but appears as its own tab in the same debug tool window. When one side pauses at a breakpoint, the other keeps running unaffected until it hits its own breakpoint. That produces a shared timeline without ever switching windows or manually syncing tools.
<!-- .idea/runConfigurations/Compound_PHP_Node.xml -->
<component name="ProjectRunConfigurationManager">
<configuration name="PHP + Node Watcher" type="CompoundRunConfigurationType">
<toRun name="Listen for PHP Debug Connections" type="PHP Remote Debug" />
<toRun name="Attach Tailwind Watcher" type="NodeJSRemoteDebugRunConfigurationType" />
<method v="2" />
</configuration>
</component>
3. Setting the Xdebug entry point correctly inside the container
For PhpStorm to hit breakpoints in PHP code at all, Xdebug must be active in the container and the client_host set correctly. In a Mark Shust setup, Xdebug is enabled via the wrapper bin/xdebug enable, which loads the matching ini file and restarts the container. The path mapping in PhpStorm matters too, so the container path /var/www/html/app/code maps correctly to the local src/app/code directory.
A common pitfall is a wrong xdebug.client_host when the container runs in its own Docker network. On Linux the Docker bridge IP usually works, on macOS and Windows host.docker.internal is the more reliable choice. Without correct mapping, Xdebug connects but PhpStorm shows breakpoints as unreachable, which is often misread as an Xdebug bug rather than a networking issue.
; docker/php/xdebug.ini
zend_extension=xdebug.so
xdebug.mode=debug
xdebug.start_with_request=yes
xdebug.client_host=host.docker.internal
xdebug.client_port=9003
xdebug.idekey=PHPSTORM
4. Starting the watcher's Node process with the inspector flag
The Tailwind watcher usually runs via an npm script that keeps the build process alive in watch mode. For PhpStorm to attach via Attach to Node.js/Chrome, the Node process needs the --inspect or --inspect-brk flag, which opens a debug port. In a Docker-based npm setup, this flag is added directly to the package.json script so it is active on every start.
If the Node process also runs inside a container, the same networking consideration as for Xdebug applies: the inspector port must be exposed from the container and configured in PhpStorm's Node.js remote configuration with the correct host address. A common mistake is binding the port only internally, so PhpStorm cannot connect from outside even though the process itself starts without errors.
{
"scripts": {
"watch:tailwind": "node --inspect=0.0.0.0:9229 ./node_modules/.bin/tailwindcss -i ./web/tailwind/tailwind-source.css -o ./web/css/styles.css --watch"
}
}
5. Creating the Compound configuration in the PhpStorm UI
In practice, go to Run, Edit Configurations, and first create the two individual configurations: a PHP Remote Debug for the Docker server and an Attach to Node.js/Chrome with the host and port of the inspector socket. Only afterward do you create a new Compound configuration via the plus icon and select both existing entries as children.
A descriptive name like PHP + Node Watcher and an assigned keyboard shortcut make the configuration practical for daily use. It's worth knowing that stopping the Compound session via the stop button cleanly disconnects both child sessions, while the Docker container itself keeps running unaffected, so no environment restart is needed.
6. Coordinating breakpoints across language boundaries
The real payoff shows up when you set a PHP breakpoint in the ViewModel class that assembles a dynamic class list, and simultaneously a Node breakpoint in the watcher's transform function that writes the CSS output. When the page loads, the PHP breakpoint hits first, because rendering happens before every CSS rebuild.
Only when you save a template file the Tailwind scanner watches does the Node breakpoint fire again. Knowing this order avoids wrong conclusions, such as assuming a class is missing from the CSS when it simply has not been rebuilt yet because the watcher never observed the affected file path.
// app/code/Mironsoft/Theme/ViewModel/HeroBanner.php
public function getWrapperClasses(): string
{
$classes = ['flex', 'items-center'];
if ($this->isFullWidth()) {
$classes[] = 'w-full'; // <- set breakpoint here
}
return implode(' ', $classes);
}
7. Practical example: tracking down a missing Tailwind class
A concrete case: a conditional class built via string concatenation in a phtml template, for example 'bg-' . $color . '-500'. The Tailwind JIT scanner cannot detect this pattern statically, because it only finds complete class names in source code, not strings assembled at runtime. The debug session shows on the PHP side that the correct class name is generated.
On the Node side, a log statement in the watcher confirms the class is missing from the final CSS because it never appears as a complete string during the scan. The fix is either to write out every possible class combination explicitly, or to add it to the safelist in the Tailwind configuration so the purge step does not remove it.
// tailwind.config.js
module.exports = {
content: ['../**/*.phtml'],
safelist: [
'bg-red-500', 'bg-green-500', 'bg-blue-500',
],
};
8. Performance pitfalls of running two debuggers at once
Two active debug engines attached to one Docker container noticeably raise the latency of every step action, especially when many watch expressions are evaluated at once. Every watch observing a complex object or a large array gets recomputed at each breakpoint, which visibly slows down the IDE's response time.
In practice it helps to keep watches to the essentials and to reduce the maximum number of displayed array elements in the debugger settings. The Force step into option should also stay disabled globally, since with an active Compound session it otherwise tries to step into framework and node_modules code, making the session feel noticeably sluggish.
9. When it is not worth the effort and what the alternatives are
For simple, clearly localized bugs, a single debug session is still faster to set up than a Compound configuration. The extra effort pays off mainly for hard-to-reproduce problems that arise exactly at the boundary between server-side rendering and client-side build, such as timing issues between cache invalidation and watcher triggers.
A lightweight alternative is correlated log output: both processes write timestamps into a shared log format, so you can reconstruct the sequence of events afterward without a live debug session. For recurring analysis this is often more practical than restarting a full Compound session every time.
| Approach | Setup effort | Strength | Limit |
|---|---|---|---|
| Separate sessions | Low | Fast for isolated bugs | No visible timing relationship |
| Compound configuration | Medium, one-time | Synchronous timeline of both processes | Higher IDE overhead |
| Correlated logs | Low, ongoing | Retrospective analysis without a live session | No interactive stepping |
| Manual window switching | None | No extra setup | Timing windows easily missed |
Mironsoft
PhpStorm setup, Docker integration, and team productivity
PhpStorm that actually runs optimally for Magento and PHP projects?
We review existing PhpStorm setups for slow indexing, unused Docker integration, and missing team conventions, then set up a configuration that is productive from the first second.
Setup Review
Optimizing indexing, interpreter, and memory settings for large Magento projects.
Docker Integration
Cleanly connecting Xdebug, PHPUnit, and database tools to the Docker setup.
Team Conventions
Standardizing inspection profiles, code style, and live templates project-wide.
10. Summary
Full-Stack Debugging in PhpStorm: The Essentials at a Glance
Core tool
A Compound Run/Debug Configuration links PHP Remote Debug and Attach to Node.js/Chrome.
Prerequisite
Xdebug with the correct client_host and a Node process started with the --inspect flag.
Typical case
Bugs at the boundary between PHP rendering and the Tailwind watcher build.
Limit
For simple bugs, a single session remains faster to set up.