Completed
Push — master ( 0bd33c...f1035c )
by Damian
07:12
created

MultiDomainDomain::hasURL()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 12
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 1
Metric Value
c 1
b 0
f 1
dl 0
loc 12
rs 9.4285
cc 3
eloc 7
nc 3
nop 1
1
<?php
2
3
namespace SilverStripe\MultiDomain;
4
5
use Exception;
6
use SilverStripe\Control\Controller;
7
use SilverStripe\Core\Config\Config;
8
use SilverStripe\Core\Object;
9
use SilverStripe\MultiDomain\MultiDomain;
10
11
/**
12
 * Class definition for an object representing a configured domain
13
 *
14
 * @package  silverstripe-multi-domain
15
 * @author  Aaron Carlino <[email protected]>
16
 */
17
class MultiDomainDomain extends Object
18
{
19
    /**
20
     * The hostname of the domain, e.g. silverstripe.org
21
     * @var string
22
     */
23
    protected $hostname;
24
25
    /**
26
     * The path that the hostname resolves to
27
     * @var string
28
     */
29
    protected $url;
30
31
    /**
32
     * The identifier of the domain, e.g. 'org','com'
33
     * @var string
34
     */
35
    protected $key;
36
37
    /**
38
     * Paths that are allowed to be accessed on the primary domain
39
     * @var array
40
     */
41
    protected $allowedPaths;
42
43
    /**
44
     * Paths that are forced from the primary domain into a vanity one,
45
     * outside the resolves_to path
46
     *
47
     * @var array
48
     */
49
    protected $forcedPaths;
50
51
    /**
52
     * The request URI
53
     *
54
     * @var string
55
     */
56
    protected $requestUri;
57
58
    /**
59
     * The request HTTP HOST
60
     *
61
     * @var string
62
     */
63
    protected $httpHost;
64
65
    /**
66
     * Constructor. Takes a key for the domain and its array of settings from the config
67
     * @param string $key
68
     * @param array $config
69
     */
70
    public function __construct($key, $config)
71
    {
72
        $this->key = $key;
73
        $this->hostname = $config['hostname'];
74
        $this->url = isset($config['resolves_to']) ? $config['resolves_to'] : null;
75
76
        $globalAllowed = (array) Config::inst()->get(MultiDomain::class, 'allow');
77
        $globalForced = (array) Config::inst()->get(MultiDomain::class, 'force');
78
        $myAllowed = isset($config['allow']) ? $config['allow'] : array ();
79
        $myForced = isset($config['force']) ? $config['force'] : array ();
80
        $this->allowedPaths = array_merge($globalAllowed, $myAllowed);
81
        $this->forcedPaths = array_merge($globalForced, $myForced);
82
83
        parent::__construct();
84
    }
85
86
    /**
87
     * Gets the hostname for the domain
88
     * @return string
89
     */
90
    public function getHostname()
91
    {
92
        return defined($this->hostname) ? constant($this->hostname) : $this->hostname;
93
    }
94
95
    /**
96
     * Gets the path that the hostname resolves to
97
     * @return string
98
     */
99
    public function getURL()
100
    {
101
        return $this->url;
102
    }
103
104
    /**
105
     * Returns true if the domain is currently in the HTTP_HOST
106
     * @return boolean
107
     */
108
    public function isActive()
109
    {
110
        if ($this->isAllowedPath($this->getRequestUri())) {
111
            return false;
112
        }
113
114
        $currentHost = $this->getHttpHost();
115
        if (strpos(':', $currentHost) !== false) {
116
            list($currentHost, $currentPort) = explode(':', $currentHost, 2);
0 ignored issues
show
Unused Code introduced by
The assignment to $currentPort is unused. Consider omitting it like so list($first,,$third).

This checks looks for assignemnts to variables using the list(...) function, where not all assigned variables are subsequently used.

Consider the following code example.

<?php

function returnThreeValues() {
    return array('a', 'b', 'c');
}

list($a, $b, $c) = returnThreeValues();

print $a . " - " . $c;

Only the variables $a and $c are used. There was no need to assign $b.

Instead, the list call could have been.

list($a,, $c) = returnThreeValues();
Loading history...
117
        }
118
119
        $allow_subdomains = MultiDomain::config()->allow_subdomains;
120
        $hostname = $this->getHostname();
121
122
        return $allow_subdomains
123
            ? (bool) preg_match('/(\.|^)'.$hostname.'$/', $currentHost)
124
            : ($currentHost == $hostname);
125
    }
126
127
    /**
128
     * Returns true if this domain is the primary domain
129
     * @return boolean
130
     */
131
    public function isPrimary()
132
    {
133
        return $this->key === MultiDomain::KEY_PRIMARY;
134
    }
135
136
    /**
137
     * Gets the native URL for a vanity domain, e.g. /partners/ for .com
138
     * returns /company/partners when .com is mapped to /company/.
139
     *
140
     * @param  string $url
141
     * @return string
142
     */
143
    public function getNativeURL($url)
144
    {
145
        if ($this->isPrimary()) {
146
            throw new Exception("Cannot convert a native URL on the primary domain");
147
        }
148
149
        if ($this->isAllowedPath($url) || $this->isForcedPath($url)) {
150
            return $url;
151
        }
152
153
        return Controller::join_links($this->getURL(), $url);
154
    }
155
156
    /**
157
     * Gets the vanity URL given a native URL. /company/partners returns /partners/
158
     * when .com is mapped to /company/.
159
     *
160
     * @param  string $url
161
     * @return string
162
     */
163
    public function getVanityURL($url)
164
    {
165
        if ($this->isPrimary() || $this->isAllowedPath($url)) {
166
            return $url;
167
        }
168
169
        $domainUrl = str_replace('/', '\/', $this->getURL());
170
        return preg_replace('/^\/?' . $domainUrl . '\//', '', $url);
171
    }
172
173
    /**
174
     * Return true if this domain contains the given URL
175
     * @param  string  $url
176
     * @return boolean
177
     */
178
    public function hasURL($url)
179
    {
180
        if ($this->isForcedPath($url)) {
181
            return true;
182
        }
183
        $domainBaseURL = trim($this->getURL(), '/');
184
        if (preg_match('/^'.$domainBaseURL.'/', $url)) {
185
            return true;
186
        }
187
188
        return false;
189
    }
190
191
    /**
192
     * Returns the key/identifier for this domain
193
     *
194
     * @return string
195
     */
196
    public function getKey()
197
    {
198
        return $this->key;
199
    }
200
201
    /**
202
     * Set the request URI
203
     *
204
     * @param  string $requestUri
205
     * @return $this
206
     */
207
    public function setRequestUri($requestUri)
208
    {
209
        $this->requestUri = (string) $requestUri;
210
        return $this;
211
    }
212
213
    /**
214
     * Return the current request URI, defaulting to retrieving it from the $_SERVER superglobal
215
     *
216
     * @return string
217
     */
218
    public function getRequestUri()
0 ignored issues
show
Coding Style introduced by
getRequestUri uses the super-global variable $_SERVER which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

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

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
219
    {
220
        return $this->requestUri ?: $_SERVER['REQUEST_URI'];
221
    }
222
223
    /**
224
     * Set the HTTP host in the request
225
     *
226
     * @param  string $httpHost
227
     * @return $this
228
     */
229
    public function setHttpHost($httpHost)
230
    {
231
        $this->httpHost = (string) $httpHost;
232
        return $this;
233
    }
234
235
    /**
236
     * Return the current HTTP host, defaulting to retrieving it from the $_SERVER superglobal
237
     *
238
     * @return string
239
     */
240
    public function getHttpHost()
0 ignored issues
show
Coding Style introduced by
getHttpHost uses the super-global variable $_SERVER which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

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

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
241
    {
242
        return $this->httpHost ?: $_SERVER['HTTP_HOST'];
243
    }
244
245
    /**
246
     * Checks a given list of wildcard patterns to see if a path is allowed
247
     * @param  string  $url
248
     * @return boolean
249
     */
250
    protected function isAllowedPath($url)
251
    {
252
        return self::match_url($url, $this->allowedPaths);
253
    }
254
255
    /**
256
     * Checks a given list of wildcard patterns to see if a path is allowed
257
     * @param  string  $url
258
     * @return boolean
259
     */
260
    protected function isForcedPath($url)
261
    {
262
        return self::match_url($url, $this->forcedPaths);
263
    }
264
265
    /**
266
     * Matches a URL against a list of wildcard patterns
267
     * @param  string $url
268
     * @param  array $patterns
269
     * @return boolean
270
     */
271
    protected static function match_url($url, $patterns)
272
    {
273
        if (!is_array($patterns)) {
274
            return false;
275
        }
276
277
        $url = ltrim($url, '/');
278
        if (substr($url, -1) !== '/') {
279
            $url .= '/';
280
        }
281
282
        foreach ($patterns as $pattern) {
283
            if (fnmatch($pattern, $url)) {
284
                return true;
285
            }
286
        }
287
288
        return false;
289
    }
290
}
291