Completed
Push — master ( 8efac5...a0c6e2 )
by Gabor
04:36
created

WebApplication::getConfig()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 2
Bugs 0 Features 0
Metric Value
c 2
b 0
f 0
dl 0
loc 4
ccs 0
cts 4
cp 0
rs 10
cc 1
eloc 2
nc 1
nop 0
crap 2
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 Psr\Http\Message\ResponseInterface;
15
use Psr\Http\Message\ServerRequestInterface;
16
use WebHemi\Adapter\DependencyInjection\DependencyInjectionAdapterInterface;
17
use WebHemi\Adapter\Http\HttpAdapterInterface;
18
use WebHemi\Adapter\Renderer\RendererAdapterInterface;
19
use WebHemi\Adapter\Router\RouterAdapterInterface;
20
use WebHemi\Application\ApplicationInterface;
21
use WebHemi\Application\EnvironmentManager;
22
use WebHemi\Middleware\DispatcherMiddleware;
23
use WebHemi\Middleware\FinalMiddleware;
24
use WebHemi\Middleware\MiddlewareInterface;
25
use WebHemi\Middleware\Pipeline\MiddlewarePipelineInterface;
26
27
/**
28
 * Class WebApplication.
29
 */
30
class WebApplication implements ApplicationInterface
31
{
32
    /** @var DependencyInjectionAdapterInterface */
33
    private $container;
34
    /** @var EnvironmentManager */
35
    private $environmentManager;
36
    /** @var MiddlewarePipelineInterface */
37
    private $pipeline;
38
39
40
    /**
41
     * ApplicationInterface constructor.
42
     *
43
     * @param DependencyInjectionAdapterInterface $container
44
     * @param EnvironmentManager                  $environmentManager
45
     * @param MiddlewarePipelineInterface         $pipeline
46
     */
47
    public function __construct(
48
        DependencyInjectionAdapterInterface $container,
49
        EnvironmentManager $environmentManager,
50
        MiddlewarePipelineInterface $pipeline
51
    ) {
52
        $this->container = $container;
53
        $this->environmentManager = $environmentManager;
54
        $this->pipeline = $pipeline;
55
    }
56
57
    /**
58
     * Returns the DI Adapter instance.
59
     *
60
     * @return DependencyInjectionAdapterInterface
61
     */
62
    public function getContainer()
63
    {
64
        return $this->container;
65
    }
66
67
    /**
68
     * Get ready to run the application: set final data for specific services.
69
     */
70
    private function prepare()
71
    {
72
        $this->container
73
            ->setServiceArgument(HttpAdapterInterface::class, $this->environmentManager->getEnvironmentData('GET'))
74
            ->setServiceArgument(HttpAdapterInterface::class, $this->environmentManager->getEnvironmentData('POST'))
75
            ->setServiceArgument(HttpAdapterInterface::class, $this->environmentManager->getEnvironmentData('SERVER'))
76
            ->setServiceArgument(HttpAdapterInterface::class, $this->environmentManager->getEnvironmentData('COOKIE'))
77
            ->setServiceArgument(HttpAdapterInterface::class, $this->environmentManager->getEnvironmentData('FILES'));
78
79
        $this->container
80
            ->setServiceArgument(
81
                RendererAdapterInterface::class,
82
                $this->environmentManager->getApplicationTemplateSettings()
83
            )
84
            ->setServiceArgument(
85
                RendererAdapterInterface::class,
86
                $this->environmentManager->getResourcePath()
87
            );
88
89
        $this->container
90
            ->setServiceArgument(
91
                RouterAdapterInterface::class,
92
                $this->environmentManager->getModuleRouteSettings()
93
            )
94
            ->setServiceArgument(
95
                RouterAdapterInterface::class,
96
                $this->environmentManager->getSelectedApplicationUri()
97
            );
98
    }
99
100
    /**
101
     * Runs the application. This is where the magic happens.
102
     * According tho the environment settings this must build up the middleware pipeline and execute it.
103
     *
104
     * a Pre-Routing Middleware can be; priority < 0:
105
     *  - LockCheck - check if the client IP is banned > S102|S403
106
     *  - Auth - if the user is not logged in, but there's a "Remember me" cookie, then logs in > S102
107
     *
108
     * Routing Middleware is fixed (RoutingMiddleware::class); priority = 0:
109
     *  - A middleware that routes the incoming Request and delegates to the matched middleware. > S102|S404|S405
110
     *    The RouteResult should be attached to the Request.
111
     *    If the Routing is not defined explicitly in the pipeline, then it will be injected with priority 0.
112
     *
113
     * a Post-Routing Middleware can be; priority between 0 and 100:
114
     *  - Acl - checks if the given route is available for the client. Also checks the auth > S102|S401|S403
115
     *  - CacheReader - checks if a suitable response body is cached. > S102|S200
116
     *
117
     * Dispatcher Middleware is fixed (DispatcherMiddleware::class); priority = 100:
118
     *  - A middleware which gets the corresponding Action middleware and applies it > S102
119
     *    If the Dispatcher is not defined explicitly in the pipeline, then it will be injected with priority 100.
120
     *    The Dispatcher should not set the response Status Code to 200 to let Post-Dispatchers to be called.
121
     *
122
     * a Post-Dispatch Middleware can be; priority > 100:
123
     *  - CacheWriter - writes response body into DataStorage (DB, File etc.) > S102
124
     *
125
     * Final Middleware is fixed (FinalMiddleware:class):
126
     *  - This middleware behaves a bit differently. It cannot be ordered, it's always the last called middleware:
127
     *    - when the middleware pipeline reached its end (typically when the Status Code is still 102)
128
     *    - when one item of the middleware pipeline returns with return response (status code is set to 200|40*|500)
129
     *    - when during the pipeline process an Exception is thrown.
130
     *
131
     * When the middleware pipeline is finished the application prints the header and the output.
132
     *
133
     * If a middleware other than the Routing, Dispatcher and Final Middleware has no priority set, it will be
134
     * considered to have priority = 50.
135
     *
136
     * @return void
137
     */
138
    public function run()
139
    {
140
        $this->prepare();
141
142
        /** @var HttpAdapterInterface $httpAdapter */
143
        $httpAdapter = $this->getContainer()->get(HttpAdapterInterface::class);
144
        /** @var ServerRequestInterface $request */
145
        $request = $httpAdapter->getRequest();
146
        /** @var ResponseInterface $response */
147
        $response = $httpAdapter->getResponse();
148
149
        $middlewareClass = $this->pipeline->start();
150
151
        while ($middlewareClass !== null) {
152
            try {
153
                /** @var MiddlewareInterface $middleware */
154
                $middleware = $this->container->get($middlewareClass);
155
156
                $requestAttributes = $request->getAttributes();
157
                // As an extra step if the action middleware is resolved, it is invoked right before the dispatcher.
158
                if ($middleware instanceof DispatcherMiddleware
159
                    && isset($requestAttributes['resolvedActionMiddleware'])
160
                ) {
161
                    /** @var MiddlewareInterface $actionMiddleware */
162
                    $actionMiddleware = $this->container->get($requestAttributes['resolvedActionMiddleware']);
163
                    $response = $actionMiddleware($request, $response);
164
                }
165
166
                $response = $middleware($request, $response);
167
            } catch (\Exception $exception) {
168
                $response = $response->withStatus(500);
169
                $request = $request->withAttribute('exception', $exception);
170
            }
171
172
            if ($response->getStatusCode() != 102) {
173
                break;
174
            }
175
176
            $middlewareClass = $this->pipeline->next();
177
        };
178
179
        // If there was no error, we mark as ready for output.
180
        if ($response->getStatusCode() == 102) {
181
            $response = $response->withStatus(200);
182
        }
183
184
        /** @var FinalMiddleware $finalMiddleware */
185
        $finalMiddleware = $this->container->get(FinalMiddleware::class);
186
187
        // Send out headers and content.
188
        $finalMiddleware($request, $response);
189
    }
190
}
191