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

ServiceAdapter   B

Complexity

Total Complexity 39

Size/Duplication

Total Lines 335
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 1

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
wmc 39
lcom 1
cbo 1
dl 0
loc 335
ccs 102
cts 102
cp 1
rs 8.2857
c 0
b 0
f 0

16 Methods

Rating   Name   Duplication   Size   Complexity  
B __construct() 0 37 4
A getDocumentRoot() 0 4 1
A getApplicationDomain() 0 4 1
A isSecuredApplication() 0 4 1
A getSelectedApplication() 0 4 1
A getSelectedApplicationUri() 0 4 1
A getRequestUri() 0 4 1
A getSelectedModule() 0 4 1
A getSelectedTheme() 0 4 1
A getResourcePath() 0 4 1
A getEnvironmentData() 0 8 2
A getClientIp() 0 12 3
B setDomain() 0 42 6
B selectModuleApplicationAndTheme() 0 38 5
B checkDirectoryIsValid() 0 8 5
B checkDomainIsValid() 0 17 5
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\Environment\ServiceAdapter\Base;
15
16
use InvalidArgumentException;
17
use WebHemi\Environment\ServiceInterface;
18
use WebHemi\Configuration\ServiceInterface as ConfigurationInterface;
19
20
/**
21
 * Class ServiceAdapter.
22
 */
