Skip to content

Commit d51ef18

Browse files
authored
fix(image): Prevent cache metadata leak exception for image URL generation. (#1039)
1 parent 560befb commit d51ef18

2 files changed

Lines changed: 127 additions & 4 deletions

File tree

src/Plugin/GraphQL/DataProducer/Entity/Fields/Image/ImageDerivative.php

Lines changed: 64 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,13 @@
33
namespace Drupal\graphql\Plugin\GraphQL\DataProducer\Entity\Fields\Image;
44

55
use Drupal\Core\Cache\RefinableCacheableDependencyInterface;
6+
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
7+
use Drupal\Core\Render\RenderContext;
8+
use Drupal\Core\Render\RendererInterface;
69
use Drupal\file\FileInterface;
710
use Drupal\graphql\Plugin\GraphQL\DataProducer\DataProducerPluginBase;
811
use Drupal\image\Entity\ImageStyle;
12+
use Symfony\Component\DependencyInjection\ContainerInterface;
913

1014
/**
1115
* @DataProducer(
@@ -26,7 +30,52 @@
2630
* }
2731
* )
2832
*/
29-
class ImageDerivative extends DataProducerPluginBase {
33+
class ImageDerivative extends DataProducerPluginBase implements ContainerFactoryPluginInterface {
34+
35+
/**
36+
* The rendering service.
37+
*
38+
* @var \Drupal\Core\Render\RendererInterface
39+
*/
40+
protected $renderer;
41+
42+
/**
43+
* {@inheritdoc}
44+
*
45+
* @codeCoverageIgnore
46+
*/
47+
public static function create(ContainerInterface $container, array $configuration, $pluginId, $pluginDefinition) {
48+
return new static(
49+
$configuration,
50+
$pluginId,
51+
$pluginDefinition,
52+
$container->get('renderer')
53+
);
54+
}
55+
56+
/**
57+
* ImageDerivative constructor.
58+
*
59+
* @param array $configuration
60+
* The plugin configuration array.
61+
* @param string $pluginId
62+
* The plugin id.
63+
* @param mixed $pluginDefinition
64+
* The plugin definition.
65+
* @param \Drupal\Core\Render\RendererInterface $renderer
66+
* The renderer service.
67+
*
68+
* @codeCoverageIgnore
69+
*/
70+
public function __construct(
71+
array $configuration,
72+
$pluginId,
73+
$pluginDefinition,
74+
RendererInterface $renderer
75+
) {
76+
parent::__construct($configuration, $pluginId, $pluginDefinition);
77+
$this->renderer = $renderer;
78+
}
3079

3180
/**
3281
* @param \Drupal\file\FileInterface $entity
@@ -67,8 +116,21 @@ public function resolve(FileInterface $entity = NULL, $style, RefinableCacheable
67116
$image_style->transformDimensions($dimensions, $entity->getFileUri());
68117
$metadata->addCacheableDependency($image_style);
69118

119+
// The underlying URL generator that will be invoked will leak cache
120+
// metadata, resulting in an exception. By wrapping within a new render
121+
// context, we can capture the leaked metadata and make sure it gets
122+
// incorporated into the response.
123+
$context = new RenderContext();
124+
$url = $this->renderer->executeInRenderContext($context, function () use ($image_style, $entity) {
125+
return $image_style->buildUrl($entity->getFileUri());
126+
});
127+
128+
if (!$context->isEmpty()) {
129+
$metadata->addCacheableDependency($context->pop());
130+
}
131+
70132
return [
71-
'url' => $image_style->buildUrl($entity->getFileUri()),
133+
'url' => $url,
72134
'width' => $dimensions['width'],
73135
'height' => $dimensions['height'],
74136
];

src/Plugin/GraphQL/DataProducer/Entity/Fields/Image/ImageUrl.php

Lines changed: 63 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,12 @@
33
namespace Drupal\graphql\Plugin\GraphQL\DataProducer\Entity\Fields\Image;
44

55
use Drupal\Core\Cache\RefinableCacheableDependencyInterface;
6+
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
7+
use Drupal\Core\Render\RenderContext;
8+
use Drupal\Core\Render\RendererInterface;
69
use Drupal\file\FileInterface;
710
use Drupal\graphql\Plugin\GraphQL\DataProducer\DataProducerPluginBase;
11+
use Symfony\Component\DependencyInjection\ContainerInterface;
812

913
/**
1014
* @DataProducer(
@@ -21,7 +25,52 @@
2125
* }
2226
* )
2327
*/
24-
class ImageUrl extends DataProducerPluginBase {
28+
class ImageUrl extends DataProducerPluginBase implements ContainerFactoryPluginInterface {
29+
30+
/**
31+
* The rendering service.
32+
*
33+
* @var \Drupal\Core\Render\RendererInterface
34+
*/
35+
protected $renderer;
36+
37+
/**
38+
* {@inheritdoc}
39+
*
40+
* @codeCoverageIgnore
41+
*/
42+
public static function create(ContainerInterface $container, array $configuration, $pluginId, $pluginDefinition) {
43+
return new static(
44+
$configuration,
45+
$pluginId,
46+
$pluginDefinition,
47+
$container->get('renderer')
48+
);
49+
}
50+
51+
/**
52+
* ImageUrl constructor.
53+
*
54+
* @param array $configuration
55+
* The plugin configuration array.
56+
* @param string $pluginId
57+
* The plugin id.
58+
* @param mixed $pluginDefinition
59+
* The plugin definition.
60+
* @param \Drupal\Core\Render\RendererInterface $renderer
61+
* The renderer service.
62+
*
63+
* @codeCoverageIgnore
64+
*/
65+
public function __construct(
66+
array $configuration,
67+
$pluginId,
68+
$pluginDefinition,
69+
RendererInterface $renderer
70+
) {
71+
parent::__construct($configuration, $pluginId, $pluginDefinition);
72+
$this->renderer = $renderer;
73+
}
2574

2675
/**
2776
* @param \Drupal\file\FileInterface $entity
@@ -34,7 +83,19 @@ public function resolve(FileInterface $entity, RefinableCacheableDependencyInter
3483
$access = $entity->access('view', NULL, TRUE);
3584
$metadata->addCacheableDependency($access);
3685
if ($access->isAllowed()) {
37-
return file_create_url($entity->getFileUri());
86+
// The underlying URL generator that will be invoked will leak cache
87+
// metadata, resulting in an exception. By wrapping within a new render
88+
// context, we can capture the leaked metadata and make sure it gets
89+
// incorporated into the response.
90+
$context = new RenderContext();
91+
$url = $this->renderer->executeInRenderContext($context, function () use ($entity) {
92+
return file_create_url($entity->getFileUri());
93+
});
94+
95+
if (!$context->isEmpty()) {
96+
$metadata->addCacheableDependency($context->pop());
97+
}
98+
return $url;
3899
}
39100
}
40101

0 commit comments

Comments
 (0)