1<?php 2/** 3 * Zend Framework (http://framework.zend.com/) 4 * 5 * @link http://github.com/zendframework/zf2 for the canonical source repository 6 * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) 7 * @license http://framework.zend.com/license/new-bsd New BSD License 8 */ 9 10namespace Zend\View\Helper; 11 12/** 13 * Helper for returning the current server URL (optionally with request URI) 14 */ 15class ServerUrl extends AbstractHelper 16{ 17 /** 18 * Host (including port) 19 * 20 * @var string 21 */ 22 protected $host; 23 24 /** 25 * Port 26 * 27 * @var int 28 */ 29 protected $port; 30 31 /** 32 * Scheme 33 * 34 * @var string 35 */ 36 protected $scheme; 37 38 /** 39 * Whether or not to query proxy servers for address 40 * 41 * @var bool 42 */ 43 protected $useProxy = false; 44 45 /** 46 * View helper entry point: 47 * Returns the current host's URL like http://site.com 48 * 49 * @param string|bool $requestUri [optional] if true, the request URI 50 * found in $_SERVER will be appended 51 * as a path. If a string is given, it 52 * will be appended as a path. Default 53 * is to not append any path. 54 * @return string 55 */ 56 public function __invoke($requestUri = null) 57 { 58 if ($requestUri === true) { 59 $path = $_SERVER['REQUEST_URI']; 60 } elseif (is_string($requestUri)) { 61 $path = $requestUri; 62 } else { 63 $path = ''; 64 } 65 66 return $this->getScheme() . '://' . $this->getHost() . $path; 67 } 68 69 /** 70 * Detect the host based on headers 71 * 72 * @return void 73 */ 74 protected function detectHost() 75 { 76 if ($this->setHostFromProxy()) { 77 return; 78 } 79 80 if (isset($_SERVER['HTTP_HOST']) && !empty($_SERVER['HTTP_HOST'])) { 81 // Detect if the port is set in SERVER_PORT and included in HTTP_HOST 82 if (isset($_SERVER['SERVER_PORT']) 83 && preg_match('/^(?P<host>.*?):(?P<port>\d+)$/', $_SERVER['HTTP_HOST'], $matches) 84 ) { 85 // If they are the same, set the host to just the hostname 86 // portion of the Host header. 87 if ((int) $matches['port'] === (int) $_SERVER['SERVER_PORT']) { 88 $this->setHost($matches['host']); 89 return; 90 } 91 92 // At this point, we have a SERVER_PORT that differs from the 93 // Host header, indicating we likely have a port-forwarding 94 // situation. As such, we'll set the host and port from the 95 // matched values. 96 $this->setPort((int) $matches['port']); 97 $this->setHost($matches['host']); 98 return; 99 } 100 101 $this->setHost($_SERVER['HTTP_HOST']); 102 103 return; 104 } 105 106 if (!isset($_SERVER['SERVER_NAME']) || !isset($_SERVER['SERVER_PORT'])) { 107 return; 108 } 109 110 $name = $_SERVER['SERVER_NAME']; 111 $this->setHost($name); 112 } 113 114 /** 115 * Detect the port 116 * 117 * @return null 118 */ 119 protected function detectPort() 120 { 121 if ($this->setPortFromProxy()) { 122 return; 123 } 124 125 if (isset($_SERVER['SERVER_PORT']) && $_SERVER['SERVER_PORT']) { 126 if ($this->isReversedProxy()) { 127 $this->setPort(443); 128 return; 129 } 130 $this->setPort($_SERVER['SERVER_PORT']); 131 return; 132 } 133 } 134 135 /** 136 * Detect the scheme 137 * 138 * @return null 139 */ 140 protected function detectScheme() 141 { 142 if ($this->setSchemeFromProxy()) { 143 return; 144 } 145 146 switch (true) { 147 case (isset($_SERVER['HTTPS']) && ($_SERVER['HTTPS'] == 'on' || $_SERVER['HTTPS'] === true)): 148 case (isset($_SERVER['HTTP_SCHEME']) && ($_SERVER['HTTP_SCHEME'] == 'https')): 149 case (443 === $this->getPort()): 150 case $this->isReversedProxy(): 151 $scheme = 'https'; 152 break; 153 default: 154 $scheme = 'http'; 155 break; 156 } 157 158 $this->setScheme($scheme); 159 } 160 161 protected function isReversedProxy() 162 { 163 return isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https'; 164 } 165 166 /** 167 * Detect if a proxy is in use, and, if so, set the host based on it 168 * 169 * @return bool 170 */ 171 protected function setHostFromProxy() 172 { 173 if (!$this->useProxy) { 174 return false; 175 } 176 177 if (!isset($_SERVER['HTTP_X_FORWARDED_HOST']) || empty($_SERVER['HTTP_X_FORWARDED_HOST'])) { 178 return false; 179 } 180 181 $host = $_SERVER['HTTP_X_FORWARDED_HOST']; 182 if (strpos($host, ',') !== false) { 183 $hosts = explode(',', $host); 184 $host = trim(array_pop($hosts)); 185 } 186 if (empty($host)) { 187 return false; 188 } 189 $this->setHost($host); 190 191 return true; 192 } 193 194 /** 195 * Set port based on detected proxy headers 196 * 197 * @return bool 198 */ 199 protected function setPortFromProxy() 200 { 201 if (!$this->useProxy) { 202 return false; 203 } 204 205 if (!isset($_SERVER['HTTP_X_FORWARDED_PORT']) || empty($_SERVER['HTTP_X_FORWARDED_PORT'])) { 206 return false; 207 } 208 209 $port = $_SERVER['HTTP_X_FORWARDED_PORT']; 210 $this->setPort($port); 211 212 return true; 213 } 214 215 /** 216 * Set the current scheme based on detected proxy headers 217 * 218 * @return bool 219 */ 220 protected function setSchemeFromProxy() 221 { 222 if (!$this->useProxy) { 223 return false; 224 } 225 226 if (isset($_SERVER['SSL_HTTPS'])) { 227 $sslHttps = strtolower($_SERVER['SSL_HTTPS']); 228 if (in_array($sslHttps, array('on', 1))) { 229 $this->setScheme('https'); 230 return true; 231 } 232 } 233 234 if (!isset($_SERVER['HTTP_X_FORWARDED_PROTO']) || empty($_SERVER['HTTP_X_FORWARDED_PROTO'])) { 235 return false; 236 } 237 238 $scheme = trim(strtolower($_SERVER['HTTP_X_FORWARDED_PROTO'])); 239 if (empty($scheme)) { 240 return false; 241 } 242 243 $this->setScheme($scheme); 244 245 return true; 246 } 247 248 /** 249 * Sets host 250 * 251 * @param string $host 252 * @return ServerUrl 253 */ 254 public function setHost($host) 255 { 256 $port = $this->getPort(); 257 $scheme = $this->getScheme(); 258 259 if (($scheme == 'http' && (null === $port || $port == 80)) 260 || ($scheme == 'https' && (null === $port || $port == 443)) 261 ) { 262 $this->host = $host; 263 return $this; 264 } 265 266 $this->host = $host . ':' . $port; 267 268 return $this; 269 } 270 271 /** 272 * Returns host 273 * 274 * @return string 275 */ 276 public function getHost() 277 { 278 if (null === $this->host) { 279 $this->detectHost(); 280 } 281 282 return $this->host; 283 } 284 285 /** 286 * Set server port 287 * 288 * @param int $port 289 * @return ServerUrl 290 */ 291 public function setPort($port) 292 { 293 $this->port = (int) $port; 294 295 return $this; 296 } 297 298 /** 299 * Retrieve the server port 300 * 301 * @return int|null 302 */ 303 public function getPort() 304 { 305 if (null === $this->port) { 306 $this->detectPort(); 307 } 308 309 return $this->port; 310 } 311 312 /** 313 * Sets scheme (typically http or https) 314 * 315 * @param string $scheme 316 * @return ServerUrl 317 */ 318 public function setScheme($scheme) 319 { 320 $this->scheme = $scheme; 321 322 return $this; 323 } 324 325 /** 326 * Returns scheme (typically http or https) 327 * 328 * @return string 329 */ 330 public function getScheme() 331 { 332 if (null === $this->scheme) { 333 $this->detectScheme(); 334 } 335 336 return $this->scheme; 337 } 338 339 /** 340 * Set flag indicating whether or not to query proxy servers 341 * 342 * @param bool $useProxy 343 * @return ServerUrl 344 */ 345 public function setUseProxy($useProxy = false) 346 { 347 $this->useProxy = (bool) $useProxy; 348 349 return $this; 350 } 351} 352