Passed
Push — master ( 22ad80...fa0027 )
by Gabor
08:44
created

ServiceAdapter::checkDomainIsValid()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 8
ccs 5
cts 5
cp 1
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 5
nc 2
nop 1
crap 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\ServiceInterface;
22
23
/**
24
 * Class ServiceAdapter.
25
 * @SuppressWarnings(PHPMD.TooManyFields)
26
 */
27
class ServiceAdapter implements ServiceInterface
28
{
29
    /** @var ConfigurationInterface */
30
    protected $configuration;
31
    /** @var Extract */
32
    protected $domainAdapter;
33
    /** @var string */
34
    protected $url;
35
    /** @var string */
36
    protected $subDomain;
37
    /** @var string */
38
    protected $mainDomain;
39
    /** @var string */
40
    protected $applicationDomain;
41
    /** @var string */
42
    protected $documentRoot;
43
    /** @var string */
44
    protected $applicationRoot;
45
    /** @var string */
46
    protected $selectedModule;
47
    /** @var string */
48
    protected $selectedApplication;
49
    /** @var string */
50
    protected $selectedApplicationUri;
51
    /** @var string */
52
    protected $selectedTheme;
53
    /** @var string */
54
    protected $selectedThemeResourcePath;
55
    /** @var array  */
56
    protected $environmentData;
57
    /** @var bool */
58
    protected $isHttps;
59
    /** @var array */
60
    protected $options = [];
61
62
    /**
63
     * ServiceAdapter constructor.
64
     *
65
     * @param ConfigurationInterface $configuration
66
     * @param array                  $getData
67
     * @param array                  $postData
68
     * @param array                  $serverData
69
     * @param array                  $cookieData
70
     * @param array                  $filesData
71
     * @param array                  $optionsData
72
     * @throws Exception
73
     */
74 5
    public function __construct(
75
        ConfigurationInterface $configuration,
76
        array $getData,
77
        array $postData,
78
        array $serverData,
79
        array $cookieData,
80
        array $filesData,
81
        array $optionsData
82
    ) {
83 5
        $this->configuration = $configuration->getConfig('applications');
84 5
        $this->domainAdapter = new Extract();
85 5
        $this->applicationRoot = realpath(__DIR__.'/../../../../../');
86
        // In case when the backend sources are out of the document root.
87 5
        $this->documentRoot = realpath($this->applicationRoot.'/');
88 5
        $this->options = $optionsData;
89
90 5
        if (isset($serverData['HTTP_REFERER'])) {
91 5
            $serverData['HTTP_REFERER'] = urldecode($serverData['HTTP_REFERER']);
92
        }
93
94 5
        $this->environmentData = [
95 5
            'GET'    => $getData,
96 5
            'POST'   => $postData,
97 5
            'SERVER' => $serverData,
98 5
            'COOKIE' => $cookieData,
99 5
            'FILES'  => $filesData,
100
        ];
101
102 5
        $this->isHttps = isset($this->environmentData['SERVER']['HTTPS']) && $this->environmentData['SERVER']['HTTPS'];
103 5
        $this->url = 'http'.($this->isHttps ? 's' : '').'://'
104 5
            .$this->environmentData['SERVER']['HTTP_HOST']
105 5
            .$this->environmentData['SERVER']['REQUEST_URI']; // contains also the query string
106
107 5
        $this->selectedModule = self::DEFAULT_MODULE;
108 5
        $this->selectedApplication = self::DEFAULT_APPLICATION;
109 5
        $this->selectedTheme = self::DEFAULT_THEME;
110 5
        $this->selectedThemeResourcePath = self::DEFAULT_THEME_RESOURCE_PATH;
111 5
        $this->selectedApplicationUri = self::DEFAULT_APPLICATION_URI;
112
113 5
        $this->setDomain()
114 5
            ->setApplication();
115 5
    }
116
117
    /**
118
     * Gets the document root path.
119
     *
120
     * @return string
121
     */
122 11
    public function getDocumentRoot() : string
123
    {
124 11
        return $this->documentRoot;
125
    }
126
127
    /**
128
     * Gets the application path.
129
     *
130
     * @return string
131
     */
132 1
    public function getApplicationRoot(): string
133
    {
134 1
        return $this->applicationRoot;
135
    }
136
137
    /**
138
     * Gets the application domain.
139
     *
140
     * @return string
141
     */
142 9
    public function getApplicationDomain() : string
143
    {
144 9
        return $this->applicationDomain;
145
    }
146
147
    /**
148
     * Gets the application SSL status.
149
     *
150
     * @return bool
151
     */
152 10
    public function isSecuredApplication() : bool
153
    {
154 10
        return $this->isHttps;
155
    }
156
157
    /**
158
     * Gets the selected application.
159
     *
160
     * @return string
161
     */
162 17
    public function getSelectedApplication() : string
163
    {
164 17
        return $this->selectedApplication;
165
    }
166
167
    /**
168
     * Get the URI path for the selected application. Required for the RouterAdapter to work with directory-based
169
     * applications correctly.
170
     *
171
     * @return string
172
     */
173 17
    public function getSelectedApplicationUri() : string
174
    {
175 17
        return $this->selectedApplicationUri;
176
    }
177
178
    /**
179
     * Gets the request URI
180
     *
181
     * @return string
182
     */
183 1
    public function getRequestUri() : string
184
    {
185 1
        return rtrim($this->environmentData['SERVER']['REQUEST_URI'], '/');
186
    }
187
188
    /**
189
     * Gets the selected module.
190
     *
191
     * @return string
192
     */
193 17
    public function getSelectedModule() : string
194
    {
195 17
        return $this->selectedModule;
196
    }
197
198
    /**
199
     * Gets the selected theme.
200
     *
201
     * @return string
202
     */
203 11
    public function getSelectedTheme() : string
204
    {
205 11
        return $this->selectedTheme;
206
    }
207
208
    /**
209
     * Gets the resource path for the selected theme.
210
     *
211
     * @return string
212
     */
213 2
    public function getResourcePath() : string
214
    {
215 2
        return $this->selectedThemeResourcePath;
216
    }
217
218
    /**
219
     * Gets the request method.
220
     *
221
     * @return string
222
     */
223 1
    public function getRequestMethod(): string
224
    {
225 1
        return $this->environmentData['SERVER']['REQUEST_METHOD'] ?? 'GET';
226
    }
227
228
    /**
229
     * Gets environment data.
230
     *
231
     * @param string $key
232
     * @return array
233
     */
234 10
    public function getEnvironmentData(string $key) : array
235
    {
236 10
        if (!isset($this->environmentData[$key])) {
237 1
            throw new InvalidArgumentException(sprintf('The "%s" is not a valid environment key.', $key));
238
        }
239
240 10
        return $this->environmentData[$key];
241
    }
242
243
    /**
244
     * Gets the client IP address.
245
     *
246
     * @return string
247
     */
248 1
    public function getClientIp() : string
249
    {
250 1
        $ipAddress = '';
251
252 1
        if (!empty($this->environmentData['SERVER']['HTTP_X_FORWARDED_FOR'])) {
253 1
            $ipAddress = $this->environmentData['SERVER']['HTTP_X_FORWARDED_FOR'];
254 1
        } elseif (!empty($this->environmentData['SERVER']['REMOTE_ADDR'])) {
255 1
            $ipAddress = $this->environmentData['SERVER']['REMOTE_ADDR'];
256
        }
257
258 1
        return (string) $ipAddress;
259
    }
260
261
    /**
262
     * Gets the execution parameters (CLI).
263
     *
264
     * @return array
265
     */
266 1
    public function getOptions() : array
267
    {
268 1
        return $this->options;
269
    }
270
271
    /**
272
     * Parses server data and tries to set domain information.
273
     *
274
     * @throws Exception
275
     * @return ServiceAdapter
276
     */
277 5
    private function setDomain() : ServiceAdapter
278
    {
279 5
        if ('dev' == getenv('APPLICATION_ENV')) {
280
            $this->domainAdapter->setExtractionMode(Extract::MODE_ALLOW_NOT_EXISTING_SUFFIXES);
281
        }
282
283
        /** @var Result $domainParts */
284 5
        $domainParts = $this->domainAdapter->parse($this->url);
285
286 5
        if (empty($domainParts->getSuffix())) {
287
            throw new Exception('This application does not support IP access');
288
        }
289
290
        // Redirecting to www when no subdomain is present
291
        // @codeCoverageIgnoreStart
292
        if (!defined('PHPUNIT_WEBHEMI_TESTSUITE') && empty($domainParts->getSubdomain())) {
293
            $schema = 'http'.($this->isSecuredApplication() ? 's' : '').'://';
294
            $uri = $this->environmentData['SERVER']['REQUEST_URI'];
295
            header('Location: '.$schema.'www'.$domainParts->getFullHost().$uri);
296
            exit;
297
        }
298
        // @codeCoverageIgnoreEnd
299
300 5
        $this->subDomain = $domainParts->getSubdomain();
301 5
        $this->mainDomain = $domainParts->getHostname().'.'.$domainParts->getSuffix();
302 5
        $this->applicationDomain = $domainParts->getFullHost();
303
304 5
        return $this;
305
    }
306
307
    /**
308
     * Sets application related data.
309
     *
310
     * @throws Exception
311
     * @return ServiceAdapter
312
     */
313 5
    private function setApplication() : ServiceAdapter
314
    {
315
        // for safety purposes
316 5
        if (!isset($this->applicationDomain)) {
317
            $this->setDomain();
318
        }
319
320 5
        $urlParts = parse_url($this->url);
321 5
        list($subDirectory) = explode('/', ltrim($urlParts['path'], '/'), 2);
322
323 5
        $applications = $this->configuration->toArray();
324 5
        $aplicationNames = array_keys($applications);
325 5
        $selectedApplication = self::DEFAULT_APPLICATION;
326
327 5
        foreach ($aplicationNames as $applicationName) {
328 5
            if ($this->checkDirectoryIsValid($applicationName, $subDirectory)
329 4
                || $this->checkDomainIsValid($applicationName)
330
            ) {
331 5
                $selectedApplication = $applicationName;
332 5
                break;
333
            }
334
        }
335
336 5
        $applicationData = $applications[$selectedApplication];
337
338 5
        $this->selectedModule = $applicationData['module'] ?? self::DEFAULT_MODULE;
339 5
        $this->selectedApplication = $selectedApplication;
0 ignored issues
show
Documentation Bug introduced by
It seems like $selectedApplication can also be of type integer. However, the property $selectedApplication is declared as type string. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
340 5
        $this->selectedTheme = $applicationData['theme'] ?? self::DEFAULT_THEME;
341 5
        $this->selectedApplicationUri = $applicationData['type'] == self::APPLICATION_TYPE_DIRECTORY
342 1
            ? '/'.$subDirectory
343 4
            : '/';
344
345
        // Final check for config and resources.
346 5
        if ($this->selectedTheme !== self::DEFAULT_THEME) {
347 1
            $this->selectedThemeResourcePath = '/resources/vendor_themes/'.$this->selectedTheme;
348
        }
349
350 5
        return $this;
351
    }
352
353
    /**
354
     * Checks from type, path it the current URI segment is valid.
355
     *
356
     * @param string $applicationName
357
     * @param string $subDirectory
358
     * @return bool
359
     */
360 5
    private function checkDirectoryIsValid(string $applicationName, string $subDirectory) : bool
361
    {
362 5
        $applications = $this->configuration->toArray();
363 5
        $applicationData = $applications[$applicationName];
364
365 5
        return $applicationName != 'website'
366 4
            && $this->applicationDomain == $applicationData['domain']
367 4
            && !empty($subDirectory)
368 3
            && $applicationData['type'] == self::APPLICATION_TYPE_DIRECTORY
369 5
            && $applicationData['path'] == '/'.$subDirectory;
370
    }
371
372
    /**
373
     * Checks from type and path if the domain is valid. If so, it sets the $subDirectory to the default.
374
     *
375
     * @param string $applicationName
376
     * @return bool
377
     */
378 4
    private function checkDomainIsValid(string $applicationName) : bool
379
    {
380 4
        $applications = $this->configuration->toArray();
381 4
        $applicationData = $applications[$applicationName];
382
383 4
        return $this->applicationDomain == $applicationData['domain']
384 4
            && $applicationData['type'] == self::APPLICATION_TYPE_DOMAIN;
385
    }
386
}
387