Completed
Pull Request — 8.x-3.x (#716)
by Sebastian
02:16
created

SubRequestBuffer   A

Complexity

Total Complexity 10

Size/Duplication

Total Lines 141
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 3

Importance

Changes 0
Metric Value
dl 0
loc 141
rs 10
c 0
b 0
f 0
wmc 10
lcom 1
cbo 3

5 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 4 1
A add() 0 8 1
A getBufferId() 0 11 1
A createRequest() 0 23 2
A resolveBufferArray() 0 35 5
1
<?php
2
3
namespace Drupal\graphql\GraphQL\Buffers;
4
5
use Drupal\Core\Cache\CacheableDependencyInterface;
6
use Drupal\Core\Url;
7
use Drupal\Core\Routing\LocalRedirectResponse;
8
use Drupal\graphql\GraphQL\Buffers\SubRequestResponse;
9
use Drupal\graphql\GraphQL\Cache\CacheableValue;
10
use Symfony\Component\HttpFoundation\Request;
11
use Symfony\Component\HttpFoundation\RequestStack;
12
use Symfony\Component\HttpKernel\HttpKernelInterface;
13
14
class SubRequestBuffer extends BufferBase {
15
16
  /**
17
   * The http kernel service.
18
   *
19
   * @var \Symfony\Component\HttpKernel\HttpKernelInterface
20
   */
21
  protected $httpKernel;
22
23
  /**
24
   * The request stack service.
25
   *
26
   * @var \Symfony\Component\HttpFoundation\RequestStack
27
   */
28
  protected $requestStack;
29
30
  /**
31
   * SubrequestBuffer constructor.
32
   *
33
   * @param \Symfony\Component\HttpKernel\HttpKernelInterface $httpKernel
34
   *   The http kernel service.
35
   * @param \Symfony\Component\HttpFoundation\RequestStack $requestStack
36
   *   The request stack service.
37
   */
38
  public function __construct(HttpKernelInterface $httpKernel, RequestStack $requestStack) {
39
    $this->httpKernel = $httpKernel;
40
    $this->requestStack = $requestStack;
41
  }
42
43
  /**
44
   * Add an item to the buffer.
45
   *
46
   * @param \Drupal\Core\Url $url
47
   *   The url object to run the subrequest on.
48
   * @param callable $extract
49
   *   The callback to run within the sub-request.
50
   *
51
   * @return \Closure
52
   *   The callback to invoke to load the result for this buffer item.
53
   */
54
  public function add(Url $url, callable $extract) {
55
    $item = new \ArrayObject([
56
      'url' => $url,
57
      'extract' => $extract,
58
    ]);
59
60
    return $this->createBufferResolver($item);
61
  }
62
63
  /**
64
   * {@inheritdoc}
65
   */
66
  protected function getBufferId($item) {
67
    /** @var \Drupal\Core\GeneratedUrl $url */
68
    $url = $item['url']->toString(TRUE);
69
70
    return hash('sha256', json_encode([
71
      'url' => $url->getGeneratedUrl(),
72
      'tags' => $url->getCacheTags(),
73
      'contexts' => $url->getCacheContexts(),
74
      'age' => $url->getCacheMaxAge(),
75
    ]));
76
  }
77
78
  /**
79
   * Create a sub-request for the given url.
80
   *
81
   * @param \Symfony\Component\HttpFoundation\Request $current
82
   *   The current main request.
83
   * @param string $url
84
   *   The url to run the subrequest on.
85
   * @param array $buffer
86
   *   The buffer.
87
   *
88
   * @return \Symfony\Component\HttpFoundation\Request
89
   *   The request object.
90
   */
91
  protected function createRequest(Request $current, array $buffer, $url) {
92
    $request = Request::create(
93
      $url,
94
      'GET',
95
      $current->query->all(),
96
      $current->cookies->all(),
97
      $current->files->all(),
98
      $current->server->all()
99
    );
100
101
    $request->attributes->set('_graphql_subrequest', function () use ($buffer) {
102
      return array_map(function ($item) {
103
        return $item['extract']($item['url']);
104
      }, $buffer);
105
    });
106
107
    $request->setRequestFormat('_graphql_subrequest');
108
    if ($session = $current->getSession()) {
109
      $request->setSession($session);
110
    }
111
    
112
    return $request;
113
  }
114
115
  /**
116
   * {@inheritdoc}
117
   */
118
  public function resolveBufferArray(array $buffer) {
119
    /** @var \Drupal\Core\GeneratedUrl $url */
120
    $url = reset($buffer)['url']->toString(TRUE);
121
122
    $current = $this->requestStack->getCurrentRequest();
123
    $target = $url->getGeneratedUrl();
124
    $request = $this->createRequest($current, $buffer, $target);
125
    
126
    /** @var \Drupal\graphql\GraphQL\Buffers\SubRequestResponse $response */
127
    $response = $this->httpKernel->handle($request, HttpKernelInterface::SUB_REQUEST);
128
    while ($response instanceof LocalRedirectResponse) {
0 ignored issues
show
Bug introduced by
The class Drupal\Core\Routing\LocalRedirectResponse does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
129
      $target = $response->getTargetUrl();
130
      $request = $this->createRequest($current, $buffer, $target);
131
      $response = $this->httpKernel->handle($request, HttpKernelInterface::SUB_REQUEST);
132
    }
133
    
134
    if (!($response instanceof SubRequestResponse)) {
135
      return array_fill_keys(array_keys($buffer), NULL);
136
    }
137
138
    // TODO:
139
    // Remove the request stack manipulation once the core issue described at
140
    // https://www.drupal.org/node/2613044 is resolved.
141
    while ($this->requestStack->getCurrentRequest() !== $current) {
142
      $this->requestStack->pop();
143
    }
144
145
    if ($url instanceof CacheableDependencyInterface) {
0 ignored issues
show
Bug introduced by
The class Drupal\Core\Cache\CacheableDependencyInterface does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
146
      $response->addCacheableDependency($url);
147
    }
148
    
149
    return array_map(function ($value) use ($response) {
150
      return new CacheableValue($value, [$response]);
151
    }, $response->getResult());
152
  }
153
154
}
155