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

Expose the internal middleware queue in MiddlewarePipe as an iterable #57

Merged
merged 4 commits into from
Jul 17, 2024
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
20 changes: 20 additions & 0 deletions src/IterableMiddlewarePipeInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?php

declare(strict_types=1);

namespace Laminas\Stratigility;

use IteratorAggregate;
use Psr\Http\Server\MiddlewareInterface;

/**
* Implementors of this interface expose composed middleware as an iterable.
*
* This is useful for inspecting the middleware that is configured to run within a pipeline. Iterating over the pipeline
* should *not* cause side effects such as de-queueing any composed middleware.
*
* @extends IteratorAggregate<int, MiddlewareInterface>
*/
interface IterableMiddlewarePipeInterface extends MiddlewarePipeInterface, IteratorAggregate
{
}
17 changes: 16 additions & 1 deletion src/MiddlewarePipe.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,15 @@

namespace Laminas\Stratigility;

use ArrayIterator;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;
use SplQueue;
use Traversable;

use function iterator_to_array;

/**
* Pipe middleware like unix pipes.
Expand All @@ -24,7 +28,7 @@
*
* @see https://github.com/senchalabs/connect
*/
final class MiddlewarePipe implements MiddlewarePipeInterface
final class MiddlewarePipe implements IterableMiddlewarePipeInterface
{
/** @var SplQueue<MiddlewareInterface> */
private SplQueue $pipeline;
Expand Down Expand Up @@ -84,4 +88,15 @@ public function pipe(MiddlewareInterface $middleware): void
{
$this->pipeline->enqueue($middleware);
}

/** @return Traversable<int, MiddlewareInterface> */
public function getIterator(): Traversable
{
return new ArrayIterator(
iterator_to_array(
clone $this->pipeline,
false,
),
);
}
}
51 changes: 45 additions & 6 deletions test/MiddlewarePipeTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use Laminas\Diactoros\Response;
use Laminas\Diactoros\ServerRequest as Request;
use Laminas\Stratigility\Exception;
use Laminas\Stratigility\IterableMiddlewarePipeInterface;
use Laminas\Stratigility\MiddlewarePipe;
use Laminas\Stratigility\MiddlewarePipeInterface;
use PHPUnit\Framework\TestCase;
Expand All @@ -17,7 +18,9 @@
use ReflectionClass;
use ReflectionMethod;
use ReflectionObject;
use SplQueue;

use function iterator_to_array;
use function sort;
use function spl_object_hash;
use function strpos;
Expand Down Expand Up @@ -64,15 +67,15 @@ public function testCanPipeInteropMiddleware(): void
static function ($argument1): bool {
self::assertInstanceOf(ServerRequestInterface::class, $argument1);
return true;
}
},
),
self::callback(
/** @psalm-suppress MissingClosureParamType */
static function ($argument2): bool {
self::assertInstanceOf(RequestHandlerInterface::class, $argument2);
return true;
}
)
},
),
)
->willReturn($response);

Expand Down Expand Up @@ -177,12 +180,12 @@ public function testHandleProcessesEnqueuedMiddleware(): void
$middleware1
->method('process')
->with(
$this->request
$this->request,
)
->willReturnCallback(
static fn(
ServerRequestInterface $request,
RequestHandlerInterface $handler): ResponseInterface => $handler->handle($request)
RequestHandlerInterface $handler): ResponseInterface => $handler->handle($request),
);
$middleware2 = $this->createMock(MiddlewareInterface::class);
$middleware2
Expand Down Expand Up @@ -211,7 +214,7 @@ public function testMiddlewarePipeOnlyImplementsMiddlewarePipeInterfaceApi(): vo
}
sort($actual);

$interfaceReflection = new ReflectionClass(MiddlewarePipeInterface::class);
$interfaceReflection = new ReflectionClass(IterableMiddlewarePipeInterface::class);
$interfaceMethods = $interfaceReflection->getMethods(ReflectionMethod::IS_PUBLIC);
$expected = [];
foreach ($interfaceMethods as $method) {
Expand All @@ -223,4 +226,40 @@ public function testMiddlewarePipeOnlyImplementsMiddlewarePipeInterfaceApi(): vo
self::assertEquals($expected, $actual);
self::assertInstanceOf(MiddlewarePipeInterface::class, $pipeline);
}

public function testThatTheIteratorYieldsEnqueuedMiddlewareInQueueOrder(): void
{
$pipeline = new MiddlewarePipe();
$a = $this->createMock(MiddlewareInterface::class);
$b = $this->createMock(MiddlewareInterface::class);
$c = $this->createMock(MiddlewareInterface::class);

$pipeline->pipe($c);
$pipeline->pipe($a);
$pipeline->pipe($b);

self::assertSame([$c, $a, $b], iterator_to_array($pipeline, false));
}

public function testThatCloningThePipelineAlsoClonesTheInternalQueue(): void
{
$pipe = new MiddlewarePipe();
$pipe->pipe($this->createMock(MiddlewareInterface::class));

$clone = clone $pipe;

$reflectionClass = new ReflectionClass(MiddlewarePipe::class);
$property = $reflectionClass->getProperty('pipeline');

$queue1 = $property->getValue($pipe);
self::assertInstanceOf(SplQueue::class, $queue1);
$queue2 = $property->getValue($clone);
self::assertInstanceOf(SplQueue::class, $queue2);

self::assertNotSame($queue1, $queue2);
self::assertSame(
iterator_to_array($queue1, false),
iterator_to_array($queue2, false),
);
}
}