Skip to content

Support XDebug step debugging #70

Open
@marc-mabe

Description

@marc-mabe

Feature Request

Q A
New Feature yes
RFC no
BC Break no

Summary

Debugging swoole applications is much harder as 1. XDebug step debugging does not work out-of-the box and 2. because printing debug messages get send into nirvana.

Both are not related a mezzio swoole but to the nature how the swoole webserver works.

I think I found a good way to support xdebug step debugging requests by manually reproducing what xdebug does normally to start a debug session on http request and would like to get your thoughts about this.

PS: Currently this is a prov-of-concept for step debugging only but it should also be possible for profiling and other debugging sessions.

Requirements to make it work:

  • xdebug installed and debug mode enabled
  • coroutines turned off (Actually didn't test with coroutines turned on yet)
  • Add XDebugSessionRequestListener as first RequestEvent listener ín development.config.php
class XDebugSessionRequestListener
{
    private bool $isAvailable = false;

    private string $startWithRequest = 'trigger';

    private string $triggerValue = '';

    public function __construct()
    {
        if (!\extension_loaded('xdebug')) {
            return;
        }

        $mode = (string)(\getenv('XDEBUG_MODE') ?: \ini_get('xdebug.mode') ?? '');
        if (!\in_array('debug', \explode(',', $mode), true)) {
            return;
        }

        // env XDEBUG_CONFIG
        $envConfig        = [];
        $envConfigEntries = \explode(' ', (string)\getenv('XDEBUG_CONFIG'));
        foreach ($envConfigEntries as $envConfigEntry) {
            $envConfigSplit                = \explode('=', $envConfigEntry);
            $envConfig[$envConfigSplit[0]] = $envConfigSplit[1] ?? null;
        }

        $startWithRequest = $envConfig['start_with_request'] ?? (string)\ini_get('xdebug.start_with_request');
        if ($startWithRequest === 'no') {
            return;
        }

        // Normalize "xdebug.start_with_request" setting
        // as the value "default" depends on "xdebug.mode":
        //    debug: trigger
        //    gcstats: no
        //    profile: yes
        //    trace: trigger
        // -> We already expect "xdebug.mode" to include "debug"
        if ($startWithRequest === 'default') {
            $startWithRequest = 'trigger';
        }

        $this->startWithRequest = $startWithRequest;
        $this->triggerValue     = $envConfig['trigger_value'] ?? (string)\ini_get('xdebug.trigger_value');
        $this->isAvailable      = true;
    }

    public function __invoke(RequestEvent $event): void
    {
        if (!$this->isAvailable) {
            return;
        }

        // XDebug is configured to auto start on each request
        if ($this->startWithRequest === 'yes') {
            $this->start();
            return;
        }

        if ($this->startWithRequest !== 'trigger') {
            return;
        }

        $request = $event->getRequest();

        $requestValue = $request->cookie['XDEBUG_TRIGGER']
            ?? $request->get['XDEBUG_TRIGGER']
            ?? $request->post['XDEBUG_TRIGGER']
            ?? null;

        // legacy triggers
        $requestValue ??= $request->cookie['XDEBUG_SESSION']
            ?? $request->get['XDEBUG_SESSION']
            ?? $request->post['XDEBUG_SESSION']
            ?? null;

        if ($requestValue === $this->triggerValue || ($requestValue !== null && !$this->triggerValue)) {
            $this->start();
        }
    }

    private function start(): void
    {
        if (\function_exists('xdebug_connect_to_client')) {
            // XDebug >= 3.1
            \xdebug_connect_to_client();
        } else {
            // XDebug < 3.1 we have to set a breakpoint
            \xdebug_break();
        }
    }
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    EnhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions