Passed
Push — master ( 7ed858...861abc )
by Gabor
02:38
created

ServiceAdapter::getEnvironmentData()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 2

Importance

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