Completed
Push — master ( 5d2770...16a580 )
by Enrico
02:38
created

Application::handleRequest()   A

Complexity

Conditions 4
Paths 17

Size

Total Lines 28
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 13
CRAP Score 4

Importance

Changes 0
Metric Value
cc 4
eloc 13
nc 17
nop 0
dl 0
loc 28
ccs 13
cts 13
cp 1
crap 4
rs 9.8333
c 0
b 0
f 0
1
<?php 
2
namespace uSILEX;
3
4
use uSILEX\Exception\HttpExceptionInterface;
5
use uSILEX\Exception\NotFoundHttpException;
6
use Symfony\Component\HttpFoundation\Request;
7
use Symfony\Component\HttpFoundation\Response;
8
use Symfony\Component\HttpFoundation\RedirectResponse;
9
use Symfony\Component\HttpFoundation\StreamedResponse;
10
use Symfony\Component\HttpFoundation\JsonResponse;
11
use Pimple\Container;
12
use Pimple\ServiceProviderInterface;
13
14
15
/*
16
 * Inspired from Silex application
17
 */
18
class Application extends Container
19
{    
20
    const VERSION = '1.0.0';
21
    
22
    protected $providers = [];
23
    protected $onRouteMatchListeners = [];
24
    protected $onResponseListeners = [];
25
    protected $routes = [];
26
    protected $booted = false;
27
28
    /**
29
     * Instantiate a new Application.
30
     *
31
     * Objects and parameters can be passed as argument to the constructor.
32
     *
33
     */
34 18
    public function __construct()
35
    {
36 18
        parent::__construct();
37 18
        $this['debug'] = false;
38 18
        $this['version'] = static::VERSION;
39 18
        $this['error_msg.short_template'] = 'Error %s';
40 18
        $this['error_msg.full_template'] = "Error %s - %s \nTrace info:\n%s\n";
41
        $this['RouteMatcher'] = function($c) {
42 4
            return new RouteMatcher($c);
43
        };
44
        $this['ControllerResolver'] = function($c) {
45 4
            return new ControllerResolver($c);
46
        };
47 18
    }
48
    
49
    
50
    /**
51
     * Transform a PHP exception into an http error message.
52
     *
53
     * @param \Exception $e a trapped exception
54
     *
55
     */
56 2
    protected function exceptionToResponse(\Throwable $e): Response
57
    {
58 2
        if ($e instanceof  HttpExceptionInterface) {
59 1
            $response = new Response($e->getMessage(), $e->getStatusCode());            
60
        } else {
61 1
            $response = new Response(
62 1
                $this['debug']
63
                    ?sprintf($this['error_msg.full_template'], $e->getCode(), $e->getMessage(), $e->getTraceAsString())
64 1
                    :sprintf($this['error_msg.short_template'], $e->getCode()),
65 1
                Response::HTTP_INTERNAL_SERVER_ERROR
66
            );
67
        }
68
        
69 2
        return $response;
70
    }
71
    
72
73 7
    public function addRoute(Route $route): Container
74
    {
75 7
        $this->routes[] = $route;
76
        
77 7
        return $this;
78
    }
79
    
80
    
81 9
    public function getRoutes(): array
82
    {
83 9
        return $this->routes;
84
    }
85
    
86
    
87 1
    public function onRouteMatch(string $serviceName)
88
    {
89 1
        $this->onRouteMatchListeners[] = $serviceName;
90
        
91 1
        return $this;
92
    }
93
    
94
    
95
    
96 2
    public function onResponse(string $serviceName)
97
    {
98 2
        $this->onResponseListeners[] = $serviceName;
99
        
100 2
        return $this;
101
    }
102
    
103
    
104 4
    public function handleRequest(): Response 
105
    {
106 4
        assert(isset($this['ControllerResolver']));
107 4
        assert(isset($this['request']));
108
        
109
        try {
110 4
            if (!$this->booted) {
111 4
                $this->boot();
112
            }
113
            
114
            // execute the controller action
115 4
            $route = $this['ControllerResolver']->getController();
116
            
117 4
            assert(isset($this[$route->getAction()]));
118
            
119
            // call onRouteMatch
120 4
            foreach( $this->onRouteMatchListeners as $serviceName) {
121 1
                $this[$serviceName];
122
            }
123
            
124 4
            $response = $this[$route->getAction()];
125
              
126
            
127 2
        } catch (\Throwable $e) {
128 2
            $response = $this->exceptionToResponse($e);
129
        }
130
        
131 4
        return $response;
132
    }
133
    
134
    
135
    /**
136
     * Registers a service provider.
137
     *
138
     * @param ServiceProviderInterface $provider A ServiceProviderInterface instance
139
     * @param array                    $values   An array of values that customizes the provider
140
     *
141
     * @return Application
142
     */
143
    public function register(ServiceProviderInterface $provider, array $values = [])
144
    {
145
        $this->providers[] = $provider;
146
        
147
        parent::register($provider, $values);
148
        
149
        return $this;
150
    }
151
    
152
    
153
    /**
154
     * Boots all service providers.
155
     *
156
     * This method is automatically called by handle(), but you can use it
157
     * to boot all service providers when not handling a request.
158
     */
159 4
    public function boot()
160
    {
161 4
        if ($this->booted) {
162
            return;
163
        }
164
        
165 4
        $this->booted = true;
166
        
167 4
        foreach ($this->providers as $provider) {
168
            if ($provider instanceof BootableProviderInterface) {
169
                $provider->boot($this);
170
            }
171
        }
172 4
    }
173
    
174
    
175
    /**
176
     * Creates a streaming response.
177
     *
178
     * @param mixed $callback A valid PHP callback
179
     * @param int   $status   The response status code
180
     * @param array $headers  An array of response headers
181
     *
182
     * @return StreamedResponse
183
     */
184
    public function stream($callback = null, $status = 200, array $headers = [])
185
    {
186
        return new StreamedResponse($callback, $status, $headers);
187
    }
188
    
189
    
190
    /**
191
     * Convert some data into a JSON response.
192
     *
193
     * @param mixed $data    The response data
194
     * @param int   $status  The response status code
195
     * @param array $headers An array of response headers
196
     *
197
     * @return JsonResponse
198
     */
199
    public function json($data = [], $status = 200, array $headers = [])
200
    {
201
        return new JsonResponse($data, $status, $headers);
202
    }
203
    
204
    
205
    /**
206
     * Handles the request and delivers the response.
207
     *
208
     */
209 2
    public function run()
210
    {
211
        // define $this['request'] just for testing purposes
212 2
        if (!isset($this['request'])) {
213 2
            $this['request'] = Request::createFromGlobals();
214
        }
215
        
216
        // define $this['response'] just for testing purposes
217 2
        if (!isset($this['response'])) {
218
            $this['response'] = $this->handleRequest();
219
        }
220
        
221
        // call onResponse hook
222 2
        foreach( $this->onResponseListeners as $serviceName) {
223 2
            $this['response'] = $this[$serviceName];
224
        }
225
          
226 2
        $this['response']->send();
227
        
228 2
    }
229
    
230
}