diff --git a/README.md b/README.md index 0e4cd82..bb16748 100644 --- a/README.md +++ b/README.md @@ -1,25 +1,24 @@ # phpdebugbar middleware [![Build Status](https://travis-ci.org/php-middleware/phpdebugbar.svg?branch=master)](https://travis-ci.org/php-middleware/phpdebugbar) -PHP Debug bar [PSR-15](https://www.php-fig.org/psr/psr-15/) middleware with [PSR-7](https://www.php-fig.org/psr/psr-7/). Also supports [PSR-11](https://www.php-fig.org/psr/psr-11/) +[PHP Debug Bar](http://phpdebugbar.com/) as framework-agnostic [PSR-15 middleware](https://www.php-fig.org/psr/psr-15/) with [PSR-7 messages](https://www.php-fig.org/psr/psr-7/) created by [PSR-17 message factories](https://www.php-fig.org/psr/psr-17/). Also provides [PSR-11 container invokable factories](https://www.php-fig.org/psr/psr-11/). -This middleware provide framework-agnostic possibility to attach [PHP Debug Bar](http://phpdebugbar.com/) to your response (html on non-html!). +Framework-agnostic way to attach [PHP Debug Bar](http://phpdebugbar.com/) to your response (html or non-html!). ## Installation ``` -composer require php-middleware/php-debug-bar +composer require --dev php-middleware/php-debug-bar ``` -To build this middleware you need to injecting inside `PhpDebugBarMiddleware` instance `DebugBar\JavascriptRenderer` (you can get it from `DebugBar\StandardDebugBar`) and add middleware to your middleware runner. Or use default factory. +To build middleware you need to inject `DebugBar\JavascriptRenderer` (you can get it from `DebugBar\StandardDebugBar`) inside `PhpDebugBarMiddleware` and add it into your middleware runner: ```php $debugbar = new DebugBar\StandardDebugBar(); $debugbarRenderer = $debugbar->getJavascriptRenderer('/phpdebugbar'); -$middleware = new PhpMiddleware\PhpDebugBar\PhpDebugBarMiddleware($debugbarRenderer); - -// OR +$middleware = new PhpMiddleware\PhpDebugBar\PhpDebugBarMiddleware($debugbarRenderer, $psr17ResponseFactory, $psr17StreamFactory); +// or use provided factory $factory = new PhpMiddleware\PhpDebugBar\PhpDebugBarMiddlewareFactory(); -$middleware = $factory(); +$middleware = $factory($psr11Container); $app = new MiddlewareRunner(); $app->add($middleware); @@ -30,40 +29,57 @@ You don't need to copy any static assets from phpdebugbar vendor! ### How to force disable or enable PHP Debug Bar? -Sometimes you want to have control when enable (or not) PHP Debug Bar: +Sometimes you want to have control when enable or disable PHP Debug Bar: * custom content negotiation, * allow to debug redirects responses. 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. +### 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). + +#### ... and PSR-11 + +If you use provided PSR-11 factories, then you container must have services registered as PSR-17 interface's name. Example for [zend-diactoros](https://github.com/zendframework/zend-diactoros) implementation and [Pimple](https://pimple.symfony.com/): + +```php +$container[Psr\Http\Message\ResponseInterface::class] = new Zend\Diactoros\ResponseFactory(); +$container[Psr\Http\Message\StreamFactoryInterface] = new Zend\Diactoros\StreamFactory(); +``` + ### How to install on Zend Expressive? -You need to register ConfigProvider and pipe provided middleware: +You need to register `PhpMiddleware\PhpDebugBar\ConfigProvider` and pipe provided middleware: ```php -$app->pipe(PhpDebugBarMiddleware::class); +$app->pipe(\PhpMiddleware\PhpDebugBar\PhpDebugBarMiddleware::class); ``` -For more follow Zend Expressive [documentation](https://docs.zendframework.com/zend-expressive/v3/features/modular-applications/). +For more - follow Zend Expressive [documentation](https://docs.zendframework.com/zend-expressive/v3/features/modular-applications/). ### How to install on Slim 3? -Add existing factory to container: +Register factories in container: ```php -$container['debugbar_middleware'] = new PhpMiddleware\PhpDebugBar\PhpDebugBarMiddlewareFactory(); +foreach (ConfigProvider::getConfig()['dependencies']['factories'] as $key => $factory) { + $container[$key] = new $factory(); +} ``` and add middleware from container to app: ```php -$app->add($app->getContainer()->get('debugbar_middleware')); +$app->add( + $app->getContainer()->get(\PhpMiddleware\PhpDebugBar\PhpDebugBarMiddleware::class) +); ``` ### How to configure using existing factories? -Put array with configuration into `config` service in your container: +Put array with configuration into `PhpMiddleware\PhpDebugBar\ConfigProvider` service in your container: ```php return [ @@ -81,10 +97,16 @@ return [ ]; ``` +You can override existing configuration by merge default configuration with your own (example): + +```php +return array_merge(PhpMiddleware\PhpDebugBar\ConfigProvider::getConfig(), $myOverritenConfig); +``` + ## It's just works with any modern php framework! Middleware tested on: * [Zend Expressive](https://github.com/zendframework/zend-expressive) * [Slim 3.x](https://github.com/slimphp/Slim) -And any other modern framework [supported middlewares and PSR-7](https://mwop.net/blog/2015-01-08-on-http-middleware-and-psr-7.html). +And any other modern framework [supported PSR-17 middlewares and PSR-7](https://mwop.net/blog/2015-01-08-on-http-middleware-and-psr-7.html). diff --git a/composer.json b/composer.json index 1adf616..23ea4cc 100644 --- a/composer.json +++ b/composer.json @@ -18,7 +18,8 @@ "psr/http-server-middleware": "^1.0", "psr/container": "^1.0", "psr/http-message": "^1.0.1", - "zendframework/zend-diactoros": "^1.1.3 || ^2.0" + "psr/http-factory": "^1.0", + "psr/http-factory-implementation": "^1.0" }, "require-dev": { "phpunit/phpunit": "^7.1.2", @@ -26,7 +27,8 @@ "slim/slim": "^3.0", "zendframework/zend-expressive": "^3.0", "zendframework/zend-expressive-fastroute": "^3.0.1", - "zendframework/zend-servicemanager": "^3.3.2" + "zendframework/zend-servicemanager": "^3.3.2", + "zendframework/zend-diactoros": "^2.0" }, "autoload": { "psr-4": { diff --git a/config/dependency.config.php b/config/dependency.config.php index d9f21cf..1680268 100644 --- a/config/dependency.config.php +++ b/config/dependency.config.php @@ -2,7 +2,10 @@ return [ 'factories' => [ - PhpMiddleware\PhpDebugBar\PhpDebugBarMiddleware::class => PhpMiddleware\PhpDebugBar\PhpDebugBarMiddlewareFactory::class, - DebugBar\DataCollector\ConfigCollector::class => PhpMiddleware\PhpDebugBar\ConfigCollectorFactory::class, + \PhpMiddleware\PhpDebugBar\PhpDebugBarMiddleware::class => \PhpMiddleware\PhpDebugBar\PhpDebugBarMiddlewareFactory::class, + \DebugBar\DataCollector\ConfigCollector::class => \PhpMiddleware\PhpDebugBar\ConfigCollectorFactory::class, + \PhpMiddleware\PhpDebugBar\ConfigProvider::class => \PhpMiddleware\PhpDebugBar\ConfigProvider::class, + \DebugBar\DebugBar::class => \PhpMiddleware\PhpDebugBar\StandardDebugBarFactory::class, + \DebugBar\JavascriptRenderer::class => \PhpMiddleware\PhpDebugBar\JavascriptRendererFactory::class, ], ]; diff --git a/phpunit.xml b/phpunit.xml index 0344107..b0c7e5f 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -2,7 +2,7 @@ - + ./test diff --git a/src/ConfigCollectorFactory.php b/src/ConfigCollectorFactory.php index faed230..69e3ffa 100644 --- a/src/ConfigCollectorFactory.php +++ b/src/ConfigCollectorFactory.php @@ -10,7 +10,7 @@ final class ConfigCollectorFactory { public function __invoke(ContainerInterface $container): ConfigCollector { - $data = $container->get('config'); + $data = $container->get(ConfigProvider::class); return new ConfigCollector($data, 'Config'); } diff --git a/src/JavascriptRendererFactory.php b/src/JavascriptRendererFactory.php index 6fa7ec4..c47fef0 100644 --- a/src/JavascriptRendererFactory.php +++ b/src/JavascriptRendererFactory.php @@ -9,26 +9,13 @@ final class JavascriptRendererFactory { - public function __invoke(ContainerInterface $container = null): JavascriptRenderer + public function __invoke(ContainerInterface $container): JavascriptRenderer { - if ($container === null || !$container->has(DebugBar::class)) { - $debugbar = (new StandardDebugBarFactory())($container); - } else { - $debugbar = $container->get(DebugBar::class); - } + $debugbar = $container->get(DebugBar::class); + $config = $container->get(ConfigProvider::class); + $rendererOptions = $config['phpmiddleware']['phpdebugbar']['javascript_renderer']; $renderer = new JavascriptRenderer($debugbar); - - $config = $container !== null && $container->has('config') ? $container->get('config') : []; - - if (isset($config['phpmiddleware']['phpdebugbar']['javascript_renderer'])) { - $rendererOptions = $config['phpmiddleware']['phpdebugbar']['javascript_renderer']; - } else { - $rendererOptions = [ - 'base_url' => '/phpdebugbar', - ]; - } - $renderer->setOptions($rendererOptions); return $renderer; diff --git a/src/PhpDebugBarMiddleware.php b/src/PhpDebugBarMiddleware.php index 5331054..55cd28f 100644 --- a/src/PhpDebugBarMiddleware.php +++ b/src/PhpDebugBarMiddleware.php @@ -5,31 +5,34 @@ use DebugBar\JavascriptRenderer as DebugBarRenderer; use Psr\Http\Message\MessageInterface; +use Psr\Http\Message\ResponseFactoryInterface; use Psr\Http\Message\ResponseInterface as Response; use Psr\Http\Message\ServerRequestInterface as ServerRequest; +use Psr\Http\Message\StreamFactoryInterface; use Psr\Http\Message\UriInterface; use Psr\Http\Server\MiddlewareInterface; use Psr\Http\Server\RequestHandlerInterface as RequestHandler; use Slim\Http\Uri as SlimUri; -use Zend\Diactoros\Response as DiactorosResponse; -use Zend\Diactoros\Response\HtmlResponse; -use Zend\Diactoros\Response\Serializer; -use Zend\Diactoros\Stream; /** - * PhpDebugBarMiddleware - * * @author Witold Wasiczko */ final class PhpDebugBarMiddleware implements MiddlewareInterface { public const FORCE_KEY = 'X-Enable-Debug-Bar'; - protected $debugBarRenderer; + private $debugBarRenderer; + private $responseFactory; + private $streamFactory; - public function __construct(DebugBarRenderer $debugbarRenderer) - { + public function __construct( + DebugBarRenderer $debugbarRenderer, + ResponseFactoryInterface $responseFactory, + StreamFactoryInterface $streamFactory + ) { $this->debugBarRenderer = $debugbarRenderer; + $this->responseFactory = $responseFactory; + $this->streamFactory = $streamFactory; } /** @@ -79,16 +82,20 @@ public function handle(ServerRequest $request): Response return $this->process($request, $handler); } - private function prepareHtmlResponseWithDebugBar(Response $response): HtmlResponse + private function prepareHtmlResponseWithDebugBar(Response $response): Response { $head = $this->debugBarRenderer->renderHead(); $body = $this->debugBarRenderer->render(); - $outResponseBody = Serializer::toString($response); + $outResponseBody = $this->serializeResponse($response); $template = '%s

