Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Drop compatibility with callable stream factories on ImplicitHeadMiddleware #68

Merged
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
18 changes: 1 addition & 17 deletions psalm-baseline.xml
Original file line number Diff line number Diff line change
@@ -1,21 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<files psalm-version="5.8.0@9cf4f60a333f779ad3bc704a555920e81d4fdcda">
<file src="src/Middleware/ImplicitHeadMiddleware.php">
<DeprecatedClass>
<code>new CallableStreamFactoryDecorator($streamFactory)</code>
</DeprecatedClass>
</file>
<file src="src/Middleware/ImplicitHeadMiddlewareFactory.php">
<DeprecatedClass>
<code><![CDATA[new CallableStreamFactoryDecorator($container->get(StreamInterface::class))]]></code>
</DeprecatedClass>
<DeprecatedMethod>
<code>detectStreamFactory</code>
</DeprecatedMethod>
<InvalidArgument>
<code><![CDATA[$container->get(StreamInterface::class)]]></code>
</InvalidArgument>
</file>
<files psalm-version="5.25.0@01a8eb06b9e9cc6cfb6a320bf9fb14331919d505">
<file src="test/InMemoryContainer.php">
<MixedReturnStatement>
<code><![CDATA[$this->services[$id]]]></code>
Expand Down
22 changes: 4 additions & 18 deletions src/Middleware/ImplicitHeadMiddleware.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,12 @@
use Fig\Http\Message\RequestMethodInterface as RequestMethod;
use Mezzio\Router\RouteResult;
use Mezzio\Router\RouterInterface;
use Mezzio\Router\Stream\CallableStreamFactoryDecorator;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Message\StreamFactoryInterface;
use Psr\Http\Message\StreamInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;

use function is_callable;

/**
* Handle implicit HEAD requests.
*
Expand Down Expand Up @@ -46,20 +42,10 @@ final class ImplicitHeadMiddleware implements MiddlewareInterface
{
public const FORWARDED_HTTP_METHOD_ATTRIBUTE = 'forwarded_http_method';

private StreamFactoryInterface $streamFactory;

/**
* @param (callable(): StreamInterface)|StreamFactoryInterface $streamFactory A factory capable of returning
* an empty StreamInterface instance to
* inject in a response.
*/
public function __construct(private RouterInterface $router, callable|StreamFactoryInterface $streamFactory)
{
if (is_callable($streamFactory)) {
$streamFactory = new CallableStreamFactoryDecorator($streamFactory);
}

$this->streamFactory = $streamFactory;
public function __construct(
private readonly RouterInterface $router,
private readonly StreamFactoryInterface $streamFactory,
) {
}

/**
Expand Down
40 changes: 8 additions & 32 deletions src/Middleware/ImplicitHeadMiddlewareFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,25 +6,22 @@

use Mezzio\Router\Exception\MissingDependencyException;
use Mezzio\Router\RouterInterface;
use Mezzio\Router\Stream\CallableStreamFactoryDecorator;
use Psr\Container\ContainerInterface;
use Psr\Http\Message\StreamFactoryInterface;
use Psr\Http\Message\StreamInterface;

/**
* Create and return an ImplicitHeadMiddleware instance.
*
* This factory depends on two other services:
*
* - Mezzio\Router\RouterInterface, which should resolve to an instance of that interface.
* - Either Psr\Http\Message\StreamFactoryInterface or Psr\Http\Message\StreamInterface, which should resolve to a
* callable that will produce an empty Psr\Http\Message\StreamInterface instance.
* - Psr\Http\Message\StreamFactoryInterface which should resolve to an instance of that interface.
*/
final class ImplicitHeadMiddlewareFactory
{
/**
* @throws MissingDependencyException If either the Mezzio\Router\RouterInterface
* or Psr\Http\Message\StreamInterface services are missing.
* or Psr\Http\Message\StreamFactoryInterface services are missing.
*/
public function __invoke(ContainerInterface $container): ImplicitHeadMiddleware
{
Expand All @@ -35,37 +32,16 @@ public function __invoke(ContainerInterface $container): ImplicitHeadMiddleware
);
}

return new ImplicitHeadMiddleware(
$container->get(RouterInterface::class),
$this->detectStreamFactory($container),
);
}

