Skip to content

Commit

Permalink
Add a PHP 8 attribute for configuration
Browse files Browse the repository at this point in the history
  • Loading branch information
mpdude committed Mar 28, 2024
1 parent 2d153e5 commit 36a27cc
Show file tree
Hide file tree
Showing 5 changed files with 130 additions and 82 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ services:

and note the service name to the Annotation:

@ReplaceWithNotModifiedResponse({"app_caching_latest_posts"})
@ReplaceWithNotModifiedResponse({"@app_caching_latest_posts"})

To combine multiple LastModifiedDeterminators, simply add all of them to the annotation:

Expand Down
5 changes: 3 additions & 2 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,12 @@

"require": {
"php": "^7.1|8.0.*|8.1.*",
"doctrine/annotations": "^1.0",
"symfony/config": "^4.4 | ^5.0 | ^6.0",
"symfony/dependency-injection": "^4.4 | ^5.0 | ^6.0",
"symfony/deprecation-contracts": "^2.0|^3.0",
"symfony/http-foundation": "^4.4 | ^5.0 | ^6.0",
"symfony/http-kernel": "^4.4 | ^5.0 | ^6.0",
"doctrine/annotations": "^1.0"
"symfony/http-kernel": "^4.4 | ^5.0 | ^6.0"
},

