Passed
Push — develop ( 96e1dd...fabd4f )
by Jens
02:30
created

Application::setHeaders()   B

Complexity

Conditions 4
Paths 4

Size

Total Lines 22
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 19
nc 4
nop 0
dl 0
loc 22
rs 8.9197
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
    const HEADER_ACCESS_CONTROL_ALLOW_ORIGIN = 'Access-Control-Allow-Origin: ';
69
    const HEADER_ACCESS_CONTROL_ALLOW_ORIGIN_CONTENT = '*';
70
71
    /**
72
     * Application constructor.
73
     * @param string $rootDir
74
     * @param string $configPath
75
     * @throws \Exception
76
     */
77
    public function __construct($rootDir, $configPath)
78
    {
79
        $this->rootDir = $rootDir;
80
        $this->configPath = $configPath;
81
82
        $this->config();
83
        $this->storage();
84
85
        Cache::getInstance()->setStoragePath($this->config->rootDir . DIRECTORY_SEPARATOR . $this->config->storageDir);
86
87
        $this->request = new Request();
88
89
        $this->setExceptionHandler();
90
91
        $this->startServices();
92
93
        $this->urlMatching();
94
95
        $this->getApplicationComponents();
96
        $this->run();
97
        $this->setHeaders();
98
        $this->render();
99
    }
100
101
    /**
102
     * Initialize the config
103
     *
104
     * @throws \Exception
105
     */
106
    private function config()
107
    {
108
        if (realpath($this->configPath) !== false) {
109
            $json = file_get_contents($this->configPath);
110
            $this->config = json_decode($json);
111
            $this->config->rootDir = $this->rootDir;
112
        } else {
113
            throw new \RuntimeException('Framework not initialized yet. Consider running composer install');
114
        }
115
    }
116
117
    /**
118
     * Initialize the storage
119
     * @throws \Exception
120
     */
121
    private function storage()
122
    {
123
        $this->storage = new Storage($this->config->rootDir . DIRECTORY_SEPARATOR . $this->config->storageDir,
124
            $this->config->rootDir . DIRECTORY_SEPARATOR . $this->config->imagesDir, $this->config->filesDir);
125
    }
126
127
    public function getAllApplicationComponentParameters()
128
    {
129
        $allParameters = array();
130
        foreach ($this->applicationComponents as $applicationComponent) {
131
            $parameters = $applicationComponent->{'object'}->getParameters();
132
            $allParameters[] = $parameters;
133
        }
134
        return $allParameters;
135
    }
136
137
    public function unlockApplicationComponentParameters()
138
    {
139
        foreach ($this->applicationComponents as $applicationComponent) {
140
            $parameters = $applicationComponent->{'object'}->getParameters();
141
            extract($parameters, EXTR_OVERWRITE);
142
        }
143
    }
144
145
    /**
146
     * @return string
147
     */
148
    public function getTemplateDir()
149
    {
150
        return $this->config->templateDir;
151
    }
152
153
    /**
154
     * @return string
155
     */
156
    public function getStorageDir()
157
    {
158
        return $this->config->storageDir;
159
    }
160
161
    public function getApplicationComponents()
162
    {
163
        $this->applicationComponents = $this->storage->getApplicationComponents()->getApplicationComponents();
164
    }
165
166
    /**
167
     * @return string
168
     */
169
    public function getRootDir()
170
    {
171
        return $this->config->rootDir;
172
    }
173
174
    private function setExceptionHandler()
175
    {
176
        $whoops = new Run;
177
        $whoops->pushHandler(new PrettyPageHandler);
178
        $whoops->register();
179
    }
180
181
    /**
182
     * @throws \Exception
183
     */
184
    private function urlMatching()
185
    {
186
        $urlMatcher = new UrlMatcher($this, $this->storage);
187
        $urlMatcher->redirectMatching($this->request);
188
        $urlMatcher->sitemapMatching($this->request);
189
    }
