ILIAS  release_8 Revision v8.19
All Data Structures Namespaces Files Functions Variables Modules Pages
PHPChunked.php
Go to the documentation of this file.
1 <?php
2 
19 declare(strict_types=1);
20 
22 
26 
34 final class PHPChunked implements ilFileDeliveryType
35 {
36  private \ILIAS\HTTP\Services $httpService;
37 
38 
44  public function __construct(Services $httpState)
45  {
46  $this->httpService = $httpState;
47  }
48 
49 
53  public function doesFileExists(string $path_to_file): bool
54  {
55  return is_readable($path_to_file);
56  }
57 
58 
62  public function prepare(string $path_to_file): bool
63  {
64  // nothing to do here
65  return true;
66  }
67 
68 
72  public function deliver(string $path_to_file, bool $file_marked_to_delete): void
73  {
74  $file = $path_to_file;
75  $fp = @fopen($file, 'rb');
76  // see https://mantis.ilias.de/view.php?id=36970
77  if ($fp === false) {
78  $response = $this->httpService->response()->withStatus(404);
79  $this->httpService->saveResponse($response);
80  $this->close();
81  }
82 
83  $size = filesize($file); // File size
84  $length = $size; // Content length
85  $start = 0; // Start byte
86  $end = $size - 1; // End byte
87  // Now that we've gotten so far without errors we send the accept range header
88  /* At the moment we only support single ranges.
89  * Multiple ranges requires some more work to ensure it works correctly
90  * and comply with the spesifications: http://www.w3.org/Protocols/rfc2616/rfc2616-sec19.html#sec19.2
91  *
92  * Multirange support annouces itself with:
93  * header('Accept-Ranges: bytes');
94  *
95  * Multirange content must be sent with multipart/byteranges mediatype,
96  * (mediatype = mimetype)
97  * as well as a boundry header to indicate the various chunks of data.
98  */
99  $response = $this->httpService->response()->withHeader("Accept-Ranges", "0-$length");
100  $this->httpService->saveResponse($response);
101  $server = $this->httpService->request()->getServerParams();
102  // header('Accept-Ranges: bytes');
103  // multipart/byteranges
104  // http://www.w3.org/Protocols/rfc2616/rfc2616-sec19.html#sec19.2
105  if (isset($server['HTTP_RANGE'])) {
106  $c_start = $start;
107  $c_end = $end;
108 
109  // Extract the range string
110  [, $range] = explode('=', $server['HTTP_RANGE'], 2);
111  // Make sure the client hasn't sent us a multibyte range
112  if (strpos($range, ',') !== false) {
113  // (?) Shoud this be issued here, or should the first
114  // range be used? Or should the header be ignored and
115  // we output the whole content?
116  $response = $this->httpService->response()->withStatus(416)->withHeader(ResponseHeader::CONTENT_RANGE, "bytes $start-$end/$size");
117  $this->httpService->saveResponse($response);
118 
119  //header("Content-Range: bytes $start-$end/$size");
120  // (?) Echo some info to the client?
121  $this->close();
122  } // fim do if
123  // If the range starts with an '-' we start from the beginning
124  // If not, we forward the file pointer
125  // And make sure to get the end byte if spesified
126  if ($range[0] === '-') {
127  // The n-number of the last bytes is requested
128  $c_start = $size - substr($range, 1);
129  } else {
130  $range = explode('-', $range);
131  $c_start = $range[0];
132  $c_end = (isset($range[1]) && is_numeric($range[1])) ? $range[1] : $size;
133  } // fim do if
134  /* Check the range and make sure it's treated according to the specs.
135  * http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html
136  */
137  // End bytes can not be larger than $end.
138  $c_end = ($c_end > $end) ? $end : $c_end;
139  // Validate the requested range and return an error if it's not correct.
140  if ($c_start > $c_end || $c_start > $size - 1 || $c_end >= $size) {
141  $response = $this->httpService->response()->withStatus(416)->withHeader(ResponseHeader::CONTENT_RANGE, "bytes $start-$end/$size");
142 
143  $this->httpService->saveResponse($response);
144  // (?) Echo some info to the client?
145  $this->close();
146  } // fim do if
147 
148  $start = $c_start;
149  $end = $c_end;
150  $length = $end - $start + 1; // Calculate new content length
151  fseek($fp, (int) $start);
152 
153  $response = $this->httpService->response()->withStatus(206);
154 
155  $this->httpService->saveResponse($response);
156  } // fim do if
157 
158  // Notify the client the byte range we'll be outputting
159  $response = $this->httpService->response()->withHeader(ResponseHeader::CONTENT_RANGE, "bytes $start-$end/$size")->withHeader(ResponseHeader::CONTENT_LENGTH, $length);
160 
161  $this->httpService->saveResponse($response);
162 
163  //render response and start buffered download
164  $this->httpService->sendResponse();
165 
166  // Start buffered download
167  $buffer = 1024 * 8;
168  while (!feof($fp) && ($p = ftell($fp)) <= $end) {
169  if ($p + $buffer > $end) {
170  // In case we're only outputtin a chunk, make sure we don't
171  // read past the length
172  $buffer = $end - $p + 1;
173  } // fim do if
174 
175  set_time_limit(0); // Reset time limit for big files
176  echo fread($fp, $buffer);
177  flush(); // Free up memory. Otherwise large files will trigger PHP's memory limit.
178  } // fim do while
179 
180  fclose($fp);
181  }
182 
183 
187  public function supportsInlineDelivery(): bool
188  {
189  return true;
190  }
191 
192 
196  public function supportsAttachmentDelivery(): bool
197  {
198  return true;
199  }
200 
201 
205  public function supportsStreaming(): bool
206  {
207  return true;
208  }
209 
210 
211  private function close(): void
212  {
213  //render response
214  $this->httpService->sendResponse();
215  exit;
216  }
217 
218 
222  public function handleFileDeletion(string $path_to_file): bool
223  {
224  return unlink($path_to_file);
225  }
226 }
exit
Definition: login.php:28
handleFileDeletion(string $path_to_file)
bool
Definition: PHPChunked.php:222
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
__construct(Services $httpState)
PHP constructor.
Definition: PHPChunked.php:44
deliver(string $path_to_file, bool $file_marked_to_delete)
absolute path to fileThis is needed at this point for header-based delivery methodsvoid ...
Definition: PHPChunked.php:72
$server
$response