Skip to content

Commit

Permalink
Merge b9e075e into 07fc209
Browse files Browse the repository at this point in the history
  • Loading branch information
Yoann-TYT committed Dec 19, 2020
2 parents 07fc209 + b9e075e commit 6a09293
Show file tree
Hide file tree
Showing 22 changed files with 472 additions and 22 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -8,6 +8,7 @@ Changelog

* Adjusted to work with PHP 8
* Dropped support for PHP 7.2
* Added support for PHP 8 Attributes

2.9.1
-----
Expand Down
13 changes: 13 additions & 0 deletions Resources/doc/reference/annotations.rst
Expand Up @@ -28,6 +28,16 @@ Invalidate a path::
{
}

From PHP 8, you can replace the doctrine annotations by PHP attributes::

use FOS\HttpCacheBundle\Configuration\InvalidatePath;

#[InvalidatePath('/articles')
#[InvalidatePath('/articles/latest')
public function editAction()
{
}

When `editAction()` returns a successful response to an :term:`unsafe <safe>`
HTTP request (POST, PUT, PATCH or DELETE), the paths `/articles` and
`/articles/latest` will be invalidated.
Expand All @@ -39,6 +49,7 @@ See :doc:`/features/invalidation` for more information.
``@InvalidateRoute``
--------------------

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

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

Like InvalidatePath annotations, you can use php attributes instead if you are using PHP 8

Set/invalidate a tag::