DebugBar

Response:

%s
%s'; $escapedOutResponseBody = htmlspecialchars($outResponseBody); $result = sprintf($template, $head, $escapedOutResponseBody, $body); - return new HtmlResponse($result); + $stream = $this->streamFactory->createStream($result); + + return $this->responseFactory->createResponse(200) + ->withBody($stream) + ->withAddedHeader('Content-type', 'text/html'); } private function attachDebugBarToResponse(Response $response): Response @@ -122,11 +129,11 @@ private function getStaticFile(UriInterface $uri): ?Response } $contentType = $this->getContentTypeByFileName($fullPathToFile); - $stream = new Stream($fullPathToFile, 'r'); + $stream = $this->streamFactory->createStreamFromResource(fopen($fullPathToFile, 'r')); - return new DiactorosResponse($stream, 200, [ - 'Content-type' => $contentType, - ]); + return $this->responseFactory->createResponse(200) + ->withBody($stream) + ->withAddedHeader('Content-type', $contentType); } private function extractPath(UriInterface $uri): string @@ -180,4 +187,47 @@ private function isRedirect(Response $response): bool return ($statusCode >= 300 || $statusCode < 400) && $response->getHeaderLine('Location') !== ''; } + + private function serializeResponse(Response $response) : string + { + $reasonPhrase = $response->getReasonPhrase(); + $headers = $this->serializeHeaders($response->getHeaders()); + $body = (string) $response->getBody(); + $format = 'HTTP/%s %d%s%s%s'; + + if (! empty($headers)) { + $headers = "\r\n" . $headers; + } + + $headers .= "\r\n\r\n"; + + return sprintf( + $format, + $response->getProtocolVersion(), + $response->getStatusCode(), + ($reasonPhrase ? ' ' . $reasonPhrase : ''), + $headers, + $body + ); + } + + private function serializeHeaders(array $headers) : string + { + $lines = []; + foreach ($headers as $header => $values) { + $normalized = $this->filterHeader($header); + foreach ($values as $value) { + $lines[] = sprintf('%s: %s', $normalized, $value); + } + } + + return implode("\r\n", $lines); + } + + private function filterHeader(string $header) : string + { + $filtered = str_replace('-', ' ', $header); + $filtered = ucwords($filtered); + return str_replace(' ', '-', $filtered); + } } diff --git a/src/PhpDebugBarMiddlewareFactory.php b/src/PhpDebugBarMiddlewareFactory.php index 27dfea3..0e0cabc 100644 --- a/src/PhpDebugBarMiddlewareFactory.php +++ b/src/PhpDebugBarMiddlewareFactory.php @@ -5,16 +5,17 @@ use DebugBar\JavascriptRenderer; use Psr\Container\ContainerInterface; +use Psr\Http\Message\ResponseFactoryInterface; +use Psr\Http\Message\StreamFactoryInterface; final class PhpDebugBarMiddlewareFactory { - public function __invoke(ContainerInterface $container = null): PhpDebugBarMiddleware + public function __invoke(ContainerInterface $container): PhpDebugBarMiddleware { - if ($container === null || !$container->has(JavascriptRenderer::class)) { - $renderer = (new JavascriptRendererFactory())($container); - } else { - $renderer = $container->get(JavascriptRenderer::class); - } - return new PhpDebugBarMiddleware($renderer); + $renderer = $container->get(JavascriptRenderer::class); + $responseFactory = $container->get(ResponseFactoryInterface::class); + $streamFactory = $container->get(StreamFactoryInterface::class); + + return new PhpDebugBarMiddleware($renderer, $responseFactory, $streamFactory); } } diff --git a/src/StandardDebugBarFactory.php b/src/StandardDebugBarFactory.php index b21ac63..21f331f 100644 --- a/src/StandardDebugBarFactory.php +++ b/src/StandardDebugBarFactory.php @@ -8,24 +8,25 @@ final class StandardDebugBarFactory { - public function __invoke(ContainerInterface $container = null): StandardDebugBar + public function __invoke(ContainerInterface $container): StandardDebugBar { $debugBar = new StandardDebugBar(); - if ($container !== null) { - $config = $container->has('config') ? $container->get('config') : []; + $config = $container->get(ConfigProvider::class); - $collectors = $config['phpmiddleware']['phpdebugbar']['collectors'] ?: []; + $collectors = $config['phpmiddleware']['phpdebugbar']['collectors']; - foreach ($collectors as $collectorName) { - $collector = $container->get($collectorName); - $debugBar->addCollector($collector); - } + foreach ($collectors as $collectorName) { + $collector = $container->get($collectorName); + $debugBar->addCollector($collector); + } + + $storage = $config['phpmiddleware']['phpdebugbar']['storage']; - if (isset($config['phpmiddleware']['phpdebugbar']['storage']) && is_string($config['phpmiddleware']['phpdebugbar']['storage'])) { - $storage = $container->get($config['phpmiddleware']['phpdebugbar']['storage']); - $debugBar->setStorage($config['phpmiddleware']['phpdebugbar']['storage']); - } + if (is_string($storage)) { + $debugBar->setStorage( + $container->get($storage) + ); } return $debugBar; diff --git a/test/PhpDebugBarMiddlewareFactoryTest.php b/test/PhpDebugBarMiddlewareFactoryTest.php deleted file mode 100644 index 0082589..0000000 --- a/test/PhpDebugBarMiddlewareFactoryTest.php +++ /dev/null @@ -1,23 +0,0 @@ - - */ -class PhpDebugBarMiddlewareFactoryTest extends TestCase -{ - public function testFactory(): void - { - $factory = new PhpDebugBarMiddlewareFactory(); - - $result = $factory(); - - $this->assertInstanceOf(PhpDebugBarMiddleware::class, $result); - } -} diff --git a/test/PhpDebugBarMiddlewareTest.php b/test/PhpDebugBarMiddlewareTest.php index 9f912c7..f4bde3d 100644 --- a/test/PhpDebugBarMiddlewareTest.php +++ b/test/PhpDebugBarMiddlewareTest.php @@ -8,7 +8,9 @@ use PhpMiddleware\PhpDebugBar\PhpDebugBarMiddleware; use PHPUnit\Framework\TestCase; use Zend\Diactoros\Response; +use Zend\Diactoros\ResponseFactory; use Zend\Diactoros\ServerRequest; +use Zend\Diactoros\StreamFactory; use Zend\Diactoros\Uri; /** @@ -26,8 +28,10 @@ protected function setUp() $this->debugbarRenderer = $this->getMockBuilder(JavascriptRenderer::class)->disableOriginalConstructor()->getMock(); $this->debugbarRenderer->method('renderHead')->willReturn('RenderHead'); $this->debugbarRenderer->method('render')->willReturn('RenderBody'); + $responseFactory = new ResponseFactory(); + $streamFactory = new StreamFactory(); - $this->middleware = new PhpDebugBarMiddleware($this->debugbarRenderer); + $this->middleware = new PhpDebugBarMiddleware($this->debugbarRenderer, $responseFactory, $streamFactory); } public function testTwoPassCallingForCompatibility(): void diff --git a/test/Slim3Test.php b/test/Slim3Test.php index 26959b0..31fea8e 100644 --- a/test/Slim3Test.php +++ b/test/Slim3Test.php @@ -3,22 +3,35 @@ namespace PhpMiddlewareTest\PhpDebugBar; -use PhpMiddleware\PhpDebugBar\PhpDebugBarMiddlewareFactory; +use PhpMiddleware\PhpDebugBar\ConfigProvider; +use PhpMiddleware\PhpDebugBar\PhpDebugBarMiddleware; +use Psr\Http\Message\ResponseFactoryInterface; use Psr\Http\Message\ResponseInterface; +use Psr\Http\Message\StreamFactoryInterface; use Slim\App; use Slim\Http\Environment; +use Zend\Diactoros\ResponseFactory; +use Zend\Diactoros\StreamFactory; final class Slim3Test extends AbstractMiddlewareRunnerTest { protected function dispatchApplication(array $server, array $pipe = []): ResponseInterface { $app = new App(); - $app->getContainer()['environment'] = function() use ($server) { + $container = $app->getContainer(); + $container[ResponseFactoryInterface::class] = new ResponseFactory(); + $container[StreamFactoryInterface::class] = new StreamFactory(); + $container['environment'] = function() use ($server) { return new Environment($server); }; - $middlewareFactory = new PhpDebugBarMiddlewareFactory(); - $middleware = $middlewareFactory(); + $config = ConfigProvider::getConfig(); + + foreach ($config['dependencies']['factories'] as $key => $factory) { + $container[$key] = new $factory(); + } + + $middleware = $container->get(PhpDebugBarMiddleware::class); $app->add($middleware); diff --git a/test/ZendExpressiveTest.php b/test/ZendExpressiveTest.php index 52bdaea..e5e99bd 100644 --- a/test/ZendExpressiveTest.php +++ b/test/ZendExpressiveTest.php @@ -6,10 +6,14 @@ use PhpMiddleware\PhpDebugBar\ConfigProvider; use PhpMiddleware\PhpDebugBar\PhpDebugBarMiddleware; use Psr\Container\ContainerInterface; +use Psr\Http\Message\ResponseFactoryInterface; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; +use Psr\Http\Message\StreamFactoryInterface; use Zend\Diactoros\Response; +use Zend\Diactoros\ResponseFactory; use Zend\Diactoros\ServerRequestFactory; +use Zend\Diactoros\StreamFactory; use Zend\Expressive\Container\ApplicationFactory; use Zend\Expressive\Container\MiddlewareContainerFactory; use Zend\Expressive\Container\MiddlewareFactoryFactory; @@ -98,8 +102,12 @@ private function createContainer(array $server): ContainerInterface $serviceManagerConfig['factories'][ResponseInterface::class] = ResponseFactoryFactory::class; $serviceManagerConfig['factories'][RouteMiddleware::class] = RouteMiddlewareFactory::class; $serviceManagerConfig['factories'][DispatchMiddleware::class] = DispatchMiddlewareFactory::class; + $serviceManagerConfig['factories'][ResponseFactory::class] = InvokableFactory::class; + $serviceManagerConfig['factories'][StreamFactory::class] = InvokableFactory::class; $serviceManagerConfig['aliases'][RouterInterface::class] = FastRouteRouter::class; $serviceManagerConfig['aliases'][\Zend\Expressive\ApplicationPipeline::class] = MiddlewarePipe::class; + $serviceManagerConfig['aliases'][ResponseFactoryInterface::class] = ResponseFactory::class; + $serviceManagerConfig['aliases'][StreamFactoryInterface::class] = StreamFactory::class; return new ServiceManager($serviceManagerConfig); }