Completed
Push — master ( a7941f...9cc640 )
by Damian
11s
created

MultiDomainDomain::getHttpHost()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 2
eloc 2
nc 2
nop 0
1
<?php
2
3
/**
4
 * Class definition for an object representing a configured domain
5
 *
6
 * @package  silverstripe-multi-domain
7
 * @author  Aaron Carlino <[email protected]>
8
 */
9
class MultiDomainDomain extends Object {
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
10
11
	/**
12
	 * The hostname of the domain, e.g. silverstripe.org
13
	 * @var string
14
	 */
15
	protected $hostname;
16
17
	/**
18
	 * The path that the hostname resolves to
19
	 * @var string
20
	 */
21
	protected $url;
22
23
	/**
24
	 * The identifier of the domain, e.g. 'org','com'
25
	 * @var string
26
	 */
27
	protected $key;
28
29
	/**
30
	 * Paths that are allowed to be accessed on the primary domain
31
	 * @var array
32
	 */
33
	protected $allowedPaths;
34
35
	/**
36
	 * Paths that are forced from the primary domain into a vanity one,
37
	 * outside the resolves_to path
38
	 *
39
	 * @var array
40
	 */
41
	protected $forcedPaths;
42
43
    /**
44
     * The request URI
45
     *
46
     * @var string
47
     */
48
    protected $requestUri;
49
50
    /**
51
     * The request HTTP HOST
52
     *
53
     * @var string
54
     */
55
    protected $httpHost;
56
57
	/**
58
	 * Constructor. Takes a key for the domain and its array of settings from the config
59
	 * @param string $key
60
	 * @param array $config
61
	 */
62
	public function __construct($key, $config) {
63
		$this->key = $key;
64
		$this->hostname = $config['hostname'];
65
		$this->url = isset($config['resolves_to']) ? $config['resolves_to'] : null;
66
67
		$globalAllowed = (array) Config::inst()->get('MultiDomain','allow');
68
		$globalForced = (array) Config::inst()->get('MultiDomain','force');
69
		$myAllowed = isset($config['allow']) ? $config['allow'] : array ();
70
		$myForced = isset($config['force']) ? $config['force'] : array ();
71
		$this->allowedPaths = array_merge($globalAllowed, $myAllowed);
72
		$this->forcedPaths = array_merge($globalForced, $myForced);
73
74
		parent::__construct();
75
	}
76
77
	/**
78
	 * Gets the hostname for the domain
79
	 * @return string
80
	 */
81
	public function getHostname() {
82
		return defined($this->hostname) ? constant($this->hostname) : $this->hostname;
83
	}
84
85
	/**
86
	 * Gets the path that the hostname resolves to
87
	 * @return string
88
	 */
89
	public function getURL() {
90
		return $this->url;
91
	}
92
93
	/**
94
	 * Returns true if the domain is currently in the HTTP_HOST
95
	 * @return boolean
96
	 */
97
	public function isActive() {
98
		if($this->isAllowedPath($this->getRequestUri())) {
99
			return false;
100
		}
101
102
		$currentHost = $this->getHttpHost();
103
		if (strpos(':', $currentHost) !== false) {
104
			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...
105
		}
106
107
		$allow_subdomains = MultiDomain::config()->allow_subdomains;
108
		$hostname = $this->getHostname();
109
110
		return $allow_subdomains ?
111
					(bool) preg_match('/(\.|^)'.$hostname.'$/', $currentHost) :
112
					($currentHost == $hostname);
113
	}
114
115
	/**
116
	 * Returns true if this domain is the primary domain
117
	 * @return boolean
118
	 */
119
	public function isPrimary() {
120
		return $this->key === MultiDomain::KEY_PRIMARY;
121
	}
122
123
	/**
124
	 * Gets the native URL for a vanity domain, e.g. /partners/ for .com
125
	 * returns /company/partners when .com is mapped to /company/.
126
	 *
127
	 * @param  string $url
128
	 * @return string
129
	 */
130
	public function getNativeURL($url) {
131
		if($this->isPrimary()) {
132
			throw new Exception("Cannot convert a native URL on the primary domain");
133
		}
134
135
		if($this->isAllowedPath($url) || $this->isForcedPath($url)) {
136
			return $url;
137
		}
138
139
		return Controller::join_links($this->getURL(), $url);
140
	}
141
142
	/**
143
	 * Gets the vanity URL given a native URL. /company/partners returns /partners/
144
	 * when .com is mapped to /company/.
145
	 *
146
	 * @param  string $url
147
	 * @return string
148
	 */
149
	public function getVanityURL($url) {
150
		if($this->isPrimary() || $this->isAllowedPath($url)) {
151
			return $url;
152
		}
153
154
        $domainUrl = str_replace('/', '\/', $this->getURL());
155
		return preg_replace('/^\/?' . $domainUrl . '\//', '', $url);
156
	}
157
158
	/**
159
	 * Return true if this domain contains the given URL
160
	 * @param  string  $url
161
	 * @return boolean
162
	 */
163
	public function hasURL($url) {
164
		if($this->isForcedPath($url)) return true;
165
		$domainBaseURL = trim($this->getURL(),'/');
166
		if(preg_match('/^'.$domainBaseURL.'/', $url)) {
167
			return true;
168
		}
169
170
		return false;
171
	}
172
173
    /**
174
     * Returns the key/identifier for this domain
175
     *
176
     * @return string
177
     */
178
    public function getKey()
179
    {
180
        return $this->key;
181
    }
182
183
    /**
184
     * Set the request URI
185
     *
186
     * @param  string $requestUri
187
     * @return $this
188
     */
189
    public function setRequestUri($requestUri)
190
    {
191
        $this->requestUri = (string) $requestUri;
192
        return $this;
193
    }
194
195
    /**
196
     * Return the current request URI, defaulting to retrieving it from the $_SERVER superglobal
197
     *
198
     * @return string
199
     */
200
    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...
201
    {
202
        return $this->requestUri ?: $_SERVER['REQUEST_URI'];
203
    }
204
205
    /**
206
     * Set the HTTP host in the request
207
     *
208
     * @param  string $httpHost
209
     * @return $this
210
     */
211
    public function setHttpHost($httpHost)
212
    {
213
        $this->httpHost = (string) $httpHost;
214
        return $this;
215
    }
216
217
    /**
218
     * Return the current HTTP host, defaulting to retrieving it from the $_SERVER superglobal
219
     *
220
     * @return string
221
     */
222
    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...
223
    {
224
        return $this->httpHost ?: $_SERVER['HTTP_HOST'];
225
    }
226
227
	/**
228
	 * Checks a given list of wildcard patterns to see if a path is allowed
229
	 * @param  string  $url
230
	 * @return boolean
231
	 */
232
	protected function isAllowedPath($url) {
233
		return self::match_url($url, $this->allowedPaths);
234
	}
235
236
	/**
237
	 * Checks a given list of wildcard patterns to see if a path is allowed
238
	 * @param  string  $url
239
	 * @return boolean
240
	 */
241
	protected function isForcedPath($url) {
242
		return self::match_url($url, $this->forcedPaths);
243
	}
244
245
	/**
246
	 * Matches a URL against a list of wildcard patterns
247
	 * @param  string $url
248
	 * @param  array $patterns
249
	 * @return boolean
250
	 */
251
	protected static function match_url($url, $patterns) {
252
		if(!is_array($patterns)) return false;
253
254
		$url = ltrim($url, '/');
255
		if(substr($url, -1) !== '/') $url .= '/';
256
257
		foreach($patterns as $pattern) {
258
			if(fnmatch($pattern, $url)) return true;
259
		}
260
261
		return false;
262
	}
263
264
}
265