Passed
Push — master ( a6d2ac...35bc93 )
by Gabor
05:20
created

EnvironmentManager::getClientIp()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 12
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 3

Importance

Changes 0
Metric Value
dl 0
loc 12
ccs 8
cts 8
cp 1
rs 9.4285
c 0
b 0
f 0
cc 3
eloc 7
nc 3
nop 0
crap 3
1
<?php
2
/**
3
 * WebHemi.
4
 *
5
 * PHP version 5.6
6
 *
7
 * @copyright 2012 - 2016 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
namespace WebHemi\Application;
13
14
use InvalidArgumentException;
15
use WebHemi\Config\ConfigInterface;
16
17
/**
18
 * Class EnvironmentManager.
19
 */
20
class EnvironmentManager
21
{
22
    const APPLICATION_TYPE_DIRECTORY = 'directory';
23
    const APPLICATION_TYPE_DOMAIN = 'domain';
24
25
    const COOKIE_AUTO_LOGIN_PREFIX = 'atln';
26
    const COOKIE_SESSION_PREFIX = 'atsn';
27
28
    const DEFAULT_APPLICATION = 'website';
29
    const DEFAULT_APPLICATION_URI = '/';
30
    const DEFAULT_MODULE = 'Website';
31
    const DEFAULT_THEME = 'default';
32
    const DEFAULT_THEME_RESOURCE_PATH = '/resources/default_theme';
33
34
    const SESSION_SALT = 'WebHemi';
35
36
    /** @var ConfigInterface */
37
    private $configuration;
38
    /** @var string */
39
    private $url;
40
    /** @var string */
41
    private $subDomain;
42
    /** @var string */
43
    private $mainDomain;
44
    /** @var string */
45
    private $applicationDomain;
46
    /** @var string */
47
    private $documentRoot;
48
    /** @var string */
49
    private $selectedModule;
50
    /** @var string */
51
    private $selectedApplication;
52
    /** @var string */
53
    private $selectedApplicationUri;
54
    /** @var string */
55
    private $selectedTheme;
56
    /** @var string */
57
    private $selectedThemeResourcePath;
58
    /** @var array  */
59
    private $environmentData;
60
    /** @var bool */
61
    private $isHttps;
62
63
    /**
64
     * ModuleManager constructor.
65
     *
66
     * @param ConfigInterface $configuration
67
     * @param array           $getData
68
     * @param array           $postData
69
     * @param array           $serverData
70
     * @param array           $cookieData
71
     * @param array           $filesData
72
     */
73 5
    public function __construct(
74
        ConfigInterface $configuration,
75
        array $getData,
76
        array $postData,
77
        array $serverData,
78
        array $cookieData,
79
        array $filesData
80
    ) {
81 5
        $this->configuration = $configuration->getConfig('applications');
82 5
        $this->documentRoot = realpath(__DIR__.'/../../../');
83
84 5
        if (isset($serverData['HTTP_REFERER'])) {
85 5
            $serverData['HTTP_REFERER'] = urldecode($serverData['HTTP_REFERER']);
86 5
        }
87
88 5
        $this->environmentData = [
89 5
            'GET'    => $getData,
90 5
            'POST'   => $postData,
91 5
            'SERVER' => $serverData,
92 5
            'COOKIE' => $cookieData,
93 5
            'FILES'  => $filesData,
94
        ];
95
96 5
        $this->isHttps = isset($this->environmentData['SERVER']['HTTPS']) && $this->environmentData['SERVER']['HTTPS'];
97 5
        $this->url = 'http'.($this->isHttps ? 's' : '').'://'
98 5
            .$this->environmentData['SERVER']['HTTP_HOST']
99 5
            .$this->environmentData['SERVER']['REQUEST_URI']; // contains also the query string
100
101 5
        $this->selectedModule = self::DEFAULT_MODULE;
102 5
        $this->selectedApplication = self::DEFAULT_APPLICATION;
103 5
        $this->selectedTheme = self::DEFAULT_THEME;
104 5
        $this->selectedThemeResourcePath = self::DEFAULT_THEME_RESOURCE_PATH;
105 5
        $this->selectedApplicationUri = self::DEFAULT_APPLICATION_URI;
106
107 5
        $this->setDomain()
108 5
            ->selectModuleApplicationAndTheme();
109 5
    }
110
111
    /**
112
     * Gets the document root path.
113
     *
114
     * @return string
115
     */
116 1
    public function getDocumentRoot()
117
    {
118 1
        return $this->documentRoot;
119
    }
120
121
    /**
122
     * Gets the application domain.
123
     *
124
     * @return string
125
     */
126 1
    public function getApplicationDomain()
127
    {
128 1
        return $this->applicationDomain;
129
    }
130
131
    /**
132
     * Gets the application SSL status.
133
     *
134
     * @return bool
135
     */
136 1
    public function isSecuredApplication()
137
    {
138 1
        return $this->isHttps;
139
    }
140
141
    /**
142
     * Gets the selected application.
143
     *
144
     * @return string
145
     */
146 4
    public function getSelectedApplication()
147
    {
148 4
        return $this->selectedApplication;
149
    }
150
151
    /**
152
     * Get the URI path for the selected application. Required for the RouterAdapter to work with directory-based
153
     * applications correctly.
154
     *
155
     * @return string
156
     */
157 4
    public function getSelectedApplicationUri()
158
    {
159 4
        return $this->selectedApplicationUri;
160
    }
161
162
    /**
163
     * Gets the request URI
164
     *
165
     * @return string
166
     */
167 1
    public function getRequestUri()
168
    {
169 1
        return $this->environmentData['SERVER']['REQUEST_URI'];
170
    }
171
172
    /**
173
     * Gets the selected module.
174
     *
175
     * @return string
176
     */
177 1
    public function getSelectedModule()
178
    {
179 1
        return $this->selectedModule;
180
    }
181
182
    /**
183
     * Gets the selected theme.
184
     *
185
     * @return string
186
     */
187 1
    public function getSelectedTheme()
188
    {
189 1
        return $this->selectedTheme;
190
    }
191
192
    /**
193
     * Gets the resource path for the selected theme.
194
     *
195
     * @return string
196
     */
197 2
    public function getResourcePath()
198
    {
199 2
        return $this->selectedThemeResourcePath;
200
    }
201
202
    /**
203
     * Gets environment data.
204
     *
205
     * @param string $key
206
     *
207
     * @return array
208
     */
209 1
    public function getEnvironmentData($key)
210
    {
211 1
        if (!isset($this->environmentData[$key])) {
212 1
            throw new InvalidArgumentException(sprintf('The "%s" is not a valid environment key.', $key));
213
        }
214
215 1
        return $this->environmentData[$key];
216
    }
217
218
    /**
219
     * Gets the client IP address.
220
     *
221
     * @return string
222
     */
223 1
    public function getClientIp()
224
    {
225 1
        $ipAddress = '';
226
227 1
        if (!empty($this->environmentData['SERVER']['HTTP_X_FORWARDED_FOR'])) {
228 1
            $ipAddress = $this->environmentData['SERVER']['HTTP_X_FORWARDED_FOR'];
229 1
        } elseif (!empty($this->environmentData['SERVER']['REMOTE_ADDR'])) {
230 1
            $ipAddress = $this->environmentData['SERVER']['REMOTE_ADDR'];
231 1
        }
232
233 1
        return (string) $ipAddress;
234
    }
235
    /**
236
     * Parses server data and tries to set domain information.
237
     *
238
     * @return EnvironmentManager
239
     */
240 5
    private function setDomain()
241
    {
242 5
        $domain = $this->environmentData['SERVER']['SERVER_NAME'];
243 5
        $subDomain = '';
244 5
        $urlParts = parse_url($this->url);
245
246
        // If the host is not an IP address, then check the sub-domain-based module names too
247 5
        if (!preg_match(
248 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])$/',
249 5
            $urlParts['host']
250 5
        )) {
251 5
            $domainParts = explode('.', $urlParts['host']);
252
            // @todo find out how to support complex TLDs like `.co.uk` or `.com.br`
253 5
            $tld = array_pop($domainParts);
254 5
            $domain = array_pop($domainParts).'.'.$tld;
255
            // the rest is the sub-domain
256 5
            $subDomain = implode('.', $domainParts);
257 5
        }
