1<?php
2
3/*
4 * This file is part of MailSo.
5 *
6 * (c) 2014 Usenko Timur
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12namespace MailSo\Base;
13
14/**
15 * @category MailSo
16 * @package Base
17 */
18class Http
19{
20	/**
21	 * @var bool
22	 */
23	private $bIsMagicQuotesOn;
24
25	/**
26	 * @access private
27	 */
28	private function __construct()
29	{
30		$this->bIsMagicQuotesOn = (bool) @\ini_get('magic_quotes_gpc');
31	}
32
33	/**
34	 * @return \MailSo\Base\Http
35	 */
36	public static function NewInstance()
37	{
38		return new self();
39	}
40
41	/**
42	 * @staticvar \MailSo\Base\Http $oInstance;
43	 *
44	 * @return \MailSo\Base\Http
45	 */
46	public static function SingletonInstance()
47	{
48		static $oInstance = null;
49		if (null === $oInstance)
50		{
51			$oInstance = self::NewInstance();
52		}
53
54		return $oInstance;
55	}
56
57	/**
58	 * @param string $sKey
59	 *
60	 * @return bool
61	 */
62	public function HasQuery($sKey)
63	{
64		return isset($_GET[$sKey]);
65	}
66
67	/**
68	 * @param string $sKey
69	 * @param mixed $mDefault = null
70	 * @param bool $bClearPercZeroZero = true
71	 *
72	 * @return mixed
73	 */
74	public function GetQuery($sKey, $mDefault = null, $bClearPercZeroZero = true)
75	{
76		return isset($_GET[$sKey]) ? \MailSo\Base\Utils::StripSlashesValue($_GET[$sKey], $bClearPercZeroZero) : $mDefault;
77	}
78
79	/**
80	 * @return array|null
81	 */
82	public function GetQueryAsArray()
83	{
84		return isset($_GET) && \is_array($_GET) ? \MailSo\Base\Utils::StripSlashesValue($_GET, true) : null;
85	}
86
87	/**
88	 * @param string $sKey
89	 *
90	 * @return bool
91	 */
92	public function HasPost($sKey)
93	{
94		return isset($_POST[$sKey]);
95	}
96
97	/**
98	 * @param string $sKey
99	 * @param mixed $mDefault = null
100	 * @param bool $bClearPercZeroZero = false
101	 *
102	 * @return mixed
103	 */
104	public function GetPost($sKey, $mDefault = null, $bClearPercZeroZero = false)
105	{
106		return isset($_POST[$sKey]) ? \MailSo\Base\Utils::StripSlashesValue($_POST[$sKey], $bClearPercZeroZero) : $mDefault;
107	}
108
109	/**
110	 * @return array|null
111	 */
112	public function GetPostAsArray()
113	{
114		return isset($_POST) && \is_array($_POST) ? \MailSo\Base\Utils::StripSlashesValue($_POST, false) : null;
115	}
116
117	/**
118	 * @param string $sKey
119	 *
120	 * @return bool
121	 */
122	public function HasRequest($sKey)
123	{
124		return isset($_REQUEST[$sKey]);
125	}
126
127	/**
128	 * @param string $sKey
129	 * @param mixed $mDefault = null
130	 *
131	 * @return mixed
132	 */
133	public function GetRequest($sKey, $mDefault = null)
134	{
135		return isset($_REQUEST[$sKey]) ? \MailSo\Base\Utils::StripSlashesValue($_REQUEST[$sKey]) : $mDefault;
136	}
137
138	/**
139	 * @param string $sKey
140	 *
141	 * @return bool
142	 */
143	public function HasServer($sKey)
144	{
145		return isset($_SERVER[$sKey]);
146	}
147
148	/**
149	 * @param string $sKey
150	 * @param mixed $mDefault = null
151	 *
152	 * @return mixed
153	 */
154	public function GetServer($sKey, $mDefault = null)
155	{
156		return isset($_SERVER[$sKey]) ? $_SERVER[$sKey] : $mDefault;
157	}
158
159	/**
160	 * @param string $sKey
161	 *
162	 * @return bool
163	 */
164	public function HasEnv($sKey)
165	{
166		return isset($_ENV[$sKey]);
167	}
168
169	/**
170	 * @param string $sKey
171	 * @param mixed $mDefault = null
172	 *
173	 * @return mixed
174	 */
175	public function GetEnv($sKey, $mDefault = null)
176	{
177		return isset($_ENV[$sKey]) ? $_ENV[$sKey] : $mDefault;
178	}
179
180	/**
181	 * @return string
182	 */
183	public function ServerProtocol()
184	{
185		return $this->GetServer('SERVER_PROTOCOL', 'HTTP/1.0');
186	}
187
188	/**
189	 * @return string
190	 */
191	public function GetMethod()
192	{
193		return $this->GetServer('REQUEST_METHOD', '');
194	}
195
196	/**
197	 * @return bool
198	 */
199	public function IsPost()
200	{
201		return ('POST' === $this->GetMethod());
202	}
203
204	/**
205	 * @return bool
206	 */
207	public function IsGet()
208	{
209		return ('GET' === $this->GetMethod());
210	}
211
212	/**
213	 * @return string
214	 */
215	public function GetQueryString()
216	{
217		return $this->GetServer('QUERY_STRING', '');
218	}
219
220	/**
221	 * @return bool
222	 */
223	public function CheckLocalhost($sServer)
224	{
225		return \in_array(\strtolower(\trim($sServer)), array(
226			'localhost', '127.0.0.1', '::1', '::1/128', '0:0:0:0:0:0:0:1'
227		));
228	}
229
230	/**
231	 * @param string $sValueToCheck = ''
232	 *
233	 * @return bool
234	 */
235	public function IsLocalhost($sValueToCheck = '')
236	{
237		if (empty($sValueToCheck))
238		{
239			$sValueToCheck = $this->GetServer('REMOTE_ADDR', '');
240		}
241
242		return $this->CheckLocalhost($sValueToCheck);
243	}
244
245	/**
246	 * @return string
247	 */
248	public function GetRawBody()
249	{
250		static $sRawBody = null;
251		if (null === $sRawBody)
252		{
253			$sBody = @\file_get_contents('php://input');
254			$sRawBody = (false !== $sBody) ? $sBody : '';
255		}
256		return $sRawBody;
257	}
258
259	/**
260	 * @param string $sHeader
261	 *
262	 * @return string
263	 */
264	public function GetHeader($sHeader)
265	{
266		$sServerKey = 'HTTP_'.\strtoupper(\str_replace('-', '_', $sHeader));
267		$sResultHeader = $this->GetServer($sServerKey, '');
268
269		if (0 === \strlen($sResultHeader) &&
270			\MailSo\Base\Utils::FunctionExistsAndEnabled('apache_request_headers'))
271		{
272			$sHeaders = \apache_request_headers();
273			if (isset($sHeaders[$sHeader]))
274			{
275				$sResultHeader = $sHeaders[$sHeader];
276			}
277		}
278
279		return $sResultHeader;
280	}
281
282	/**
283	 * @param bool $bCheckProxy = true
284	 *
285	 * @return string
286	 */
287	public function GetScheme($bCheckProxy = true)
288	{
289		return $this->IsSecure($bCheckProxy) ? 'https' : 'http';
290	}
291
292	/**
293	 * @param bool $bCheckProxy = true
294	 *
295	 * @return bool
296	 */
297	public function IsSecure($bCheckProxy = true)
298	{
299		$sHttps = \strtolower($this->GetServer('HTTPS', ''));
300		if ('on' === $sHttps || ('' === $sHttps && '443' === (string) $this->GetServer('SERVER_PORT', '')))
301		{
302			return true;
303		}
304
305		if ($bCheckProxy && (
306			('https' === \strtolower($this->GetServer('HTTP_X_FORWARDED_PROTO', ''))) ||
307			('on' === \strtolower($this->GetServer('HTTP_X_FORWARDED_SSL', '')))
308		))
309		{
310			return true;
311		}
312
313		return false;
314	}
315
316	/**
317	 * @param bool $bWithRemoteUserData = false
318	 * @param bool $bWithoutWWW = true
319	 * @param bool $bWithoutPort = false
320	 *
321	 * @return string
322	 */
323	public function GetHost($bWithRemoteUserData = false, $bWithoutWWW = true, $bWithoutPort = false)
324	{
325		$sHost = $this->GetServer('HTTP_HOST', '');
326		if (0 === \strlen($sHost))
327		{
328			$sName = $this->GetServer('SERVER_NAME');
329			$iPort = (int) $this->GetServer('SERVER_PORT', 80);
330
331			$sHost = (\in_array($iPort, array(80, 433))) ? $sName : $sName.':'.$iPort;
332		}
333
334		if ($bWithoutWWW)
335		{
336			$sHost = 'www.' === \substr(\strtolower($sHost), 0, 4) ? \substr($sHost, 4) : $sHost;
337		}
338
339		if ($bWithRemoteUserData)
340		{
341			$sUser = \trim($this->HasServer('REMOTE_USER') ? $this->GetServer('REMOTE_USER', '') : '');
342			$sHost = (0 < \strlen($sUser) ? $sUser.'@' : '').$sHost;
343		}
344
345		if ($bWithoutPort)
346		{
347			$sHost = \preg_replace('/:[\d]+$/', '', $sHost);
348		}
349
350		return $sHost;
351	}
352
353	/**
354	 * @param bool $bCheckProxy = false
355	 *
356	 * @return string
357	 */
358	public function GetClientIp($bCheckProxy = false)
359	{
360		$sIp = '';
361		if ($bCheckProxy && null !== $this->GetServer('HTTP_CLIENT_IP', null))
362		{
363			$sIp = $this->GetServer('HTTP_CLIENT_IP', '');
364		}
365		else if ($bCheckProxy && null !== $this->GetServer('HTTP_X_FORWARDED_FOR', null))
366		{
367			$sIp = $this->GetServer('HTTP_X_FORWARDED_FOR', '');
368		}
369		else
370		{
371			$sIp = $this->GetServer('REMOTE_ADDR', '');
372		}
373
374		return $sIp;
375	}
376
377	/**
378	 * @param string $sUrl
379	 * @param array $aPost = array()
380	 * @param string $sCustomUserAgent = 'MailSo Http User Agent (v1)'
381	 * @param int $iCode = 0
382	 * @param \MailSo\Log\Logger $oLogger = null
383	 * @param int $iTimeout = 20
384	 * @param string $sProxy = ''
385	 * @param string $sProxyAuth = ''
386	 *
387	 * @return string|bool
388	 */
389	public function SendPostRequest($sUrl, $aPost = array(), $sCustomUserAgent = 'MailSo Http User Agent (v1)', &$iCode = 0,
390		$oLogger = null, $iTimeout = 20, $sProxy = '', $sProxyAuth = '')
391	{
392		$aOptions = array(
393			CURLOPT_URL => $sUrl,
394			CURLOPT_HEADER => false,
395			CURLOPT_FAILONERROR => true,
396			CURLOPT_SSL_VERIFYPEER => false,
397			CURLOPT_RETURNTRANSFER => true,
398			CURLOPT_POST => true,
399			CURLOPT_POSTFIELDS => \http_build_query($aPost, '', '&'),
400			CURLOPT_TIMEOUT => (int) $iTimeout
401		);
402
403		if (0 < \strlen($sCustomUserAgent))
404		{
405			$aOptions[CURLOPT_USERAGENT] = $sCustomUserAgent;
406		}
407
408		if (0 < \strlen($sProxy))
409		{
410			$aOptions[CURLOPT_PROXY] = $sProxy;
411			if (0 < \strlen($sProxyAuth))
412			{
413				$aOptions[CURLOPT_PROXYUSERPWD] = $sProxyAuth;
414			}
415		}
416
417		$oCurl = \curl_init();
418		\curl_setopt_array($oCurl, $aOptions);
419
420		if ($oLogger)
421		{
422			$oLogger->Write('cURL: Send post request: '.$sUrl);
423		}
424
425		$mResult = \curl_exec($oCurl);
426
427		$iCode = (int) \curl_getinfo($oCurl, CURLINFO_HTTP_CODE);
428		$sContentType = (string) \curl_getinfo($oCurl, CURLINFO_CONTENT_TYPE);
429
430		if ($oLogger)
431		{
432			$oLogger->Write('cURL: Post request result: (Status: '.$iCode.', ContentType: '.$sContentType.')');
433			if (false === $mResult || 200 !== $iCode)
434			{
435				$oLogger->Write('cURL: Error: '.\curl_error($oCurl), \MailSo\Log\Enumerations\Type::WARNING);
436			}
437		}
438
439		if (\is_resource($oCurl))
440		{
441			\curl_close($oCurl);
442		}
443
444		return $mResult;
445	}
446
447	/**
448	 * @param string $sUrl
449	 * @param array $aOptions
450	 * @param \MailSo\Log\Logger $oLogger = null
451	 *
452	 * @return string
453	 */
454	static public function DetectAndHackFollowLocationUrl($sUrl, &$aOptions, $oLogger = null)
455	{
456		$sSafeMode = \strtolower(\trim(@\ini_get('safe_mode')));
457		$bSafeMode = 'on' === $sSafeMode || '1' === $sSafeMode;
458
459		$sNewUrl = null;
460		$sUrl = isset($aOptions[CURLOPT_URL]) ? $aOptions[CURLOPT_URL] : $sUrl;
461
462		if (isset($aOptions[CURLOPT_FOLLOWLOCATION]) && $aOptions[CURLOPT_FOLLOWLOCATION] && 0 < \strlen($sUrl) &&
463			($bSafeMode || \ini_get('open_basedir') !== ''))
464		{
465			$aOptions[CURLOPT_FOLLOWLOCATION] = false;
466
467			$iMaxRedirects = isset($aOptions[CURLOPT_MAXREDIRS]) ? $aOptions[CURLOPT_MAXREDIRS] : 5;
468			$iRedirectLimit = $iMaxRedirects;
469
470			if ($iRedirectLimit > 0)
471			{
472				$sNewUrl = $sUrl;
473
474				$oCurl = \curl_init($sUrl);
475
476				$aAddOptions = array(
477					CURLOPT_URL => $sUrl,
478					CURLOPT_HEADER => true,
479					CURLOPT_NOBODY => true,
480					CURLOPT_FAILONERROR => false,
481					CURLOPT_SSL_VERIFYPEER => false,
482					CURLOPT_FOLLOWLOCATION => false,
483					CURLOPT_FORBID_REUSE => false,
484					CURLOPT_RETURNTRANSFER => true,
485					CURLOPT_TIMEOUT => 5
486				);
487
488				if (isset($aOptions[CURLOPT_HTTPHEADER]) && \is_array($aOptions[CURLOPT_HTTPHEADER]) && 0 < \count($aOptions[CURLOPT_HTTPHEADER]))
489				{
490					$aAddOptions[CURLOPT_HTTPHEADER] = $aOptions[CURLOPT_HTTPHEADER];
491				}
492
493				\curl_setopt_array($oCurl, $aAddOptions);
494
495				do
496				{
497					\curl_setopt($oCurl, CURLOPT_URL, $sNewUrl);
498
499					$sHeader = \curl_exec($oCurl);
500					if (\curl_errno($oCurl))
501					{
502						$iCode = 0;
503					}
504					else
505					{
506						$iCode = \curl_getinfo($oCurl, CURLINFO_HTTP_CODE);
507						if ($iCode === 301 || $iCode === 302)
508						{
509							$aMatches = array();
510							\preg_match('/Location:(.*?)\n/', $sHeader, $aMatches);
511							$sNewUrl = \trim(\array_pop($aMatches));
512
513							if ($oLogger)
514							{
515								$oLogger->Write('cUrl: Location URL: '.$sNewUrl);
516							}
517						}
518						else
519						{
520							$iCode = 0;
521						}
522					}
523
524				} while ($iCode && --$iRedirectLimit);
525
526				\curl_close($oCurl);
527				if ($iRedirectLimit > 0 && 0 < \strlen($sNewUrl))
528				{
529					$aOptions[CURLOPT_URL] = $sNewUrl;
530				}
531			}
532		}
533
534		return null === $sNewUrl ? $sUrl : $sNewUrl;
535	}
536
537	/**
538	 * @param string $sUrl
539	 * @param resource $rFile
540	 * @param string $sCustomUserAgent = 'MailSo Http User Agent (v1)'
541	 * @param string $sContentType = ''
542	 * @param int $iCode = 0
543	 * @param \MailSo\Log\Logger $oLogger = null
544	 * @param int $iTimeout = 10
545	 * @param string $sProxy = ''
546	 * @param string $sProxyAuth = ''
547	 * @param array $aHttpHeaders = array()
548	 * @param bool $bFollowLocation = true
549	 *
550	 * @return bool
551	 */
552	public function SaveUrlToFile($sUrl, $rFile, $sCustomUserAgent = 'MailSo Http User Agent (v1)', &$sContentType = '', &$iCode = 0,
553		$oLogger = null, $iTimeout = 10, $sProxy = '', $sProxyAuth = '', $aHttpHeaders = array(), $bFollowLocation = true)
554	{
555		if (null === $sCustomUserAgent)
556		{
557			$sCustomUserAgent = 'MailSo Http User Agent (v1)';
558		}
559
560		if (!is_resource($rFile))
561		{
562			if ($oLogger)
563			{
564				$oLogger->Write('cURL: input resource invalid.', \MailSo\Log\Enumerations\Type::WARNING);
565			}
566
567			return false;
568		}
569
570		$sUrl = \trim($sUrl);
571		if ('//' === substr($sUrl, 0, 2))
572		{
573			$sUrl = 'http:'.$sUrl;
574		}
575
576		$aOptions = array(
577			CURLOPT_URL => $sUrl,
578			CURLOPT_HEADER => false,
579			CURLOPT_FAILONERROR => true,
580			CURLOPT_SSL_VERIFYPEER => false,
581			CURLOPT_RETURNTRANSFER => true,
582			CURLOPT_FOLLOWLOCATION => !!$bFollowLocation,
583			CURLOPT_MAXREDIRS => 7,
584			CURLOPT_FILE => $rFile,
585			CURLOPT_TIMEOUT => (int) $iTimeout
586		);
587
588		if (0 < \strlen($sCustomUserAgent))
589		{
590			$aOptions[CURLOPT_USERAGENT] = $sCustomUserAgent;
591		}
592
593		if (0 < \strlen($sProxy))
594		{
595			$aOptions[CURLOPT_PROXY] = $sProxy;
596			if (0 < \strlen($sProxyAuth))
597			{
598				$aOptions[CURLOPT_PROXYUSERPWD] = $sProxyAuth;
599			}
600		}
601
602		if (\is_array($aHttpHeaders) && 0 < \count($aHttpHeaders))
603		{
604			$aOptions[CURLOPT_HTTPHEADER] = $aHttpHeaders;
605		}
606
607		if ($oLogger)
608		{
609			$oLogger->Write('cUrl: URL: '.$sUrl);
610//			if (isset($aOptions[CURLOPT_HTTPHEADER]) && \is_array($aOptions[CURLOPT_HTTPHEADER]) && 0 < \count($aOptions[CURLOPT_HTTPHEADER]))
611//			{
612//				$oLogger->Write('cUrl: Headers: '.\print_r($aOptions[CURLOPT_HTTPHEADER], true));
613//			}
614		}
615
616		\MailSo\Base\Http::DetectAndHackFollowLocationUrl($sUrl, $aOptions, $oLogger);
617
618		$oCurl = \curl_init();
619		\curl_setopt_array($oCurl, $aOptions);
620
621		$bResult = \curl_exec($oCurl);
622
623		$iCode = (int) \curl_getinfo($oCurl, CURLINFO_HTTP_CODE);
624		$sContentType = (string) \curl_getinfo($oCurl, CURLINFO_CONTENT_TYPE);
625
626		if ($oLogger)
627		{
628			$oLogger->Write('cUrl: Request result: '.($bResult ? 'true' : 'false').' (Status: '.$iCode.', ContentType: '.$sContentType.')');
629			if (!$bResult || 200 !== $iCode)
630			{
631				$oLogger->Write('cUrl: Error: '.\curl_error($oCurl), \MailSo\Log\Enumerations\Type::WARNING);
632			}
633		}
634
635		if (\is_resource($oCurl))
636		{
637			\curl_close($oCurl);
638		}
639
640		return $bResult;
641	}
642
643	/**
644	 * @param string $sUrl
645	 * @param string $sCustomUserAgent = 'MailSo Http User Agent (v1)'
646	 * @param string $sContentType = ''
647	 * @param int $iCode = 0
648	 * @param \MailSo\Log\Logger $oLogger = null
649	 * @param int $iTimeout = 10
650	 * @param string $sProxy = ''
651	 * @param string $sProxyAuth = ''
652	 * @param array $aHttpHeaders = array()
653	 * @param bool $bFollowLocation = true
654	 *
655	 * @return string|bool
656	 */
657	public function GetUrlAsString($sUrl, $sCustomUserAgent = 'MailSo Http User Agent (v1)', &$sContentType = '', &$iCode = 0,
658		$oLogger = null, $iTimeout = 10, $sProxy = '', $sProxyAuth = '', $aHttpHeaders = array(), $bFollowLocation = true)
659	{
660		$rMemFile = \MailSo\Base\ResourceRegistry::CreateMemoryResource();
661		if ($this->SaveUrlToFile($sUrl, $rMemFile, $sCustomUserAgent, $sContentType, $iCode, $oLogger, $iTimeout, $sProxy, $sProxyAuth, $aHttpHeaders, $bFollowLocation) && \is_resource($rMemFile))
662		{
663			\rewind($rMemFile);
664			return \stream_get_contents($rMemFile);
665		}
666
667		return false;
668	}
669
670	/**
671	 * @param int $iExpireTime
672	 * @param bool $bSetCacheHeader = true
673	 * @param string $sEtag = ''
674	 *
675	 * @return bool
676	 */
677	public function ServerNotModifiedCache($iExpireTime, $bSetCacheHeader = true, $sEtag = '')
678	{
679		$bResult = false;
680		if (0 < $iExpireTime)
681		{
682			$iUtcTimeStamp = \time();
683			$sIfModifiedSince = $this->GetHeader('If-Modified-Since', '');
684			if (0 === \strlen($sIfModifiedSince))
685			{
686				if ($bSetCacheHeader)
687				{
688					@\header('Cache-Control: public', true);
689					@\header('Pragma: public', true);
690					@\header('Last-Modified: '.\gmdate('D, d M Y H:i:s', $iUtcTimeStamp - $iExpireTime).' UTC', true);
691					@\header('Expires: '.\gmdate('D, j M Y H:i:s', $iUtcTimeStamp + $iExpireTime).' UTC', true);
692
693					if (0 < strlen($sEtag))
694					{
695						\header('Etag: '.$sEtag, true);
696					}
697				}
698			}
699			else
700			{
701				$this->StatusHeader(304);
702				$bResult = true;
703			}
704		}
705
706		return $bResult;
707	}
708
709	/**
710	 * @staticvar boolean $bCache
711	 */
712	public function ServerNoCache()
713	{
714		static $bCache = false;
715		if (false === $bCache)
716		{
717			$bCache = true;
718			@\header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
719			@\header('Last-Modified: '.\gmdate('D, d M Y H:i:s').' GMT');
720			@\header('Cache-Control: no-store, no-cache, must-revalidate, max-age=0');
721			@\header('Cache-Control: post-check=0, pre-check=0', false);
722			@\header('Pragma: no-cache');
723		}
724	}
725
726	/**
727	 * @staticvar boolean $bCache
728	 * @param string $sEtag
729	 * @param int $iLastModified
730	 * @param int $iExpires
731	 */
732	public function ServerUseCache($sEtag, $iLastModified, $iExpires)
733	{
734		static $bCache = false;
735		if (false === $bCache)
736		{
737			$bCache = true;
738			@\header('Cache-Control: private', true);
739			@\header('ETag: '.$sEtag, true);
740			@\header('Last-Modified: '.\gmdate('D, d M Y H:i:s', $iLastModified).' UTC', true);
741			@\header('Expires: '.\gmdate('D, j M Y H:i:s', $iExpires).' UTC', true);
742		}
743	}
744
745	/**
746	 * @param int $iStatus
747	 *
748	 * @return void
749	 */
750	public function StatusHeader($iStatus, $sCustomStatusText = '')
751	{
752		$iStatus = (int) $iStatus;
753		if (99 < $iStatus)
754		{
755			$aStatus = array(
756				200 => 'OK',
757				206 => 'Partial Content',
758				301 => 'Moved Permanently',
759				304 => 'Not Modified',
760				400 => 'Bad Request',
761				401 => 'Unauthorized',
762				403 => 'Forbidden',
763				404 => 'Not Found',
764				405 => 'Method Not Allowed',
765				416 => 'Requested range not satisfiable'
766			);
767
768			$sCustomStatusText = \trim($sCustomStatusText);
769			$sHeaderHead = \ini_get('cgi.rfc2616_headers') && false !== \strpos(\strtolower(\php_sapi_name()), 'cgi') ? 'Status:' : $this->ServerProtocol();
770			$sHeaderText = (0 === \strlen($sCustomStatusText) && isset($aStatus[$iStatus]) ? $aStatus[$iStatus] : $sCustomStatusText);
771
772			\header(\trim($sHeaderHead.' '.$iStatus.' '.$sHeaderText), true, $iStatus);
773		}
774	}
775
776	/**
777	 * @return string
778	 */
779	public function GetPath()
780	{
781		$sUrl = \ltrim(\substr($this->GetServer('SCRIPT_NAME', ''), 0, \strrpos($this->GetServer('SCRIPT_NAME', ''), '/')), '/');
782		return '' === $sUrl ? '/' : '/'.$sUrl.'/';
783	}
784
785	/**
786	 * @return string
787	 */
788	public function GetUrl()
789	{
790		return $this->GetServer('REQUEST_URI', '');
791	}
792
793	/**
794	 * @return string
795	 */
796	public function GetFullUrl()
797	{
798		return $this->GetScheme().'://'.$this->GetHost(true, false).$this->GetPath();
799	}
800}
801