Passed
Push — master ( 0f66d0...a5c111 )
by Gabor
16:59 queued 08:35
created

WebApplication::initSession()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 20
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 0
Metric Value
dl 0
loc 20
ccs 0
cts 0
cp 0
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 12
nc 2
nop 0
crap 6
1
<?php
2
/**
3
 * WebHemi.
4
 *
5
 * PHP version 7.1
6
 *
7
 * @copyright 2012 - 2017 Gixx-web (http://www.gixx-web.com)
8
 * @license   https://opensource.org/licenses/MIT The MIT License (MIT)
9
 *
10
 * @link      http://www.gixx-web.com
11
 */
12
declare(strict_types = 1);
13
14
namespace WebHemi\Application\Web;
15
16
use Throwable;
17
use WebHemi\Adapter\Http\ResponseInterface;
18
use WebHemi\Adapter\Http\ServerRequestInterface;
19
use WebHemi\Adapter\Http\HttpAdapterInterface;
20
use WebHemi\Application\AbstractApplication;
21
use WebHemi\Application\EnvironmentManager;
22
use WebHemi\Application\PipelineManager;
23
use WebHemi\Application\SessionManager;
24
use WebHemi\Middleware\DispatcherMiddleware;
25
use WebHemi\Middleware\FinalMiddleware;
26
use WebHemi\Middleware\MiddlewareInterface;
27
28
/**
29
 * Class WebApplication.
30
 */
