Completed
Pull Request — master (#138)
by Abdul Malik
02:11
created

App::run()   A

Complexity

Conditions 2
Paths 3

Size

Total Lines 18
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 11
CRAP Score 2

Importance

Changes 10
Bugs 0 Features 2
Metric Value
c 10
b 0
f 2
dl 0
loc 18
ccs 11
cts 11
cp 1
rs 9.4286
cc 2
eloc 12
nc 3
nop 2
crap 2
1
<?php
2
3
namespace Penny;
4
5
use Exception;
6
use RuntimeException;
7
use Penny\Config\Loader;
8
use Penny\Event\PennyEvmInterface;
9
use Penny\Event\PennyEventInterface;
10
use Penny\Route\RouteInfoInterface;
11
use Interop\Container\ContainerInterface;
12
13
class App
14
{
15
    /**
16
     * Dependency Injection container.
17
     *
18
     * @var ContainerInterface
19
     */
20
    private $container;
21
22
    /**
23
     * Application initialization.
24
     *
25
     * @param ContainerInterface $container Dependency Injection container.
26
     *
27
     * @throws Exception If no router is defined.
28
     */
29 36
    public function __construct(ContainerInterface $container = null)
30
    {
31 36
        if ($container === null) {
32 4
            $container = Container\PHPDiFactory::buildContainer(Loader::load());
33 2
        }
34
35 36
        if ($container->has('router') === false) {
36 2
            throw new Exception('Define router config');
37
        }
38
39 36
        $this->container = $container;
40 36
    }
41
42
    /**
43
     * Container getter.
44
     *
45
     * @return ContainerInterface
46
     */
47 36
    public function getContainer()
48
    {
49 36
        return $this->container;
50
    }
51
52
    /**
53
     * Penny dispatcher getter.
54
     *
55
     * @return Dispatcher
56
     */
57 30
    private function getDispatcher()
58
    {
59 30
        $dispatcher = $this->container->get('dispatcher');
60 30
        if (!is_callable($dispatcher)) {
61 2
            throw new \RuntimeException('Dispatcher must be a callable');
62
        }
63
64 28
        return $dispatcher;
65
    }
66
67
    /**
68
     * Penny HTTP flow event getter.
69
     *
70
     * @return PennyEvmInterface
71
     */
72 28
    private function getEventManager()
73
    {
74 28
        return $this->container->get('event_manager');
75
    }
76
77
    /**
78
     * Setup event with Request and Response provided
79
     *
80
     * @param mixed|null $request  Representation of an outgoing,
81
     *  client-side request.
82
     * @param mixed|null $response Representation of an incoming,
83
     *  server-side response.
84
     *
85
     * @throws RuntimeException if event did not supported.
86
     */
87 32
    private function setUpEventWithRequestResponse($request, $response)
88
    {
89 32
        $event = $this->getContainer()->get('http_flow_event');
90 32
        if (!$event instanceof PennyEventInterface) {
91 2
            throw new RuntimeException('This event did not supported');
92
        }
93
94 30
        if ($request !== null) {
95 30
            $event->setRequest($request);
96 15
        }
97 30
        if ($response !== null) {
98 30
            $event->setResponse($response);
99 15
        }
100
101 30
        return $event;
102
    }
103
104
    /**
105
     * Application execution.
106
     *
107
     * @param mixed|null $request  Representation of an outgoing,
108
     *  client-side request.
109
     * @param mixed|null $response Representation of an incoming,
110
     *  server-side response.
111
     *
112
     * @return mixed
113
     */
114 32
    public function run($request = null, $response = null)
115
    {
116 32
        $event = $this->setUpEventWithRequestResponse($request, $response);
117
118 30
        $dispatcher   = $this->getDispatcher();
119 28
        $eventManager = $this->getEventManager();
120
121
        try {
122 28
            $routeInfo = call_user_func($dispatcher, $event->getRequest());
123 22
            $this->handleRoute($routeInfo, $event);
124 18
        } catch (Exception $exception) {
125 7
            return $this->triggerWithException($eventManager, $event, 'dispatch_error', $exception)
126 7
                        ->getResponse();
127
        }
128 20
        $this->handleResponse($eventManager, $event, $routeInfo);
129
130 18
        return $event->getResponse();
131
    }
132
133
    /**
134
     * Handle Route.
135
     *
136
     * @param RouteInfoInterface $routeInfo
137
     * @param PennyEventInterface $event
138
     *
139
     * @throws RuntimeException if dispatch does not return RouteInfo object.
140
     */
141 20
    private function handleRoute(
142 1
        RouteInfoInterface $routeInfo,
143
        PennyEventInterface $event
144
    ) {
145 20
        if (!$routeInfo instanceof RouteInfoInterface) {
146
            throw new RuntimeException('Dispatch does not return RouteInfo object');
147
        }
148
149 20
        $event->setRouteInfo($routeInfo);
150 20
        $event->setName($routeInfo->getName());
151 20
    }
152
153
    /**
154
     * Handle Response.
155
     *
156
     * @param PennyEvmInterface $eventManager
157
     * @param PennyEventInterface $event
158
     * @param RouteInfoInterface $routeInfo
159
     */
160
    private function handleResponse(
161
        PennyEvmInterface $eventManager,
162
        PennyEventInterface $event,
163
        RouteInfoInterface $routeInfo
164
    ) {
165 20
        $eventManager->attach($event->getName(), function ($event) use ($routeInfo) {
166 18
            $event->setResponse(call_user_func_array(
167 18
                $routeInfo->getCallable(),
168 18
                [$event->getRequest(), $event->getResponse()] + $routeInfo->getParams()
169 9
            ));
170 20
        }, 0);
171
172
        try {
173 20
            $eventManager->trigger($event);
174 11
        } catch (Exception $exception) {
175 2
            $this->triggerWithException($eventManager, $event, $routeInfo->getName().'_error', $exception);
176
        }
177 18
    }
178
179
    /**
180
     * Event Manager trigger with exception
181
     *
182
     * @param PennyEvmInterface $eventManager
183
     * @param PennyEventInterface $event
184
     * @param string $name
185
     * @param Exception $exception
186
     *
187
     * @return PennyEventInterface
188
     */
189 9
    private function triggerWithException(
190
        PennyEvmInterface $eventManager,
191
        PennyEventInterface $event,
192
        $name,
193
        Exception $exception
194
    ) {
195 9
        $event->setName($name);
196 9
        $event->setException($exception);
197 9
        $eventManager->trigger($event);
198
199 7
        return $event;
200
    }
201
}
202