Passed
Pull Request — master (#9)
by Pavel
11:27
created

RpcController::handleSingleRequest()   C

Complexity

Conditions 7
Paths 9

Size

Total Lines 42
Code Lines 25

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 12
CRAP Score 16.8703

Importance

Changes 0
Metric Value
dl 0
loc 42
c 0
b 0
f 0
ccs 12
cts 29
cp 0.4138
rs 6.7272
cc 7
eloc 25
nc 9
nop 1
crap 16.8703
1
<?php
2
3
namespace Bankiru\Api\Rpc\Controller;
4
5
use Bankiru\Api\Rpc\Event\FilterControllerEvent;
6
use Bankiru\Api\Rpc\Event\FilterResponseEvent;
7
use Bankiru\Api\Rpc\Event\FinishRequestEvent;
8
use Bankiru\Api\Rpc\Event\GetExceptionResponseEvent;
9
use Bankiru\Api\Rpc\Event\GetResponseEvent;
10
use Bankiru\Api\Rpc\Event\ViewEvent;
11
use Bankiru\Api\Rpc\Routing\ControllerResolver\ControllerResolverInterface;
12
use Bankiru\Api\Rpc\Routing\Exception\MethodNotFoundException;
13
use Bankiru\Api\Rpc\RpcEvents;
14
use Bankiru\Api\Rpc\RpcRequestInterface;
15
use ScayTrase\Api\Rpc\RpcResponseInterface;
16
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
17
use Symfony\Component\DependencyInjection\ContainerInterface;
18
use Symfony\Component\DependencyInjection\Exception\ExceptionInterface;
19
use Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException;
20
use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException;
21
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
22
use Symfony\Component\HttpKernel\KernelInterface;
23
24
abstract class RpcController implements ContainerAwareInterface
25
{
26
    /** @var  ContainerInterface */
27
    private $container;
28
29
    /**
30
     * Sets the container.
31
     *
32
     * @param ContainerInterface|null $container A ContainerInterface instance or null
33
     *
34
     * @throws ServiceNotFoundException
35
     * @throws ServiceCircularReferenceException
36
     */
37 9
    public function setContainer(ContainerInterface $container = null)
38
    {
39 9
        $this->container = $container;
40 9
    }
41
42
    /**
43
     * @param RpcRequestInterface $request
44
     * @param string              $endpoint
45
     *
46
     * @return RpcResponseInterface
47
     * @throws \Exception
48
     */
49 9
    protected function getResponse(RpcRequestInterface $request, $endpoint)
50
    {
51 9
        $request->getAttributes()->set('_endpoint', $endpoint);
52
53
        try {
54 9
            $rpcResponse = $this->handleSingleRequest($request);
55 9
        } catch (\Exception $e) {
56 5
            $rpcResponse = $this->handleException($e, $request);
57
        }
58
59 4
        return $rpcResponse;
60
    }
61
62
    /**
63
     * @param RpcRequestInterface $request
64
     *
65
     * @return RpcResponseInterface
66
     *
67
     * @throws \RuntimeException
68
     * @throws \InvalidArgumentException
69
     * @throws \LogicException
70
     * @throws MethodNotFoundException
71
     */
72 9
    protected function handleSingleRequest(RpcRequestInterface $request)
73
    {
74
        // request
75 9
        $event = new GetResponseEvent($this->getKernel(), $request);
76 9
        $this->getDispatcher()->dispatch(RpcEvents::REQUEST, $event);
77 8
        if ($event->hasResponse()) {
78
            return $this->filterResponse($event->getResponse(), $request);
79
        }
80
        // load controller
81 8
        if (false === $controller = $this->getResolver()->getController($request)) {
82
            throw new MethodNotFoundException($request->getMethod());
83
        }
84 8
        $event = new FilterControllerEvent($this->getKernel(), $request, $controller);
85 8
        $this->getDispatcher()->dispatch(RpcEvents::CONTROLLER, $event);
86 7
        $controller = $event->getController();
87
        // controller arguments
88 7
        $arguments = $this->getResolver()->getArguments($request, $controller);
89
        // call controller
90 4
        $response = call_user_func_array($controller, $arguments);
91
        // view
92 4
        if (!$response instanceof RpcResponseInterface) {
93
            $event = new ViewEvent($this->getKernel(), $request, $response);
94
            $this->getDispatcher()->dispatch(RpcEvents::VIEW, $event);
95
            if ($event->hasResponse()) {
96
                $response = $event->getResponse();
97
            }
98
            /** @noinspection NotOptimalIfConditionsInspection */
99
            if (!$response instanceof RpcResponseInterface) {
100
                $msg = sprintf(
101
                    'The controller must return a RpcResponseInterface response (%s given).',
102
                    $this->varToString($response)
103
                );
104
                // the user may have forgotten to return something
105
                if (null === $response) {
106
                    $msg .= ' Did you forget to add a return statement somewhere in your controller?';
107
                }
108
                throw new \LogicException($msg);
109
            }
110
        }
111
112 4
        return $this->filterResponse($response, $request);
113
    }
114
115
    /**
116
     * @param $name
117
     *
118
     * @return object|null
119
     * @throws ServiceNotFoundException
120
     * @throws ServiceCircularReferenceException
121
     */
122 9
    protected function get($name)
123
    {
124 9
        return $this->container->get($name);
125
    }
126
127
    /**
128
     * Filters a response object.
129
     *
130
     * @param RpcResponseInterface $response A Response instance
131
     * @param RpcRequestInterface  $request  An error message in case the response is not a Response object
132
     *
133
     * @return RpcResponseInterface The filtered Response instance
134
     * @throws \RuntimeException
135
     */
136 4
    protected function filterResponse(RpcResponseInterface $response, RpcRequestInterface $request)
137
    {
138 4
        $event = new FilterResponseEvent($this->getKernel(), $request, $response);
139 4
        $this->getDispatcher()->dispatch(RpcEvents::RESPONSE, $event);
140 4
        $this->finishRequest($request);
141
142 4
        return $event->getResponse();
143
    }
144
145
    /**
146
     * Publishes the finish request event, then pop the request from the stack.
147
     *
148
     * Note that the order of the operations is important here, otherwise
149
     * operations such as {@link RequestStack::getParentRequest()} can lead to
150
     * weird results.
151
     *
152
     * @param RpcRequestInterface $request
153
     *
154
     * @throws \RuntimeException
155
     */
156 9
    protected function finishRequest(RpcRequestInterface $request)
157
    {
158 9
        $this->getDispatcher()->dispatch(
159 9
            RpcEvents::FINISH_REQUEST,
160 9
            new FinishRequestEvent($this->getKernel(), $request)
161 9
        );
162 9
    }
163
164
    /**
165
     * @return ControllerResolverInterface
166
     */
167
    abstract protected function getResolver();
168
169
    /**
170
     * @param $var
171
     *
172
     * @return string
173
     */
174 View Code Duplication
    protected function varToString($var)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
175
    {
176
        if (is_object($var)) {
177
            return sprintf('Object(%s)', get_class($var));
178
        }
179
        if (is_array($var)) {
180
            $a = [];
181
            foreach ($var as $k => $v) {
182
                $a[] = sprintf('%s => %s', $k, $this->varToString($v));
183
            }
184
185
            return sprintf('Array(%s)', implode(', ', $a));
186
        }
187
        if (is_resource($var)) {
188
            return sprintf('Resource(%s)', get_resource_type($var));
189
        }
190
        if (null === $var) {
191
            return 'null';
192
        }
193
        if (false === $var) {
194
            return 'false';
195
        }
196
        if (true === $var) {
197
            return 'true';
198
        }
199
200
        return (string)$var;
201
    }
202
203
    /**
204
     * Handles an exception by trying to convert it to a Response.
205
     *
206
     * @param \Exception          $e       An \Exception instance
207
     * @param RpcRequestInterface $request A Request instance
208
     *
209
     * @return RpcResponseInterface A Response instance
210
     *
211
     * @throws \Exception
212
     */
213 5
    protected function handleException(\Exception $e, RpcRequestInterface $request)
214
    {
215 5
        $event = new GetExceptionResponseEvent($this->getKernel(), $request, $e);
216 5
        $this->getDispatcher()->dispatch(RpcEvents::EXCEPTION, $event);
217
        // a listener might have replaced the exception
218 5
        $e = $event->getException();
219 5
        if (!$event->hasResponse()) {
220 5
            $this->finishRequest($request);
221 5
            throw $e;
222
        }
223
        $response = $event->getResponse();
224
225
        try {
226
            return $this->filterResponse($response, $request);
227
        } catch (\Exception $e) {
228
            return $response;
229
        }
230
    }
231
232
    /**
233
     * @return EventDispatcherInterface
234
     */
235 9
    protected function getDispatcher()
236
    {
237 9
        return $this->get('event_dispatcher');
238
    }
239
240
    /**
241
     * @return KernelInterface
242
     * @throws \RuntimeException
243
     */
244 9
    private function getKernel()
245
    {
246
        try {
247
            /** @var KernelInterface|null $kernel */
248 9
            $kernel = $this->get('kernel');
249 9
        } catch (ExceptionInterface $e) {
250
            throw new \RuntimeException('Cannot obtain Kernel from container: ' . $e->getMessage(), $e->getCode(), $e);
251
        }
252 9
        if (null === $kernel) {
253
            throw new \RuntimeException('Cannot obtain Kernel from container');
254
        }
255
256 9
        return $kernel;
257
    }
258
}
259