Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 29 additions & 10 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
@@ -1,46 +1,65 @@
name: CI
on:
- push
push:
branches:
- master
pull_request:
jobs:
phpstan:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
uses: actions/checkout@v4

- name: Setup PHP, with composer and extensions
uses: shivammathur/setup-php@v2
with:
php-version: 7.3
php-version: '8.0'

- name: Install dependencies with Composer
uses: ramsey/composer-install@v1
uses: ramsey/composer-install@v3

- name: Run phpstan
run: vendor/bin/phpstan analyse --level=6 src/
tests:
strategy:
fail-fast: false
matrix:
dependencies:
- highest
- lowest
# PHP 7.3 and 7.4 are not built here: no laminas-diactoros release
# that Composer 2.10+ accepts as a psr/http-factory-implementation
# provider supports them, so the dev dependencies cannot even be
# installed on those versions.
php-versions:
- 7.3
- 7.4
- 8.0
- 8.1
- '8.0'
- '8.1'
- '8.2'
- '8.3'
exclude:
# Lowest bounds of the dev dependencies (Slim 3.0, Pimple 3.0,
# vfsStream 1.6.8) were released before PHP 8.1 and fatal on load
# there. That is a limitation of those old releases, not of this
# library: every tested PHP version is covered by the highest jobs.
- dependencies: lowest
php-versions: '8.1'
- dependencies: lowest
php-versions: '8.2'
- dependencies: lowest
php-versions: '8.3'
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
uses: actions/checkout@v4

- name: Setup PHP, with composer and extensions
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php-versions }}

- name: Install dependencies with Composer
uses: ramsey/composer-install@v1
uses: ramsey/composer-install@v3
with:
dependency-versions: ${{ matrix.dependencies }}

Expand Down
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,14 @@ Sometimes you want to have control when enable or disable PHP Debug Bar:
We allow you to disable attaching phpdebugbar using `X-Enable-Debug-Bar: false` header, cookie or request attribute.
To force enable just send request with `X-Enable-Debug-Bar` header, cookie or request attribute with `true` value.

### AJAX requests

Requests sent with `X-Requested-With: XMLHttpRequest` are attached to the debug bar
already initialized by the main request: the middleware appends only the collected data
of the AJAX request (rendered as a `(ajax)` dataset) instead of the initialization code
and assets. Without it every AJAX response would create another debug bar on top of the
existing one.

### PSR-17

This package isn't require any PSR-7 implementation - you need to provide it by own. Middleware require ResponseFactory and StreamFactory interfaces. [List of existing interfaces](https://packagist.org/providers/psr/http-factory-implementation).
Expand Down
3 changes: 2 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
],
"require": {
"php": "^7.3 || ^8.0",
"maximebf/debugbar": "^1.4",
"maximebf/debugbar": "^1.18",
"psr/http-server-handler": "^1.0",
"psr/http-server-middleware": "^1.0",
"psr/container-implementation": "^1.0 || ^2.0",
Expand All @@ -23,6 +23,7 @@
"require-dev": {
"phpunit/phpunit": "^9.1.4",
"mikey179/vfsstream": "^1.6.8",
"symfony/var-dumper": "^4.4.30",
"slim/slim": "^3.0",
"mezzio/mezzio": "^3.0",
"mezzio/mezzio-fastroute": "^3.0.1",
Expand Down
19 changes: 15 additions & 4 deletions src/PhpDebugBarMiddleware.php
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ public function process(ServerRequest $request, RequestHandler $handler): Respon
}

if ($this->isHtmlResponse($response)) {
return $this->attachDebugBarToHtmlResponse($response);
return $this->attachDebugBarToHtmlResponse($response, !$this->isXmlHttpRequest($request));
}

return $this->prepareHtmlResponseWithDebugBar($response);
Expand Down Expand Up @@ -122,10 +122,16 @@ private function prepareHtmlResponseWithDebugBar(Response $response): Response
->withAddedHeader('Content-type', 'text/html');
}