/**
* BC Preserving StreamFactoryInterface Retrieval
*
* Preserves existing behaviour in the 3.x series by fetching a `StreamInterface` callable and wrapping it in a
* decorator that implements StreamFactoryInterface. If `StreamInterface` callable is unavailable, attempt to
* fetch a `StreamFactoryInterface`, throwing a MissingDependencyException if neither are found.
*
* @deprecated Will be removed in version 4.0.0
*/
private function detectStreamFactory(ContainerInterface $container): StreamFactoryInterface
{
$hasStreamFactory = $container->has(StreamFactoryInterface::class);
$hasDeprecatedCallable = $container->has(StreamInterface::class);

if (! $hasStreamFactory && ! $hasDeprecatedCallable) {
if (! $container->has(StreamFactoryInterface::class)) {
throw MissingDependencyException::dependencyForService(
StreamInterface::class,
StreamFactoryInterface::class,
ImplicitHeadMiddleware::class
);
}

if ($hasDeprecatedCallable) {
return new CallableStreamFactoryDecorator($container->get(StreamInterface::class));
}

return $container->get(StreamFactoryInterface::class);
return new ImplicitHeadMiddleware(
$container->get(RouterInterface::class),
$container->get(StreamFactoryInterface::class),
);
}
}
43 changes: 0 additions & 43 deletions src/Stream/CallableStreamFactoryDecorator.php

This file was deleted.

6 changes: 2 additions & 4 deletions src/Test/AbstractImplicitMethodsIntegrationTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
use Generator;
use Laminas\Diactoros\Response;
use Laminas\Diactoros\ServerRequest;
use Laminas\Diactoros\Stream;
use Laminas\Diactoros\StreamFactory;
use Laminas\Stratigility\MiddlewarePipe;
use Mezzio\Router\Middleware\DispatchMiddleware;
use Mezzio\Router\Middleware\ImplicitHeadMiddleware;
Expand Down Expand Up @@ -56,9 +56,7 @@ public function getImplicitHeadMiddleware(RouterInterface $router): ImplicitHead
{
return new ImplicitHeadMiddleware(
$router,
function () {
return new Stream('php://temp', 'rw');
}
new StreamFactory(),
);
}

Expand Down
95 changes: 11 additions & 84 deletions test/Middleware/ImplicitHeadMiddlewareFactoryTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,122 +8,49 @@
use Mezzio\Router\Exception\MissingDependencyException;
use Mezzio\Router\Middleware\ImplicitHeadMiddlewareFactory;
use Mezzio\Router\RouterInterface;
use MezzioTest\Router\InMemoryContainer;
use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
use Psr\Container\ContainerInterface;
use Psr\Http\Message\StreamFactoryInterface;
use Psr\Http\Message\StreamInterface;

use function in_array;

