# RangeStream

> Read-only PSR-7 stream limited to a byte range of an underlying stream.

[API reference](/docs/api/index.md) › [PSR bridge](/docs/extensions/psr-bridge/reference/index.md) › [adapter](/docs/extensions/psr-bridge/reference/adapter/index.md) › RangeStream

| final class | `yii2\extensions\psrbridge\adapter\RangeStream` |
| --- | --- |
| Implements | `Psr\Http\Message\StreamInterface` |
| Source | [`src/adapter/RangeStream.php`](https://github.com/yii2-extensions/psr-bridge/blob/main/src/adapter/RangeStream.php#L32) |

Read-only PSR-7 stream limited to a byte range of an underlying stream.

Wraps any `StreamInterface` and exposes only bytes in the inclusive range `[$begin, $end]`, enforcing the upper
bound so consumers cannot read past `$end` even when calling `StreamInterface::getContents()` or repeated
`StreamInterface::read()` calls. Reads proxy to the underlying stream chunk by chunk without buffering the range
into memory or temporary storage.

This is a bounded view, not a transparent decorator for the wrapped stream. The wrapped stream resource and its
original URI can represent bytes outside the declared range, so they are not exposed through [`detach()`](/docs/extensions/psr-bridge/reference/adapter/rangestream/index.md#method-detach) or
`getMetadata('uri')`.

## Methods

| Method | Description | Defined by |
| --- | --- | --- |
| [`__construct()`](#method-__construct) | Creates a new instance of the [`RangeStream`](/docs/extensions/psr-bridge/reference/adapter/rangestream/index.md) class. | `RangeStream` |
| [`__toString()`](#method-__toString) | Returns range contents as a `string`, rewinding seekable streams and returning an empty `string` on failure. | `RangeStream` |
| [`close()`](#method-close) | Closes the underlying stream and releases the internal reference. | `RangeStream` |
| [`detach()`](#method-detach) | Closes the wrapped stream and releases the internal reference without exposing the underlying resource. | `RangeStream` |
| [`eof()`](#method-eof) | Indicates whether the read cursor has reached the end of the range or the underlying stream. | `RangeStream` |
| [`getContents()`](#method-getContents) | Reads every remaining byte within the range and returns them as a single `string`. | `RangeStream` |
| [`getMetadata()`](#method-getMetadata) | Returns metadata associated with this bounded stream view. | `RangeStream` |
| [`getSize()`](#method-getSize) | Returns the size of the range, not the size of the underlying stream. | `RangeStream` |
| [`isReadable()`](#method-isReadable) | Indicates whether the underlying stream reports as readable. | `RangeStream` |
| [`isSeekable()`](#method-isSeekable) | Indicates whether seeking within the range is supported. | `RangeStream` |
| [`isWritable()`](#method-isWritable) | Indicates that the stream is read-only. | `RangeStream` |
| [`read()`](#method-read) | Reads up to `$length` bytes from the current position, capped at the bytes remaining in the range. | `RangeStream` |
| [`rewind()`](#method-rewind) | Rewinds the read cursor to the start of the range. | `RangeStream` |
| [`seek()`](#method-seek) | Seeks to an offset relative to the range, clamped to `[0, $length]`. | `RangeStream` |
| [`tell()`](#method-tell) | Returns the current read position relative to the start of the range. | `RangeStream` |
| [`write()`](#method-write) | Rejects every write attempt because the stream is read-only. | `RangeStream` |

## Method details

### __construct() <span id="method-__construct"></span>

```php
public function __construct(Psr\Http\Message\StreamInterface $stream, int $begin, int $end)
```

Creates a new instance of the [`RangeStream`](/docs/extensions/psr-bridge/reference/adapter/rangestream/index.md) class.

Rewinds the underlying stream to `$begin` only when it reports as seekable; non-seekable streams must already be
positioned at `$begin` by the caller.

**Parameters**

| Parameter | Type | Description |
| --- | --- | --- |
| `$stream` | `Psr\Http\Message\StreamInterface` | Underlying stream to expose a bounded view of. |
| `$begin` | `int` | Inclusive byte offset where the range starts (`0`-based, absolute in the underlying stream). |
| `$end` | `int` | Inclusive byte offset where the range ends (absolute in the underlying stream). |

**Throws**

- [`RuntimeException`](https://www.php.net/manual/en/class.runtimeexception.php): when the range is invalid or initial positioning fails.

### __toString() <span id="method-__toString"></span>

```php
public function __toString(): string
```

Returns range contents as a `string`, rewinding seekable streams and returning an empty `string` on failure.

On non-seekable streams, only the remaining range bytes can be returned.

**Returns**

`string`: Bytes within the range, or empty `string` when any error occurs.

### close() <span id="method-close"></span>

```php
public function close(): void
```

Closes the underlying stream and releases the internal reference.

### detach() <span id="method-detach"></span>

```php
public function detach(): resource|null
```

Closes the wrapped stream and releases the internal reference without exposing the underlying resource.

The wrapped resource is not detached because it can contain bytes outside this bounded view. Returning that
resource would allow consumers to bypass the range limit. Consumers that need the body contents must read through
[`read()`](/docs/extensions/psr-bridge/reference/adapter/rangestream/index.md#method-read) or [`getContents()`](/docs/extensions/psr-bridge/reference/adapter/rangestream/index.md#method-getContents).

**Returns**

`resource|null`: Always `null`; no detachable resource can safely represent the bounded view.

### eof() <span id="method-eof"></span>

```php
public function eof(): bool
```

Indicates whether the read cursor has reached the end of the range or the underlying stream.

**Returns**

`bool`: `true` when no more bytes can be read within the range; `false` otherwise.

### getContents() <span id="method-getContents"></span>

```php
public function getContents(): string
```

Reads every remaining byte within the range and returns them as a single `string`.

Iterates [`read()`](/docs/extensions/psr-bridge/reference/adapter/rangestream/index.md#method-read) in chunks of `READ_BUFFER_LENGTH` bytes until the range or the underlying stream
reaches EOF; no intermediate copy of the full range is held by the underlying stream.

**Returns**

`string`: Concatenation of every chunk read until the end of the range or underlying stream EOF.

### getMetadata() <span id="method-getMetadata"></span>

```php
public function getMetadata(string|null $key = null): mixed
```

Returns metadata associated with this bounded stream view.

Resource locator metadata such as `uri` is omitted because the underlying URI identifies the unbounded wrapped
stream, not this range-limited view.

**Parameters**

| Parameter | Type | Description |
| --- | --- | --- |
| `$key` | `string\|null` | Specific metadata key, or `null` to return the full metadata array. |

**Returns**

`mixed`: Metadata value for `$key`, the full metadata array when `$key` is `null`, or `null` when no stream is available and `$key` is provided.

### getSize() <span id="method-getSize"></span>

```php
public function getSize(): int|null
```

Returns the size of the range, not the size of the underlying stream.

**Returns**

`int|null`: Length of the range in bytes, or `null` after the underlying stream has been detached or closed.

### isReadable() <span id="method-isReadable"></span>

```php
public function isReadable(): bool
```

Indicates whether the underlying stream reports as readable.

**Returns**

`bool`: `true` when the underlying stream is readable; `false` otherwise or when no stream is available.

### isSeekable() <span id="method-isSeekable"></span>

```php
public function isSeekable(): bool
```

Indicates whether seeking within the range is supported.

**Returns**

`bool`: `true` when the underlying stream is seekable; `false` otherwise or when no stream is available.

### isWritable() <span id="method-isWritable"></span>

```php
public function isWritable(): bool
```

Indicates that the stream is read-only.

**Returns**

`bool`: Always `false`; range streams reject all writes.

### read() <span id="method-read"></span>

```php
public function read(int $length): string
```

Reads up to `$length` bytes from the current position, capped at the bytes remaining in the range.

**Parameters**

| Parameter | Type | Description |
| --- | --- | --- |
| `$length` | `int` | Maximum number of bytes to read. |

**Returns**

`string`: Bytes read, or empty `string` at EOF, when closed or detached, or when `$length` is `0`.

**Throws**

- [`RuntimeException`](https://www.php.net/manual/en/class.runtimeexception.php): when the length is negative or an underlying operation fails.

### rewind() <span id="method-rewind"></span>

```php
public function rewind(): void
```

Rewinds the read cursor to the start of the range.

**Throws**

- [`RuntimeException`](https://www.php.net/manual/en/class.runtimeexception.php): when the stream is closed or seeking fails.

### seek() <span id="method-seek"></span>

```php
public function seek(int $offset, int $whence = \SEEK_SET): void
```

Seeks to an offset relative to the range, clamped to `[0, $length]`.

**Parameters**

| Parameter | Type | Description |
| --- | --- | --- |
| `$offset` | `int` | Byte offset to seek to, interpreted according to `$whence`. |
| `$whence` | `int` | One of `SEEK_SET` (absolute), `SEEK_CUR` (relative to current), `SEEK_END` (relative to end). |

**Throws**

- [`RuntimeException`](https://www.php.net/manual/en/class.runtimeexception.php): when the target/mode is invalid, the stream is closed, or seeking fails.

### tell() <span id="method-tell"></span>

```php
public function tell(): int
```

Returns the current read position relative to the start of the range.

**Returns**

`int`: Position in bytes within `[0, $length]`.

**Throws**

- [`RuntimeException`](https://www.php.net/manual/en/class.runtimeexception.php): when the stream is closed or its position is unavailable.

### write() <span id="method-write"></span>

```php
public function write(string $string): int
```

Rejects every write attempt because the stream is read-only.

**Parameters**

| Parameter | Type | Description |
| --- | --- | --- |
| `$string` | `string` | Bytes the caller attempted to write (ignored). |

**Returns**

`int`: Never returns; included to satisfy the `StreamInterface` contract.

**Throws**

- [`RuntimeException`](https://www.php.net/manual/en/class.runtimeexception.php): always.