31
class WebApplication extends AbstractApplication
32
{
33
    /**
34
     * Starts the session.
35
     *
36
     * @return void
37
     *
38
     * @codeCoverageIgnore - not testing session (yet)
39
     */
40
    private function initSession() : void
41
    {
42
        if (defined('PHPUNIT_WEBHEMI_TESTSUITE')) {
43
            return;
44
        }
45
46
        /** @var SessionManager $sessionManager */
47
        $sessionManager = $this->getContainer()->get(SessionManager::class);
48
        /** @var EnvironmentManager $environmentManager */
49
        $environmentManager = $this->getContainer()->get(EnvironmentManager::class);
50
51
        $name = $environmentManager->getSelectedApplication();
52
        $timeOut = 3600;
53
        $path = $environmentManager->getSelectedApplicationUri();
54
        $domain = $environmentManager->getApplicationDomain();
55
        $secure = $environmentManager->isSecuredApplication();
56
        $httpOnly = true;
57
58
        $sessionManager->start($name, $timeOut, $path, $domain, $secure, $httpOnly);
59
    }
60
61
    /**
62
     * Runs the application. This is where the magic happens.
63
     * According tho the environment settings this must build up the middleware pipeline and execute it.
64
     *
65
     * a Pre-Routing Middleware can be; priority < 0:
66
     *  - LockCheck - check if the client IP is banned > S102|S403
67
     *  - Auth - if the user is not logged in, but there's a "Remember me" cookie, then logs in > S102
68
     *
69
     * Routing Middleware is fixed (RoutingMiddleware::class); priority = 0:
70
     *  - A middleware that routes the incoming Request and delegates to the matched middleware. > S102|S404|S405
71
     *    The RouteResult should be attached to the Request.
72
     *    If the Routing is not defined explicitly in the pipeline, then it will be injected with priority 0.
73
     *
74
     * a Post-Routing Middleware can be; priority between 0 and 100:
75
     *  - Acl - checks if the given route is available for the client. Also checks the auth > S102|S401|S403
76
     *  - CacheReader - checks if a suitable response body is cached. > S102|S200
77
     *
78
     * Dispatcher Middleware is fixed (DispatcherMiddleware::class); priority = 100:
79
     *  - A middleware which gets the corresponding Action middleware and applies it > S102
80
     *    If the Dispatcher is not defined explicitly in the pipeline, then it will be injected with priority 100.
81
     *    The Dispatcher should not set the response Status Code to 200 to let Post-Dispatchers to be called.
82
     *
83
     * a Post-Dispatch Middleware can be; priority > 100:
84
     *  - CacheWriter - writes response body into DataStorage (DB, File etc.) > S102
85
     *
86
     * Final Middleware is fixed (FinalMiddleware:class):
87
     *  - This middleware behaves a bit differently. It cannot be ordered, it's always the last called middleware:
88
     *    - when the middleware pipeline reached its end (typically when the Status Code is still 102)
89
     *    - when one item of the middleware pipeline returns with return response (status code is set to 200|40*|500)
90
     *    - when during the pipeline process an Exception is thrown.
91
     *
92
     * When the middleware pipeline is finished the application prints the header and the output.
93
     *
94
     * If a middleware other than the Routing, Dispatcher and Final Middleware has no priority set, it will be
95
     * considered to have priority = 50.
96
     *
97
     * @return void
98
     */
99 5
    public function run() : void
100
    {
101
        // Start session.
102 5
        $this->initSession();
103
104
        /** @var HttpAdapterInterface $httpAdapter */
105 5
        $httpAdapter = $this->getContainer()->get(HttpAdapterInterface::class);
106
        /** @var PipelineManager $pipelineManager */
107 5
        $pipelineManager = $this->getContainer()->get(PipelineManager::class);
108
        /** @var EnvironmentManager $environmentManager */
109 5
        $environmentManager = $this->getContainer()->get(EnvironmentManager::class);
110
111
        /** @var ServerRequestInterface $request */
112 5
        $request = $httpAdapter->getRequest()
113 5
            ->withAttribute(
114 5
                ServerRequestInterface::REQUEST_ATTR_DISPATCH_DATA,
115
                [
116 5
                    'selected_module' => $environmentManager->getSelectedModule(),
117 5
                    'application_domain' => 'http'.($environmentManager->isSecuredApplication() ? 's' : '')
118 5
                        .'://'
119 5
                        .$environmentManager->getApplicationDomain()
120
                ]
121
            );
122
123
        /** @var ResponseInterface $response */
124 5
        $response = $httpAdapter->getResponse();
125
        /** @var string $middlewareClass */
126 5
        $middlewareClass = $pipelineManager->start();
127
128 5
        while ($middlewareClass !== null
129 5
            && $response->getStatusCode() == ResponseInterface::STATUS_PROCESSING
130
        ) {
131
            try {
132
                /** @var MiddlewareInterface $middleware */
133 5
                $middleware = $this->getContainer()->get($middlewareClass);
134 5
                $requestAttributes = $request->getAttributes();
135
136
                // As an extra step if the action middleware is resolved, it is invoked right before the dispatcher.
137
                // Only the container knows how to instantiate it in the right way, and the container must not be
138
                // injected into other classes. It seems like a hack but it is by purpose.
139 5
                if ($middleware instanceof DispatcherMiddleware
140 5
                    && isset($requestAttributes[ServerRequestInterface::REQUEST_ATTR_RESOLVED_ACTION_CLASS])
141
                ) {
142
                    /** @var MiddlewareInterface $actionMiddleware */
143 3
                    $actionMiddleware = $this->getContainer()
144 3
                        ->get($requestAttributes[ServerRequestInterface::REQUEST_ATTR_RESOLVED_ACTION_CLASS]);
145 3
                    $request = $request->withAttribute(
146 3
                        ServerRequestInterface::REQUEST_ATTR_ACTION_MIDDLEWARE,
147
                        $actionMiddleware
148
                    );
149
                }
150
151 5
                $middleware($request, $response);
152 2
            } catch (Throwable $exception) {
0 ignored issues
show
Bug introduced by
The class Throwable does not exist. Is this class maybe located in a folder that is not analyzed, or in a newer version of your dependencies than listed in your composer.lock/composer.json?
Loading history...
153 2
                $code = ResponseInterface::STATUS_INTERNAL_SERVER_ERROR;
154
155 2
                if (in_array($exception->getCode(), [403, 404])) {
156 1
                    $code = $exception->getCode();
157
                }
158
159 2
                $response = $response->withStatus($code);
160 2
                $request = $request->withAttribute(
161 2
                    ServerRequestInterface::REQUEST_ATTR_MIDDLEWARE_EXCEPTION,
162
                    $exception
163
                );
164
            }
165
166 5
            $middlewareClass = $pipelineManager->next();
167
        };
168
169
        // If there was no error, we mark as ready for output.
170 5
        if ($response->getStatusCode() == ResponseInterface::STATUS_PROCESSING) {
171 1
            $response = $response->withStatus(ResponseInterface::STATUS_OK);
172
        }
173
174
        /** @var FinalMiddleware $finalMiddleware */
175 5
        $finalMiddleware = $this->getContainer()->get(FinalMiddleware::class);
176
177
        // Send out headers and content.
178 5
        $finalMiddleware($request, $response);
179 5
    }
180
}
181