Passed
Push — master ( a14261...5f8bab )
by Gabor
05:22
created

ServiceAdapter::setDomain()   B

Complexity

Conditions 6
Paths 12

Size

Total Lines 42
Code Lines 23

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 17
CRAP Score 6

Importance

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