Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 4.3.x will end 7 October 2024 (12 months).
  • Bug fixes for security issues in 4.3.x will end 21 April 2025 (18 months).
  • PHP version: minimum PHP 8.0.0 Note: minimum PHP version has increased since Moodle 4.1. PHP 8.2.x is supported too.
   1  <?php
   2  
   3  declare(strict_types=1);
   4  
   5  namespace GuzzleHttp\Psr7;
   6  
   7  use Psr\Http\Message\StreamInterface;
   8  
   9  /**
  10   * Provides a read only stream that pumps data from a PHP callable.
  11   *
  12   * When invoking the provided callable, the PumpStream will pass the amount of
  13   * data requested to read to the callable. The callable can choose to ignore
  14   * this value and return fewer or more bytes than requested. Any extra data
  15   * returned by the provided callable is buffered internally until drained using
  16   * the read() function of the PumpStream. The provided callable MUST return
  17   * false when there is no more data to read.
  18   */
  19  final class PumpStream implements StreamInterface
  20  {
  21      /** @var callable|null */
  22      private $source;
  23  
  24      /** @var int|null */
  25      private $size;
  26  
  27      /** @var int */
  28      private $tellPos = 0;
  29  
  30      /** @var array */
  31      private $metadata;
  32  
  33      /** @var BufferStream */
  34      private $buffer;
  35  
  36      /**
  37       * @param callable(int): (string|null|false)  $source  Source of the stream data. The callable MAY
  38       *                                                     accept an integer argument used to control the
  39       *                                                     amount of data to return. The callable MUST
  40       *                                                     return a string when called, or false|null on error
  41       *                                                     or EOF.
  42       * @param array{size?: int, metadata?: array} $options Stream options:
  43       *                                                     - metadata: Hash of metadata to use with stream.
  44       *                                                     - size: Size of the stream, if known.
  45       */
  46      public function __construct(callable $source, array $options = [])
  47      {
  48          $this->source = $source;
  49          $this->size = $options['size'] ?? null;
  50          $this->metadata = $options['metadata'] ?? [];
  51          $this->buffer = new BufferStream();
  52      }
  53  
  54      public function __toString(): string
  55      {
  56          try {
  57              return Utils::copyToString($this);
  58          } catch (\Throwable $e) {
  59              if (\PHP_VERSION_ID >= 70400) {
  60                  throw $e;
  61              }
  62              trigger_error(sprintf('%s::__toString exception: %s', self::class, (string) $e), E_USER_ERROR);
  63              return '';
  64          }
  65      }
  66  
  67      public function close(): void
  68      {
  69          $this->detach();
  70      }
  71  
  72      public function detach()
  73      {
  74          $this->tellPos = 0;
  75          $this->source = null;
  76  
  77          return null;
  78      }
  79  
  80      public function getSize(): ?int
  81      {
  82          return $this->size;
  83      }
  84  
  85      public function tell(): int
  86      {
  87          return $this->tellPos;
  88      }
  89  
  90      public function eof(): bool
  91      {
  92          return $this->source === null;
  93      }
  94  
  95      public function isSeekable(): bool
  96      {
  97          return false;
  98      }
  99  
 100      public function rewind(): void
 101      {
 102          $this->seek(0);
 103      }
 104  
 105      public function seek($offset, $whence = SEEK_SET): void
 106      {
 107          throw new \RuntimeException('Cannot seek a PumpStream');
 108      }
 109  
 110      public function isWritable(): bool
 111      {
 112          return false;
 113      }
 114  
 115      public function write($string): int
 116      {
 117          throw new \RuntimeException('Cannot write to a PumpStream');
 118      }
 119  
 120      public function isReadable(): bool
 121      {
 122          return true;
 123      }
 124  
 125      public function read($length): string
 126      {
 127          $data = $this->buffer->read($length);
 128          $readLen = strlen($data);
 129          $this->tellPos += $readLen;
 130          $remaining = $length - $readLen;
 131  
 132          if ($remaining) {
 133              $this->pump($remaining);
 134              $data .= $this->buffer->read($remaining);
 135              $this->tellPos += strlen($data) - $readLen;
 136          }
 137  
 138          return $data;
 139      }
 140  
 141      public function getContents(): string
 142      {
 143          $result = '';
 144          while (!$this->eof()) {
 145              $result .= $this->read(1000000);
 146          }
 147  
 148          return $result;
 149      }
 150  
 151      /**
 152       * {@inheritdoc}
 153       *
 154       * @return mixed
 155       */
 156      public function getMetadata($key = null)
 157      {
 158          if (!$key) {
 159              return $this->metadata;
 160          }
 161  
 162          return $this->metadata[$key] ?? null;
 163      }
 164  
 165      private function pump(int $length): void
 166      {
 167          if ($this->source) {
 168              do {
 169                  $data = call_user_func($this->source, $length);
 170                  if ($data === false || $data === null) {
 171                      $this->source = null;
 172                      return;
 173                  }
 174                  $this->buffer->write($data);
 175                  $length -= strlen($data);
 176              } while ($length > 0);
 177          }
 178      }
 179  }