-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
- Loading branch information
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Api\App\Attribute; | ||
|
||
use Api\App\Exception\DeprecationSunsetException; | ||
use Api\App\Message; | ||
use Attribute; | ||
use Laminas\Validator\Date; | ||
Check warning on line 10 in src/App/src/Attribute/MethodDeprecation.php
|
||
|
||
use function sprintf; | ||
|
||
#[Attribute(Attribute::TARGET_METHOD)] | ||
final readonly class MethodDeprecation | ||
{ | ||
public function __construct(public string $sunset, public string $link, public string $deprecationReason = '') | ||
{ | ||
if (! (new Date())->isValid($sunset)) { | ||
throw new DeprecationSunsetException(sprintf(Message::INVALID_VALUE, 'sunset')); | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Api\App\Attribute; | ||
|
||
use Api\App\Exception\DeprecationSunsetException; | ||
use Api\App\Message; | ||
use Attribute; | ||
use Laminas\Validator\Date; | ||
Check warning on line 10 in src/App/src/Attribute/ResourceDeprecation.php
|
||
|
||
use function sprintf; | ||
|
||
#[Attribute(Attribute::TARGET_CLASS)] | ||
final readonly class ResourceDeprecation | ||
{ | ||
public function __construct(public string $sunset, public string $link, public string $deprecationReason = '') | ||
{ | ||
if (! (new Date())->isValid($sunset)) { | ||
throw new DeprecationSunsetException(sprintf(Message::INVALID_VALUE, 'sunset')); | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Api\App\Exception; | ||
|
||
use RuntimeException; | ||
|
||
class DeprecationConflictException extends RuntimeException | ||
{ | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Api\App\Exception; | ||
|
||
use RuntimeException; | ||
|
||
class DeprecationSunsetException extends RuntimeException | ||
{ | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,116 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Api\App\Middleware; | ||
|
||
use Api\App\Attribute\MethodDeprecation; | ||
use Api\App\Attribute\ResourceDeprecation; | ||
use Api\App\Exception\DeprecationConflictException; | ||
use Api\App\Handler\ResponseTrait; | ||
use Api\App\Message; | ||
use Mezzio\Middleware\LazyLoadingMiddleware; | ||
Check warning on line 12 in src/App/src/Middleware/DeprecationMiddleware.php
|
||
use Mezzio\Router\RouteResult; | ||
Check warning on line 13 in src/App/src/Middleware/DeprecationMiddleware.php
|
||
use Psr\Http\Message\ResponseInterface; | ||
Check warning on line 14 in src/App/src/Middleware/DeprecationMiddleware.php
|
||
use Psr\Http\Message\ServerRequestInterface; | ||
Check warning on line 15 in src/App/src/Middleware/DeprecationMiddleware.php
|
||
use Psr\Http\Server\MiddlewareInterface; | ||
Check warning on line 16 in src/App/src/Middleware/DeprecationMiddleware.php
|
||
use Psr\Http\Server\RequestHandlerInterface; | ||
Check warning on line 17 in src/App/src/Middleware/DeprecationMiddleware.php
|
||
use ReflectionClass; | ||
use ReflectionException; | ||
use ReflectionMethod; | ||
|
||
use function array_keys; | ||
use function sprintf; | ||
|
||
class DeprecationMiddleware implements MiddlewareInterface | ||
{ | ||
use ResponseTrait; | ||
|
||
public const RESOURCE_DEPRECATION_ATTRIBUTE = ResourceDeprecation::class; | ||
public const METHOD_DEPRECATION_ATTRIBUTE = MethodDeprecation::class; | ||
|
||
/** | ||
* @throws ReflectionException | ||
*/ | ||
public function process( | ||
ServerRequestInterface $request, | ||
RequestHandlerInterface $handler | ||
): ResponseInterface { | ||
$response = $handler->handle($request); | ||
$routeResult = $request->getAttribute(RouteResult::class); | ||
if (! $routeResult instanceof RouteResult || $routeResult->isFailure()) { | ||
return $response; | ||
} | ||
|
||
$reflectionHandler = null; | ||
$routeMiddleware = $routeResult->getMatchedRoute()->getMiddleware(); | ||
if ($routeMiddleware instanceof LazyLoadingMiddleware) { | ||
$reflectionMiddlewareClass = new ReflectionClass($routeMiddleware->middlewareName); | ||
if ($reflectionMiddlewareClass->implementsInterface(RequestHandlerInterface::class)) { | ||
$reflectionHandler = $reflectionMiddlewareClass; | ||
} | ||
} else { | ||
$reflectionClass = new ReflectionClass($routeMiddleware); | ||
$middlewarePipeline = $reflectionClass->getProperty('pipeline')->getValue($routeMiddleware); | ||
for ($middlewarePipeline->rewind(); $middlewarePipeline->valid(); $middlewarePipeline->next()) { | ||
$reflectionMiddlewareClass = new ReflectionClass($middlewarePipeline->current()->middlewareName); | ||
if ($reflectionMiddlewareClass->implementsInterface(RequestHandlerInterface::class)) { | ||
$reflectionHandler = $reflectionMiddlewareClass; | ||
} | ||
} | ||
} | ||
|
||
if (! $reflectionHandler) { | ||
return $response; | ||
} | ||
|
||
$attributes = $this->getAttributes($reflectionHandler, self::RESOURCE_DEPRECATION_ATTRIBUTE); | ||
foreach ($reflectionHandler->getMethods() as $method) { | ||
$methodRef = new ReflectionMethod($method->class, $method->name); | ||
$attributes += $this->getAttributes($methodRef, self::METHOD_DEPRECATION_ATTRIBUTE); | ||
} | ||
|
||
if ([self::RESOURCE_DEPRECATION_ATTRIBUTE, self::METHOD_DEPRECATION_ATTRIBUTE] === array_keys($attributes)) { | ||
throw new DeprecationConflictException( | ||
sprintf( | ||
Message::RESTRICTION_DEPRECATION, | ||
self::RESOURCE_DEPRECATION_ATTRIBUTE, | ||
self::METHOD_DEPRECATION_ATTRIBUTE | ||
) | ||
); | ||
} | ||
|
||
$sunset = ''; | ||
$link = ''; | ||
if ($attributes[self::RESOURCE_DEPRECATION_ATTRIBUTE] ?? '') { | ||
$sunset = $attributes[self::RESOURCE_DEPRECATION_ATTRIBUTE]['sunset']; | ||
$link = $attributes[self::RESOURCE_DEPRECATION_ATTRIBUTE]['link']; | ||
} | ||
|
||
if ($attributes[self::METHOD_DEPRECATION_ATTRIBUTE] ?? '') { | ||
$sunset = $attributes[self::METHOD_DEPRECATION_ATTRIBUTE]['sunset']; | ||
$link = $attributes[self::METHOD_DEPRECATION_ATTRIBUTE]['link']; | ||
} | ||
|
||
if ($sunset) { | ||
$response = $response->withHeader('sunset', $sunset); | ||
} | ||
|
||
if ($link) { | ||
$response = $response->withHeader('link', $link); | ||
} | ||
|
||
return $response; | ||
} | ||
|
||
public function getAttributes(ReflectionClass|ReflectionMethod $reflection, string $type): array | ||
{ | ||
$attributes = []; | ||
foreach ($reflection->getAttributes($type) as $attribute) { | ||
$attribute->newInstance(); | ||
$attributes[$attribute->getName()] = $attribute->getArguments(); | ||
} | ||
|
||
return $attributes; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace ApiTest\Unit\App\Attribute; | ||
|
||
use Api\App\Attribute\MethodDeprecation; | ||
use Api\App\Exception\DeprecationSunsetException; | ||
use PHPUnit\Framework\TestCase; | ||
Check warning on line 9 in test/Unit/App/Attribute/MethodDeprecationTest.php
|
||
use ReflectionClass; | ||
|
||
class MethodDeprecationTest extends TestCase | ||
{ | ||
public function testInvalidDateThrowsException(): void | ||
{ | ||
$class = new class { | ||
#[MethodDeprecation( | ||
sunset: 'invalid-01-01', | ||
link: 'test-link', | ||
deprecationReason: 'test-deprecation-reason', | ||
)] | ||
public function test() | ||
{ | ||
} | ||
}; | ||
|
||
$reflectionClass = new ReflectionClass($class); | ||
$attributes = $this->getAttributes($reflectionClass); | ||
|
||
$this->expectException(DeprecationSunsetException::class); | ||
|
||
$attributes[0]->newInstance(); | ||
} | ||
|
||
public function testValidDatePassesValidation(): void | ||
{ | ||
$class = new class { | ||
#[MethodDeprecation( | ||
sunset: '2038-01-01', | ||
link: 'test-link', | ||
deprecationReason: 'test-deprecation-reason', | ||
)] | ||
public function test() | ||
{ | ||
} | ||
}; | ||
|
||
$reflectionClass = new ReflectionClass($class); | ||
$attributes = $this->getAttributes($reflectionClass); | ||
|
||
$attribute = $attributes[0]->newInstance(); | ||
|
||
$this->assertSame('2038-01-01', $attribute->sunset); | ||
$this->assertSame('test-link', $attribute->link); | ||
$this->assertSame('test-deprecation-reason', $attribute->deprecationReason); | ||
} | ||
|
||
private function getAttributes(ReflectionClass $reflectionClass): array | ||
{ | ||
$methods = $reflectionClass->getMethods(); | ||
return $methods[0]->getAttributes(MethodDeprecation::class); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace ApiTest\Unit\App\Attribute; | ||
|
||
use Api\App\Attribute\ResourceDeprecation; | ||
use Api\App\Exception\DeprecationSunsetException; | ||
use PHPUnit\Framework\TestCase; | ||
Check warning on line 9 in test/Unit/App/Attribute/ResourceDeprecationTest.php
|
||
use ReflectionClass; | ||
|
||
class ResourceDeprecationTest extends TestCase | ||
{ | ||
public function testInvalidDateThrowsException(): void | ||
{ | ||
$class = new #[ResourceDeprecation( | ||
sunset: 'invalid-01-01', | ||
link: 'test-link', | ||
deprecationReason: 'test-deprecation-reason', | ||
)] class { | ||
}; | ||
|
||
$reflectionClass = new ReflectionClass($class); | ||
$attributes = $reflectionClass->getAttributes(ResourceDeprecation::class); | ||
|
||
$this->expectException(DeprecationSunsetException::class); | ||
|
||
$attributes[0]->newInstance(); | ||
} | ||
|
||
public function testValidDatePassesValidation(): void | ||
{ | ||
$class = new #[ResourceDeprecation( | ||
sunset: '2038-01-01', | ||
link: 'test-link', | ||
deprecationReason: 'test-deprecation-reason', | ||
)] class { | ||
}; | ||
|
||
$reflectionClass = new ReflectionClass($class); | ||
$attributes = $reflectionClass->getAttributes(ResourceDeprecation::class); | ||
|
||
$attribute = $attributes[0]->newInstance(); | ||
|
||
$this->assertSame('2038-01-01', $attribute->sunset); | ||
$this->assertSame('test-link', $attribute->link); | ||
$this->assertSame('test-deprecation-reason', $attribute->deprecationReason); | ||
} | ||
} |