private function attachDebugBarToHtmlResponse(Response $response): Response
/**
* @param bool $initialize Render the debug bar initialization code and assets.
* Must be false for responses attached to an already
* initialized debug bar (XMLHttpRequest), otherwise a
* second debug bar is created on top of the existing one.
*/
private function attachDebugBarToHtmlResponse(Response $response, bool $initialize = true): Response
{
$head = $this->debugBarRenderer->renderHead();
$body = $this->debugBarRenderer->render();
$head = $initialize ? $this->debugBarRenderer->renderHead() : '';
$body = $this->debugBarRenderer->render($initialize);
$responseBody = $response->getBody();

if (! $responseBody->eof() && $responseBody->isSeekable()) {
Expand Down Expand Up @@ -205,6 +211,11 @@ private function isHtml(MessageInterface $message, string $headerName): bool
return strpos($message->getHeaderLine($headerName), 'text/html') !== false;
}

private function isXmlHttpRequest(ServerRequest $request): bool
{
return strtolower($request->getHeaderLine('X-Requested-With')) === 'xmlhttprequest';
}

private function isRedirect(Response $response): bool
{
$statusCode = $response->getStatusCode();
Expand Down
21 changes: 21 additions & 0 deletions test/AbstractMiddlewareRunnerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,27 @@ final public function testAppendJsIntoHtmlContent(): void
$this->assertStringContainsString('"/phpdebugbar/debugbar.js"', $responseBody);
}

final public function testNotAppendInitializationCodeIntoXmlHttpRequestContent(): void
{
$response = $this->dispatchApplication([
'REQUEST_URI' => '/hello',
'REQUEST_METHOD' => 'GET',
'HTTP_ACCEPT' => 'text/html',
'HTTP_X_REQUESTED_WITH' => 'XMLHttpRequest',
], [
'/hello' => function (ServerRequestInterface $request) {
return new Response\HtmlResponse('Hello!');
},
]);

$responseBody = (string) $response->getBody();

$this->assertStringContainsString('Hello!', $responseBody);
$this->assertStringContainsString('phpdebugbar.addDataSet(', $responseBody);
$this->assertStringNotContainsString('var phpdebugbar = new PhpDebugBar.DebugBar();', $responseBody);
$this->assertStringNotContainsString('"/phpdebugbar/debugbar.js"', $responseBody);
}

final public function testGetStatics(): void
{
$response = $this->dispatchApplication([
Expand Down
4 changes: 4 additions & 0 deletions test/MezzioTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
use Mezzio\Container\ServerRequestErrorResponseGeneratorFactory;
use Mezzio\MiddlewareContainer;
use Mezzio\MiddlewareFactory;
use Mezzio\MiddlewareFactoryInterface;
use Mezzio\Response\ServerRequestErrorResponseGenerator;
use Mezzio\Router\FastRouteRouter;
use Mezzio\Router\FastRouteRouterFactory;
Expand All @@ -34,6 +35,7 @@
use Mezzio\Router\RouterInterface;
use Laminas\HttpHandlerRunner\Emitter\EmitterInterface;
use Laminas\HttpHandlerRunner\RequestHandlerRunner;
use Laminas\HttpHandlerRunner\RequestHandlerRunnerInterface;
use Laminas\ServiceManager\Factory\InvokableFactory;
use Laminas\ServiceManager\ServiceManager;
use Laminas\Stratigility\MiddlewarePipe;
Expand Down Expand Up @@ -104,6 +106,8 @@ private function createContainer(array $server): ContainerInterface
$serviceManagerConfig['factories'][DispatchMiddleware::class] = DispatchMiddlewareFactory::class;
$serviceManagerConfig['factories'][ResponseFactory::class] = InvokableFactory::class;
$serviceManagerConfig['factories'][StreamFactory::class] = InvokableFactory::class;
$serviceManagerConfig['aliases'][MiddlewareFactoryInterface::class] = MiddlewareFactory::class;
$serviceManagerConfig['aliases'][RequestHandlerRunnerInterface::class] = RequestHandlerRunner::class;
$serviceManagerConfig['aliases'][RouterInterface::class] = FastRouteRouter::class;
$serviceManagerConfig['aliases'][\Mezzio\ApplicationPipeline::class] = MiddlewarePipe::class;
$serviceManagerConfig['aliases'][ResponseFactoryInterface::class] = ResponseFactory::class;
Expand Down
43 changes: 42 additions & 1 deletion test/PhpDebugBarMiddlewareTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,9 @@ protected function setUp(): void
$this->debugbarRenderer = $this->getMockBuilder(JavascriptRenderer::class)->disableOriginalConstructor()->getMock();
$this->debugbarRenderer->method('renderHead')->willReturn('RenderHead');
$this->debugbarRenderer->method('getBaseUrl')->willReturn('/phpdebugbar');
$this->debugbarRenderer->method('render')->willReturn('RenderBody');
$this->debugbarRenderer->method('render')->willReturnCallback(function (bool $initialize = true): string {
return $initialize ? 'RenderBody' : 'RenderBodyWithoutInit';
});
$responseFactory = new ResponseFactory();
$streamFactory = new StreamFactory();

Expand Down Expand Up @@ -244,6 +246,45 @@ public function testForceNotAttachDebugbarIfAttributePresents(): void
$this->assertSame('ResponseBody', (string) $result->getBody());
}

public function testNotRenderInitializationCodeForXmlHttpRequest(): void
{
$request = new ServerRequest([], [], null, null, 'php://input', ['Accept' => 'text/html', 'X-Requested-With' => 'XMLHttpRequest']);
$response = new Response('php://memory', 200, ['Content-Type' => 'text/html']);
$response->getBody()->write('ResponseBody');
$requestHandler = new RequestHandlerStub($response);

$result = $this->middleware->process($request, $requestHandler);

$this->assertTrue($requestHandler->isCalled(), 'Request handler is not called');
$this->assertSame($response, $result);
$this->assertSame('ResponseBodyRenderBodyWithoutInit', (string) $result->getBody());
}

public function testNotRenderInitializationCodeForLowercasedXmlHttpRequestHeaderValue(): void
{
$request = new ServerRequest([], [], null, null, 'php://input', ['Accept' => 'text/html', 'X-Requested-With' => 'xmlhttprequest']);
$response = new Response('php://memory', 200, ['Content-Type' => 'text/html']);
$response->getBody()->write('ResponseBody');
$requestHandler = new RequestHandlerStub($response);

$result = $this->middleware->process($request, $requestHandler);

$this->assertSame('ResponseBodyRenderBodyWithoutInit', (string) $result->getBody());
}

public function testNotAttachDebugbarToXmlHttpRequestIfForceDisabled(): void
{
$request = new ServerRequest([], [], null, null, 'php://input', ['Accept' => 'text/html', 'X-Requested-With' => 'XMLHttpRequest', 'X-Enable-Debug-Bar' => 'false']);
$response = new Response('php://memory', 200, ['Content-Type' => 'text/html']);
$response->getBody()->write('ResponseBody');
$requestHandler = new RequestHandlerStub($response);

$result = $this->middleware->process($request, $requestHandler);

$this->assertSame($response, $result);
$this->assertSame('ResponseBody', (string) $result->getBody());
}

public function testAppendsToEndOfHtmlResponse(): void
{
$html = '<html><head><title>Foo</title></head><body>Content</body>';
Expand Down
Loading