2 declare(strict_types
=1);
4 use wcf\system\application\ApplicationHandler
;
5 use wcf\system\event\EventHandler
;
6 use wcf\system\request\RequestHandler
;
7 use wcf\system\request\RouteHandler
;
11 * Contains header-related functions.
14 * @copyright 2001-2018 WoltLab GmbH
15 * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
16 * @package WoltLabSuite\Core\Util
18 final class HeaderUtil
{
29 protected static $enableGzipCompression = false;
35 public static $output = '';
38 * Alias to php setcookie() function.
41 * @param string $value
42 * @param integer $expire
44 public static function setCookie($name, $value = '', $expire = 0) {
45 $application = ApplicationHandler
::getInstance()->getActiveApplication();
46 $addDomain = (mb_strpos($application->cookieDomain
, '.') === false || StringUtil
::endsWith($application->cookieDomain
, '.lan') || StringUtil
::endsWith($application->cookieDomain
, '.local')) ?
false : true;
47 $cookieDomain = $application->cookieDomain
;
48 if ($addDomain && strpos($cookieDomain, ':') !== false) {
49 $cookieDomain = explode(':', $cookieDomain, 2)[0];
52 @header
('Set-Cookie: '.rawurlencode(COOKIE_PREFIX
.$name).'='.rawurlencode((string) $value).($expire ?
'; expires='.gmdate('D, d-M-Y H:i:s', $expire).' GMT; max-age='.($expire - TIME_NOW
) : '').'; path=/'.($addDomain ?
'; domain='.$cookieDomain : '').(RouteHandler
::secureConnection() ?
'; secure' : '').'; HttpOnly', false);
56 * Sends the headers of a page.
58 public static function sendHeaders() {
60 @header
('Content-Type: text/html; charset=UTF-8');
62 // send no cache headers
63 if (!PACKAGE_ID ||
!WCF
::getSession()->spiderID
) {
64 self
::sendNoCacheHeaders();
67 if (HTTP_ENABLE_GZIP
&& !defined('HTTP_DISABLE_GZIP')) {
68 if (function_exists('gzcompress') && !@ini_get
('zlib.output_compression') && !@ini_get
('output_handler') && isset($_SERVER['HTTP_ACCEPT_ENCODING']) && strpos($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip') !== false) {
69 self
::$enableGzipCompression = true;
71 if (strpos($_SERVER['HTTP_ACCEPT_ENCODING'], 'x-gzip') !== false) {
72 @header
('Content-Encoding: x-gzip');
75 @header
('Content-Encoding: gzip');
80 // send Internet Explorer compatibility mode
81 @header
('X-UA-Compatible: IE=edge');
83 // send X-Frame-Options
84 if (HTTP_SEND_X_FRAME_OPTIONS
) {
85 @header
('X-Frame-Options: SAMEORIGIN');
88 ob_start([self
::class, 'parseOutput']);
92 * Sends no cache headers.
94 public static function sendNoCacheHeaders() {
95 @header
('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
96 @header
('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT');
97 @header
('Cache-Control: max-age=0, no-cache, no-store, must-revalidate');
98 @header
('Pragma: no-cache');
102 * Disables gzip compression on runtime in case of an exception. You should not call
103 * this method at all, it exists for exception handling only.
105 public static function exceptionDisableGzip() {
106 self
::$enableGzipCompression = false;
110 * Parses the rendered output.
112 * @param string $output
115 public static function parseOutput($output) {
116 self
::$output = $output;
118 if (!PACKAGE_ID || RequestHandler
::getInstance()->isACPRequest()) {
119 // force javascript relocation
120 self
::$output = preg_replace('~<script([^>]*)>~', '<script data-relocate="true"\\1>', self
::$output);
123 // move script tags to the bottom of the page
125 self
::$output = preg_replace_callback('~(?P<conditionBefore><!--\[IF [^<]+\s*)?<script data-relocate="true"(?P<script>.*?</script>\s*)(?P<conditionAfter><!\[ENDIF]-->\s*)?~s', function($matches) use (&$javascript) {
127 if (isset($matches['conditionBefore'])) $match .= $matches['conditionBefore'];
128 $match .= '<script' . $matches['script'];
129 if (isset($matches['conditionAfter'])) $match .= $matches['conditionAfter'];
131 $javascript[] = $match;
135 self
::$output = str_replace('<!-- JAVASCRIPT_RELOCATE_POSITION -->', implode("\n", $javascript), self
::$output);
137 // 3rd party plugins may differ the actual output before it is sent to the browser
138 // please be aware, that $eventObj is not available here due to this being a static
139 // class. Use HeaderUtil::$output to modify it.
140 if (!defined('NO_IMPORTS')) EventHandler
::getInstance()->fireAction(self
::class, 'parseOutput');
143 if (self
::$enableGzipCompression) {
144 $size = strlen(self
::$output);
145 $crc = crc32(self
::$output);
147 $newOutput = "\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff";
148 $newOutput .= substr(gzcompress(self
::$output, self
::GZIP_LEVEL
), 2, -4);
149 $newOutput .= pack('V', $crc);
150 $newOutput .= pack('V', $size);
152 self
::$output = $newOutput;
155 return self
::$output;
159 * Redirects the user agent to given location.
161 * @param string $location
162 * @param boolean $sendStatusCode
163 * @param boolean $temporaryRedirect
165 public static function redirect($location, $sendStatusCode = false, $temporaryRedirect = true) {
166 if ($sendStatusCode) {
167 if ($temporaryRedirect) @header
('HTTP/1.1 307 Temporary Redirect');
168 else @header
('HTTP/1.1 301 Moved Permanently');
171 header('Location: '.$location);
175 * Does a delayed redirect.
177 * @param string $location
178 * @param string $message
179 * @param integer $delay
180 * @param string $status
182 public static function delayedRedirect($location, $message, $delay = 5, $status = 'success') {
183 WCF
::getTPL()->assign([
185 'message' => $message,
187 'templateName' => 'redirect',
188 'templateNameApplication' => 'wcf',
191 WCF
::getTPL()->display('redirect');
195 * Forbid creation of HeaderUtil objects.
197 private function __construct() {