PluginProbe ʕ •ᴥ•ʔ
FAPI Member / 2.2.31
FAPI Member v2.2.31
2.2.33 2.2.32 trunk 1.9.47 2.1.18 2.2.24 2.2.25 2.2.26 2.2.28 2.2.29 2.2.30 2.2.31
fapi-member / libs / nette / http / src / Http / Response.php
fapi-member / libs / nette / http / src / Http Last commit date
Context.php 1 month ago FileUpload.php 1 month ago Helpers.php 1 month ago IRequest.php 1 month ago IResponse.php 1 month ago Request.php 1 month ago RequestFactory.php 1 month ago Response.php 1 month ago Session.php 1 month ago SessionSection.php 1 month ago Url.php 1 month ago UrlImmutable.php 1 month ago UrlScript.php 1 month ago UserStorage.php 1 month ago
Response.php
231 lines
1 <?php
2
3 /**
4 * This file is part of the Nette Framework (https://nette.org)
5 * Copyright (c) 2004 David Grudl (https://davidgrudl.com)
6 */
7 declare (strict_types=1);
8 namespace FapiMember\Library\Nette\Http;
9
10 use FapiMember\Library\Nette;
11 use FapiMember\Library\Nette\Utils\DateTime;
12 /**
13 * HttpResponse class.
14 *
15 * @property-read array $headers
16 */
17 final class Response implements IResponse
18 {
19 use Nette\SmartObject;
20 /** @var string The domain in which the cookie will be available */
21 public $cookieDomain = '';
22 /** @var string The path in which the cookie will be available */
23 public $cookiePath = '/';
24 /** @var bool Whether the cookie is available only through HTTPS */
25 public $cookieSecure = \false;
26 /** @deprecated */
27 public $cookieHttpOnly;
28 /** @var bool Whether warn on possible problem with data in output buffer */
29 public $warnOnBuffer = \true;
30 /** @deprecated */
31 private static $fixIE = \true;
32 /** @var int HTTP response code */
33 private $code = self::S200_OK;
34 public function __construct()
35 {
36 if (is_int($code = http_response_code())) {
37 $this->code = $code;
38 }
39 }
40 /**
41 * Sets HTTP response code.
42 * @return static
43 * @throws Nette\InvalidArgumentException if code is invalid
44 * @throws Nette\InvalidStateException if HTTP headers have been sent
45 */
46 public function setCode(int $code, ?string $reason = null)
47 {
48 if ($code < 100 || $code > 599) {
49 throw new Nette\InvalidArgumentException("Bad HTTP response '{$code}'.");
50 }
51 self::checkHeaders();
52 $this->code = $code;
53 $protocol = $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.1';
54 $reason = $reason ?? self::ReasonPhrases[$code] ?? 'Unknown status';
55 header("{$protocol} {$code} {$reason}");
56 return $this;
57 }
58 /**
59 * Returns HTTP response code.
60 */
61 public function getCode(): int
62 {
63 return $this->code;
64 }
65 /**
66 * Sends an HTTP header and overwrites previously sent header of the same name.
67 * @return static
68 * @throws Nette\InvalidStateException if HTTP headers have been sent
69 */
70 public function setHeader(string $name, ?string $value)
71 {
72 self::checkHeaders();
73 if ($value === null) {
74 header_remove($name);
75 } elseif (strcasecmp($name, 'Content-Length') === 0 && ini_get('zlib.output_compression')) {
76 // ignore, PHP bug #44164
77 } else {
78 header($name . ': ' . $value);
79 }
80 return $this;
81 }
82 /**
83 * Sends an HTTP header and doesn't overwrite previously sent header of the same name.
84 * @return static
85 * @throws Nette\InvalidStateException if HTTP headers have been sent
86 */
87 public function addHeader(string $name, string $value)
88 {
89 self::checkHeaders();
90 header($name . ': ' . $value, \false);
91 return $this;
92 }
93 /**
94 * Deletes a previously sent HTTP header.
95 * @return static
96 * @throws Nette\InvalidStateException if HTTP headers have been sent
97 */
98 public function deleteHeader(string $name)
99 {
100 self::checkHeaders();
101 header_remove($name);
102 return $this;
103 }
104 /**
105 * Sends a Content-type HTTP header.
106 * @return static
107 * @throws Nette\InvalidStateException if HTTP headers have been sent
108 */
109 public function setContentType(string $type, ?string $charset = null)
110 {
111 $this->setHeader('Content-Type', $type . ($charset ? '; charset=' . $charset : ''));
112 return $this;
113 }
114 /**
115 * Response should be downloaded with 'Save as' dialog.
116 * @return static
117 * @throws Nette\InvalidStateException if HTTP headers have been sent
118 */
119 public function sendAsFile(string $fileName)
120 {
121 $this->setHeader('Content-Disposition', 'attachment; filename="' . str_replace('"', '', $fileName) . '"; ' . "filename*=utf-8''" . rawurlencode($fileName));
122 return $this;
123 }
124 /**
125 * Redirects to another URL. Don't forget to quit the script then.
126 * @throws Nette\InvalidStateException if HTTP headers have been sent
127 */
128 public function redirect(string $url, int $code = self::S302_Found): void
129 {
130 $this->setCode($code);
131 $this->setHeader('Location', $url);
132 if (preg_match('#^https?:|^\s*+[a-z0-9+.-]*+[^:]#i', $url)) {
133 $escapedUrl = htmlspecialchars($url, \ENT_IGNORE | \ENT_QUOTES, 'UTF-8');
134 echo "<h1>Redirect</h1>\n\n<p><a href=\"{$escapedUrl}\">Please click here to continue</a>.</p>";
135 }
136 }
137 /**
138 * Sets the expiration of the HTTP document using the `Cache-Control` and `Expires` headers.
139 * The parameter is either a time interval (as text) or `null`, which disables caching.
140 * @return static
141 * @throws Nette\InvalidStateException if HTTP headers have been sent
142 */
143 public function setExpiration(?string $expire)
144 {
145 $this->setHeader('Pragma', null);
146 if (!$expire) {
147 // no cache
148 $this->setHeader('Cache-Control', 's-maxage=0, max-age=0, must-revalidate');
149 $this->setHeader('Expires', 'Mon, 23 Jan 1978 10:00:00 GMT');
150 return $this;
151 }
152 $expire = DateTime::from($expire);
153 $this->setHeader('Cache-Control', 'max-age=' . ($expire->format('U') - time()));
154 $this->setHeader('Expires', Helpers::formatDate($expire));
155 return $this;
156 }
157 /**
158 * Returns whether headers have already been sent from the server to the browser,
159 * so it is no longer possible to send headers or change the response code.
160 */
161 public function isSent(): bool
162 {
163 return headers_sent();
164 }
165 /**
166 * Returns the sent HTTP header, or `null` if it does not exist. The parameter is case-insensitive.
167 */
168 public function getHeader(string $header): ?string
169 {
170 if (func_num_args() > 1) {
171 trigger_error(__METHOD__ . '() parameter $default is deprecated, use operator ??', \E_USER_DEPRECATED);
172 }
173 $header .= ':';
174 $len = strlen($header);
175 foreach (headers_list() as $item) {
176 if (strncasecmp($item, $header, $len) === 0) {
177 return ltrim(substr($item, $len));
178 }
179 }
180 return null;
181 }
182 /**
183 * Returns all sent HTTP headers as associative array.
184 */
185 public function getHeaders(): array
186 {
187 $headers = [];
188 foreach (headers_list() as $header) {
189 $a = strpos($header, ':');
190 $headers[substr($header, 0, $a)] = (string) substr($header, $a + 2);
191 }
192 return $headers;
193 }
194 /**
195 * Sends a cookie.
196 * @param string|int|\DateTimeInterface $expire expiration time, value null means "until the browser session ends"
197 * @return static
198 * @throws Nette\InvalidStateException if HTTP headers have been sent
199 */
200 public function setCookie(string $name, string $value, $expire, ?string $path = null, ?string $domain = null, ?bool $secure = null, ?bool $httpOnly = null, ?string $sameSite = null)
201 {
202 self::checkHeaders();
203 $options = ['expires' => $expire ? (int) DateTime::from($expire)->format('U') : 0, 'path' => $path ?? ($domain ? '/' : $this->cookiePath), 'domain' => $domain ?? ($path ? '' : $this->cookieDomain), 'secure' => $secure ?? $this->cookieSecure, 'httponly' => $httpOnly ?? \true, 'samesite' => $sameSite = $sameSite ?? self::SameSiteLax];
204 if (\PHP_VERSION_ID >= 70300) {
205 setcookie($name, $value, $options);
206 } else {
207 setcookie($name, $value, $options['expires'], $options['path'] . ($sameSite ? "; SameSite={$sameSite}" : ''), $options['domain'], $options['secure'], $options['httponly']);
208 }
209 return $this;
210 }
211 /**
212 * Deletes a cookie.
213 * @throws Nette\InvalidStateException if HTTP headers have been sent
214 */
215 public function deleteCookie(string $name, ?string $path = null, ?string $domain = null, ?bool $secure = null): void
216 {
217 $this->setCookie($name, '', 0, $path, $domain, $secure);
218 }
219 private function checkHeaders(): void
220 {
221 if (\PHP_SAPI === 'cli') {
222 } elseif (headers_sent($file, $line)) {
223 throw new Nette\InvalidStateException('Cannot send header after HTTP headers have been sent' . ($file ? " (output started at {$file}:{$line})." : '.'));
224 } elseif ($this->warnOnBuffer && ob_get_length() && !array_filter(ob_get_status(\true), function (array $i): bool {
225 return !$i['chunk_size'];
226 })) {
227 trigger_error('Possible problem: you are sending a HTTP header while already having some data in output buffer. Try Tracy\OutputDebugger or send cookies/start session earlier.');
228 }
229 }
230 }
231