Passed
Push — master ( 7ce289...8aaaed )
by Dāvis
04:53
created

MiddlewarePass::makeOptions()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 3
nc 2
nop 2
dl 0
loc 5
rs 9.4285
c 0
b 0
f 0
1
<?php
2
3
namespace Sludio\HelperBundle\DependencyInjection\Compiler;
4
5
use GuzzleHttp\HandlerStack;
0 ignored issues
show
Bug introduced by
The type GuzzleHttp\HandlerStack was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
6
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
7
use Symfony\Component\DependencyInjection\ContainerBuilder;
8
use Symfony\Component\DependencyInjection\Definition;
9
use Symfony\Component\DependencyInjection\Exception\LogicException;
10
use Symfony\Component\DependencyInjection\Reference;
11
12
/**
13
 * Csa Guzzle middleware compiler pass.
14
 *
15
 * @author Charles Sarrazin <[email protected]>
16
 * @author Tobias Schultze <http://tobion.de>
17
 */
18
class MiddlewarePass implements CompilerPassInterface
19
{
20
    const MIDDLEWARE_TAG = 'sludio_helper.guzzle.middleware';
21
    const CLIENT_TAG = 'sludio_helper.guzzle.client';
22
23
    public function process(ContainerBuilder $container)
24
    {
25
        $middleware = $this->findAvailableMiddleware($container);
26
27
        $this->registerMiddleware($container, $middleware);
28
    }
29
30
    /**
31
     * Fetches the list of available middleware.
32
     *
33
     * @param ContainerBuilder $container
34
     *
35
     * @return array
36
     * @throws LogicException
37
     */
38
    private function findAvailableMiddleware(ContainerBuilder $container)
39
    {
40
        $services = $container->findTaggedServiceIds(self::MIDDLEWARE_TAG);
41
        $middleware = [];
42
43
        foreach ($services as $id => $tags) {
44
            if (\count($tags) > 1) {
45
                throw new LogicException(sprintf('Middleware should only use a single \'%s\' tag', self::MIDDLEWARE_TAG));
46
            }
47
48
            if (!isset($tags[0]['alias'])) {
49
                throw new LogicException(sprintf('The \'alias\' attribute is mandatory for the \'%s\' tag', self::MIDDLEWARE_TAG));
50
            }
51
52
            $priority = isset($tags[0]['priority']) ? $tags[0]['priority'] : 0;
53
54
            $middleware[$priority][] = [
55
                'alias' => $tags[0]['alias'],
56
                'id' => $id,
57
            ];
58
        }
59
60
        krsort($middleware);
61
62
        return !empty($middleware) ? call_user_func_array('array_merge', $middleware) : [];
63
    }
64
65
    private function getHandlerStack($options, $container, &$handlerStack)
66
    {
67
        if (!isset($options['handler'])) {
68
            $handlerStack = new Definition(HandlerStack::class);
69
            $handlerStack->setFactory([
70
                HandlerStack::class,
71
                'create',
72
            ]);
73
            $handlerStack->setPublic(false);
74
        } else {
75
            $handlerStack = $this->wrapHandlerInHandlerStack($options['handler'], $container);
76
        }
77
    }
78
79
    private function makeOptions(array $arguments = [], &$options)
80
    {
81
        $options = [];
82
        if (!empty($arguments)) {
83
            $options = array_shift($arguments);
84
        }
85
    }
86
87
    /**
88
     * Sets up handlers and registers middleware for each tagged client.
89
     *
90
     * @param ContainerBuilder $container
91
     * @param array            $middlewareBag
92
     *
93
     * @throws \Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException
94
     */
95
    private function registerMiddleware(ContainerBuilder $container, array $middlewareBag)
96
    {
97
        $clients = $container->findTaggedServiceIds(self::CLIENT_TAG);
98
99
        foreach ($clients as $clientId => $tags) {
100
            if (\count($tags) > 1) {
101
                throw new LogicException(sprintf('Clients should use a single \'%s\' tag', self::CLIENT_TAG));
102
            }
103
104
            try {
105
                $clientMiddleware = $this->filterClientMiddleware($middlewareBag, $tags);
106
            } catch (LogicException $e) {
107
                continue;
108
            }
109
110
            if (empty($clientMiddleware)) {
111
                continue;
112
            }
113
114
            $clientDefinition = $container->findDefinition($clientId);
115
116
            $arguments = $clientDefinition->getArguments();
117
            $this->makeOptions($arguments, $options);
118
119
            $this->getHandlerStack($options, $container, $handlerStack);
120
            $this->addMiddlewareToHandlerStack($handlerStack, $clientMiddleware);
121
            $options['handler'] = $handlerStack;
122
123
            array_unshift($arguments, $options);
124
            $clientDefinition->setArguments($arguments);
125
        }
126
    }
127
128
    private function makeLists($tag, &$whiteList, &$blackList)
129
    {
130
        $whiteList = $blackList = [];
131
        $clientMiddlewareList = explode(' ', $tag['middleware']);
132
        foreach ($clientMiddlewareList as $middleware) {
133
            if ('!' === $middleware[0]) {
134
                $blackList[] = substr($middleware, 1);
135
            } else {
136
                $whiteList[] = $middleware;
137
            }
138
        }
139
140
        if ($whiteList && $blackList) {
141
            throw new LogicException('You cannot mix whitelisting and blacklisting of middleware at the same time.');
142
        }
143
    }
144
145
    /**
146
     * @param array $middlewareBag The list of availables middleware
147
     * @param array $tags          The tags containing middleware configuration
148
     *
149
     * @return array The list of middleware to enable for the client
150
     *
151
     * @throws LogicException When middleware configuration is invalid
152
     */
153
    private function filterClientMiddleware(array $middlewareBag, array $tags)
154
    {
155
        if (!isset($tags[0]['middleware'])) {
156
            return !empty($middlewareBag) ? $middlewareBag : null;
157
        }
158
159
        $this->makeLists($tags[0], $whiteList, $blackList);
160
161
        if ($whiteList) {
162
            return array_filter($middlewareBag, function ($value) use ($whiteList) {
163
                return \in_array($value['alias'], $whiteList, true);
164
            });
165
        }
166
167
        return array_filter($middlewareBag, function ($value) use ($blackList) {
168
            return !\in_array($value['alias'], $blackList, true);
169
        });
170
    }
171
172
    /**
173
     * @param Reference|Definition|callable $handler   The configured Guzzle handler
174
     * @param ContainerBuilder              $container The container builder
175
     *
176
     * @return Definition
177
     * @throws \Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException
178
     */
179
    private function wrapHandlerInHandlerStack($handler, ContainerBuilder $container)
180
    {
181
        if ($handler instanceof Reference) {
182
            $handler = $container->getDefinition((string)$handler);
183
        }
184
185
        if ($handler instanceof Definition && HandlerStack::class === $handler->getClass()) {
186
            // no need to wrap the Guzzle handler if it already resolves to a HandlerStack
187
            return $handler;
188
        }
189
190
        $handlerDefinition = new Definition(HandlerStack::class);
191
        $handlerDefinition->setArguments([$handler]);
192
        $handlerDefinition->setPublic(false);
193
194
        return $handlerDefinition;
195
    }
196
197
    private function addMiddlewareToHandlerStack(Definition $handlerStack, array $middlewareBag)
198
    {
199
        foreach ($middlewareBag as $middleware) {
200
            $handlerStack->addMethodCall('push', [
201
                new Reference($middleware['id']),
202
                $middleware['alias'],
203
            ]);
204
        }
205
    }
206
}
207