Skip to content

Commit

Permalink
Drop compatibility with callable stream factories on `ImplicitHeadMid…
Browse files Browse the repository at this point in the history
…dleware`

Signed-off-by: George Steel <george@net-glue.co.uk>
  • Loading branch information
gsteel committed Jul 18, 2024
1 parent ed003a2 commit bf5a037
Show file tree
Hide file tree
Showing 7 changed files with 32 additions and 160 deletions.
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),
);
}
}
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

0 comments on commit bf5a037

Please sign in to comment.