diff --git a/src/Widget/CacheTrait.php b/src/Widget/CacheTrait.php index e77ef732..92426130 100644 --- a/src/Widget/CacheTrait.php +++ b/src/Widget/CacheTrait.php @@ -55,6 +55,6 @@ trait CacheTrait private function createKey() { - return $this->getName() . $this->getTarget() . $this->getZone() . $this->getPriority(); + return sprintf('%s-%s-%s', $this->getSlug(), $this->getZone(), $this->getCacheDuration()); } } diff --git a/src/Widget/Injector/QueueProcessor.php b/src/Widget/Injector/QueueProcessor.php index 09af8e7a..fb86b3ca 100644 --- a/src/Widget/Injector/QueueProcessor.php +++ b/src/Widget/Injector/QueueProcessor.php @@ -4,9 +4,11 @@ declare(strict_types=1); namespace Bolt\Widget\Injector; +use Bolt\Widget\CacheAware; use Bolt\Widget\RequestAware; use Bolt\Widget\ResponseAware; use Bolt\Widget\WidgetInterface; +use Psr\SimpleCache\CacheInterface; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Tightenco\Collect\Support\Collection; @@ -44,7 +46,7 @@ class QueueProcessor return $response; } - public function process(Response $response, Request $request, Collection $queue, string $zone): void + public function process(Response $response, Request $request, Collection $queue, CacheInterface $cache, string $zone): void { /** @var WidgetInterface $widget */ foreach ($queue as $widget) { @@ -55,6 +57,9 @@ class QueueProcessor if ($widget instanceof ResponseAware) { $widget->setResponse($response); } + if ($widget instanceof CacheAware) { + $widget->setCache($cache); + } $this->injector->inject($widget, $response); } } diff --git a/src/Widget/NewsWidget.php b/src/Widget/NewsWidget.php index e4411cf2..c758767d 100644 --- a/src/Widget/NewsWidget.php +++ b/src/Widget/NewsWidget.php @@ -12,9 +12,10 @@ use Bolt\Widget\Injector\RequestZone; use GuzzleHttp\Client; use GuzzleHttp\Exception\RequestException; -class NewsWidget extends BaseWidget implements TwigAware, RequestAware, CacheAware +class NewsWidget extends BaseWidget implements TwigAware, RequestAware, CacheAware, StopwatchAware { use CacheTrait; + use StopwatchTrait; protected $name = 'News Widget'; protected $target = AdditionalTarget::WIDGET_BACK_DASHBOARD_ASIDE_TOP; diff --git a/src/Widget/RequestTrait.php b/src/Widget/RequestTrait.php index 153dbe77..87613385 100644 --- a/src/Widget/RequestTrait.php +++ b/src/Widget/RequestTrait.php @@ -9,6 +9,7 @@ use Symfony\Component\HttpFoundation\Request; trait RequestTrait { + /** @var Request */ private $request; public function setRequest(Request $request): self diff --git a/src/Widget/ResponseTrait.php b/src/Widget/ResponseTrait.php index af9fbff3..46aa9e6c 100644 --- a/src/Widget/ResponseTrait.php +++ b/src/Widget/ResponseTrait.php @@ -9,6 +9,7 @@ use Symfony\Component\HttpFoundation\Response; trait ResponseTrait { + /** @var Response */ private $response; public function setResponse(Response $response): self diff --git a/src/Widget/StopwatchAware.php b/src/Widget/StopwatchAware.php new file mode 100644 index 00000000..127f8688 --- /dev/null +++ b/src/Widget/StopwatchAware.php @@ -0,0 +1,18 @@ +stopwatch = $stopwatch; + + $this->stopwatch->start('widget.' . $this->getSlug()); + } + + public function stopStopwatch(): void + { + $this->stopwatch->stop('widget.' . $this->getSlug()); + } +} diff --git a/src/Widget/TwigTrait.php b/src/Widget/TwigTrait.php index c618ffdc..2e9f1a2a 100644 --- a/src/Widget/TwigTrait.php +++ b/src/Widget/TwigTrait.php @@ -9,6 +9,7 @@ use Twig\Environment; trait TwigTrait { + /** @var Environment */ private $twig; public function setTwig(Environment $twig): self diff --git a/src/Widget/WeatherWidget.php b/src/Widget/WeatherWidget.php index 0e9ebcf3..b5c6b42c 100644 --- a/src/Widget/WeatherWidget.php +++ b/src/Widget/WeatherWidget.php @@ -9,13 +9,17 @@ use Bolt\Widget\Injector\RequestZone; use GuzzleHttp\Client; use GuzzleHttp\Exception\RequestException; -class WeatherWidget extends BaseWidget implements TwigAware +class WeatherWidget extends BaseWidget implements TwigAware, CacheAware, StopwatchAware { + use CacheTrait; + use StopwatchTrait; + protected $name = 'Weather Widget'; protected $target = AdditionalTarget::WIDGET_BACK_DASHBOARD_ASIDE_TOP; protected $priority = 200; protected $template = '@bolt/widgets/weather.twig'; protected $zone = RequestZone::BACKEND; + protected $cacheDuration = 3600; /** @var string Open API key, don't use more than once per second */ public const KEY = '0acbdeea56dfafe244ac87707c5fdcb2'; diff --git a/src/Widgets.php b/src/Widgets.php index 5a8d2e96..cb153336 100644 --- a/src/Widgets.php +++ b/src/Widgets.php @@ -8,12 +8,14 @@ use Bolt\Widget\CacheAware; use Bolt\Widget\Injector\QueueProcessor; use Bolt\Widget\Injector\RequestZone; use Bolt\Widget\RequestAware; +use Bolt\Widget\StopwatchAware; use Bolt\Widget\TwigAware; use Bolt\Widget\WidgetInterface; use Psr\SimpleCache\CacheInterface; use Symfony\Component\HttpFoundation\RequestStack; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\StreamedResponse; +use Symfony\Component\Stopwatch\Stopwatch; use Tightenco\Collect\Support\Collection; use Twig\Environment; @@ -37,13 +39,22 @@ class Widgets /** @var CacheInterface */ private $cache; - public function __construct(RequestStack $requestStack, QueueProcessor $queueProcessor, Environment $twig, CacheInterface $cache) - { + /** @var Stopwatch */ + private $stopwatch; + + public function __construct( + RequestStack $requestStack, + QueueProcessor $queueProcessor, + Environment $twig, + CacheInterface $cache, + Stopwatch $stopwatch +) { $this->queue = new Collection([]); $this->requestStack = $requestStack; $this->queueProcessor = $queueProcessor; $this->twig = $twig; $this->cache = $cache; + $this->stopwatch = $stopwatch; } public function registerWidget(WidgetInterface $widget): void @@ -85,6 +96,10 @@ class Widgets private function invokeWidget(WidgetInterface $widget, array $params = []): string { + if ($widget instanceof StopwatchAware) { + $widget->startStopwatch($this->stopwatch); + } + if ($widget instanceof RequestAware) { $widget->setRequest($this->requestStack->getCurrentRequest()); } @@ -94,7 +109,13 @@ class Widgets } // Call the magic `__invoke` method on the $widget object - return $widget($params); + $renderedWidget = $widget($params); + + if ($widget instanceof StopwatchAware) { + $widget->stopStopwatch(); + } + + return $renderedWidget; } public function processQueue(Response $response): Response @@ -111,11 +132,12 @@ class Widgets } $queue = $this->queue; + $cache = $this->cache; return $this->queueProcessor->guardResponse( $response, - function (Response $response) use ($request, $queue, $zone): void { - $this->queueProcessor->process($response, $request, $queue, $zone); + function (Response $response) use ($request, $queue, $cache, $zone): void { + $this->queueProcessor->process($response, $request, $queue, $cache, $zone); } ); } diff --git a/tests/php/Widget/WidgetsTest.php b/tests/php/Widget/WidgetsTest.php index 49573f82..d4318488 100644 --- a/tests/php/Widget/WidgetsTest.php +++ b/tests/php/Widget/WidgetsTest.php @@ -19,26 +19,34 @@ use Symfony\Component\Cache\Simple\Psr6Cache; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\RequestStack; use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\Stopwatch\Stopwatch; use Twig\Environment; use Twig\Loader\ArrayLoader; class WidgetsTest extends StringTestCase { - public function testProcessWidgetsInQueue(): void + private function getWidgetsObject(array $templates = ['weather.twig' => '[Hello, weather!]'], $zone = RequestZone::BACKEND): Widgets { $queueProcessor = new QueueProcessor(new HtmlInjector()); $requestStack = new RequestStack(); $request = Request::createFromGlobals(); - RequestZone::setToRequest($request, RequestZone::BACKEND); + RequestZone::setToRequest($request, $zone); $requestStack->push($request); - $loader = new ArrayLoader(['weather.twig' => '[Hello, weather!]']); + $loader = new ArrayLoader($templates); $twig = new Environment($loader); $cache = new Psr6Cache(new TraceableAdapter(new FilesystemAdapter())); + $stopwatch = new Stopwatch(); + + return new Widgets($requestStack, $queueProcessor, $twig, $cache, $stopwatch); + } + + public function testProcessWidgetsInQueue(): void + { + $widgets = $this->getWidgetsObject(); - $widgets = new Widgets($requestStack, $queueProcessor, $twig, $cache); $response = new Response('foo'); $snippet = (new SnippetWidget()) @@ -54,16 +62,7 @@ class WidgetsTest extends StringTestCase public function testRenderWidget(): void { - $queueProcessor = new QueueProcessor(new HtmlInjector()); - $requestStack = new RequestStack(); - $requestStack->push(Request::createFromGlobals()); - - $loader = new ArrayLoader(['weather.twig' => '[Hello, weather!]']); - $twig = new Environment($loader); - - $cache = new Psr6Cache(new TraceableAdapter(new FilesystemAdapter())); - - $widgets = new Widgets($requestStack, $queueProcessor, $twig, $cache); + $widgets = $this->getWidgetsObject(); $weatherWidget = new WeatherWidget(); $weatherWidget->setTemplate('weather.twig'); @@ -79,16 +78,7 @@ class WidgetsTest extends StringTestCase public function testRenderWidgetWithExtraParameters(): void { - $queueProcessor = new QueueProcessor(new HtmlInjector()); - $requestStack = new RequestStack(); - $requestStack->push(Request::createFromGlobals()); - - $loader = new ArrayLoader(['dummy.twig' => '[Hello, {{ foo }}!]']); - $twig = new Environment($loader); - - $cache = new Psr6Cache(new TraceableAdapter(new FilesystemAdapter())); - - $widgets = new Widgets($requestStack, $queueProcessor, $twig, $cache); + $widgets = $this->getWidgetsObject(['dummy.twig' => '[Hello, {{ foo }}!]']); $widget = new DummyWidget(); $widget->setTemplate('dummy.twig'); @@ -103,17 +93,7 @@ class WidgetsTest extends StringTestCase public function testProcessHeaderWidget(): void { - $request = new Request(); - RequestZone::setToRequest($request, RequestZone::FRONTEND); - $requestStack = new RequestStack(); - $requestStack->push($request); - - $queueProcessor = new QueueProcessor(new HtmlInjector()); - $twig = new Environment(new ArrayLoader()); - - $cache = new Psr6Cache(new TraceableAdapter(new FilesystemAdapter())); - - $widgets = new Widgets($requestStack, $queueProcessor, $twig, $cache); + $widgets = $this->getWidgetsObject([], RequestZone::FRONTEND); $response = new Response('foo'); @@ -127,18 +107,7 @@ class WidgetsTest extends StringTestCase public function testProcessWeatherWidgetInTarget(): void { - $request = new Request(); - RequestZone::setToRequest($request, RequestZone::BACKEND); - $requestStack = new RequestStack(); - $requestStack->push($request); - - $queueProcessor = new QueueProcessor(new HtmlInjector()); - $loader = new ArrayLoader(['weather.twig' => '[Hello, weather!]']); - $twig = new Environment($loader); - - $cache = new Psr6Cache(new TraceableAdapter(new FilesystemAdapter())); - - $widgets = new Widgets($requestStack, $queueProcessor, $twig, $cache); + $widgets = $this->getWidgetsObject(); $response = new Response('foo'); @@ -159,18 +128,7 @@ class WidgetsTest extends StringTestCase public function testProcessWeatherWidgetInTarget2(): void { - $request = new Request(); - RequestZone::setToRequest($request, RequestZone::BACKEND); - $requestStack = new RequestStack(); - $requestStack->push($request); - - $queueProcessor = new QueueProcessor(new HtmlInjector()); - $loader = new ArrayLoader(['weather.twig' => '[Hello, weather!]']); - $twig = new Environment($loader); - - $cache = new Psr6Cache(new TraceableAdapter(new FilesystemAdapter())); - - $widgets = new Widgets($requestStack, $queueProcessor, $twig, $cache); + $widgets = $this->getWidgetsObject(); $response = new Response('foo');