23
class ServiceAdapter implements ServiceInterface
24
{
25
    /** @var ConfigurationInterface */
26
    private $configuration;
27
    /** @var string */
28
    private $url;
29
    /** @var string */
30
    private $subDomain;
31
    /** @var string */
32
    private $mainDomain;
33
    /** @var string */
34
    private $applicationDomain;
35
    /** @var string */
36
    private $documentRoot;
37
    /** @var string */
38
    private $selectedModule;
39
    /** @var string */
40
    private $selectedApplication;
41
    /** @var string */
42
    private $selectedApplicationUri;
43
    /** @var string */
44
    private $selectedTheme;
45
    /** @var string */
46
    private $selectedThemeResourcePath;
47
    /** @var array  */
48
    private $environmentData;
49
    /** @var bool */
50
    private $isHttps;
51
52
    /**
53
     * ServiceAdapter constructor.
54
     *
55
     * @param ConfigurationInterface $configuration
56
     * @param array                  $getData
57
     * @param array                  $postData
58
     * @param array                  $serverData
59
     * @param array                  $cookieData
60
     * @param array                  $filesData
61
     */
62 5
    public function __construct(
63
        ConfigurationInterface $configuration,
64
        array $getData,
65
        array $postData,
66
        array $serverData,
67
        array $cookieData,
68
        array $filesData
69
    ) {
70 5
        $this->configuration = $configuration->getConfig('applications');
71 5
        $this->documentRoot = realpath(__DIR__ . '/../../../../../');
72
73 5
        if (isset($serverData['HTTP_REFERER'])) {
74 5
            $serverData['HTTP_REFERER'] = urldecode($serverData['HTTP_REFERER']);
75
        }
76
77 5
        $this->environmentData = [
78 5
            'GET'    => $getData,
79 5
            'POST'   => $postData,
80 5
            'SERVER' => $serverData,
81 5
            'COOKIE' => $cookieData,
82 5
            'FILES'  => $filesData,
83
        ];
84
85 5
        $this->isHttps = isset($this->environmentData['SERVER']['HTTPS']) && $this->environmentData['SERVER']['HTTPS'];
86 5
        $this->url = 'http'.($this->isHttps ? 's' : '').'://'
87 5
            .$this->environmentData['SERVER']['HTTP_HOST']
88 5
            .$this->environmentData['SERVER']['REQUEST_URI']; // contains also the query string
89
90 5
        $this->selectedModule = self::DEFAULT_MODULE;
91 5
        $this->selectedApplication = self::DEFAULT_APPLICATION;
92 5
        $this->selectedTheme = self::DEFAULT_THEME;
93 5
        $this->selectedThemeResourcePath = self::DEFAULT_THEME_RESOURCE_PATH;
94 5
        $this->selectedApplicationUri = self::DEFAULT_APPLICATION_URI;
95
96 5
        $this->setDomain()
97 5
            ->selectModuleApplicationAndTheme();
98 5
    }
99
100
    /**
101
     * Gets the document root path.
102
     *
103
     * @return string
104
     */
105 1
    public function getDocumentRoot() : string
106
    {
107 1
        return $this->documentRoot;
108
    }
109
110
    /**
111
     * Gets the application domain.
112
     *
113
     * @return string
114
     */
115 1
    public function getApplicationDomain() : string
116
    {
117 1
        return $this->applicationDomain;
118
    }
119
120
    /**
121
     * Gets the application SSL status.
122
     *
123
     * @return bool
124
     */
125 1
    public function isSecuredApplication() : bool
126
    {
127 1
        return $this->isHttps;
128
    }
129
130
    /**
131
     * Gets the selected application.
132
     *
133
     * @return string
134
     */
135 4
    public function getSelectedApplication() : string
136
    {
137 4
        return $this->selectedApplication;
138
    }
139
140
    /**
141
     * Get the URI path for the selected application. Required for the RouterAdapter to work with directory-based
142
     * applications correctly.
143
     *
144
     * @return string
145
     */
146 4
    public function getSelectedApplicationUri() : string
147
    {
148 4
        return $this->selectedApplicationUri;
149
    }
150
151
    /**
152
     * Gets the request URI
153
     *
154
     * @return string
155
     */
156 1
    public function getRequestUri() : string
157
    {
158 1
        return $this->environmentData['SERVER']['REQUEST_URI'];
159
    }
160
161
    /**
162
     * Gets the selected module.
163
     *
164
     * @return string
165
     */
166 1
    public function getSelectedModule() : string
167
    {
168 1
        return $this->selectedModule;
169
    }
170
171
    /**
172
     * Gets the selected theme.
173
     *
174
     * @return string
175
     */
176 1
    public function getSelectedTheme() : string
177
    {
178 1
        return $this->selectedTheme;
179
    }
180
181
    /**
182
     * Gets the resource path for the selected theme.
183
     *
184
     * @return string
185
     */
186 2
    public function getResourcePath() : string
187
    {
188 2
        return $this->selectedThemeResourcePath;
189
    }
190
191
    /**
192
     * Gets environment data.
193
     *
194
     * @param string $key
195
     * @return array
196
     */
197 1
    public function getEnvironmentData(string $key) : array
198
    {
199 1
        if (!isset($this->environmentData[$key])) {
200 1
            throw new InvalidArgumentException(sprintf('The "%s" is not a valid environment key.', $key));
201
        }
202
203 1
        return $this->environmentData[$key];
204
    }
205
206
    /**
207
     * Gets the client IP address.
208
     *
209
     * @return string
210
     */
211 1
    public function getClientIp() : string
212
    {
213 1
        $ipAddress = '';
214
215 1
        if (!empty($this->environmentData['SERVER']['HTTP_X_FORWARDED_FOR'])) {
216 1
            $ipAddress = $this->environmentData['SERVER']['HTTP_X_FORWARDED_FOR'];
217 1
        } elseif (!empty($this->environmentData['SERVER']['REMOTE_ADDR'])) {
218 1
            $ipAddress = $this->environmentData['SERVER']['REMOTE_ADDR'];
219
        }
220
221 1
        return (string) $ipAddress;
222
    }
223
    /**
224
     * Parses server data and tries to set domain information.
225
     *
226
     * @return ServiceAdapter
227
     */
228 5
    private function setDomain() : ServiceAdapter
229
    {
230 5
        $domain = $this->environmentData['SERVER']['SERVER_NAME'];
231 5
        $subDomain = '';
232 5
        $urlParts = parse_url($this->url);
233
234
        // If the host is not an IP address, then check the sub-domain-based module names too
235 5
        if (!preg_match(
236 5
            '/^((\d|[1-9]\d|1\d{2}|2[0-4]\d|25[0-5])\.){3}(\d|[1-9]\d|1\d{2}|2[0-4]\d|25[0-5])$/',
237 5
            $urlParts['host']
238
        )) {
239 5
            $domainParts = explode('.', $urlParts['host']);
240
            // @todo find out how to support complex TLDs like `.co.uk` or `.com.br`
241 5
            $tld = array_pop($domainParts);
242 5
            $domain = array_pop($domainParts).'.'.$tld;
243
            // the rest is the sub-domain
244 5
            $subDomain = implode('.', $domainParts);
245
        }
246
247
        // If no sub-domain presents, then it should be handled as the default sub-domain set for the 'website'
248 5
        if (empty($subDomain)) {
249 3
            $subDomain = $this->configuration->getData('website/path')[0];
250
        }
251
252 5
        $this->subDomain = $subDomain;
253 5
        $this->mainDomain = $domain;
254 5
        $this->applicationDomain = $this->subDomain.'.'.$this->mainDomain;
255
256
        // Redirecting when the app domain is not equal to the server data
257
        // @codeCoverageIgnoreStart
258
        if (!defined('PHPUNIT_WEBHEMI_TESTSUITE')
259
            && $this->environmentData['SERVER']['HTTP_HOST'] != $this->applicationDomain
260
        ) {
261
            $schema = 'http'.($this->isSecuredApplication() ? 's' : '').'://';
262
            $uri = $this->environmentData['SERVER']['REQUEST_URI'];
263
            header('Location: '.$schema.$this->applicationDomain.$uri);
264
            exit;
265
        }
266
        // @codeCoverageIgnoreEnd
267
268 5
        return $this;
269
    }
270
271
    /**
272
     * From the parsed domain data, selects the application, module and theme.
273
     *
274
     * @return ServiceAdapter
275
     */
276 5
    private function selectModuleApplicationAndTheme() : ServiceAdapter
277
    {
278 5
        $urlParts = parse_url($this->url);
279 5
        $applications = $this->configuration->toArray();
280
281
        // Only the first segment is important (if exists).
282 5
        list($subDirectory) = explode('/', ltrim($urlParts['path'], '/'), 2);
283
284
        $applicationDataFixture = [
285 5
            'type' => self::APPLICATION_TYPE_DIRECTORY,
286 5
            'module' => self::DEFAULT_MODULE,
287 5
            'theme' => self::DEFAULT_THEME,
288
        ];
289
290
        // Run through the available application-modules to validate and find active module
291 5
        foreach ($applications as $applicationName => $applicationData) {
292
            // Don't risk, fix.
293 5
            $applicationData = array_merge($applicationDataFixture, $applicationData);
294
295 5
            if ($this->checkDirectoryIsValid($applicationName, $applicationData, $subDirectory)
296 5
                || $this->checkDomainIsValid($applicationName, $applicationData, $subDirectory)
297
            ) {
298 5
                $this->selectedModule = $applicationData['module'];
299 5
                $this->selectedApplication = (string) $applicationName;
300 5
                $this->selectedTheme = $applicationData['theme'];
301
302 5
                $this->selectedApplicationUri = '/'.$subDirectory;
303 5
                break;
304
            }
305
        }
306
307
        // Final check for config and resources.
308 5
        if ($this->selectedTheme !== self::DEFAULT_THEME) {
309 1
            $this->selectedThemeResourcePath = '/resources/vendor_themes/'.$this->selectedTheme;
310
        }
311
312 5
        return $this;
313
    }
314
315
    /**
316
     * Checks from type, path it the current URI segment is valid.
317
     *
318
     * @param string $applicationName
319
     * @param array  $applicationData
320
     * @param string $subDirectory
321
     * @return bool
322
     */
323 5
    private function checkDirectoryIsValid(string $applicationName, array $applicationData, string $subDirectory) : bool
324
    {
325 5
        return $this->subDomain == $this->configuration->getData('website/path')[0]
326 5
            && $applicationName != 'website'
327 5
            && !empty($subDirectory)
328 5
            && $applicationData['type'] == self::APPLICATION_TYPE_DIRECTORY
329 5
            && $applicationData['path'] == $subDirectory;
330
    }
331
332
    /**
333
     * Checks from type and path if the domain is valid. If so, it sets the $subDirectory to the default.
334
     *
335
     * @param string $applicationName
336
     * @param array  $applicationData
337
     * @param string $subDirectory
338
     * @return bool
339
     */
340 4
    private function checkDomainIsValid(string $applicationName, array $applicationData, string&$subDirectory) : bool
341
    {
342 4
        $isSubdomain = $applicationName == 'website'
343
            || (
344 3
                $this->subDomain != $this->configuration->getData('website/path')[0]
345 3
                && $applicationData['type'] == self::APPLICATION_TYPE_DOMAIN
346 4
                && $applicationData['path'] == $this->subDomain
347
            );
348
349
        // If this method get called and will return TRUE, it means the $subDirectory paramtere will be used only for
350
        // setting the right selectedApplicationUri. To avoid complexity, we change it here. Doesn't matter.
351 4
        if ($isSubdomain) {
352 4
            $subDirectory = '';
353
        }
354
355 4
        return $isSubdomain;
356
    }
357
}
358