Skip to content

Conversation

@datamweb
Copy link
Contributor

@datamweb datamweb commented Jan 1, 2026

Description
This PR enhances the DebugToolbar's detection logic to support third-party libraries that bypass the framework's Response object (e.g., Dompdf).

Currently, CodeIgniter relies solely on its internal Response object to decide whether to inject the toolbar. However, many libraries output content directly using native PHP header() and echo. In these cases, the Toolbar assumes the content is HTML and injects itself, which is often undesirable for binary streams.

I've implemented a Native State Awareness mechanism in Toolbar::prepare(). The toolbar now acts as a secondary guard, validating the environment's actual state before injection.

This issue has been raised before by several community members on the forum. At the time, we were unable to clearly identify the root cause, as the behavior only manifests when third-party libraries bypass the framework’s abstraction layer and interact directly with native PHP output mechanisms.

After revisiting the problem with a deeper analysis of the runtime behavior, I was able to trace the issue to a mismatch between the framework’s assumed response state and the actual native PHP state. Based on that insight, this PR introduces a more resilient detection mechanism that addresses not only the originally reported cases, but also a broader class of similar issues.

See: https://forum.codeigniter.com/showthread.php?tid=82461

Checklist:

  • Securely signed commits
  • Component(s) with PHPDoc blocks, only if necessary or adds value (without duplication)
  • Unit testing, with >80% coverage
  • User guide updated
  • Conforms to style guide

@datamweb datamweb marked this pull request as draft January 1, 2026 08:03
@michalsn
Copy link
Member

michalsn commented Jan 1, 2026

I haven't investigated this problem, but my first impression is that we should rather make it as simple as possible.

We can check for $response->hasHeader('Debugbar-Skip') response header and then skip the Toolbar.

We can add a new method to the Response class:

public function withoutDebugbar()
{
    if (CI_DEBUG) {
        $this->setHeader('Debugbar-Skip', 'true');
    }

    return $this;
}

This won't resolve anything automatically, but it will provide developers with the tools to remove the Debugbar when needed.

@datamweb
Copy link
Contributor Author

datamweb commented Jan 2, 2026

@michalsn Thanks for the feedback!

I completely agree that keeping things simple and explicit is key. The withoutDebugbar() methde is a great addition for standard CI4 responses.

However, the specific problem this PR aims to solve is related to third-party libraries (like Dompdf, ...) that bypass the framework's Response object entirely.

In these scenarios:
The library outputs content directly using native echo and header().
https://github.com/dompdf/dompdf/blob/a098928a5a17ffffbf4be755dc7dfc7c0e4c83fc/src/Adapter/CPDF.php#L964-L985

The developer often doesn't interact with $this->response at all. Even if they call $this->response->withoutDebugbar(), it might happen after the library has already flushed the output, or the Toolbar might inject itself because it doesn't see the native Content-Type set by the library.

I’ve done my best to explain the topic as clearly and completely as possible, even though putting it into words is a bit challenging for me.
For better understanding, please take a look at the output of the code below.

<?php

namespace App\Controllers;

class Home extends BaseController
{
    public function index()
    {

        if (! headers_sent()) {
            header('Content-Type: application/pdf');
            header('X-Library: Dompdf');
        }

        $ciHeaders = $this->response->headers();

        $nativeHeaders = headers_list();
        
        header_remove('Content-Type');
        header('Content-Type: text/plain');

        echo "=== REALITY CHECK ===\n";
        echo "What PHP Native says (Actual Output):\n";

        foreach ($nativeHeaders as $h) {
            echo ' - ' . $h . "\n";
        }

        echo "\n=== CI4 DELUSION ===\n";
        echo "What \$this->response thinks (Framework Object):\n";
        if (empty($ciHeaders)) {
            echo " - (Empty / Default text/html)\n";
        } else {
            foreach ($ciHeaders as $h) {
                echo ' - ' . $h->getValueLine() . "\n";
            }
        }

        exit;
    }
}

From my perspective, this PR is now ready for review.

The changelogs will be uploaded later.

@datamweb datamweb marked this pull request as ready for review January 2, 2026 07:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants