Passed
Push — master ( ccc9fa...bee35f )
by Gabor
03:14
created

ServiceAdapter   B

Complexity

Total Complexity 36

Size/Duplication

Total Lines 274
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 4

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
wmc 36
lcom 1
cbo 4
dl 0
loc 274
ccs 88
cts 88
cp 1
rs 8.8
c 0
b 0
f 0

12 Methods

Rating   Name   Duplication   Size   Complexity  
B __construct() 0 42 4
A getRequestUri() 0 4 1
A getRequestMethod() 0 4 1
A getEnvironmentData() 0 8 2
A getClientIp() 0 12 3
A setDomain() 0 19 2
A setAdapterOptions() 0 12 4
A checkSubdomain() 0 10 4
B setApplication() 0 32 4
A getSelectedApplicationName() 0 16 4
B checkDirectoryIsValid() 0 11 5
A checkDomainIsValid() 0 8 2
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 Exception;
17
use InvalidArgumentException;
18
use LayerShifter\TLDExtract\Extract;
19
use LayerShifter\TLDExtract\Result;
20
use WebHemi\Configuration\ServiceInterface as ConfigurationInterface;
21
use WebHemi\Environment\AbstractAdapter;
22
23
/**
24
 * Class ServiceAdapter.
25
 * @SuppressWarnings(PHPMD.TooManyFields)
26
 */
27
class ServiceAdapter extends AbstractAdapter
28
{
29
    /** @var Extract */
30
    private $domainAdapter;
31
32
    /**
33
     * ServiceAdapter constructor.
34
     *
35
     * @param ConfigurationInterface $configuration
36
     * @param array                  $getData
37
     * @param array                  $postData
38
     * @param array                  $serverData
39
     * @param array                  $cookieData
40
     * @param array                  $filesData
41
     * @param array                  $optionsData
42
     * @throws Exception
43
     */
44 6
    public function __construct(
45
        ConfigurationInterface $configuration,
46
        array $getData,
47
        array $postData,
48
        array $serverData,
49
        array $cookieData,
50
        array $filesData,
51
        array $optionsData
52
    ) {
53 6
        $this->configuration = $configuration->getConfig('applications');
54 6
        $this->domainAdapter = new Extract();
55 6
        $this->applicationRoot = realpath(__DIR__.'/../../../../../');
56
        // In case when the backend sources are out of the document root.
57 6
        $this->documentRoot = realpath($this->applicationRoot.'/');
58 6
        $this->options = $optionsData;
59
60 6
        if (isset($serverData['HTTP_REFERER'])) {
61 6
            $serverData['HTTP_REFERER'] = urldecode($serverData['HTTP_REFERER']);
62
        }
63
64 6
        $this->environmentData = [
65 6
            'GET'    => $getData,
66 6
            'POST'   => $postData,
67 6
            'SERVER' => $serverData,
68 6
            'COOKIE' => $cookieData,
69 6
            'FILES'  => $filesData,
70
        ];
71
72 6
        $this->isHttps = isset($this->environmentData['SERVER']['HTTPS']) && $this->environmentData['SERVER']['HTTPS'];
73 6
        $this->url = 'http'.($this->isHttps ? 's' : '').'://'
74 6
            .$this->environmentData['SERVER']['HTTP_HOST']
75 6
            .$this->environmentData['SERVER']['REQUEST_URI']; // contains also the query string
76
77 6
        $this->selectedModule = self::DEFAULT_MODULE;
78 6
        $this->selectedApplication = self::DEFAULT_APPLICATION;
79 6
        $this->selectedTheme = self::DEFAULT_THEME;
80 6
        $this->selectedThemeResourcePath = self::DEFAULT_THEME_RESOURCE_PATH;
81 6
        $this->selectedApplicationUri = self::DEFAULT_APPLICATION_URI;
82
83 6
        $this->setDomain()
84 5
            ->setApplication();
85 5
    }
86
87
    /**
88
     * Gets the request URI
89
     *
90
     * @return string
91
     */
92 1
    public function getRequestUri() : string
93
    {
94 1
        return rtrim($this->environmentData['SERVER']['REQUEST_URI'], '/');
95
    }
96
97
    /**
98
     * Gets the request method.
99
     *
100
     * @return string
101
     */
102 1
    public function getRequestMethod(): string
103
    {
104 1
        return $this->environmentData['SERVER']['REQUEST_METHOD'] ?? 'GET';
105
    }
106
107
    /**
108
     * Gets environment data.
109
     *
110
     * @param string $key
111
     * @return array
112
     */
113 10
    public function getEnvironmentData(string $key) : array
114
    {
115 10
        if (!isset($this->environmentData[$key])) {
116 1
            throw new InvalidArgumentException(sprintf('The "%s" is not a valid environment key.', $key));
117
        }
118
119 10
        return $this->environmentData[$key];
120
    }
121
122
    /**
123
     * Gets the client IP address.
124
     *
125
     * @return string
126
     */
127 1
    public function getClientIp() : string
128
    {
129 1
        $ipAddress = '';
130
131 1
        if (!empty($this->environmentData['SERVER']['HTTP_X_FORWARDED_FOR'])) {
132 1
            $ipAddress = $this->environmentData['SERVER']['HTTP_X_FORWARDED_FOR'];
133 1
        } elseif (!empty($this->environmentData['SERVER']['REMOTE_ADDR'])) {
134 1
            $ipAddress = $this->environmentData['SERVER']['REMOTE_ADDR'];
135
        }
136
137 1
        return (string) $ipAddress;
138
    }
139
140
    /**
141
     * Parses server data and tries to set domain information.
142
     *
143
     * @throws Exception
144
     * @return ServiceAdapter
145
     */
146 6
    private function setDomain() : ServiceAdapter
147
    {
148 6
        $this->setAdapterOptions();
149
150
        /** @var Result $domainParts */
151 6
        $domainParts = $this->domainAdapter->parse($this->url);
152
153 6
        if (empty($domainParts->getSuffix())) {
154 1
            throw new Exception('This application does not support IP access');
155
        }
156
157 5
        $this->checkSubdomain($domainParts);
158
159 5
        $this->subDomain = $domainParts->getSubdomain();
160 5
        $this->mainDomain = $domainParts->getHostname().'.'.$domainParts->getSuffix();
161 5
        $this->applicationDomain = $domainParts->getFullHost();
162
163 5
        return $this;
164
    }
165
166
    /**
167
     * Set some adapter specific options.
168
     *
169
     * @return int
170
     *
171
     * @codeCoverageIgnore - don't test third party library
172
     */
173
    private function setAdapterOptions() : int
174
    {
175
        try {
176
            if (!defined('PHPUNIT_WEBHEMI_TESTSUITE') && 'dev' == getenv('APPLICATION_ENV')) {
177
                $this->domainAdapter->setExtractionMode(Extract::MODE_ALLOW_NOT_EXISTING_SUFFIXES);
178
            }
179
        } catch (\Throwable $exception) {
180
            return $exception->getCode();
181
        }
182
183
        return 0;
184
    }
185
186
    /**
187
     * Checks whether the subdomain exists, and rediretcs if no.
188
     *
189
     * @param Result $domainParts
190
     *
191
     * @codeCoverageIgnore - don't test redirect
192
     */
193
    private function checkSubdomain(Result $domainParts) : void
194
    {
195
        // Redirecting to www when no subdomain is present
196
        if (!defined('PHPUNIT_WEBHEMI_TESTSUITE') && empty($domainParts->getSubdomain())) {
197
            $schema = 'http'.($this->isSecuredApplication() ? 's' : '').'://';
198
            $uri = $this->environmentData['SERVER']['REQUEST_URI'];
199
            header('Location: '.$schema.'www.'.$domainParts->getFullHost().$uri);
200
            exit;
201
        }
202
    }
203
204
    /**
205
     * Sets application related data.
206
     *
207
     * @throws Exception
208
     * @return ServiceAdapter
209
     */
210 5
    private function setApplication() : ServiceAdapter
211
    {
212
        // @codeCoverageIgnoreStart
213
        if (!isset($this->applicationDomain)) {
214
            // For safety purposes only, But it can't happen unless somebody change/overwrite the constructor.
215
            throw new Exception('Domain is not set');
216
        }
217
        // @codeCoverageIgnoreEnd
218
219 5
        $urlParts = parse_url($this->url);
220 5
        list($subDirectory) = explode('/', ltrim($urlParts['path'], '/'), 2);
221
222 5
        $applications = $this->configuration->toArray();
223 5
        $aplicationNames = array_keys($applications);
224 5
        $selectedApplication = $this->getSelectedApplicationName($aplicationNames, $subDirectory);
225
226 5
        $applicationData = $applications[$selectedApplication];
227
228 5
        $this->selectedModule = $applicationData['module'] ?? self::DEFAULT_MODULE;
229 5
        $this->selectedApplication = $selectedApplication;
230 5
        $this->selectedTheme = $applicationData['theme'] ?? self::DEFAULT_THEME;
231 5
        $this->selectedApplicationUri = $applicationData['type'] == self::APPLICATION_TYPE_DIRECTORY
232 1
            ? '/'.$subDirectory
233 4
            : '/';
234
235
        // Final check for config and resources.
236 5
        if ($this->selectedTheme !== self::DEFAULT_THEME) {
237 1
            $this->selectedThemeResourcePath = '/resources/vendor_themes/'.$this->selectedTheme;
238
        }
239
240 5
        return $this;
241
    }
242
243
    /**
244
     * Gets the selected application's name.
245
     *
246
     * @param array $aplicationNames
247
     * @param string $subDirectory
248
     * @return string
249
     */
250 5
    private function getSelectedApplicationName(array $aplicationNames, string $subDirectory) : string
251
    {
252 5
        $selectedApplication = self::DEFAULT_APPLICATION;
253
254
        /** @var string $applicationName */
255 5
        foreach ($aplicationNames as $applicationName) {
256 5
            if ($this->checkDirectoryIsValid($applicationName, $subDirectory)
257 5
                || $this->checkDomainIsValid($applicationName)
258
            ) {
259 5
                $selectedApplication = (string) $applicationName;
260 5
                break;
261
            }
262
        }
263
264 5
        return $selectedApplication;
265
    }
266
267
    /**
268
     * Checks from type, path it the current URI segment is valid.
269
     *
270
     * @param string $applicationName
271
     * @param string $subDirectory
272
     * @return bool
273
     */
274 5
    private function checkDirectoryIsValid(string $applicationName, string $subDirectory) : bool
275
    {
276 5
        $applications = $this->configuration->toArray();
277 5
        $applicationData = $applications[$applicationName];
278
279 5
        return $applicationName != 'website'
280 5
            && $this->applicationDomain == $applicationData['domain']
281 5
            && !empty($subDirectory)
282 5
            && $applicationData['type'] == self::APPLICATION_TYPE_DIRECTORY
283 5
            && $applicationData['path'] == '/'.$subDirectory;
284
    }
285
286
    /**
287
     * Checks from type and path if the domain is valid. If so, it sets the $subDirectory to the default.
288
     *
289
     * @param string $applicationName
290
     * @return bool
291
     */
292 4
    private function checkDomainIsValid(string $applicationName) : bool
293
    {
294 4
        $applications = $this->configuration->toArray();
295 4
        $applicationData = $applications[$applicationName];
296
297 4
        return $this->applicationDomain == $applicationData['domain']
298 4
            && $applicationData['type'] == self::APPLICATION_TYPE_DOMAIN;
299
    }
300
}
301