#[CoversClass(ImplicitHeadMiddlewareFactory::class)]
final class ImplicitHeadMiddlewareFactoryTest extends TestCase
{
/** @var ContainerInterface&MockObject */
private ContainerInterface $container;

private InMemoryContainer $container;
private ImplicitHeadMiddlewareFactory $factory;

protected function setUp(): void
{
parent::setUp();

$this->container = $this->createMock(ContainerInterface::class);
$this->container = new InMemoryContainer();
$this->factory = new ImplicitHeadMiddlewareFactory();
}

public function testFactoryRaisesExceptionIfRouterInterfaceServiceIsMissing(): void
{
$this->container
->expects(self::once())
->method('has')
->with(RouterInterface::class)
->willReturn(false);

$this->expectException(MissingDependencyException::class);
$this->expectExceptionMessage(RouterInterface::class);

($this->factory)($this->container);
}

public function testFactoryRaisesExceptionIfStreamFactoryServiceIsMissing(): void
public function testFactoryRaisesExceptionIfStreamFactoryInterfaceServiceIsMissing(): void
{
$this->container
->expects(self::exactly(3))
->method('has')
->with(self::callback(function (string $arg): bool {
self::assertTrue(in_array($arg, [
RouterInterface::class,
StreamFactoryInterface::class,
StreamInterface::class,
]));
return true;
}))
->willReturnOnConsecutiveCalls(true, false, false);
$this->container->set(RouterInterface::class, $this->createMock(RouterInterface::class));

$this->expectException(MissingDependencyException::class);

($this->factory)($this->container);
}

public function testFactoryProducesImplicitHeadMiddlewareWithCallableStreamFactory(): void
{
$router = $this->createMock(RouterInterface::class);
$streamFactory = static function (): void {
};

$this->container
->expects(self::exactly(3))
->method('has')
->with(self::callback(function (string $arg): bool {
self::assertTrue(in_array($arg, [
RouterInterface::class,
StreamFactoryInterface::class,
StreamInterface::class,
]));
return true;
}))
->willReturnOnConsecutiveCalls(true, false, true);

$this->container
->expects(self::exactly(2))
->method('get')
->with(self::callback(function (string $arg): bool {
self::assertTrue(in_array($arg, [RouterInterface::class, StreamInterface::class]));
return true;
}))
->willReturnOnConsecutiveCalls($router, $streamFactory);
$this->expectExceptionMessage(StreamFactoryInterface::class);

($this->factory)($this->container);
}

public function testFactoryProducesImplicitHeadMiddlewareWithStreamFactoryInterface(): void
{
$router = $this->createMock(RouterInterface::class);
$streamFactory = new StreamFactory();

$this->container
->expects(self::exactly(3))
->method('has')
->with(self::callback(function (string $arg): bool {
self::assertTrue(in_array($arg, [
RouterInterface::class,
StreamFactoryInterface::class,
StreamInterface::class,
]));
return true;
}))
->willReturn(true, true, false);

$this->container
->expects(self::exactly(2))
->method('get')
->with(self::callback(function (string $arg): bool {
self::assertTrue(in_array($arg, [RouterInterface::class, StreamFactoryInterface::class]));
return true;
}))
->willReturnOnConsecutiveCalls($router, $streamFactory);

$this->container->set(RouterInterface::class, $this->createMock(RouterInterface::class));
$this->container->set(StreamFactoryInterface::class, new StreamFactory());
($this->factory)($this->container);

$this->expectNotToPerformAssertions();
}
}
4 changes: 2 additions & 2 deletions test/Middleware/ImplicitHeadMiddlewareTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use Fig\Http\Message\RequestMethodInterface as RequestMethod;
use Laminas\Diactoros\Response\TextResponse;
use Laminas\Diactoros\ServerRequest;
use Laminas\Diactoros\StreamFactory;
use Mezzio\Router\Middleware\ImplicitHeadMiddleware;
use Mezzio\Router\Route;
use Mezzio\Router\RouteResult;
Expand All @@ -16,7 +17,6 @@
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Message\StreamInterface;
use Psr\Http\Server\MiddlewareInterface;

/** @covers \Mezzio\Router\Middleware\ImplicitHeadMiddleware */
Expand All @@ -34,7 +34,7 @@ protected function setUp(): void
$this->router = $this->createMock(RouterInterface::class);
$this->middleware = new ImplicitHeadMiddleware(
$this->router,
fn (): StreamInterface => $this->createMock(StreamInterface::class),
new StreamFactory(),
);
}

Expand Down
7 changes: 4 additions & 3 deletions test/ServiceManagerIntegrationTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,14 @@

namespace MezzioTest\Router;

use Laminas\Diactoros\StreamFactory;
use Laminas\ServiceManager\ServiceManager;
use Mezzio\Router;
use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\TestCase;
use Psr\Container\ContainerInterface;
use Psr\Http\Message\ResponseFactoryInterface;
use Psr\Http\Message\StreamInterface;
use Psr\Http\Message\StreamFactoryInterface;

use function array_merge_recursive;

Expand All @@ -34,8 +35,8 @@ protected function setUp(): void
Router\RouterInterface::class => function (): Router\RouterInterface {
return $this->createMock(Router\RouterInterface::class);
},
StreamInterface::class => function (): callable {
return fn (): StreamInterface => $this->createMock(StreamInterface::class);
StreamFactoryInterface::class => function (): StreamFactoryInterface {
return new StreamFactory();
},
],
],
Expand Down
Loading