"require-dev": {
Expand Down
88 changes: 11 additions & 77 deletions src/NotModified/Annotation/ReplaceWithNotModifiedResponse.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,88 +9,22 @@

namespace Webfactory\HttpCacheBundle\NotModified\Annotation;

use DateTime;
use RuntimeException;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\Request;
use Webfactory\HttpCacheBundle\NotModified\LastModifiedDeterminator;

/**
* This Annotation determines the latest last modified date over all of its LastModifiedDeterminators. This date is used
* by the \Webfactory\HttpCacheBundle\NotModified\EventListener to possibly replace the execution of a controller with
* sending a Not Modified HTTP response.
*
* @Annotation
* @deprecated, to be replaced by attribute-based configuration
*/
final class ReplaceWithNotModifiedResponse
final class ReplaceWithNotModifiedResponse extends \Webfactory\HttpCacheBundle\NotModified\Attribute\ReplaceWithNotModifiedResponse
{
/** @var array */
private $parameters;

/** @var LastModifiedDeterminator[] */
private $lastModifiedDeterminators;

/** @var ContainerInterface */
private $container;

/** @var DateTime|null */
private $lastModified;

public function __construct(array $parameters)
{
$this->parameters = $parameters;
}

/**
* @return DateTime|null
*/
public function determineLastModified(Request $request)
{
$this->initialiseLastModifiedDeterminators();

foreach ($this->lastModifiedDeterminators as $lastModifiedDeterminator) {
$lastModifiedOfCurrentDeterminator = $lastModifiedDeterminator->getLastModified($request);
if (null === $this->lastModified || $this->lastModified < $lastModifiedOfCurrentDeterminator) {
$this->lastModified = $lastModifiedOfCurrentDeterminator;
}
}

return $this->lastModified;
}

public function setContainer(ContainerInterface $container)
{
$this->container = $container;
}

private function initialiseLastModifiedDeterminators()
{
if (0 === count($this->parameters['value'])) {
throw new RuntimeException('The annotation '.get_class($this).' has to be parametrised with LastModifiedDeterminators.');
}

foreach ($this->parameters['value'] as $lastModifiedDeterminatorDescription) {
$lastModifiedDeterminator = null;

if (is_string($lastModifiedDeterminatorDescription)) {
if ('@' === $lastModifiedDeterminatorDescription[0]) {
$lastModifiedDeterminator = $this->container->get(substr($lastModifiedDeterminatorDescription, 1));
} else {
$lastModifiedDeterminator = new $lastModifiedDeterminatorDescription();
}
}

if (is_array($lastModifiedDeterminatorDescription)) {
$lastModifiedDeterminatorClass = key($lastModifiedDeterminatorDescription);
$lastModifiedDeterminatorParameter = current($lastModifiedDeterminatorDescription);
$lastModifiedDeterminator = new $lastModifiedDeterminatorClass($lastModifiedDeterminatorParameter);
}

if (!($lastModifiedDeterminator instanceof LastModifiedDeterminator)) {
throw new RuntimeException('The class "'.get_class($lastModifiedDeterminator).'" does not implement '.LastModifiedDeterminator::class.'.');
}

$this->lastModifiedDeterminators[] = $lastModifiedDeterminator;
}
trigger_deprecation(
'webfactory/http-cache-bundle',
'1.4.0',
'The %s annotation has been deprecated, use the %s attribute instead.',
NotModified\Annotation\ReplaceWithNotModifiedResponse::class,
NotModified\Attribute\ReplaceWithNotModifiedResponse::class
);

parent::__construct($parameters['value']);
}
}
97 changes: 97 additions & 0 deletions src/NotModified/Attribute/ReplaceWithNotModifiedResponse.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
<?php

/*
* (c) webfactory GmbH <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Webfactory\HttpCacheBundle\NotModified\Attribute;

use DateTime;
use RuntimeException;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\Request;
use Webfactory\HttpCacheBundle\NotModified\LastModifiedDeterminator;

/**
* This attribute determines the latest last modified date over all of its LastModifiedDeterminators. This date is used
* by the \Webfactory\HttpCacheBundle\NotModified\EventListener to possibly replace the execution of a controller with
* sending a Not Modified HTTP response.
*
* @final
*/
#[\Attribute(\Attribute::TARGET_METHOD)]
class ReplaceWithNotModifiedResponse
{
/** @var array */
private $parameters;

/** @var LastModifiedDeterminator[] */
private $lastModifiedDeterminators;

/** @var ContainerInterface */
private $container;

/** @var DateTime|null */
private $lastModified;

public function __construct(array $parameters)
{
$this->parameters = $parameters;
}

/**
* @return DateTime|null
*/
public function determineLastModified(Request $request)
{
$this->initialiseLastModifiedDeterminators();

foreach ($this->lastModifiedDeterminators as $lastModifiedDeterminator) {
$lastModifiedOfCurrentDeterminator = $lastModifiedDeterminator->getLastModified($request);
if (null === $this->lastModified || $this->lastModified < $lastModifiedOfCurrentDeterminator) {
$this->lastModified = $lastModifiedOfCurrentDeterminator;
}
}

return $this->lastModified;
}

public function setContainer(ContainerInterface $container)
{
$this->container = $container;
}

private function initialiseLastModifiedDeterminators()
{
if (0 === count($this->parameters)) {
throw new RuntimeException('The attribute '.get_class($this).' has to be parametrised with LastModifiedDeterminators.');
}

foreach ($this->parameters as $lastModifiedDeterminatorDescription) {
$lastModifiedDeterminator = null;

if (is_string($lastModifiedDeterminatorDescription)) {
if ('@' === $lastModifiedDeterminatorDescription[0]) {
$lastModifiedDeterminator = $this->container->get(substr($lastModifiedDeterminatorDescription, 1));
} else {
$lastModifiedDeterminator = new $lastModifiedDeterminatorDescription();
}
}

if (is_array($lastModifiedDeterminatorDescription)) {
$lastModifiedDeterminatorClass = key($lastModifiedDeterminatorDescription);
$lastModifiedDeterminatorParameter = current($lastModifiedDeterminatorDescription);
$lastModifiedDeterminator = new $lastModifiedDeterminatorClass($lastModifiedDeterminatorParameter);
}

if (!($lastModifiedDeterminator instanceof LastModifiedDeterminator)) {
throw new RuntimeException('The class "'.get_class($lastModifiedDeterminator).'" does not implement '.LastModifiedDeterminator::class.'.');
}

$this->lastModifiedDeterminators[] = $lastModifiedDeterminator;
}
}
}
20 changes: 18 additions & 2 deletions src/NotModified/EventListener.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Event\ControllerEvent;
use Symfony\Component\HttpKernel\Event\ResponseEvent;
use Webfactory\HttpCacheBundle\NotModified\Annotation\ReplaceWithNotModifiedResponse;
use Webfactory\HttpCacheBundle\NotModified;

/**
* Symfony EventListener for adding a "last modified" header to the response on the one hand. On the other hand, it
Expand Down Expand Up @@ -118,8 +118,24 @@ private function findAnnotation(callable $controllerCallable)
[$class, $methodName] = $controllerCallable;
$method = new ReflectionMethod($class, $methodName);

if (PHP_MAJOR_VERSION >= 8) {
$attributes = $method->getAttributes(NotModified\Attribute\ReplaceWithNotModifiedResponse::class);

if ($attributes) {
return $attributes[0]->newInstance();
}
}

/** @var ReplaceWithNotModifiedResponse|null $annotation */
$annotation = $this->reader->getMethodAnnotation($method, ReplaceWithNotModifiedResponse::class);
$annotation = $this->reader->getMethodAnnotation($method, NotModified\Annotation\ReplaceWithNotModifiedResponse::class);

if ($annotation) {
trigger_deprecation(
'webfactory/http-cache-bundle',
'1.4.0',
'Configuring webfactory/http-cache-bundle with annotations is deprecated, use attributes instead.'
);
}

return $annotation;
}
Expand Down

0 comments on commit 36a27cc

Please sign in to comment.