190
191
    private function run()
192
    {
193
        $applicationRunner = new ApplicationRunner($this->storage, $this->request);
194
        $applicationRunner->runApplicationComponents($this->applicationComponents);
195
        $applicationRunner->runSitemapComponents($this->matchedSitemapItems);
196
    }
197
198
    /**
199
     * @throws \Exception
200
     */
201
    private function render()
202
    {
203
        $applicationRenderer = new ApplicationRenderer($this, $this->storage, $this->request);
204
        $applicationRenderer->renderApplicationComponents($this->applicationComponents);
205
        $applicationRenderer->renderSitemapComponents($this->matchedSitemapItems);
206
    }
207
208
    private function startServices()
209
    {
210
        FileService::getInstance()->init($this->storage);
211
        ImageService::getInstance()->init($this->storage);
212
        ValuelistService::getInstance()->init($this->storage);
213
    }
214
215
    public function addMatchedSitemapItem($matchedClone)
216
    {
217
        $this->matchedSitemapItems[] = $matchedClone;
218
    }
219
220
    /**
221
     * Sets default headers. Please note that caching headers are set
222
     * here: \CloudControl\Cms\cc\application\ApplicationRenderer::setCachingHeaders
223
     * and here: \CloudControl\Cms\cc\application\ApplicationRenderer::setNotCachingHeaders
224
     */
225
    private function setHeaders()
226
    {
227
        if (PHP_SAPI === 'cli') {
228
            return;
229
        }
230
        header(self::HEADER_X_POWERED_BY . self::HEADER_X_POWERED_BY_CONTENT);
231
        header(self::HEADER_X_FRAME_OPTIONS . self::HEADER_X_FRAME_OPTIONS_CONTENT);
232
        header(self::HEADER_X_CONTENT_TYPE_OPTIONS . self::HEADER_X_CONTENT_TYPE_OPTIONS_CONTENT);
233
        header(self::HEADER_REFERRER_POLICY . self::HEADER_REFERRER_POLICY_CONTENT);
234
        header(self::HEADER_X_XSS_PROTECTION . self::HEADER_X_XSS_PROTECTION_CONTENT);
235
        header(self::HEADER_SET_COOKIE . '__Host-sess=' . session_id() . '; path=' . Request::$subfolders . '; Secure; HttpOnly; SameSite');
236
        header(self::HEADER_ACCESS_CONTROL_ALLOW_ORIGIN . self::HEADER_ACCESS_CONTROL_ALLOW_ORIGIN_CONTENT);
237
        if (Request::isSecure()) {
238
            header(self::HEADER_CONTENT_SECURITY_POLICY . self::HEADER_CONTENT_SECURITY_POLICY_CONTENT_SECURE);
239
            header(self::HEADER_X_CONTENT_SECURITY_POLICY . self::HEADER_CONTENT_SECURITY_POLICY_CONTENT_SECURE);
240
            header(self::HEADER_STRICT_TRANSPORT_SECURITY . self::HEADER_STRICT_TRANSPORT_SECURITY_CONTENT);
241
        } elseif (Request::isLocalhost()) {
242
            header(self::HEADER_CONTENT_SECURITY_POLICY . self::HEADER_CONTENT_SECURITY_POLICY_CONTENT_LOCALHOST);
243
            header(self::HEADER_X_CONTENT_SECURITY_POLICY . self::HEADER_CONTENT_SECURITY_POLICY_CONTENT_LOCALHOST);
244
        } else {
245
            header(self::HEADER_CONTENT_SECURITY_POLICY . self::HEADER_CONTENT_SECURITY_POLICY_CONTENT_INSECURE);
246
            header(self::HEADER_X_CONTENT_SECURITY_POLICY . self::HEADER_CONTENT_SECURITY_POLICY_CONTENT_INSECURE);
247
        }
248
249
    }
250
}