Completed
Push — master ( 7774b2...9b3d0e )
by Pavel
03:24
created

RpcController::varToString()   C

Complexity

Conditions 8
Paths 8

Size

Total Lines 28
Code Lines 17

Duplication

Lines 28
Ratio 100 %

Code Coverage

Tests 0
CRAP Score 72

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 28
loc 28
ccs 0
cts 18
cp 0
rs 5.3846
cc 8
eloc 17
nc 8
nop 1
crap 72
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\Http\RequestInterface;
12
use Bankiru\Api\Rpc\Routing\ControllerResolver\ControllerResolverInterface;
13
use Bankiru\Api\Rpc\Routing\Exception\MethodNotFoundException;
14
use Bankiru\Api\Rpc\RpcEvents;
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
    /** @var  EventDispatcherInterface */
29
    private $dispatcher;
30
31
    /**
32
     * Sets the container.
33
     *
34
     * @param ContainerInterface|null $container A ContainerInterface instance or null
35
     *
36
     * @throws ServiceNotFoundException
37
     * @throws ServiceCircularReferenceException
38
     */
39 7
    public function setContainer(ContainerInterface $container = null)
40
    {
41 7
        $this->container  = $container;
42 7
        $this->dispatcher = $container->get('event_dispatcher');
0 ignored issues
show
Bug introduced by
It seems like $container is not always an object, but can also be of type null. Maybe add an additional type check?

If a variable is not always an object, we recommend to add an additional type check to ensure your method call is safe:

function someFunction(A $objectMaybe = null)
{
    if ($objectMaybe instanceof A) {
        $objectMaybe->doSomething();
    }
}
Loading history...
43 7
    }
44
45
    /**
46
     * @param RequestInterface $request
47
     * @param string           $endpoint
48
     *
49
     * @return RpcResponseInterface
50
     * @throws \Exception
51
     */
52 7
    protected function getResponse(RequestInterface $request, $endpoint)
53
    {
54 7
        $request->getAttributes()->set('_endpoint', $endpoint);
55
56
        try {
57 7
            $rpcResponse = $this->handleSingleRequest($request);
58 7
        } catch (\Exception $e) {
59 4
            $rpcResponse = $this->handleException($e, $request);
60
        }
61
62 3
        return $rpcResponse;
63
    }
64
65
    /**
66
     * @param RequestInterface $request
67
     *
68
     * @return RpcResponseInterface
69
     *
70
     * @throws \RuntimeException
71
     * @throws \InvalidArgumentException
72
     * @throws \LogicException
73
     * @throws MethodNotFoundException
74
     */
75 7
    protected function handleSingleRequest(RequestInterface $request)
76
    {
77
        // request
78 7
        $event = new GetResponseEvent($this->getKernel(), $request);
79 7
        $this->dispatcher->dispatch(RpcEvents::REQUEST, $event);
80 7
        if ($event->hasResponse()) {
81
            return $this->filterResponse($event->getResponse(), $request);
82
        }
83
        // load controller
84 7
        if (false === $controller = $this->getResolver()->getController($request)) {
85 1
            throw new MethodNotFoundException($request->getMethod());
86
        }
87 6
        $event = new FilterControllerEvent($this->getKernel(), $request, $controller);
88 6
        $this->dispatcher->dispatch(RpcEvents::CONTROLLER, $event);
89 6
        $controller = $event->getController();
90
        // controller arguments
91 6
        $arguments = $this->getResolver()->getArguments($request, $controller);
92
        // call controller
93 3
        $response = call_user_func_array($controller, $arguments);
94
        // view
95 3
        if (!$response instanceof RpcResponseInterface) {
96
            $event = new ViewEvent($this->getKernel(), $request, $response);
97
            $this->dispatcher->dispatch(RpcEvents::VIEW, $event);
98
            if ($event->hasResponse()) {
99
                $response = $event->getResponse();
100
            }
101
            /** @noinspection NotOptimalIfConditionsInspection */
102
            if (!$response instanceof RpcResponseInterface) {
103
                $msg = sprintf(
104
                    'The controller must return a RpcResponseInterface response (%s given).',
105
                    $this->varToString($response)
106
                );
107
                // the user may have forgotten to return something
108
                if (null === $response) {
109
                    $msg .= ' Did you forget to add a return statement somewhere in your controller?';
110
                }
111
                throw new \LogicException($msg);
112
            }
113
        }
114
115 3
        return $this->filterResponse($response, $request);
116
    }