258
259
        // If no sub-domain presents, then it should be handled as the default sub-domain set for the 'website'
260 5
        if (empty($subDomain)) {
261 3
            $subDomain = $this->configuration->getData('website/path');
262 3
        }
263
264 5
        $this->subDomain = $subDomain;
265 5
        $this->mainDomain = $domain;
266 5
        $this->applicationDomain = $this->subDomain.'.'.$this->mainDomain;
267
268
        // Redirecting when the app domain is not equal to the server data
269
        // @codeCoverageIgnoreStart
270
        if (!defined('PHPUNIT_WEBHEMI_TESTSUITE')
271
            && $this->environmentData['SERVER']['SERVER_NAME'] != $this->applicationDomain
272
        ) {
273
            $schema = 'http'.($this->isSecuredApplication() ? 's' : '').'://';
274
            $uri = $this->environmentData['SERVER']['REQUEST_URI'];
275
            header('Location: '.$schema.$this->applicationDomain.$uri);
276
            exit;
0 ignored issues
show
Coding Style Compatibility introduced by
The method setDomain() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
277
        }
278
        // @codeCoverageIgnoreEnd
279
280 5
        return $this;
281
    }
282
283
    /**
284
     * From the parsed domain data, selects the application, module and theme.
285
     *
286
     * @return EnvironmentManager
287
     */
