Passed
Branch master (30ae21)
by Gabor
03:15
created

WebApplication::getContainer()   A

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