Skip to content

Commit 4efd3c4

Browse files
Yoann-TYTddeboer
andauthored
Added support for php 8 attributes (#556)
* Need to fix unit tests * Units tests are fixed. Need to add functionnals tests for invalidatePath and invalidateRoute attributes * Units tests are fixed. Add a new EventListener CatchAttributesListener to catch attributes for InvalidatePath / InvalidateRoute / Tag * Fix some typo after review * Typo. 7.2 is no longer managed by FOSHttpCacheBundle * fix StyleCI * rm .php-version * DebugClassLoaded is enable by default from 4.2, so it has to disabled on phpunit. I removed @runInSeparateProcess annotations on tests. ( It was a mistake, the real problem was that DebugClassLoaded enabled ) I added some documentation about PHP 8 Attributes on annotations.rst. I modified CHANGELOG.md I removed fos_http_cache.controller_resolver service and I use controller_resolver of Symfony instead I use PHP_VERSION_ID instead method_exists to check if we can use PHP Attributes. I renamed CatchAttibutesListener to Php8AttributesListener * Styleci * Update Resources/doc/reference/annotations.rst Co-authored-by: David de Boer <[email protected]> Co-authored-by: David de Boer <[email protected]>
1 parent 07fc209 commit 4efd3c4

22 files changed

+472
-22
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ Changelog
88

99
* Adjusted to work with PHP 8
1010
* Dropped support for PHP 7.2
11+
* Added support for PHP 8 Attributes
1112

1213
2.9.1
1314
-----

Resources/doc/reference/annotations.rst

+13
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,16 @@ Invalidate a path::
2828
{
2929
}
3030

31+
From PHP 8, you can replace the doctrine annotations by PHP attributes::
32+
33+
use FOS\HttpCacheBundle\Configuration\InvalidatePath;
34+
35+
#[InvalidatePath('/articles')]
36+
#[InvalidatePath('/articles/latest')]
37+
public function editAction()
38+
{
39+
}
40+
3141
When `editAction()` returns a successful response to an :term:`unsafe <safe>`
3242
HTTP request (POST, PUT, PATCH or DELETE), the paths `/articles` and
3343
`/articles/latest` will be invalidated.
@@ -39,6 +49,7 @@ See :doc:`/features/invalidation` for more information.
3949
``@InvalidateRoute``
4050
--------------------
4151

52+
Like InvalidatePath annotations, you can use php attributes instead if you are using PHP 8
4253
Invalidate a route with parameters::
4354

4455
use FOS\HttpCacheBundle\Configuration\InvalidateRoute;
@@ -88,6 +99,8 @@ HTTP header (``X-Cache-Tags``, by default).
8899
Any non-safe request to the ``editAction`` that returns a successful response
89100
will trigger invalidation of both the ``news`` and the ``news-123`` tags.
90101

102+
Like InvalidatePath annotations, you can use php attributes instead if you are using PHP 8
103+
91104
Set/invalidate a tag::
92105

93106
/**

phpunit.xml.dist

+13
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,19 @@
2525

2626
<listeners>
2727
<listener class="\Mockery\Adapter\Phpunit\TestListener" />
28+
<!--DebugClassLoaded is enable by default from 4.2
29+
https://github.com/symfony/symfony/pull/28412
30+
It has to be disabled to avoid tests failed
31+
-->
32+
<listener class="Symfony\Bridge\PhpUnit\SymfonyTestsListener">
33+
<arguments>
34+
<array>
35+
<element key="debug-class-loader">
36+
<integer>0</integer>
37+
</element>
38+
</array>
39+
</arguments>
40+
</listener>
2841
</listeners>
2942

3043
<php>

src/Configuration/InvalidatePath.php

+14
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,27 @@
1616
/**
1717
* @Annotation
1818
*/
19+
#[\Attribute(\Attribute::IS_REPEATABLE | \Attribute::TARGET_CLASS | \Attribute::TARGET_METHOD)]
1920
class InvalidatePath extends ConfigurationAnnotation
2021
{
2122
/**
2223
* @var array
2324
*/
2425
private $paths;
2526

27+
public function __construct(
28+
$data = []
29+
) {
30+
$values = [];
31+
if (is_string($data)) {
32+
$values['value'] = $data;
33+
} else {
34+
$values = $data;
35+
}
36+
37+
parent::__construct($values);
38+
}
39+
2640
/**
2741
* Handle path given without explicit key.
2842
*

src/Configuration/InvalidateRoute.php

+17
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
/**
1919
* @Annotation
2020
*/
21+
#[\Attribute(\Attribute::IS_REPEATABLE | \Attribute::TARGET_CLASS | \Attribute::TARGET_METHOD)]
2122
class InvalidateRoute extends ConfigurationAnnotation
2223
{
2324
/**
@@ -30,6 +31,22 @@ class InvalidateRoute extends ConfigurationAnnotation
3031
*/
3132
private $params;
3233

34+
public function __construct(
35+
$data = [],
36+
$params = []
37+
) {
38+
$values = [];
39+
if (is_string($data)) {
40+
$values['value'] = $data;
41+
} else {
42+
$values = $data;
43+
}
44+
45+
$values['params'] = $values['params'] ?? $params;
46+
47+
parent::__construct($values);
48+
}
49+
3350
/**
3451
* Handle route name given without explicit key.
3552
*

src/Configuration/Tag.php

+17
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,29 @@
1919
/**
2020
* @Annotation
2121
*/
22+
#[\Attribute(\Attribute::IS_REPEATABLE | \Attribute::TARGET_CLASS | \Attribute::TARGET_METHOD)]
2223
class Tag extends ConfigurationAnnotation
2324
{
2425
private $tags;
2526

2627
private $expression;
2728

29+
public function __construct(
30+
$data = [],
31+
$expression = null
32+
) {
33+
$values = [];
34+
if (is_string($data)) {
35+
$values['value'] = $data;
36+
} else {
37+
$values = $data;
38+
}
39+
40+
$values['expression'] = $values['expression'] ?? $expression;
41+
42+
parent::__construct($values);
43+
}
44+
2845
/**
2946
* Handle tags given without explicit key.
3047
*

src/DependencyInjection/FOSHttpCacheExtension.php

+4
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,10 @@ public function load(array $configs, ContainerBuilder $container)
142142

143143
$loader->load('flash_message.xml');
144144
}
145+
146+
if (\PHP_VERSION_ID >= 80000) {
147+
$loader->load('php8_attributes.xml');
148+
}
145149
}
146150

147151
private function loadCacheable(ContainerBuilder $container, array $config)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
<?php
2+
3+
namespace FOS\HttpCacheBundle\EventListener;
4+
5+
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ConfigurationInterface;
6+
use Symfony\Bundle\FrameworkBundle\Controller\ControllerResolver;
7+
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
8+
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
9+
use Symfony\Component\HttpKernel\Event\RequestEvent;
10+
use Symfony\Component\HttpKernel\Kernel;
11+
use Symfony\Component\HttpKernel\KernelEvents;
12+
13+
if (Kernel::MAJOR_VERSION >= 5) {
14+
class_alias(RequestEvent::class, 'FOS\HttpCacheBundle\EventListener\AttributeRequestEvent');
15+
} else {
16+
class_alias(GetResponseEvent::class, 'FOS\HttpCacheBundle\EventListener\AttributeRequestEvent');
17+
}
18+
19+
/**
20+
* On kernel.request event, this event handler fetch PHP8 attributes.
21+
* It is available from PHP 8.0.0.
22+
*
23+
* @author Yoann Chocteau <[email protected]>
24+
*/
25+
class Php8AttributesListener implements EventSubscriberInterface
26+
{
27+
/**
28+
* @var ControllerResolver
29+
*/
30+
private $controllerResolver;
31+
32+
public function __construct(ControllerResolver $controllerResolver)
33+
{
34+
if (\PHP_VERSION_ID < 80000) {
35+
throw new \Exception(sprintf('Php8AttributesListener must not be loaded for PHP %s', phpversion()));
36+
}
37+
$this->controllerResolver = $controllerResolver;
38+
}
39+
40+
public function onKernelRequest(AttributeRequestEvent $event)
41+
{
42+
$request = $event->getRequest();
43+
$controller = $this->controllerResolver->getController($request);
44+
45+
if (!is_array($controller) || 2 !== count($controller)) {
46+
return;
47+
}
48+
49+
$class = new \ReflectionClass($controller[0]);
50+
$method = $class->getMethod($controller[1]);
51+
$attributes = [];
52+
$addAttributes = function ($instance) use (&$attributes) {
53+
if (
54+
$instance instanceof ConfigurationInterface &&
55+
in_array(
56+
$instance->getAliasName(), [
57+
'tag', 'invalidate_path', 'invalidate_route',
58+
])
59+
) {
60+
$attributes['_'.$instance->getAliasName()][] = $instance;
61+
}
62+
};
63+
64+
foreach ($class->getAttributes() as $classAttribute) {
65+
$addAttributes($classAttribute->newInstance());
66+
}
67+
foreach ($method->getAttributes() as $methodAttribute) {
68+
$addAttributes($methodAttribute->newInstance());
69+
}
70+
71+
foreach ($attributes as $key => $attr) {
72+
$request->attributes->set(
73+
$key,
74+
array_merge($attr, $request->attributes->get($key, []))
75+
);
76+
}
77+
}
78+
79+
public static function getSubscribedEvents()
80+
{
81+
return [
82+
KernelEvents::REQUEST => 'onKernelRequest',
83+
];
84+
}
85+
}
+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<?xml version="1.0" ?>
2+
3+
<container xmlns="http://symfony.com/schema/dic/services"
4+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
5+
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
6+
<services>
7+
<service id="fos_http_cache.event_listener.php8_attributes" class="FOS\HttpCacheBundle\EventListener\Php8AttributesListener">
8+
<argument type="service" id="controller_resolver" />
9+
<tag name="kernel.event_subscriber" />
10+
</service>
11+
</services>
12+
</container>

tests/Functional/Command/ClearCommandTest.php

-9
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,6 @@
1616

1717
class ClearCommandTest extends CommandTestCase
1818
{
19-
/**
20-
* @runInSeparateProcess
21-
*/
2219
public function testExecuteClearVerbose()
2320
{
2421
$client = self::createClient();
@@ -42,9 +39,6 @@ public function testExecuteClearVerbose()
4239
$this->assertEquals("Sent 1 invalidation request(s)\n", $output);
4340
}
4441

45-
/**
46-
* @runInSeparateProcess
47-
*/
4842
public function testExecuteBanVerbose()
4943
{
5044
$client = self::createClient();
@@ -72,9 +66,6 @@ public function testExecuteBanVerbose()
7266
$this->assertEquals("Sent 1 invalidation request(s)\n", $output);
7367
}
7468

75-
/**
76-
* @runInSeparateProcess
77-
*/
7869
public function testExecuteErrorVerbose()
7970
{
8071
$client = self::createClient();

tests/Functional/DependencyInjection/ServiceTest.php

-3
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,6 @@ protected function bootDebugKernel()
4242
return static::$kernel;
4343
}
4444

45-
/**
46-
* @runInSeparateProcess
47-
*/
4845
public function testCanBeLoaded()
4946
{
5047
/** @var Container $container */

tests/Functional/EventListener/CacheControlListenerTest.php

-6
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,6 @@ class CacheControlListenerTest extends WebTestCase
1818
{
1919
use MockeryPHPUnitIntegration;
2020

21-
/**
22-
* @runInSeparateProcess
23-
*/
2421
public function testIsCached()
2522
{
2623
$client = static::createClient();
@@ -30,9 +27,6 @@ public function testIsCached()
3027
$this->assertEquals('public', $response->headers->get('Cache-Control'));
3128
}
3229

33-
/**
34-
* @runInSeparateProcess
35-
*/
3630
public function testNotCached()
3731
{
3832
$client = static::createClient();

0 commit comments

Comments
 (0)