288 5
    private function selectModuleApplicationAndTheme()
289
    {
290 5
        $urlParts = parse_url($this->url);
291 5
        $applications = $this->configuration->toArray();
292
293
        // Only the first segment is important (if exists).
294 5
        list($subDirectory) = explode('/', ltrim($urlParts['path'], '/'), 2);
295
296
        $applicationDataFixture = [
297 5
            'type' => self::APPLICATION_TYPE_DIRECTORY,
298 5
            'module' => self::DEFAULT_MODULE,
299 5
            'theme' => self::DEFAULT_THEME,
300 5
        ];
301
302
        // Run through the available application-modules to validate and find active module
303 5
        foreach ($applications as $applicationName => $applicationData) {
304
            // Don't risk, fix.
305 5
            $applicationData = array_merge($applicationDataFixture, $applicationData);
306
307 5
            if ($this->checkDirectoryIsValid($applicationName, $applicationData, $subDirectory)
308 4
                || $this->checkDomainIsValid($applicationName, $applicationData, $subDirectory)
309 5
            ) {
310 5
                $this->selectedModule = $applicationData['module'];
311 5
                $this->selectedApplication = (string) $applicationName;
312 5
                $this->selectedTheme = $applicationData['theme'];
313
314 5
                $this->selectedApplicationUri = '/'.$subDirectory;
315 5
                break;
316
            }
317 5
        }
318
319
        // Final check for config and resources.
320 5
        if ($this->selectedTheme !== self::DEFAULT_THEME) {
321 1
            $this->selectedThemeResourcePath = '/resources/vendor_themes/'.$this->selectedTheme;
322 1
        }
323
324 5
        return $this;
325
    }
326
327
    /**
328
     * Checks from type, path it the current URI segment is valid.
329
     *
330
     * @param string $applicationName
331
     * @param array  $applicationData
332
     * @param string $subDirectory
333
     *
334
     * @return bool
335
     */
336 5
    private function checkDirectoryIsValid($applicationName, $applicationData, $subDirectory)
337
    {
338 5
        return $this->subDomain == $this->configuration->getData('website/path')
339 5
            && $applicationName != 'website'
340 5
            && !empty($subDirectory)
341 5
            && $applicationData['type'] == self::APPLICATION_TYPE_DIRECTORY
342 5
            && $applicationData['path'] == $subDirectory;
343
    }
344
345
    /**
346
     * Checks from type and path if the domain is valid. If so, it sets the $subDirectory to the default.
347
     *
348
     * @param string $applicationName
349
     * @param array  $applicationData
350
     * @param string $subDirectory
351
     *
352
     * @return bool
353
     */
354 4
    private function checkDomainIsValid($applicationName, $applicationData, &$subDirectory)
355
    {
356
        $isSubdomain = $applicationName == 'website'
357 4
            || (
358 3
                $this->subDomain != $this->configuration->getData('website/path')
359 3
                && $applicationData['type'] == self::APPLICATION_TYPE_DOMAIN
360 3
                && $applicationData['path'] == $this->subDomain
361 4
            );
362
363
        // If this method get called and will return TRUE, it means the $subDirectory paramtere will be used only for
364
        // setting the right selectedApplicationUri. To avoid complexity, we change it here. Doesn't matter.
365 4
        if ($isSubdomain) {
366 4
            $subDirectory = '';
367 4
        }
368
369 4
        return $isSubdomain;
370
    }
371
}
372