Passed
Push — master ( 8587fa...100bd0 )
by Gabor
04:15
created

ServiceAdapter::getRequestMethod()   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
ccs 2
cts 2
cp 1
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 0
crap 1
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\Configuration\ServiceInterface as ConfigurationInterface;
18
use WebHemi\Environment\ServiceInterface;
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 the request method.
193
     *
194
     * @return string
195
     */
196 1
    public function getRequestMethod(): string
197
    {
198 1
        return $this->environmentData['SERVER']['REQUEST_METHOD'] ?? 'GET';
199
    }
200
201
    /**
202
     * Gets environment data.
203
     *
204
     * @param string $key
205
     * @return array
206
     */
207 1
    public function getEnvironmentData(string $key) : array
208
    {
209 1
        if (!isset($this->environmentData[$key])) {
210 1
            throw new InvalidArgumentException(sprintf('The "%s" is not a valid environment key.', $key));
211
        }
212
213 1
        return $this->environmentData[$key];
214
    }
215
216
    /**
217
     * Gets the client IP address.
218
     *
219
     * @return string
220
     */
221 1
    public function getClientIp() : string
222
    {
223 1
        $ipAddress = '';
224
225 1
        if (!empty($this->environmentData['SERVER']['HTTP_X_FORWARDED_FOR'])) {
226 1
            $ipAddress = $this->environmentData['SERVER']['HTTP_X_FORWARDED_FOR'];
227 1
        } elseif (!empty($this->environmentData['SERVER']['REMOTE_ADDR'])) {
228 1
            $ipAddress = $this->environmentData['SERVER']['REMOTE_ADDR'];
229
        }
230
231 1
        return (string) $ipAddress;
232
    }
233
    /**
234
     * Parses server data and tries to set domain information.
235
     *
236
     * @return ServiceAdapter
237
     */
238 5
    private function setDomain() : ServiceAdapter
239
    {
240 5
        $domain = $this->environmentData['SERVER']['SERVER_NAME'];
241 5
        $subDomain = '';
242 5
        $urlParts = parse_url($this->url);
243
244
        // If the host is not an IP address, then check the sub-domain-based module names too
245 5
        if (!preg_match(
246 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])$/',
247 5
            $urlParts['host']
248
        )) {
249 5
            $domainParts = explode('.', $urlParts['host']);
250
            // @todo find out how to support complex TLDs like `.co.uk` or `.com.br`
251 5
            $tld = array_pop($domainParts);
252 5
            $domain = array_pop($domainParts).'.'.$tld;
253
            // the rest is the sub-domain
254 5
            $subDomain = implode('.', $domainParts);
255
        }
256
257
        // If no sub-domain presents, then it should be handled as the default sub-domain set for the 'website'
258 5
        if (empty($subDomain)) {
259 3
            $subDomain = $this->configuration->getData('website/path')[0];
260
        }
261
262 5
        $this->subDomain = $subDomain;
263 5
        $this->mainDomain = $domain;
264 5
        $this->applicationDomain = $this->subDomain.'.'.$this->mainDomain;
265
266
        // Redirecting when the app domain is not equal to the server data
267
        // @codeCoverageIgnoreStart
268
        if (!defined('PHPUNIT_WEBHEMI_TESTSUITE')
269
            && $this->environmentData['SERVER']['HTTP_HOST'] != $this->applicationDomain
270
        ) {
271
            $schema = 'http'.($this->isSecuredApplication() ? 's' : '').'://';
272
            $uri = $this->environmentData['SERVER']['REQUEST_URI'];
273
            header('Location: '.$schema.$this->applicationDomain.$uri);
274
            exit;
275
        }
276
        // @codeCoverageIgnoreEnd
277
278 5
        return $this;
279
    }
280
281
    /**
282
     * From the parsed domain data, selects the application, module and theme.
283
     *
284
     * @return ServiceAdapter
285
     */
286 5
    private function selectModuleApplicationAndTheme() : ServiceAdapter
287
    {
288 5
        $urlParts = parse_url($this->url);
289 5
        $applications = $this->configuration->toArray();
290
291
        // Only the first segment is important (if exists).
292 5
        list($subDirectory) = explode('/', ltrim($urlParts['path'], '/'), 2);
293
294
        $applicationDataFixture = [
295 5
            'type' => self::APPLICATION_TYPE_DIRECTORY,
296 5
            'module' => self::DEFAULT_MODULE,
297 5
            'theme' => self::DEFAULT_THEME,
298
        ];
299
300
        // Run through the available application-modules to validate and find active module
301 5
        foreach ($applications as $applicationName => $applicationData) {
302
            // Don't risk, fix.
303 5
            $applicationData = array_merge($applicationDataFixture, $applicationData);
304
305 5
            if ($this->checkDirectoryIsValid($applicationName, $applicationData, $subDirectory)
306 5
                || $this->checkDomainIsValid($applicationName, $applicationData, $subDirectory)
307
            ) {
308 5
                $this->selectedModule = $applicationData['module'];
309 5
                $this->selectedApplication = (string) $applicationName;
310 5
                $this->selectedTheme = $applicationData['theme'];
311
312 5
                $this->selectedApplicationUri = '/'.$subDirectory;
313 5
                break;
314
            }
315
        }
316
317
        // Final check for config and resources.
318 5
        if ($this->selectedTheme !== self::DEFAULT_THEME) {
319 1
            $this->selectedThemeResourcePath = '/resources/vendor_themes/'.$this->selectedTheme;
320
        }
321
322 5
        return $this;
323
    }
324
325
    /**
326
     * Checks from type, path it the current URI segment is valid.
327
     *
328
     * @param string $applicationName
329
     * @param array  $applicationData
330
     * @param string $subDirectory
331
     * @return bool
332
     */
333 5
    private function checkDirectoryIsValid(string $applicationName, array $applicationData, string $subDirectory) : bool
334
    {
335 5
        return $this->subDomain == $this->configuration->getData('website/path')[0]
336 5
            && $applicationName != 'website'
337 5
            && !empty($subDirectory)
338 5
            && $applicationData['type'] == self::APPLICATION_TYPE_DIRECTORY
339 5
            && $applicationData['path'] == $subDirectory;
340
    }
341
342
    /**
343
     * Checks from type and path if the domain is valid. If so, it sets the $subDirectory to the default.
344
     *
345
     * @param string $applicationName
346
     * @param array  $applicationData
347
     * @param string $subDirectory
348
     * @return bool
349
     */
350 4
    private function checkDomainIsValid(string $applicationName, array $applicationData, string&$subDirectory) : bool
351
    {
352 4
        $isSubdomain = $applicationName == 'website'
353
            || (
354 3
                $this->subDomain != $this->configuration->getData('website/path')[0]
355 3
                && $applicationData['type'] == self::APPLICATION_TYPE_DOMAIN
356 4
                && $applicationData['path'] == $this->subDomain
357
            );
358
359
        // If this method get called and will return TRUE, it means the $subDirectory paramtere will be used only for
360
        // setting the right selectedApplicationUri. To avoid complexity, we change it here. Doesn't matter.
361 4
        if ($isSubdomain) {
362 4
            $subDirectory = '';
363
        }
364
365 4
        return $isSubdomain;
366
    }
367
}
368