117
118
    /**
119
     * @return KernelInterface
120
     * @throws \RuntimeException
121
     */
122 7
    private function getKernel()
123
    {
124
        try {
125
            /** @var KernelInterface|null $kernel */
126 7
            $kernel = $this->get('kernel');
127 7
        } catch (ExceptionInterface $e) {
128
            throw new \RuntimeException('Cannot obtain Kernel from container: ' . $e->getMessage(), $e->getCode(), $e);
129
        }
130 7
        if (null === $kernel) {
131
            throw new \RuntimeException('Cannot obtain Kernel from container');
132
        }
133
134 7
        return $kernel;
135
    }
136
137
    /**
138
     * @param $name
139
     *
140
     * @return object|null
141
     * @throws ServiceNotFoundException
142
     * @throws ServiceCircularReferenceException
143
     */
144 7
    protected function get($name)
145
    {
146 7
        return $this->container->get($name);
147
    }
148
149
    /**
150
     * Filters a response object.
151
     *
152
     * @param RpcResponseInterface $response A Response instance
153
     * @param RequestInterface     $request  An error message in case the response is not a Response object
154
     *
155
     * @return RpcResponseInterface The filtered Response instance
156
     * @throws \RuntimeException
157
     */
158 3
    protected function filterResponse(RpcResponseInterface $response, RequestInterface $request)
159
    {
160 3
        $event = new FilterResponseEvent($this->getKernel(), $request, $response);
161 3
        $this->dispatcher->dispatch(RpcEvents::RESPONSE, $event);
162 3
        $this->finishRequest($request);
163
164 3
        return $event->getResponse();
165
    }
166
167
    /**
168
     * Publishes the finish request event, then pop the request from the stack.
169
     *
170
     * Note that the order of the operations is important here, otherwise
171
     * operations such as {@link RequestStack::getParentRequest()} can lead to
172
     * weird results.
173
     *
174
     * @param RequestInterface $request
175
     *
176
     * @throws \RuntimeException
177
     */
178 7
    protected function finishRequest(RequestInterface $request)
179
    {
180 7
        $this->dispatcher->dispatch(
181 7
            RpcEvents::FINISH_REQUEST,
182 7
            new FinishRequestEvent($this->getKernel(), $request)
183 7
        );
184 7
    }
185
186
    /**
187
     * @return ControllerResolverInterface
188
     */
189
    abstract protected function getResolver();
190
191
    /**
192
     * @param $var
193
     *
194
     * @return string
195
     */
196 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...
197
    {
198
        if (is_object($var)) {
199
            return sprintf('Object(%s)', get_class($var));
200
        }
201
        if (is_array($var)) {
202
            $a = [];
203
            foreach ($var as $k => $v) {
204
                $a[] = sprintf('%s => %s', $k, $this->varToString($v));
205
            }
206
207
            return sprintf('Array(%s)', implode(', ', $a));
208
        }
209
        if (is_resource($var)) {
210
            return sprintf('Resource(%s)', get_resource_type($var));
211
        }
212
        if (null === $var) {
213
            return 'null';
214
        }
215
        if (false === $var) {
216
            return 'false';
217
        }
218
        if (true === $var) {
219
            return 'true';
220
        }
221
222
        return (string)$var;
223
    }
224
225
    /**
226
     * Handles an exception by trying to convert it to a Response.
227
     *
228
     * @param \Exception       $e       An \Exception instance
229
     * @param RequestInterface $request A Request instance
230
     *
231
     * @return RpcResponseInterface A Response instance
232
     *
233
     * @throws \Exception
234
     */
235 4
    protected function handleException(\Exception $e, RequestInterface $request)
236
    {
237 4
        $event = new GetExceptionResponseEvent($this->getKernel(), $request, $e);
238 4
        $this->dispatcher->dispatch(RpcEvents::EXCEPTION, $event);
239
        // a listener might have replaced the exception
240 4
        $e = $event->getException();
241 4
        if (!$event->hasResponse()) {
242 4
            $this->finishRequest($request);
243 4
            throw $e;
244
        }
245
        $response = $event->getResponse();
246
247
        try {
248
            return $this->filterResponse($response, $request);
249
        } catch (\Exception $e) {
250
            return $response;
251
        }
252
    }
253
}
254