/**
Expand Down
13 changes: 13 additions & 0 deletions phpunit.xml.dist
Expand Up @@ -25,6 +25,19 @@

<listeners>
<listener class="\Mockery\Adapter\Phpunit\TestListener" />
<!--DebugClassLoaded is enable by default from 4.2
https://github.com/symfony/symfony/pull/28412
It has to be disabled to avoid tests failed
-->
<listener class="Symfony\Bridge\PhpUnit\SymfonyTestsListener">
<arguments>
<array>
<element key="debug-class-loader">
<integer>0</integer>
</element>
</array>
</arguments>
</listener>
</listeners>

<php>
Expand Down
14 changes: 14 additions & 0 deletions src/Configuration/InvalidatePath.php
Expand Up @@ -16,13 +16,27 @@
/**
* @Annotation
*/
#[\Attribute(\Attribute::IS_REPEATABLE | \Attribute::TARGET_CLASS | \Attribute::TARGET_METHOD)]
class InvalidatePath extends ConfigurationAnnotation
{
/**
* @var array
*/
private $paths;

public function __construct(
$data = []
) {
$values = [];
if (is_string($data)) {
$values['value'] = $data;
} else {
$values = $data;
}

parent::__construct($values);
}

/**
* Handle path given without explicit key.
*
Expand Down
17 changes: 17 additions & 0 deletions src/Configuration/InvalidateRoute.php
Expand Up @@ -18,6 +18,7 @@
/**
* @Annotation
*/
#[\Attribute(\Attribute::IS_REPEATABLE | \Attribute::TARGET_CLASS | \Attribute::TARGET_METHOD)]
class InvalidateRoute extends ConfigurationAnnotation
{
/**
Expand All @@ -30,6 +31,22 @@ class InvalidateRoute extends ConfigurationAnnotation
*/
private $params;

public function __construct(
$data = [],
$params = []
) {
$values = [];
if (is_string($data)) {
$values['value'] = $data;
} else {
$values = $data;
}

$values['params'] = $values['params'] ?? $params;

parent::__construct($values);
}

/**
* Handle route name given without explicit key.
*
Expand Down
17 changes: 17 additions & 0 deletions src/Configuration/Tag.php
Expand Up @@ -19,12 +19,29 @@
/**
* @Annotation
*/
#[\Attribute(\Attribute::IS_REPEATABLE | \Attribute::TARGET_CLASS | \Attribute::TARGET_METHOD)]
class Tag extends ConfigurationAnnotation
{
private $tags;

private $expression;

public function __construct(
$data = [],
$expression = null
) {
$values = [];
if (is_string($data)) {
$values['value'] = $data;
} else {
$values = $data;
}

$values['expression'] = $values['expression'] ?? $expression;

parent::__construct($values);
}

/**
* Handle tags given without explicit key.
*
Expand Down
4 changes: 4 additions & 0 deletions src/DependencyInjection/FOSHttpCacheExtension.php
Expand Up @@ -142,6 +142,10 @@ public function load(array $configs, ContainerBuilder $container)

$loader->load('flash_message.xml');
}

if (\PHP_VERSION_ID >= 80000) {
$loader->load('php8_attributes.xml');
}
}

private function loadCacheable(ContainerBuilder $container, array $config)
Expand Down
85 changes: 85 additions & 0 deletions src/EventListener/Php8AttributesListener.php
@@ -0,0 +1,85 @@
<?php

namespace FOS\HttpCacheBundle\EventListener;

use Sensio\Bundle\FrameworkExtraBundle\Configuration\ConfigurationInterface;
use Symfony\Bundle\FrameworkBundle\Controller\ControllerResolver;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\HttpKernel\Event\RequestEvent;
use Symfony\Component\HttpKernel\Kernel;
use Symfony\Component\HttpKernel\KernelEvents;

if (Kernel::MAJOR_VERSION >= 5) {
class_alias(RequestEvent::class, 'FOS\HttpCacheBundle\EventListener\AttributeRequestEvent');
} else {
class_alias(GetResponseEvent::class, 'FOS\HttpCacheBundle\EventListener\AttributeRequestEvent');
}

/**
* On kernel.request event, this event handler fetch PHP8 attributes.
* It is available from PHP 8.0.0.
*
* @author Yoann Chocteau <yoann@kezaweb.fr>
*/
class Php8AttributesListener implements EventSubscriberInterface
{
/**
* @var ControllerResolver
*/
private $controllerResolver;

public function __construct(ControllerResolver $controllerResolver)
{
if (\PHP_VERSION_ID < 80000) {
throw new \Exception(sprintf('Php8AttributesListener must not be loaded for PHP %s', phpversion()));
}
$this->controllerResolver = $controllerResolver;
}

public function onKernelRequest(AttributeRequestEvent $event)
{
$request = $event->getRequest();
$controller = $this->controllerResolver->getController($request);

if (!is_array($controller) || 2 !== count($controller)) {
return;
}

$class = new \ReflectionClass($controller[0]);
$method = $class->getMethod($controller[1]);
$attributes = [];
$addAttributes = function ($instance) use (&$attributes) {
if (
$instance instanceof ConfigurationInterface &&
in_array(
$instance->getAliasName(), [
'tag', 'invalidate_path', 'invalidate_route',
])
) {
$attributes['_'.$instance->getAliasName()][] = $instance;
}
};

foreach ($class->getAttributes() as $classAttribute) {
$addAttributes($classAttribute->newInstance());
}
foreach ($method->getAttributes() as $methodAttribute) {
$addAttributes($methodAttribute->newInstance());
}

foreach ($attributes as $key => $attr) {
$request->attributes->set(
$key,
array_merge($attr, $request->attributes->get($key, []))
);
}
}

public static function getSubscribedEvents()
{
return [
KernelEvents::REQUEST => 'onKernelRequest',
];
}
}
12 changes: 12 additions & 0 deletions src/Resources/config/php8_attributes.xml
@@ -0,0 +1,12 @@
<?xml version="1.0" ?>

<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
<services>
<service id="fos_http_cache.event_listener.php8_attributes" class="FOS\HttpCacheBundle\EventListener\Php8AttributesListener">
<argument type="service" id="controller_resolver" />
<tag name="kernel.event_subscriber" />
</service>
</services>
</container>
9 changes: 0 additions & 9 deletions tests/Functional/Command/ClearCommandTest.php
Expand Up @@ -16,9 +16,6 @@

class ClearCommandTest extends CommandTestCase
{
/**
* @runInSeparateProcess
*/
public function testExecuteClearVerbose()
{
$client = self::createClient();
Expand All @@ -42,9 +39,6 @@ public function testExecuteClearVerbose()
$this->assertEquals("Sent 1 invalidation request(s)\n", $output);
}

/**
* @runInSeparateProcess
*/
public function testExecuteBanVerbose()
{
$client = self::createClient();
Expand Down Expand Up @@ -72,9 +66,6 @@ public function testExecuteBanVerbose()
$this->assertEquals("Sent 1 invalidation request(s)\n", $output);
}

/**
* @runInSeparateProcess
*/
public function testExecuteErrorVerbose()
{
$client = self::createClient();
Expand Down
3 changes: 0 additions & 3 deletions tests/Functional/DependencyInjection/ServiceTest.php
Expand Up @@ -42,9 +42,6 @@ protected function bootDebugKernel()
return static::$kernel;
}

/**
* @runInSeparateProcess
*/
public function testCanBeLoaded()
{
/** @var Container $container */
Expand Down
6 changes: 0 additions & 6 deletions tests/Functional/EventListener/CacheControlListenerTest.php
Expand Up @@ -18,9 +18,6 @@ class CacheControlListenerTest extends WebTestCase
{
use MockeryPHPUnitIntegration;

/**
* @runInSeparateProcess
*/
public function testIsCached()
{
$client = static::createClient();
Expand All @@ -30,9 +27,6 @@ public function testIsCached()
$this->assertEquals('public', $response->headers->get('Cache-Control'));
}

/**
* @runInSeparateProcess
*/
public function testNotCached()
{
$client = static::createClient();
Expand Down

0 comments on commit 6a09293

Please sign in to comment.