PluginProbe ʕ •ᴥ•ʔ
GiveWP – Donation Plugin and Fundraising Platform / 4.16.5.1
GiveWP – Donation Plugin and Fundraising Platform v4.16.5.1
4.16.7.2 4.16.7.1 4.16.7 4.16.6.1 4.16.6 4.16.5.1 4.16.5 4.16.4 4.16.3 4.16.2 4.16.1 4.16.0 4.15.5 4.15.4 4.15.3 4.15.2 4.15.1 4.15.0 2.3.0 2.3.1 2.3.2 2.30.0 2.31.0 2.31.1 2.32.0 2.33.0 2.33.1 2.33.2 2.33.3 2.33.4 2.33.5 2.4.0 2.4.1 2.4.2 2.4.3 2.4.4 2.4.5 2.4.6 2.4.7 2.5.0 2.5.1 2.5.10 2.5.11 2.5.12 2.5.13 2.5.2 2.5.3 2.5.4 2.5.5 2.5.6 2.5.7 2.5.8 2.5.9 2.6.0 2.6.1 2.6.2 2.6.3 2.7.0 2.7.1 2.7.2 2.7.3 2.7.4 2.7.5 2.8.0 2.8.1 2.9.0 2.9.1 2.9.2 2.9.3 2.9.4 2.9.5 2.9.6 2.9.7 3.0.0 3.0.1 3.0.2 3.0.3 3.0.4 3.1.0 3.1.1 3.1.2 3.10.0 3.11.0 3.12.0 3.12.1 3.12.2 3.12.3 3.13.0 3.14.0 3.14.1 3.14.2 3.15.0 3.15.1 3.16.0 3.16.1 3.16.2 3.16.3 3.16.4 3.16.5 3.17.0 3.17.1 3.17.2 3.18.0 3.19.0 3.19.1 3.19.2 3.19.3 3.19.4 3.2.0 3.2.1 3.2.2 3.20.0 3.21.0 3.21.1 3.22.0 3.22.1 3.22.2 3.3.0 3.3.1 3.4.0 3.4.1 3.4.2 3.5.0 3.5.1 3.6.0 3.6.1 3.6.2 3.7.0 3.8.0 3.9.0 4.0.0 4.1.0 4.1.1 4.10.0 4.10.1 4.11.0 4.12.0 4.13.0 4.13.1 4.13.2 4.14.0 4.14.1 4.14.2 4.14.3 4.14.4 4.14.5 4.14.6 4.2.0 4.2.1 4.3.0 4.3.1 4.3.2 4.4.0 4.5.0 4.6.1 4.7.0 4.7.1 4.8.0 4.8.1 4.9.0 trunk 1.9.0 2.0.0 2.0.1 2.0.2 2.0.3 2.0.4 2.0.5 2.0.6 2.0.7 2.1.0 2.1.1 2.1.2 2.1.3 2.1.4 2.1.5 2.1.6 2.1.7 2.1.8 2.10.0 2.10.1 2.10.2 2.10.3 2.10.4 2.11.0 2.11.1 2.11.2 2.11.3 2.12.0 2.12.1 2.12.2 2.12.3 2.13.0 2.13.1 2.13.2 2.13.3 2.13.4 2.14.0 2.15.0 2.16.0 2.16.1 2.17.0 2.17.1 2.17.3 2.18.0 2.18.1 2.19.1 2.19.2 2.19.3 2.19.4 2.19.5 2.19.6 2.19.7 2.19.8 2.2.0 2.2.1 2.2.2 2.2.3 2.2.4 2.2.5 2.2.6 2.20.0 2.20.1 2.20.2 2.21.0 2.21.1 2.21.2 2.21.3 2.21.4 2.22.0 2.22.1 2.22.2 2.22.3 2.23.0 2.23.1 2.23.2 2.24.0 2.24.1 2.24.2 2.25.0 2.25.1 2.25.2 2.25.3 2.26.0 2.27.0 2.27.1 2.27.2 2.27.3 2.28.0 2.29.0 2.29.1 2.29.2
give / vendor / vendor-prefixed / symfony / http-foundation / BinaryFileResponse.php
give / vendor / vendor-prefixed / symfony / http-foundation Last commit date
Exception 1 year ago File 1 year ago RateLimiter 1 year ago Session 1 year ago Test 1 year ago AcceptHeader.php 1 year ago AcceptHeaderItem.php 1 year ago BinaryFileResponse.php 1 year ago Cookie.php 1 year ago ExpressionRequestMatcher.php 1 year ago FileBag.php 1 year ago HeaderBag.php 1 year ago HeaderUtils.php 1 year ago InputBag.php 1 year ago IpUtils.php 1 year ago JsonResponse.php 1 year ago LICENSE 1 year ago ParameterBag.php 1 year ago RedirectResponse.php 1 year ago Request.php 9 months ago RequestMatcher.php 1 year ago RequestMatcherInterface.php 1 year ago RequestStack.php 1 year ago Response.php 1 year ago ResponseHeaderBag.php 1 year ago ServerBag.php 1 year ago StreamedResponse.php 1 year ago UrlHelper.php 1 year ago
BinaryFileResponse.php
419 lines
1 <?php
2
3 /*
4 * This file is part of the Symfony package.
5 *
6 * (c) Fabien Potencier <fabien@symfony.com>
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12 namespace Give\Vendors\Symfony\Component\HttpFoundation;
13
14 use Give\Vendors\Symfony\Component\HttpFoundation\File\Exception\FileException;
15 use Give\Vendors\Symfony\Component\HttpFoundation\File\File;
16
17 /**
18 * BinaryFileResponse represents an HTTP response delivering a file.
19 *
20 * @author Niklas Fiekas <niklas.fiekas@tu-clausthal.de>
21 * @author stealth35 <stealth35-php@live.fr>
22 * @author Igor Wiedler <igor@wiedler.ch>
23 * @author Jordan Alliot <jordan.alliot@gmail.com>
24 * @author Sergey Linnik <linniksa@gmail.com>
25 */
26 class BinaryFileResponse extends Response
27 {
28 protected static $trustXSendfileTypeHeader = false;
29
30 /**
31 * @var File
32 */
33 protected $file;
34 protected $offset = 0;
35 protected $maxlen = -1;
36 protected $deleteFileAfterSend = false;
37 protected $chunkSize = 16 * 1024;
38
39 /**
40 * @param \SplFileInfo|string $file The file to stream
41 * @param int $status The response status code
42 * @param array $headers An array of response headers
43 * @param bool $public Files are public by default
44 * @param string|null $contentDisposition The type of Content-Disposition to set automatically with the filename
45 * @param bool $autoEtag Whether the ETag header should be automatically set
46 * @param bool $autoLastModified Whether the Last-Modified header should be automatically set
47 */
48 public function __construct($file, int $status = 200, array $headers = [], bool $public = true, ?string $contentDisposition = null, bool $autoEtag = false, bool $autoLastModified = true)
49 {
50 parent::__construct(null, $status, $headers);
51
52 $this->setFile($file, $contentDisposition, $autoEtag, $autoLastModified);
53
54 if ($public) {
55 $this->setPublic();
56 }
57 }
58
59 /**
60 * @param \SplFileInfo|string $file The file to stream
61 * @param int $status The response status code
62 * @param array $headers An array of response headers
63 * @param bool $public Files are public by default
64 * @param string|null $contentDisposition The type of Content-Disposition to set automatically with the filename
65 * @param bool $autoEtag Whether the ETag header should be automatically set
66 * @param bool $autoLastModified Whether the Last-Modified header should be automatically set
67 *
68 * @return static
69 *
70 * @deprecated since Symfony 5.2, use __construct() instead.
71 */
72 public static function create($file = null, int $status = 200, array $headers = [], bool $public = true, ?string $contentDisposition = null, bool $autoEtag = false, bool $autoLastModified = true)
73 {
74 trigger_deprecation('symfony/http-foundation', '5.2', 'The "%s()" method is deprecated, use "new %s()" instead.', __METHOD__, static::class);
75
76 return new static($file, $status, $headers, $public, $contentDisposition, $autoEtag, $autoLastModified);
77 }
78
79 /**
80 * Sets the file to stream.
81 *
82 * @param \SplFileInfo|string $file The file to stream
83 *
84 * @return $this
85 *
86 * @throws FileException
87 */
88 public function setFile($file, ?string $contentDisposition = null, bool $autoEtag = false, bool $autoLastModified = true)
89 {
90 if (!$file instanceof File) {
91 if ($file instanceof \SplFileInfo) {
92 $file = new File($file->getPathname());
93 } else {
94 $file = new File((string) $file);
95 }
96 }
97
98 if (!$file->isReadable()) {
99 throw new FileException('File must be readable.');
100 }
101
102 $this->file = $file;
103
104 if ($autoEtag) {
105 $this->setAutoEtag();
106 }
107
108 if ($autoLastModified) {
109 $this->setAutoLastModified();
110 }
111
112 if ($contentDisposition) {
113 $this->setContentDisposition($contentDisposition);
114 }
115
116 return $this;
117 }
118
119 /**
120 * Gets the file.
121 *
122 * @return File
123 */
124 public function getFile()
125 {
126 return $this->file;
127 }
128
129 /**
130 * Sets the response stream chunk size.
131 *
132 * @return $this
133 */
134 public function setChunkSize(int $chunkSize): self
135 {
136 if ($chunkSize < 1 || $chunkSize > \PHP_INT_MAX) {
137 throw new \LogicException('The chunk size of a BinaryFileResponse cannot be less than 1 or greater than PHP_INT_MAX.');
138 }
139
140 $this->chunkSize = $chunkSize;
141
142 return $this;
143 }
144
145 /**
146 * Automatically sets the Last-Modified header according the file modification date.
147 *
148 * @return $this
149 */
150 public function setAutoLastModified()
151 {
152 $this->setLastModified(\DateTime::createFromFormat('U', $this->file->getMTime()));
153
154 return $this;
155 }
156
157 /**
158 * Automatically sets the ETag header according to the checksum of the file.
159 *
160 * @return $this
161 */
162 public function setAutoEtag()
163 {
164 $this->setEtag(base64_encode(hash_file('sha256', $this->file->getPathname(), true)));
165
166 return $this;
167 }
168
169 /**
170 * Sets the Content-Disposition header with the given filename.
171 *
172 * @param string $disposition ResponseHeaderBag::DISPOSITION_INLINE or ResponseHeaderBag::DISPOSITION_ATTACHMENT
173 * @param string $filename Optionally use this UTF-8 encoded filename instead of the real name of the file
174 * @param string $filenameFallback A fallback filename, containing only ASCII characters. Defaults to an automatically encoded filename
175 *
176 * @return $this
177 */
178 public function setContentDisposition(string $disposition, string $filename = '', string $filenameFallback = '')
179 {
180 if ('' === $filename) {
181 $filename = $this->file->getFilename();
182 }
183
184 if ('' === $filenameFallback && (!preg_match('/^[\x20-\x7e]*$/', $filename) || str_contains($filename, '%'))) {
185 $encoding = mb_detect_encoding($filename, null, true) ?: '8bit';
186
187 for ($i = 0, $filenameLength = mb_strlen($filename, $encoding); $i < $filenameLength; ++$i) {
188 $char = mb_substr($filename, $i, 1, $encoding);
189
190 if ('%' === $char || \ord($char) < 32 || \ord($char) > 126) {
191 $filenameFallback .= '_';
192 } else {
193 $filenameFallback .= $char;
194 }
195 }
196 }
197
198 $dispositionHeader = $this->headers->makeDisposition($disposition, $filename, $filenameFallback);
199 $this->headers->set('Content-Disposition', $dispositionHeader);
200
201 return $this;
202 }
203
204 /**
205 * {@inheritdoc}
206 */
207 public function prepare(Request $request)
208 {
209 if ($this->isInformational() || $this->isEmpty()) {
210 parent::prepare($request);
211
212 $this->maxlen = 0;
213
214 return $this;
215 }
216
217 if (!$this->headers->has('Content-Type')) {
218 $this->headers->set('Content-Type', $this->file->getMimeType() ?: 'application/octet-stream');
219 }
220
221 parent::prepare($request);
222
223 $this->offset = 0;
224 $this->maxlen = -1;
225
226 if (false === $fileSize = $this->file->getSize()) {
227 return $this;
228 }
229 $this->headers->remove('Transfer-Encoding');
230 $this->headers->set('Content-Length', $fileSize);
231
232 if (!$this->headers->has('Accept-Ranges')) {
233 // Only accept ranges on safe HTTP methods
234 $this->headers->set('Accept-Ranges', $request->isMethodSafe() ? 'bytes' : 'none');
235 }
236
237 if (self::$trustXSendfileTypeHeader && $request->headers->has('X-Sendfile-Type')) {
238 // Use X-Sendfile, do not send any content.
239 $type = $request->headers->get('X-Sendfile-Type');
240 $path = $this->file->getRealPath();
241 // Fall back to scheme://path for stream wrapped locations.
242 if (false === $path) {
243 $path = $this->file->getPathname();
244 }
245 if ('x-accel-redirect' === strtolower($type)) {
246 // Do X-Accel-Mapping substitutions.
247 // @link https://github.com/rack/rack/blob/main/lib/rack/sendfile.rb
248 // @link https://mattbrictson.com/blog/accelerated-rails-downloads
249 if (!$request->headers->has('X-Accel-Mapping')) {
250 throw new \LogicException('The "X-Accel-Mapping" header must be set when "X-Sendfile-Type" is set to "X-Accel-Redirect".');
251 }
252 $parts = HeaderUtils::split($request->headers->get('X-Accel-Mapping'), ',=');
253 foreach ($parts as $part) {
254 [$pathPrefix, $location] = $part;
255 if (substr($path, 0, \strlen($pathPrefix)) === $pathPrefix) {
256 $path = $location.substr($path, \strlen($pathPrefix));
257 // Only set X-Accel-Redirect header if a valid URI can be produced
258 // as nginx does not serve arbitrary file paths.
259 $this->headers->set($type, $path);
260 $this->maxlen = 0;
261 break;
262 }
263 }
264 } else {
265 $this->headers->set($type, $path);
266 $this->maxlen = 0;
267 }
268 } elseif ($request->headers->has('Range') && $request->isMethod('GET')) {
269 // Process the range headers.
270 if (!$request->headers->has('If-Range') || $this->hasValidIfRangeHeader($request->headers->get('If-Range'))) {
271 $range = $request->headers->get('Range');
272
273 if (str_starts_with($range, 'bytes=')) {
274 [$start, $end] = explode('-', substr($range, 6), 2) + [1 => 0];
275
276 $end = ('' === $end) ? $fileSize - 1 : (int) $end;
277
278 if ('' === $start) {
279 $start = $fileSize - $end;
280 $end = $fileSize - 1;
281 } else {
282 $start = (int) $start;
283 }
284
285 if ($start <= $end) {
286 $end = min($end, $fileSize - 1);
287 if ($start < 0 || $start > $end) {
288 $this->setStatusCode(416);
289 $this->headers->set('Content-Range', sprintf('bytes */%s', $fileSize));
290 } elseif ($end - $start < $fileSize - 1) {
291 $this->maxlen = $end < $fileSize ? $end - $start + 1 : -1;
292 $this->offset = $start;
293
294 $this->setStatusCode(206);
295 $this->headers->set('Content-Range', sprintf('bytes %s-%s/%s', $start, $end, $fileSize));
296 $this->headers->set('Content-Length', $end - $start + 1);
297 }
298 }
299 }
300 }
301 }
302
303 if ($request->isMethod('HEAD')) {
304 $this->maxlen = 0;
305 }
306
307 return $this;
308 }
309
310 private function hasValidIfRangeHeader(?string $header): bool
311 {
312 if ($this->getEtag() === $header) {
313 return true;
314 }
315
316 if (null === $lastModified = $this->getLastModified()) {
317 return false;
318 }
319
320 return $lastModified->format('D, d M Y H:i:s').' GMT' === $header;
321 }
322
323 /**
324 * {@inheritdoc}
325 */
326 public function sendContent()
327 {
328 try {
329 if (!$this->isSuccessful()) {
330 return parent::sendContent();
331 }
332
333 if (0 === $this->maxlen) {
334 return $this;
335 }
336
337 $out = fopen('php://output', 'w');
338 $file = fopen($this->file->getPathname(), 'r');
339
340 ignore_user_abort(true);
341
342 if (0 !== $this->offset) {
343 fseek($file, $this->offset);
344 }
345
346 $length = $this->maxlen;
347 while ($length && !feof($file)) {
348 $read = $length > $this->chunkSize || 0 > $length ? $this->chunkSize : $length;
349
350 if (false === $data = fread($file, $read)) {
351 break;
352 }
353 while ('' !== $data) {
354 $read = fwrite($out, $data);
355 if (false === $read || connection_aborted()) {
356 break 2;
357 }
358 if (0 < $length) {
359 $length -= $read;
360 }
361 $data = substr($data, $read);
362 }
363 }
364
365 fclose($out);
366 fclose($file);
367 } finally {
368 if ($this->deleteFileAfterSend && is_file($this->file->getPathname())) {
369 unlink($this->file->getPathname());
370 }
371 }
372
373 return $this;
374 }
375
376 /**
377 * {@inheritdoc}
378 *
379 * @throws \LogicException when the content is not null
380 */
381 public function setContent(?string $content)
382 {
383 if (null !== $content) {
384 throw new \LogicException('The content cannot be set on a BinaryFileResponse instance.');
385 }
386
387 return $this;
388 }
389
390 /**
391 * {@inheritdoc}
392 */
393 public function getContent()
394 {
395 return false;
396 }
397
398 /**
399 * Trust X-Sendfile-Type header.
400 */
401 public static function trustXSendfileTypeHeader()
402 {
403 self::$trustXSendfileTypeHeader = true;
404 }
405
406 /**
407 * If this is set to true, the file will be unlinked after the request is sent
408 * Note: If the X-Sendfile header is used, the deleteFileAfterSend setting will not be used.
409 *
410 * @return $this
411 */
412 public function deleteFileAfterSend(bool $shouldDelete = true)
413 {
414 $this->deleteFileAfterSend = $shouldDelete;
415
416 return $this;
417 }
418 }
419