ILIAS  release_10 Revision v10.1-43-ga1241a92c2f
DefaultResponseSenderStrategy.php
Go to the documentation of this file.
1 <?php
2 
4 
6 
7 /******************************************************************************
8  *
9  * This file is part of ILIAS, a powerful learning management system.
10  *
11  * ILIAS is licensed with the GPL-3.0, you should have received a copy
12  * of said license along with the source code.
13  *
14  * If this is not the case or you just want to try ILIAS, you'll find
15  * us at:
16  * https://www.ilias.de
17  * https://github.com/ILIAS-eLearning
18  *
19  *****************************************************************************/
29 {
30  private const METHOD_FPASSTHRU = 'fpassthru';
31  private const METHOD_READFILE = 'readfile';
32  private string $method;
33  private int $chunk_size;
34  private int $memory_limit;
35 
36  public function __construct()
37  {
38  $this->memory_limit = $this->initMemoryLimit();
39  $this->chunk_size = $this->initChunkSize();
40  $this->method = self::METHOD_FPASSTHRU;
41  }
42 
43  private function initMemoryLimit(): int
44  {
45  $ini_memory_limit = ini_get('memory_limit');
46  $memory_limit = null;
47  if (preg_match('/^(\d+)(.)$/', $ini_memory_limit, $matches)) {
48  switch (($matches[2] ?? null)) {
49  case 'G':
50  $memory_limit = (int) $matches[1] * 1024 * 1024 * 1024; // nnnG -> nnn GB
51  break;
52  case 'M':
53  $memory_limit = (int) $matches[1] * 1024 * 1024; // nnnM -> nnn MB
54  break;
55  case 'K':
56  $memory_limit = (int) $matches[1] * 1024; // nnnK -> nnn KB
57  break;
58  default:
59  $memory_limit = (int) $matches[1]; // nnn -> nnn B
60  }
61  }
62 
63  return $memory_limit ?? 128 * 1024 * 1024;
64  }
65 
66  private function initChunkSize(): int
67  {
68  return (int) round(max($this->memory_limit / 4, 8 * 1024));
69  }
70 
78  public function sendResponse(ResponseInterface $response): void
79  {
80  //check if the request is already send
81  if (headers_sent()) {
82  throw new ResponseSendingException("Response was already sent.");
83  }
84 
85  //set status code
86  http_response_code($response->getStatusCode());
87 
88  //render all headers
89  foreach (array_keys($response->getHeaders()) as $key) {
90  // See Mantis #37385.
91  if (strtolower($key) === 'set-cookie') {
92  foreach ($response->getHeader($key) as $header) {
93  header("$key: " . $header, false);
94  }
95  } else {
96  header("$key: " . $response->getHeaderLine($key));
97  }
98  }
99 
100  //rewind body stream
101  $stream = $response->getBody();
102  $stream->rewind();
103 
104  // check body size
105  $body_size = $stream->getSize();
106  if ($body_size > $this->memory_limit) {
107  $this->method = self::METHOD_READFILE;
108  }
109 
110  //detach psr-7 stream from resource
111  $resource = $stream->detach();
112 
113  $sendStatus = false;
114 
115  if (is_resource($resource)) {
116  set_time_limit(0);
117  try {
118  ob_end_clean(); // see https://mantis.ilias.de/view.php?id=32046
119  } catch (\Throwable $t) {
120  }
121  switch ($this->method) {
122  case self::METHOD_FPASSTHRU:
123  $sendStatus = fpassthru($resource);
124  break;
125  case self::METHOD_READFILE:
126  // more memory friendly than fpassthru
127  $sendStatus = true;
128  while (!feof($resource)) {
129  echo $return = fread($resource, $this->chunk_size);
130  $sendStatus = $sendStatus && $return !== false;
131 
132  }
133  break;
134  }
135 
136  //free up resources
137  fclose($resource);
138  }
139 
140  //check if the body was successfully send to the client
141  if ($sendStatus === false) {
142  throw new ResponseSendingException("Could not send body content to client.");
143  }
144  }
145 }
$response
Definition: xapitoken.php:90
sendResponse(ResponseInterface $response)
Sends the rendered response to the client.
header()
expected output: > ILIAS shows the rendered Component.
Definition: header.php:13