Completed
Push — master ( 055f1b...f72a4b )
by Pavel
52s
created

RpcController::getResponse()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 12
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 2

Importance

Changes 1
Bugs 1 Features 0
Metric Value
c 1
b 1
f 0
dl 0
loc 12
ccs 6
cts 6
cp 1
rs 9.4285
cc 2
eloc 7
nc 2
nop 2
crap 2
1
<?php
2
/**
3
 * Created by PhpStorm.
4
 * User: batanov.pavel
5
 * Date: 16.03.2016
6
 * Time: 10:12
7
 */
8
9
namespace Bankiru\Api\Rpc\Controller;
10
11
use Bankiru\Api\Rpc\Event\FilterControllerEvent;
12
use Bankiru\Api\Rpc\Event\FilterResponseEvent;
13
use Bankiru\Api\Rpc\Event\FinishRequestEvent;
14
use Bankiru\Api\Rpc\Event\GetExceptionResponseEvent;
15
use Bankiru\Api\Rpc\Event\GetResponseEvent;
16
use Bankiru\Api\Rpc\Event\ViewEvent;
17
use Bankiru\Api\Rpc\Http\RequestInterface;
18
use Bankiru\Api\Rpc\Routing\ControllerResolver\ControllerResolverInterface;
19
use Bankiru\Api\Rpc\Routing\Exception\MethodNotFoundException;
20
use Bankiru\Api\Rpc\RpcEvents;
21
use ScayTrase\Api\Rpc\RpcResponseInterface;
22
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
23
use Symfony\Component\DependencyInjection\ContainerInterface;
24
use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException;
25
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
26
use Symfony\Component\HttpKernel\KernelInterface;
27
28
abstract class RpcController implements ContainerAwareInterface
29
{
30
    /** @var  ContainerInterface */
31
    private $container;
32
    /** @var  EventDispatcherInterface */
33
    private $dispatcher;
34
35
    /**
36
     * Sets the container.
37
     *
38
     * @param ContainerInterface|null $container A ContainerInterface instance or null
39
     */
40 7
    public function setContainer(ContainerInterface $container = null)
41
    {
42 7
        $this->container  = $container;
43 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...
44 7
    }
45
46
    /**
47
     * @param RequestInterface $request
48
     * @param string           $endpoint
49
     *
50
     * @return RpcResponseInterface
51
     * @throws \Exception
52
     */
53 7
    protected function getResponse(RequestInterface $request, $endpoint)
54
    {
55 7
        $request->getAttributes()->set('_endpoint', $endpoint);
56
57
        try {
58 7
            $rpcResponse = $this->handleSingleRequest($request);
59 7
        } catch (\Exception $e) {
60 4
            $rpcResponse = $this->handleException($e, $request);
61
        }
62
63 3
        return $rpcResponse;
64
    }
65
66
    /**
67
     * @param RequestInterface $request
68
     *
69
     * @return RpcResponseInterface
70
     *
71
     * @throws \RuntimeException
72
     * @throws \InvalidArgumentException
73
     * @throws \LogicException
74
     * @throws MethodNotFoundException
75
     */
76 7
    protected function handleSingleRequest(RequestInterface $request)
77
    {
78
        // request
79 7
        $event = new GetResponseEvent($this->getKernel(), $request);
80 7
        $this->dispatcher->dispatch(RpcEvents::REQUEST, $event);
81 7
        if ($event->hasResponse()) {
82
            return $this->filterResponse($event->getResponse(), $request);
83
        }
84
        // load controller
85 7
        if (false === $controller = $this->getResolver()->getController($request)) {
86 1
            throw new MethodNotFoundException($request->getMethod());
87
        }
88 6
        $event = new FilterControllerEvent($this->getKernel(), $request, $controller);
89 6
        $this->dispatcher->dispatch(RpcEvents::CONTROLLER, $event);
90 6
        $controller = $event->getController();
91
        // controller arguments
92 6
        $arguments = $this->getResolver()->getArguments($request, $controller);
93
        // call controller
94 3
        $response = call_user_func_array($controller, $arguments);
95
        // view
96 3
        if (!$response instanceof RpcResponseInterface) {
97
            $event = new ViewEvent($this->getKernel(), $request, $response);
98
            $this->dispatcher->dispatch(RpcEvents::VIEW, $event);
99
            if ($event->hasResponse()) {
100
                $response = $event->getResponse();
101
            }
102
            /** @noinspection NotOptimalIfConditionsInspection */
103
            if (!$response instanceof RpcResponseInterface) {
104
                $msg = sprintf(
105
                    'The controller must return a RpcResponseInterface response (%s given).',
106
                    $this->varToString($response)
107
                );
108
                // the user may have forgotten to return something
109
                if (null === $response) {
110
                    $msg .= ' Did you forget to add a return statement somewhere in your controller?';
111
                }
112
                throw new \LogicException($msg);
113
            }
114
        }
115
116 3
        return $this->filterResponse($response, $request);
117
    }
118
119
    /**
120
     * @return KernelInterface
121
     */
122 7
    private function getKernel()
123
    {
124 7
        return $this->get('kernel');
125
    }
126
127
    /**
128
     * @param $name
129
     *
130
     * @return object|null
131
     * @throws ServiceNotFoundException
132
     */
133 7
    protected function get($name)
134
    {
135
        /** @noinspection ExceptionsAnnotatingAndHandlingInspection */
136 7
        return $this->container->get($name);
137
    }
138
139
    /**
140
     * Filters a response object.
141
     *
142
     * @param RpcResponseInterface $response A Response instance
143
     * @param RequestInterface     $request  An error message in case the response is not a Response object
144
     *
145
     * @return RpcResponseInterface The filtered Response instance
146
     */
147 3
    protected function filterResponse(RpcResponseInterface $response, RequestInterface $request)
148
    {
149 3
        $event = new FilterResponseEvent($this->getKernel(), $request, $response);
150 3
        $this->dispatcher->dispatch(RpcEvents::RESPONSE, $event);
151 3
        $this->finishRequest($request);
152
153 3
        return $event->getResponse();
154
    }
155
156
    /**
157
     * Publishes the finish request event, then pop the request from the stack.
158
     *
159
     * Note that the order of the operations is important here, otherwise
160
     * operations such as {@link RequestStack::getParentRequest()} can lead to
161
     * weird results.
162
     *
163
     * @param RequestInterface $request
164
     */
165 7
    protected function finishRequest(RequestInterface $request)
166
    {
167 7
        $this->dispatcher->dispatch(
168 7
            RpcEvents::FINISH_REQUEST,
169 7
            new FinishRequestEvent($this->getKernel(), $request)
170 7
        );
171 7
    }
172
173
    /**
174
     * @return ControllerResolverInterface
175
     */
176
    abstract protected function getResolver();
177
178
    /**
179
     * @param $var
180
     *
181
     * @return string
182
     */
183 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...
184
    {
185
        if (is_object($var)) {
186
            return sprintf('Object(%s)', get_class($var));
187
        }
188
        if (is_array($var)) {
189
            $a = [];
190
            foreach ($var as $k => $v) {
191
                $a[] = sprintf('%s => %s', $k, $this->varToString($v));
192
            }
193
194
            return sprintf('Array(%s)', implode(', ', $a));
195
        }
196
        if (is_resource($var)) {
197
            return sprintf('Resource(%s)', get_resource_type($var));
198
        }
199
        if (null === $var) {
200
            return 'null';
201
        }
202
        if (false === $var) {
203
            return 'false';
204
        }
205
        if (true === $var) {
206
            return 'true';
207
        }
208
209
        return (string)$var;
210
    }
211
212
    /**
213
     * Handles an exception by trying to convert it to a Response.
214
     *
215
     * @param \Exception       $e       An \Exception instance
216
     * @param RequestInterface $request A Request instance
217
     *
218
     * @return RpcResponseInterface A Response instance
219
     *
220
     * @throws \Exception
221
     */
222 4
    protected function handleException(\Exception $e, RequestInterface $request)
223
    {
224 4
        $event = new GetExceptionResponseEvent($this->getKernel(), $request, $e);
225 4
        $this->dispatcher->dispatch(RpcEvents::EXCEPTION, $event);
226
        // a listener might have replaced the exception
227 4
        $e = $event->getException();
228 4
        if (!$event->hasResponse()) {
229 4
            $this->finishRequest($request);
230 4
            throw $e;
231
        }
232
        $response = $event->getResponse();
233
234
        try {
235
            return $this->filterResponse($response, $request);
1 ignored issue
show
Bug introduced by
It seems like $response defined by $event->getResponse() on line 232 can be null; however, Bankiru\Api\Rpc\Controll...oller::filterResponse() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
236
        } catch (\Exception $e) {
237
            return $response;
238
        }
239
    }
240
}
241