Verzeichnisstruktur phpBB-3.3.16


Veröffentlicht
27.04.2026

So funktioniert es


Auf das letzte Element klicken. Dies geht jeweils ein Schritt zurück

Auf das Icon klicken, dies öffnet das Verzeichnis. Nochmal klicken schließt das Verzeichnis.
Auf den Verzeichnisnamen klicken, dies zeigt nur das Verzeichnis mit Inhalt an

      (Beispiel Datei-Icons)

Auf das Icon klicken um den Quellcode anzuzeigen

BinaryFileResponse.php

Zuletzt modifiziert: 01.05.2026, 11:26 - Dateigröße: 12.24 KiB


 001  <?php
 002   
 003  /*
 004   * This file is part of the Symfony package.
 005   *
 006   * (c) Fabien Potencier <fabien@symfony.com>
 007   *
 008   * For the full copyright and license information, please view the LICENSE
 009   * file that was distributed with this source code.
 010   */
 011   
 012  namespace Symfony\Component\HttpFoundation;
 013   
 014  use Symfony\Component\HttpFoundation\File\Exception\FileException;
 015  use Symfony\Component\HttpFoundation\File\File;
 016   
 017  /**
 018   * BinaryFileResponse represents an HTTP response delivering a file.
 019   *
 020   * @author Niklas Fiekas <niklas.fiekas@tu-clausthal.de>
 021   * @author stealth35 <stealth35-php@live.fr>
 022   * @author Igor Wiedler <igor@wiedler.ch>
 023   * @author Jordan Alliot <jordan.alliot@gmail.com>
 024   * @author Sergey Linnik <linniksa@gmail.com>
 025   */
 026  class BinaryFileResponse extends Response
 027  {
 028      protected static $trustXSendfileTypeHeader = false;
 029   
 030      /**
 031       * @var File
 032       */
 033      protected $file;
 034      protected $offset = 0;
 035      protected $maxlen = -1;
 036      protected $deleteFileAfterSend = false;
 037   
 038      /**
 039       * @param \SplFileInfo|string $file               The file to stream
 040       * @param int                 $status             The response status code
 041       * @param array               $headers            An array of response headers
 042       * @param bool                $public             Files are public by default
 043       * @param string|null         $contentDisposition The type of Content-Disposition to set automatically with the filename
 044       * @param bool                $autoEtag           Whether the ETag header should be automatically set
 045       * @param bool                $autoLastModified   Whether the Last-Modified header should be automatically set
 046       */
 047      public function __construct($file, $status = 200, $headers = [], $public = true, $contentDisposition = null, $autoEtag = false, $autoLastModified = true)
 048      {
 049          parent::__construct(null, $status, $headers);
 050   
 051          $this->setFile($file, $contentDisposition, $autoEtag, $autoLastModified);
 052   
 053          if ($public) {
 054              $this->setPublic();
 055          }
 056      }
 057   
 058      /**
 059       * @param \SplFileInfo|string $file               The file to stream
 060       * @param int                 $status             The response status code
 061       * @param array               $headers            An array of response headers
 062       * @param bool                $public             Files are public by default
 063       * @param string|null         $contentDisposition The type of Content-Disposition to set automatically with the filename
 064       * @param bool                $autoEtag           Whether the ETag header should be automatically set
 065       * @param bool                $autoLastModified   Whether the Last-Modified header should be automatically set
 066       *
 067       * @return static
 068       */
 069      public static function create($file = null, $status = 200, $headers = [], $public = true, $contentDisposition = null, $autoEtag = false, $autoLastModified = true)
 070      {
 071          return new static($file, $status, $headers, $public, $contentDisposition, $autoEtag, $autoLastModified);
 072      }
 073   
 074      /**
 075       * Sets the file to stream.
 076       *
 077       * @param \SplFileInfo|string $file               The file to stream
 078       * @param string              $contentDisposition
 079       * @param bool                $autoEtag
 080       * @param bool                $autoLastModified
 081       *
 082       * @return $this
 083       *
 084       * @throws FileException
 085       */
 086      public function setFile($file, $contentDisposition = null, $autoEtag = false, $autoLastModified = true)
 087      {
 088          if (!$file instanceof File) {
 089              if ($file instanceof \SplFileInfo) {
 090                  $file = new File($file->getPathname());
 091              } else {
 092                  $file = new File((string) $file);
 093              }
 094          }
 095   
 096          if (!$file->isReadable()) {
 097              throw new FileException('File must be readable.');
 098          }
 099   
 100          $this->file = $file;
 101   
 102          if ($autoEtag) {
 103              $this->setAutoEtag();
 104          }
 105   
 106          if ($autoLastModified) {
 107              $this->setAutoLastModified();
 108          }
 109   
 110          if ($contentDisposition) {
 111              $this->setContentDisposition($contentDisposition);
 112          }
 113   
 114          return $this;
 115      }
 116   
 117      /**
 118       * Gets the file.
 119       *
 120       * @return File The file to stream
 121       */
 122      public function getFile()
 123      {
 124          return $this->file;
 125      }
 126   
 127      /**
 128       * Automatically sets the Last-Modified header according the file modification date.
 129       */
 130      public function setAutoLastModified()
 131      {
 132          $this->setLastModified(\DateTime::createFromFormat('U', $this->file->getMTime()));
 133   
 134          return $this;
 135      }
 136   
 137      /**
 138       * Automatically sets the ETag header according to the checksum of the file.
 139       */
 140      public function setAutoEtag()
 141      {
 142          $this->setEtag(base64_encode(hash_file('sha256', $this->file->getPathname(), true)));
 143   
 144          return $this;
 145      }
 146   
 147      /**
 148       * Sets the Content-Disposition header with the given filename.
 149       *
 150       * @param string $disposition      ResponseHeaderBag::DISPOSITION_INLINE or ResponseHeaderBag::DISPOSITION_ATTACHMENT
 151       * @param string $filename         Optionally use this UTF-8 encoded filename instead of the real name of the file
 152       * @param string $filenameFallback A fallback filename, containing only ASCII characters. Defaults to an automatically encoded filename
 153       *
 154       * @return $this
 155       */
 156      public function setContentDisposition($disposition, $filename = '', $filenameFallback = '')
 157      {
 158          if ('' === $filename) {
 159              $filename = $this->file->getFilename();
 160          }
 161   
 162          if ('' === $filenameFallback && (!preg_match('/^[\x20-\x7e]*$/', $filename) || false !== strpos($filename, '%'))) {
 163              $encoding = mb_detect_encoding($filename, null, true) ?: '8bit';
 164   
 165              for ($i = 0, $filenameLength = mb_strlen($filename, $encoding); $i < $filenameLength; ++$i) {
 166                  $char = mb_substr($filename, $i, 1, $encoding);
 167   
 168                  if ('%' === $char || \ord($char) < 32 || \ord($char) > 126) {
 169                      $filenameFallback .= '_';
 170                  } else {
 171                      $filenameFallback .= $char;
 172                  }
 173              }
 174          }
 175   
 176          $dispositionHeader = $this->headers->makeDisposition($disposition, $filename, $filenameFallback);
 177          $this->headers->set('Content-Disposition', $dispositionHeader);
 178   
 179          return $this;
 180      }
 181   
 182      /**
 183       * {@inheritdoc}
 184       */
 185      public function prepare(Request $request)
 186      {
 187          if (!$this->headers->has('Content-Type')) {
 188              $this->headers->set('Content-Type', $this->file->getMimeType() ?: 'application/octet-stream');
 189          }
 190   
 191          if ('HTTP/1.0' !== $request->server->get('SERVER_PROTOCOL')) {
 192              $this->setProtocolVersion('1.1');
 193          }
 194   
 195          $this->ensureIEOverSSLCompatibility($request);
 196   
 197          $this->offset = 0;
 198          $this->maxlen = -1;
 199   
 200          if (false === $fileSize = $this->file->getSize()) {
 201              return $this;
 202          }
 203          $this->headers->set('Content-Length', $fileSize);
 204   
 205          if (!$this->headers->has('Accept-Ranges')) {
 206              // Only accept ranges on safe HTTP methods
 207              $this->headers->set('Accept-Ranges', $request->isMethodSafe(false) ? 'bytes' : 'none');
 208          }
 209   
 210          if (self::$trustXSendfileTypeHeader && $request->headers->has('X-Sendfile-Type')) {
 211              // Use X-Sendfile, do not send any content.
 212              $type = $request->headers->get('X-Sendfile-Type');
 213              $path = $this->file->getRealPath();
 214              // Fall back to scheme://path for stream wrapped locations.
 215              if (false === $path) {
 216                  $path = $this->file->getPathname();
 217              }
 218              if ('x-accel-redirect' === strtolower($type)) {
 219                  // Do X-Accel-Mapping substitutions.
 220                  // @link https://www.nginx.com/resources/wiki/start/topics/examples/x-accel/#x-accel-redirect
 221                  foreach (explode(',', $request->headers->get('X-Accel-Mapping', '')) as $mapping) {
 222                      $mapping = explode('=', $mapping, 2);
 223   
 224                      if (2 === \count($mapping)) {
 225                          $pathPrefix = trim($mapping[0]);
 226                          $location = trim($mapping[1]);
 227   
 228                          if (substr($path, 0, \strlen($pathPrefix)) === $pathPrefix) {
 229                              $path = $location.substr($path, \strlen($pathPrefix));
 230                              // Only set X-Accel-Redirect header if a valid URI can be produced
 231                              // as nginx does not serve arbitrary file paths.
 232                              $this->headers->set($type, $path);
 233                              $this->maxlen = 0;
 234                              break;
 235                          }
 236                      }
 237                  }
 238              } else {
 239                  $this->headers->set($type, $path);
 240                  $this->maxlen = 0;
 241              }
 242          } elseif ($request->headers->has('Range') && $request->isMethod('GET')) {
 243              // Process the range headers.
 244              if (!$request->headers->has('If-Range') || $this->hasValidIfRangeHeader($request->headers->get('If-Range'))) {
 245                  $range = $request->headers->get('Range');
 246   
 247                  if (0 === strpos($range, 'bytes=')) {
 248                      list($start, $end) = explode('-', substr($range, 6), 2) + [0];
 249   
 250                      $end = ('' === $end) ? $fileSize - 1 : (int) $end;
 251   
 252                      if ('' === $start) {
 253                          $start = $fileSize - $end;
 254                          $end = $fileSize - 1;
 255                      } else {
 256                          $start = (int) $start;
 257                      }
 258   
 259                      if ($start <= $end) {
 260                          $end = min($end, $fileSize - 1);
 261                          if ($start < 0 || $start > $end) {
 262                              $this->setStatusCode(416);
 263                              $this->headers->set('Content-Range', sprintf('bytes */%s', $fileSize));
 264                          } elseif ($end - $start < $fileSize - 1) {
 265                              $this->maxlen = $end < $fileSize ? $end - $start + 1 : -1;
 266                              $this->offset = $start;
 267   
 268                              $this->setStatusCode(206);
 269                              $this->headers->set('Content-Range', sprintf('bytes %s-%s/%s', $start, $end, $fileSize));
 270                              $this->headers->set('Content-Length', $end - $start + 1);
 271                          }
 272                      }
 273                  }
 274              }
 275          }
 276   
 277          return $this;
 278      }
 279   
 280      private function hasValidIfRangeHeader($header)
 281      {
 282          if ($this->getEtag() === $header) {
 283              return true;
 284          }
 285   
 286          if (null === $lastModified = $this->getLastModified()) {
 287              return false;
 288          }
 289   
 290          return $lastModified->format('D, d M Y H:i:s').' GMT' === $header;
 291      }
 292   
 293      /**
 294       * Sends the file.
 295       *
 296       * {@inheritdoc}
 297       */
 298      public function sendContent()
 299      {
 300          if (!$this->isSuccessful()) {
 301              return parent::sendContent();
 302          }
 303   
 304          if (0 === $this->maxlen) {
 305              return $this;
 306          }
 307   
 308          $out = fopen('php://output', 'wb');
 309          $file = fopen($this->file->getPathname(), 'rb');
 310   
 311          stream_copy_to_stream($file, $out, $this->maxlen, $this->offset);
 312   
 313          fclose($out);
 314          fclose($file);
 315   
 316          if ($this->deleteFileAfterSend && file_exists($this->file->getPathname())) {
 317              unlink($this->file->getPathname());
 318          }
 319   
 320          return $this;
 321      }
 322   
 323      /**
 324       * {@inheritdoc}
 325       *
 326       * @throws \LogicException when the content is not null
 327       */
 328      public function setContent($content)
 329      {
 330          if (null !== $content) {
 331              throw new \LogicException('The content cannot be set on a BinaryFileResponse instance.');
 332          }
 333   
 334          return $this;
 335      }
 336   
 337      /**
 338       * {@inheritdoc}
 339       */
 340      public function getContent()
 341      {
 342          return false;
 343      }
 344   
 345      /**
 346       * Trust X-Sendfile-Type header.
 347       */
 348      public static function trustXSendfileTypeHeader()
 349      {
 350          self::$trustXSendfileTypeHeader = true;
 351      }
 352   
 353      /**
 354       * If this is set to true, the file will be unlinked after the request is sent
 355       * Note: If the X-Sendfile header is used, the deleteFileAfterSend setting will not be used.
 356       *
 357       * @param bool $shouldDelete
 358       *
 359       * @return $this
 360       */
 361      public function deleteFileAfterSend($shouldDelete)
 362      {
 363          $this->deleteFileAfterSend = $shouldDelete;
 364   
 365          return $this;
 366      }
 367  }
 368