Passed
Push — master ( a1bf27...3a2d72 )
by Jens
04:20
created

Application::setHeaders()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 21
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 18
nc 4
nop 0
dl 0
loc 21
rs 9.0534
c 0
b 0
f 0
1
<?php
2
3
namespace CloudControl\Cms\cc;
4
5
use CloudControl\Cms\cc\application\ApplicationRenderer;
6
use CloudControl\Cms\cc\application\ApplicationRunner;
7
use CloudControl\Cms\cc\application\UrlMatcher;
8
use CloudControl\Cms\services\FileService;
9
use CloudControl\Cms\services\ImageService;
10
use CloudControl\Cms\services\ValuelistService;
11
use CloudControl\Cms\storage\Cache;
12
use CloudControl\Cms\storage\Storage;
13
use Whoops\Handler\PrettyPageHandler;
14
use Whoops\Run;
15
16
class Application
17
{
18
    /**
19
     * @var string
20
     */
21
    protected $rootDir;
22
    /**
23
     * @var string
24
     */
25
    protected $configPath;
26
    /**
27
     * @var \stdClass
28
     */
29
    private $config;
30
    /**
31
     * @var \CloudControl\Cms\storage\Storage
32
     */
33
    private $storage;
34
35
    /**
36
     * @var \CloudControl\Cms\cc\Request
37
     */
38
    private $request;
39
40
    /**
41
     * @var array
42
     */
43
    private $matchedSitemapItems = array();
44
45
    /**
46
     * @var array
47
     */
48
    private $applicationComponents = array();
49
50
    const HEADER_X_POWERED_BY = 'X-Powered-By: ';
51
    const HEADER_X_POWERED_BY_CONTENT = 'Cloud Control - https://getcloudcontrol.org';
52
    const HEADER_X_FRAME_OPTIONS = "X-Frame-Options: ";
53
    const HEADER_X_FRAME_OPTIONS_CONTENT = "SAMEORIGIN";
54
    const HEADER_X_CONTENT_TYPE_OPTIONS = 'X-Content-Type-Options: ';
55
    const HEADER_X_CONTENT_TYPE_OPTIONS_CONTENT = 'nosniff';
56
    const HEADER_REFERRER_POLICY = 'Referrer-Policy: ';
57
    const HEADER_REFERRER_POLICY_CONTENT = 'strict-origin-when-cross-origin';
58
    const HEADER_X_XSS_PROTECTION = 'X-XSS-Protection: ';
59
    const HEADER_X_XSS_PROTECTION_CONTENT = '1; mode=block';
60
    const HEADER_SET_COOKIE = 'Set-Cookie: ';
61
    const HEADER_CONTENT_SECURITY_POLICY = 'Content-Security-Policy: ';
62
    const HEADER_CONTENT_SECURITY_POLICY_CONTENT_SECURE = 'default-src https: \'unsafe-inline\' \'unsafe-eval\'';
63
    const HEADER_CONTENT_SECURITY_POLICY_CONTENT_INSECURE = 'default-src \'self\' https: \'unsafe-inline\' \'unsafe-eval\'';
64
    const HEADER_CONTENT_SECURITY_POLICY_CONTENT_LOCALHOST = 'default-src * \'unsafe-inline\' \'unsafe-eval\' data: blob:;';
65
    const HEADER_X_CONTENT_SECURITY_POLICY = 'X-Content-Security-Policy: '; // For IE
66
    const HEADER_STRICT_TRANSPORT_SECURITY = 'Strict-Transport-Security: ';
67
    const HEADER_STRICT_TRANSPORT_SECURITY_CONTENT = 'max-age=31536000;';
68
69
    /**
70
     * Application constructor.
71
     * @param string $rootDir
72
     * @param string $configPath
73
     * @throws \Exception
74
     */
75
    public function __construct($rootDir, $configPath)
76
    {
77
        $this->rootDir = $rootDir;
78
        $this->configPath = $configPath;
79
80
        $this->config();
81
        $this->storage();
82
83
        Cache::getInstance()->setStoragePath($this->config->rootDir . DIRECTORY_SEPARATOR . $this->config->storageDir);
84
85
        $this->request = new Request();
86
87
        $this->setExceptionHandler();
88
89
        $this->startServices();
90
91
        $this->urlMatching();
92
93
        $this->getApplicationComponents();
94
        $this->run();
95
        $this->setHeaders();
96
        $this->render();
97
    }
98
99
    /**
100
     * Initialize the config
101
     *
102
     * @throws \Exception
103
     */
104
    private function config()
105
    {
106
        if (realpath($this->configPath) !== false) {
107
            $json = file_get_contents($this->configPath);
108
            $this->config = json_decode($json);
109
            $this->config->rootDir = $this->rootDir;
110
        } else {
111
            throw new \RuntimeException('Framework not initialized yet. Consider running composer install');
112
        }
113
    }
114
115
    /**
116
     * Initialize the storage
117
     */
118
    private function storage()
119
    {
120
        $this->storage = new Storage($this->config->rootDir . DIRECTORY_SEPARATOR . $this->config->storageDir,
121
            $this->config->rootDir . DIRECTORY_SEPARATOR . $this->config->imagesDir, $this->config->filesDir);
122
    }
123
124
    public function getAllApplicationComponentParameters()
125
    {
126
        $allParameters = array();
127
        foreach ($this->applicationComponents as $applicationComponent) {
128
            $parameters = $applicationComponent->{'object'}->getParameters();
129
            $allParameters[] = $parameters;
130
        }
131
        return $allParameters;
132
    }
133
134
    public function unlockApplicationComponentParameters()
135
    {
136
        foreach ($this->applicationComponents as $applicationComponent) {
137
            $parameters = $applicationComponent->{'object'}->getParameters();
138
            extract($parameters, EXTR_OVERWRITE);
139
        }
140
    }
141
142
    /**
143
     * @return string
144
     */
145
    public function getTemplateDir()
146
    {
147
        return $this->config->templateDir;
148
    }
149
150
    /**
151
     * @return string
152
     */
153
    public function getStorageDir()
154
    {
155
        return $this->config->storageDir;
156
    }
157
158
    public function getApplicationComponents()
159
    {
160
        $this->applicationComponents = $this->storage->getApplicationComponents()->getApplicationComponents();
161
    }
162
163
    /**
164
     * @return string
165
     */
166
    public function getRootDir()
167
    {
168
        return $this->config->rootDir;
169
    }
170
171
    private function setExceptionHandler()
172
    {
173
        $whoops = new Run;
174
        $whoops->pushHandler(new PrettyPageHandler);
175
        $whoops->register();
176
    }
177
178
    /**
179
     * @throws \Exception
180
     */
181
    private function urlMatching()
182
    {
183
        $urlMatcher = new UrlMatcher($this, $this->storage);
184
        $urlMatcher->redirectMatching($this->request);
185
        $urlMatcher->sitemapMatching($this->request);
186
    }
187
188
    private function run()
189
    {
190
        $applicationRunner = new ApplicationRunner($this->storage, $this->request);
191
        $applicationRunner->runApplicationComponents($this->applicationComponents);
192
        $applicationRunner->runSitemapComponents($this->matchedSitemapItems);
193
    }
194
195
    /**
196
     * @throws \Exception
197
     */
198
    private function render()
199
    {
200
        $applicationRenderer = new ApplicationRenderer($this, $this->storage, $this->request);
201
        $applicationRenderer->renderApplicationComponents($this->applicationComponents);
202
        $applicationRenderer->renderSitemapComponents($this->matchedSitemapItems);
203
    }
204
205
    private function startServices()
206
    {
207
        FileService::getInstance()->init($this->storage);
208
        ImageService::getInstance()->init($this->storage);
209
        ValuelistService::getInstance()->init($this->storage);
210
    }
211
212
    public function addMatchedSitemapItem($matchedClone)
213
    {
214
        $this->matchedSitemapItems[] = $matchedClone;
215
    }
216
217
    /**
218
     * Sets default headers. Please note that caching headers are set
219
     * here: \CloudControl\Cms\cc\application\ApplicationRenderer::setCachingHeaders
220
     * and here: \CloudControl\Cms\cc\application\ApplicationRenderer::setNotCachingHeaders
221
     */
222
    private function setHeaders()
223
    {
224
        if (PHP_SAPI === 'cli') {
225
            return;
226
        }
227
        header(self::HEADER_X_POWERED_BY . self::HEADER_X_POWERED_BY_CONTENT);
228
        header(self::HEADER_X_FRAME_OPTIONS . self::HEADER_X_FRAME_OPTIONS_CONTENT);
229
        header(self::HEADER_X_CONTENT_TYPE_OPTIONS . self::HEADER_X_CONTENT_TYPE_OPTIONS_CONTENT);
230
        header(self::HEADER_REFERRER_POLICY . self::HEADER_REFERRER_POLICY_CONTENT);
231
        header(self::HEADER_X_XSS_PROTECTION . self::HEADER_X_XSS_PROTECTION_CONTENT);
232
        header(self::HEADER_SET_COOKIE . '__Host-sess=' . session_id() . '; path=' . Request::$subfolders . '; Secure; HttpOnly; SameSite');
233
        if (Request::isSecure()) {
234
            header(self::HEADER_CONTENT_SECURITY_POLICY . self::HEADER_CONTENT_SECURITY_POLICY_CONTENT_SECURE);
235
            header(self::HEADER_X_CONTENT_SECURITY_POLICY . self::HEADER_CONTENT_SECURITY_POLICY_CONTENT_SECURE);
236
            header(self::HEADER_STRICT_TRANSPORT_SECURITY . self::HEADER_STRICT_TRANSPORT_SECURITY_CONTENT);
237
        } elseif (Request::isLocalhost()) {
238
            header(self::HEADER_CONTENT_SECURITY_POLICY . self::HEADER_CONTENT_SECURITY_POLICY_CONTENT_LOCALHOST);
239
            header(self::HEADER_X_CONTENT_SECURITY_POLICY . self::HEADER_CONTENT_SECURITY_POLICY_CONTENT_LOCALHOST);
240
        } else {
241
            header(self::HEADER_CONTENT_SECURITY_POLICY . self::HEADER_CONTENT_SECURITY_POLICY_CONTENT_INSECURE);
242
            header(self::HEADER_X_CONTENT_SECURITY_POLICY . self::HEADER_CONTENT_SECURITY_POLICY_CONTENT_INSECURE);
243
        }
244
245
    }
246
}