Passed
Push — master ( 0d72e8...44be60 )
by Gabor
04:58
created

ServiceAdapter::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 4
ccs 3
cts 3
cp 1
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 1
crap 1
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\ServiceAdapter\Base;
15
16
use Throwable;
17
use WebHemi\Application\ServiceInterface;
18
use WebHemi\DependencyInjection\ServiceInterface as DependencyInjectionInterface;
19
use WebHemi\Environment\ServiceInterface as EnvironmentInterface;
20
use WebHemi\Http\ResponseInterface;
21
use WebHemi\Http\ServerRequestInterface;
22
use WebHemi\Http\ServiceInterface as HttpInterface;
23
use WebHemi\Middleware\Common as CommonMiddleware;
24
use WebHemi\Middleware\MiddlewareInterface;
25
use WebHemi\MiddlewarePipeline\ServiceInterface as PipelineInterface;
26
use WebHemi\Session\ServiceInterface as SessionInterface;
27
28
/**
29
 * Class ServiceAdapter
30
 */
31
class ServiceAdapter implements ServiceInterface
32
{
33
    /** @var DependencyInjectionInterface */
34
    private $container;
35
36
    /**
37
     * ServiceAdapter constructor.
38
     *
39
     * @param DependencyInjectionInterface $container
40
     */
41 6
    public function __construct(DependencyInjectionInterface $container)
42
    {
43 6
        $this->container = $container;
44 6
    }
45
46
    /**
47
     * Starts the session.
48
     *
49
     * @return void
50
     *
51
     * @codeCoverageIgnore - not testing session (yet)
52
     */
53
    private function initSession() : void
54
    {
55
        if (defined('PHPUNIT_WEBHEMI_TESTSUITE')) {
56
            return;
57
        }
58
59
        /** @var SessionInterface $sessionManager */
60
        $sessionManager = $this->container->get(SessionInterface::class);
61
        /** @var EnvironmentInterface $environmentManager */
62
        $environmentManager = $this->container->get(EnvironmentInterface::class);
63
64
        $name = $environmentManager->getSelectedApplication();
65
        $timeOut = 3600;
66
        $path = $environmentManager->getSelectedApplicationUri();
67
        $domain = $environmentManager->getApplicationDomain();
68
        $secure = $environmentManager->isSecuredApplication();
69
        $httpOnly = true;
70
71
        $sessionManager->start($name, $timeOut, $path, $domain, $secure, $httpOnly);
72
    }
73
74
    /**
75
     * Runs the application. This is where the magic happens.
76
     * According tho the environment settings this must build up the middleware pipeline and execute it.
77
     *
78
     * a Pre-Routing Middleware can be; priority < 0:
79
     *  - LockCheck - check if the client IP is banned > S102|S403
80
     *  - Auth - if the user is not logged in, but there's a "Remember me" cookie, then logs in > S102
81
     *
82
     * Routing Middleware is fixed (RoutingMiddleware::class); priority = 0:
83
     *  - A middleware that routes the incoming Request and delegates to the matched middleware. > S102|S404|S405
84
     *    The RouteResult should be attached to the Request.
85
     *    If the Routing is not defined explicitly in the pipeline, then it will be injected with priority 0.
86
     *
87
     * a Post-Routing Middleware can be; priority between 0 and 100:
88
     *  - Acl - checks if the given route is available for the client. Also checks the auth > S102|S401|S403
89
     *  - CacheReader - checks if a suitable response body is cached. > S102|S200
90
     *
91
     * Dispatcher Middleware is fixed (DispatcherMiddleware::class); priority = 100:
92
     *  - A middleware which gets the corresponding Action middleware and applies it > S102
93
     *    If the Dispatcher is not defined explicitly in the pipeline, then it will be injected with priority 100.
94
     *    The Dispatcher should not set the response Status Code to 200 to let Post-Dispatchers to be called.
95
     *
96
     * a Post-Dispatch Middleware can be; priority > 100:
97
     *  - CacheWriter - writes response body into DataStorage (DB, File etc.) > S102
98
     *
99
     * Final Middleware is fixed (FinalMiddleware:class):
100
     *  - This middleware behaves a bit differently. It cannot be ordered, it's always the last called middleware:
101
     *    - when the middleware pipeline reached its end (typically when the Status Code is still 102)
102
     *    - when one item of the middleware pipeline returns with return response (status code is set to 200|40*|500)
103
     *    - when during the pipeline process an Exception is thrown.
104
     *
105
     * When the middleware pipeline is finished the application prints the header and the output.
106
     *
107
     * If a middleware other than the Routing, Dispatcher and Final Middleware has no priority set, it will be
108
     * considered to have priority = 50.
109
     *
110
     * @return void
111
     */
112 5
    public function run() : void
113
    {
114
        // Start session.
115 5
        $this->initSession();
116
117
        /** @var PipelineInterface $pipelineManager */
118 5
        $pipelineManager = $this->container->get(PipelineInterface::class);
119 5
        $request = $this->getRequest();
120 5
        $response = $this->getResponse();
121
122
        /** @var string $middlewareClass */
123 5
        $middlewareClass = $pipelineManager->start();
124
125 5
        while ($middlewareClass !== null
126 5
            && $response->getStatusCode() == ResponseInterface::STATUS_PROCESSING
127
        ) {
128 5
            $this->invokeMiddleware($middlewareClass, $request, $response);
129 5
            $middlewareClass = $pipelineManager->next();
130
        };
131
132
        // If there was no error, we mark as ready for output.
133 5
        if ($response->getStatusCode() == ResponseInterface::STATUS_PROCESSING) {
134 1
            $response = $response->withStatus(ResponseInterface::STATUS_OK);
135
        }
136
137
        /** @var CommonMiddleware\FinalMiddleware $finalMiddleware */
138 5
        $finalMiddleware = $this->container->get(CommonMiddleware\FinalMiddleware::class);
139
140
        // Send out headers and content.
141 5
        $finalMiddleware($request, $response);
142 5
    }
143
144
    /**
145
     * Gets the Request object.
146
     *
147
     * @return ServerRequestInterface
148
     */
149 5
    private function getRequest()
150
    {
151
        /** @var HttpInterface $httpAdapter */
152 5
        $httpAdapter = $this->container->get(HttpInterface::class);
153
        /** @var EnvironmentInterface $environmentManager */
154 5
        $environmentManager = $this->container->get(EnvironmentInterface::class);
155
156
        /** @var ServerRequestInterface $request */
157 5
        $request = $httpAdapter->getRequest()
158 5
            ->withAttribute(
159 5
                ServerRequestInterface::REQUEST_ATTR_DISPATCH_DATA,
160
                [
161 5
                    'selected_module' => $environmentManager->getSelectedModule(),
162 5
                    'application_domain' => 'http'.($environmentManager->isSecuredApplication() ? 's' : '')
163 5
                        .'://'
164 5
                        .$environmentManager->getApplicationDomain()
165
                ]
166
            );
167
168 5
        return $request;
169
    }
170
171
    /**
172
     * Gets the Response object.
173
     *
174
     * @return ResponseInterface
175
     */
176 5
    private function getResponse()
177
    {
178
        /** @var HttpInterface $httpAdapter */
179 5
        $httpAdapter = $this->container->get(HttpInterface::class);
180
181
        /** @var ResponseInterface $response */
182 5
        return $httpAdapter->getResponse();
183
    }
184
185
    /**
186
     * Instantiates and invokes a middleware
187
     *
188
     * @param string $middlewareClass
189
     * @param ServerRequestInterface $request
190
     * @param ResponseInterface $response
191
     */
192 5
    private function invokeMiddleware(
193
        string $middlewareClass,
194
        ServerRequestInterface&$request,
195
        ResponseInterface&$response
196
    ) : void {
197
        try {
198
            /** @var MiddlewareInterface $middleware */
199 5
            $middleware = $this->container->get($middlewareClass);
200 5
            $requestAttributes = $request->getAttributes();
201
202
            // As an extra step if an action middleware is resolved, it should be invoked by the dispatcher.
203 5
            if ($middleware instanceof CommonMiddleware\DispatcherMiddleware
204 5
                && isset($requestAttributes[ServerRequestInterface::REQUEST_ATTR_RESOLVED_ACTION_CLASS])
205
            ) {
206
                /** @var MiddlewareInterface $actionMiddleware */
207 3
                $actionMiddleware = $this->container
208 3
                    ->get($requestAttributes[ServerRequestInterface::REQUEST_ATTR_RESOLVED_ACTION_CLASS]);
209 3
                $request = $request->withAttribute(
210 3
                    ServerRequestInterface::REQUEST_ATTR_ACTION_MIDDLEWARE,
211
                    $actionMiddleware
212
                );
213
            }
214
215 5
            $middleware($request, $response);
216 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...
217 2
            $code = ResponseInterface::STATUS_INTERNAL_SERVER_ERROR;
218
219 2
            if (in_array($exception->getCode(), [403, 404])) {
220 1
                $code = $exception->getCode();
221
            }
222
223 2
            $response = $response->withStatus($code);
224 2
            $request = $request->withAttribute(
225 2
                ServerRequestInterface::REQUEST_ATTR_MIDDLEWARE_EXCEPTION,
226
                $exception
227
            );
228
        }
229 5
    }
230
}
231