Application::exists()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 2
Bugs 0 Features 1
Metric Value
c 2
b 0
f 1
dl 0
loc 4
ccs 2
cts 2
cp 1
rs 10
cc 1
eloc 2
nc 1
nop 1
crap 1
1
<?php
2
namespace Fwk\Core;
3
4
use Fwk\Events\Dispatcher;
5
use Symfony\Component\HttpFoundation\Request;
6
use Fwk\Di\Container;
7
use Fwk\Core\Events\RequestEvent;
8
use Fwk\Core\Events\DispatchEvent;
9
use Fwk\Core\Events\BeforeActionEvent;
10
use Fwk\Core\Events\AfterActionEvent;
11
use Fwk\Core\Events\EndEvent;
12
use Fwk\Core\Events\BootEvent;
13
use Fwk\Core\Events\ErrorEvent;
14
use Fwk\Core\Events\ResponseEvent;
15
use Symfony\Component\HttpFoundation\Response;
16
17
class Application extends Dispatcher implements \ArrayAccess
18
{
19
    /**
20
     * Application name
21
     * @var string
22
     */
23
    protected $name;
24
25
    /**
26
     * List of registered actions
27
     * @var array
28
     */
29
    protected $actions = array();
30
31
    /**
32
     * Services container (Di)
33
     * @var Container
34
     */
35
    protected $services;
36
37
    /**
38
     * The default action (i.e the "homepage")
39
     * @var string
40
     */
41
    protected $defaultAction;
42
43
    /**
44
     * Constructor
45
     *
46
     * @param string    $name     Application name
47
     * @param Container $services Services Container (di)
0 ignored issues
show
Documentation introduced by
Should the type for parameter $services not be null|Container?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
48
     *
49
     * @return void
0 ignored issues
show
Comprehensibility Best Practice introduced by
Adding a @return annotation to constructors is generally not recommended as a constructor does not have a meaningful return value.

Adding a @return annotation to a constructor is not recommended, since a constructor does not have a meaningful return value.

Please refer to the PHP core documentation on constructors.

Loading history...
50
     */
51 44
    public function __construct($name, Container $services = null)
52
    {
53 44
        $this->name = $name;
54
55 44
        if (null === $services) {
56 42
            $services = new Container();
57 42
        }
58
59 44
        $this->services = $services;
60 44
    }
61
62
    /**
63
     * Registers an action
64
     *
65
     * @param string      $actionName Name of the action
66
     * @param ActionProxy $proxy      Proxy instance to the action
67
     *
68
     * @return Application
69
     */
70 33
    public function register($actionName, ActionProxy $proxy)
71
    {
72 33
        $this->actions[$actionName] = $proxy;
73
74 33
        return $this;
75
    }
76
77
    /**
78
     * Unregisters an action
79
     *
80
     * @param string $actionName Name of the action
81
     *
82
     * @return Application
83
     * @throws Exception if action is not registered
84
     */
85 2
    public function unregister($actionName)
86
    {
87 2
        if (!array_key_exists($actionName, $this->actions)) {
88 1
            throw new Exception("$actionName is not a registered Action");
89
        }
90
91 1
        unset($this->actions[$actionName]);
92
93 1
        return $this;
94
    }
95
96
    /**
97
     * Returns the ActionProxy of a registered action
98
     *
99
     * @param string $actionName name of the action
100
     *
101
     * @return ActionProxy the proxy instance to the action
102
     * @throws Exception if action is not registered
103
     */
104 25
    public function get($actionName)
105
    {
106 25
        if (!array_key_exists($actionName, $this->actions)) {
107 1
            throw new Exception("$actionName is not a registered Action");
108
        }
109
110 24
        return $this->actions[$actionName];
111
    }
112
113
    /**
114
     * Tells if an action is registered
115
     *
116
     * @param string $actionName Name of the action
117
     *
118
     * @return boolean
119
     */
120 28
    public function exists($actionName)
121
    {
122 28
        return array_key_exists($actionName, $this->actions);
123
    }
124
125
    /**
126
     * Returns the list (array) of all registered actions (keys) and their
127
     * according ActionProxy (values)
128
     *
129
     * @return array
130
     */
131 1
    public function getActions()
132
    {
133 1
        return $this->actions;
134
    }
135
136
    /**
137
     * Instanciates a new Application
138
     * (useful for chaining)
139
     *
140
     * @param string    $name     Application name
141
     * @param Container $services Services Container
0 ignored issues
show
Documentation introduced by
Should the type for parameter $services not be null|Container?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
142
     *
143
     * @return Application App instance
144
     */
145 38
    public static function factory($name, Container $services = null)
146
    {
147 38
        return new self($name, $services);
148
    }
149
150
    /**
151
     * Returns the Application name
152
     *
153
     * @return string
154
     */
155 1
    public function getName()
156
    {
157 1
        return $this->name;
158
    }
159
160
    /**
161
     * Returns the Services Container
162
     *
163
     * @return Container
164
     */
165 35
    public function getServices()
166
    {
167 35
        return $this->services;
168
    }
169
170
    /**
171
     * Defines a Services Container
172
     *
173
     * @param Container $services Services Container (Di)
174
     *
175
     * @return Application
176
     */
177
    public function setServices(Container $services)
178
    {
179
        $this->services = $services;
180
181
        return $this;
182
    }
183
184
    /**
185
     * Runs the Application for a defined (or new) HTTP request
186
     *
187
     * @param Request $request The HTTP request (optional)
0 ignored issues
show
Documentation introduced by
Should the type for parameter $request not be null|Request?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
188
     *
189
     * @return mixed
190
     */
191 27
    public function run(Request $request = null)
192
    {
193 27
        $this->notify(new BootEvent($this));
194
195 26
        if (null === $request) {
196 1
            $request = Request::createFromGlobals();
197 1
        }
198
199 26
        $context = new Context($request);
200 1
        try {
201 26
            $this->notify(new RequestEvent($request, $this, $context));
202
203 26
            if (!$context->isReady()) {
204 6
                $this->notify(new DispatchEvent($this, $context));
205 5
            }
206
207 25
            if (!$context->isReady()) {
208 3
                if (null === $this->defaultAction || $context->isError()) {
209 1
                    throw new Exception('No action found');
210
                }
211 2
                $context->setActionName($this->defaultAction);
212 2
            }
213
214 24
            $result = $this->runAction($context);
215
216 23
            if (!$context->isDone()) {
217 18
                if ($result instanceof Response) {
218 2
                    $context->setResponse($result);
219 18
                } elseif (is_string($result)) {
220 11
                    $context->setResponse(new Response($result));
221 11
                }
222 18
            }
223
224 23
            if ($context->getResponse() instanceof Response) {
225 18
                $this->notify(
226 18
                    new ResponseEvent(
227 18
                        $context->getResponse(),
228 18
                        $this,
229
                        $context
230 18
                    )
231 18
                );
232 18
            }
233 26
        } catch(\Exception $exp) {
234 3
            $event = new ErrorEvent($exp, $this, $context);
235 3
            $this->notify($event);
236
237 3
            if (!$event->isStopped()) {
238 3
                throw $exp;
239
            } else {
240
                return null;
241
            }
242
        }
243
244 23
        $endEvent = new EndEvent($this, $context);
245 23
        $this->notify($endEvent);
246
247 23
        if ($endEvent->isStopped()) {
248
            return null;
249
        }
250
251 23
        if ($context->getResponse() instanceof Response) {
252 18
            return $context->getResponse();
253
        }
254
255 5
        return $context->getResult();
256
    }
257
258
    /**
259
     *
260
     * @param Context $context
261
     *
262
     * @return mixed
263
     */
264 24
    public function runAction(Context $context)
265
    {
266 24
        if (!$context->isReady()) {
267
            throw new Exception('Context is not ready (i.e. no action defined)');
268
        }
269
270 24
        if (!$this->exists($context->getActionName())) {
271 1
            throw new Exception('Unregistered action "'. $context->getActionName() .'"');
272
        }
273
274 23
        $proxy = $this->get($context->getActionName());
275 23
        $this->notify(new BeforeActionEvent($proxy, $this, $context));
276 23
        if ($context->isDone()) {
277
            return $context->getResult();
278
        }
279 23
        $result = $proxy->execute($this, $context);
280 23
        $context->setResult($result);
281 23
        $this->notify(new AfterActionEvent($proxy, $this, $context));
282
283 23
        return $result;
284
    }
285
286
    /**
287
     * Returns the default action name (if any)
288
     *
289
     * @return string
290
     */
291 2
    public function getDefaultAction()
292
    {
293 2
        return $this->defaultAction;
294
    }
295
296
    /**
297
     * Defines a default action. Basically the one which answers on /
298
     *
299
     * @param string $defaultAction Default action name
300
     *
301
     * @return Application
302
     */
303 2
    public function setDefaultAction($defaultAction)
304
    {
305 2
        $this->defaultAction = $defaultAction;
306
307 2
        return $this;
308
    }
309
310 2
    public function offsetExists($actionName)
311
    {
312 2
        return $this->exists($actionName);
313
    }
314
315 1
    public function offsetGet($actionName)
316
    {
317 1
        return $this->get($actionName);
318
    }
319
320 12
    public function offsetSet($actionName, $proxy)
321
    {
322 12
        if (!$proxy instanceof ActionProxy) {
323 12
            $proxy = Action\ProxyFactory::factory($proxy);
324 12
        }
325
326 12
        return $this->register($actionName, $proxy);
327
    }
328
329 1
    public function offsetUnset($actionName)
330
    {
331 1
        return $this->unregister($actionName);
332
    }
333
334
    /**
335
     * Adds a Plugin to the Application.
336
     *
337
     * @param Plugin $plugin The plugin to be loaded
338
     *
339
     * @return Application
340
     */
341 1
    public function plugin(Plugin $plugin)
342
    {
343 1
        $services = $this->getServices();
344
345
        // A Plugin can also be a listener of the Application Events
346 1
        $this->addListener($plugin);
347
348 1
        $plugin->loadServices($services);
349 1
        $plugin->load($this);
350
351 1
        return $this;
352
    }
353
}