{"id":18941223,"url":"https://github.com/reactphp/stream","last_synced_at":"2025-05-13T20:22:16.002Z","repository":{"id":3856659,"uuid":"4941503","full_name":"reactphp/stream","owner":"reactphp","description":"Event-driven readable and writable streams for non-blocking I/O in ReactPHP.","archived":false,"fork":false,"pushed_at":"2025-03-12T07:52:01.000Z","size":455,"stargazers_count":658,"open_issues_count":2,"forks_count":62,"subscribers_count":28,"default_branch":"3.x","last_synced_at":"2025-05-08T04:03:14.490Z","etag":null,"topics":["php","reactphp","stream"],"latest_commit_sha":null,"homepage":"https://reactphp.org/stream/","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/reactphp.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null},"funding":{"github":["reactphp","clue","WyriHaximus"],"open_collective":"reactphp"}},"created_at":"2012-07-07T21:33:40.000Z","updated_at":"2025-05-01T19:56:31.000Z","dependencies_parsed_at":"2023-07-07T06:02:09.968Z","dependency_job_id":"32e92730-cb9f-4ef4-a31c-02f379a738f7","html_url":"https://github.com/reactphp/stream","commit_stats":{"total_commits":270,"total_committers":23,"mean_commits":11.73913043478261,"dds":0.4814814814814815,"last_synced_commit":"751374f91421e82117a2019fbb3b12bd47a7d0a8"},"previous_names":[],"tags_count":37,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/reactphp%2Fstream","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/reactphp%2Fstream/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/reactphp%2Fstream/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/reactphp%2Fstream/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/reactphp","download_url":"https://codeload.github.com/reactphp/stream/tar.gz/refs/heads/3.x","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253295578,"owners_count":21885651,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["php","reactphp","stream"],"created_at":"2024-11-08T12:26:46.045Z","updated_at":"2025-05-13T20:22:15.981Z","avatar_url":"https://github.com/reactphp.png","language":"PHP","readme":"# Stream\n\n[![CI status](https://github.com/reactphp/stream/actions/workflows/ci.yml/badge.svg)](https://github.com/reactphp/stream/actions)\n[![installs on Packagist](https://img.shields.io/packagist/dt/react/stream?color=blue\u0026label=installs%20on%20Packagist)](https://packagist.org/packages/react/stream)\n\nEvent-driven readable and writable streams for non-blocking I/O in [ReactPHP](https://reactphp.org/).\n\n\u003e **Development version:** This branch contains the code for the upcoming v3\n\u003e release. For the code of the current stable v1 release, check out the\n\u003e [`1.x` branch](https://github.com/reactphp/stream/tree/1.x).\n\u003e\n\u003e The upcoming v3 release will be the way forward for this package. However,\n\u003e we will still actively support v1 for those not yet on the latest version.\n\u003e See also [installation instructions](#install) for more details.\n\nIn order to make the [EventLoop](https://github.com/reactphp/event-loop)\neasier to use, this component introduces the powerful concept of \"streams\".\nStreams allow you to efficiently process huge amounts of data (such as a multi\nGigabyte file download) in small chunks without having to store everything in\nmemory at once.\nThey are very similar to the streams found in PHP itself,\nbut have an interface more suited for async, non-blocking I/O.\n\n**Table of contents**\n\n* [Stream usage](#stream-usage)\n  * [ReadableStreamInterface](#readablestreaminterface)\n    * [data event](#data-event)\n    * [end event](#end-event)\n    * [error event](#error-event)\n    * [close event](#close-event)\n    * [isReadable()](#isreadable)\n    * [pause()](#pause)\n    * [resume()](#resume)\n    * [pipe()](#pipe)\n    * [close()](#close)\n  * [WritableStreamInterface](#writablestreaminterface)\n    * [drain event](#drain-event)\n    * [pipe event](#pipe-event)\n    * [error event](#error-event-1)\n    * [close event](#close-event-1)\n    * [isWritable()](#iswritable)\n    * [write()](#write)\n    * [end()](#end)\n    * [close()](#close-1)\n  * [DuplexStreamInterface](#duplexstreaminterface)\n* [Creating streams](#creating-streams)\n  * [ReadableResourceStream](#readableresourcestream)\n  * [WritableResourceStream](#writableresourcestream)\n  * [DuplexResourceStream](#duplexresourcestream)\n  * [ThroughStream](#throughstream)\n  * [CompositeStream](#compositestream)\n* [Usage](#usage)\n* [Install](#install)\n* [Tests](#tests)\n* [License](#license)\n* [More](#more)\n\n## Stream usage\n\nReactPHP uses the concept of \"streams\" throughout its ecosystem to provide a\nconsistent higher-level abstraction for processing streams of arbitrary data\ncontents and size.\nWhile a stream itself is a quite low-level concept, it can be used as a powerful\nabstraction to build higher-level components and protocols on top.\n\nIf you're new to this concept, it helps to think of them as a water pipe:\nYou can consume water from a source or you can produce water and forward (pipe)\nit to any destination (sink).\n\nSimilarly, streams can either be\n\n* readable (such as `STDIN` terminal input) or\n* writable (such as `STDOUT` terminal output) or\n* duplex (both readable *and* writable, such as a TCP/IP connection)\n\nAccordingly, this package defines the following three interfaces\n\n* [`ReadableStreamInterface`](#readablestreaminterface)\n* [`WritableStreamInterface`](#writablestreaminterface)\n* [`DuplexStreamInterface`](#duplexstreaminterface)\n\n### ReadableStreamInterface\n\nThe `ReadableStreamInterface` is responsible for providing an interface for\nread-only streams and the readable side of duplex streams.\n\nBesides defining a few methods, this interface also implements the\n`EventEmitterInterface` which allows you to react to certain events.\n\nThe event callback functions MUST be a valid `callable` that obeys strict\nparameter definitions and MUST accept event parameters exactly as documented.\nThe event callback functions MUST NOT throw an `Exception`.\nThe return value of the event callback functions will be ignored and has no\neffect, so for performance reasons you're recommended to not return any\nexcessive data structures.\n\nEvery implementation of this interface MUST follow these event semantics in\norder to be considered a well-behaving stream.\n\n\u003e Note that higher-level implementations of this interface may choose to\n  define additional events with dedicated semantics not defined as part of\n  this low-level stream specification. Conformance with these event semantics\n  is out of scope for this interface, so you may also have to refer to the\n  documentation of such a higher-level implementation.\n\n#### data event\n\nThe `data` event will be emitted whenever some data was read/received\nfrom this source stream.\nThe event receives a single mixed argument for incoming data.\n\n```php\n$stream-\u003eon('data', function (mixed $data): void {\n    echo $data;\n});\n```\n\nThis event MAY be emitted any number of times, which may be zero times if\nthis stream does not send any data at all.\nIt SHOULD not be emitted after an `end` or `close` event.\n\nThe given `$data` argument may be of mixed type, but it's usually\nrecommended it SHOULD be a `string` value or MAY use a type that allows\nrepresentation as a `string` for maximum compatibility.\n\nMany common streams (such as a TCP/IP connection or a file-based stream)\nwill emit the raw (binary) payload data that is received over the wire as\nchunks of `string` values.\n\nDue to the stream-based nature of this, the sender may send any number\nof chunks with varying sizes. There are no guarantees that these chunks\nwill be received with the exact same framing the sender intended to send.\nIn other words, many lower-level protocols (such as TCP/IP) transfer the\ndata in chunks that may be anywhere between single-byte values to several\ndozens of kilobytes. You may want to apply a higher-level protocol to\nthese low-level data chunks in order to achieve proper message framing.\n  \n#### end event\n\nThe `end` event will be emitted once the source stream has successfully\nreached the end of the stream (EOF).\n\n```php\n$stream-\u003eon('end', function (): void {\n    echo 'END';\n});\n```\n\nThis event SHOULD be emitted once or never at all, depending on whether\na successful end was detected.\nIt SHOULD NOT be emitted after a previous `end` or `close` event.\nIt MUST NOT be emitted if the stream closes due to a non-successful\nend, such as after a previous `error` event.\n\nAfter the stream is ended, it MUST switch to non-readable mode,\nsee also `isReadable()`.\n\nThis event will only be emitted if the *end* was reached successfully,\nnot if the stream was interrupted by an unrecoverable error or explicitly\nclosed. Not all streams know this concept of a \"successful end\".\nMany use-cases involve detecting when the stream closes (terminates)\ninstead, in this case you should use the `close` event.\nAfter the stream emits an `end` event, it SHOULD usually be followed by a\n`close` event.\n\nMany common streams (such as a TCP/IP connection or a file-based stream)\nwill emit this event if either the remote side closes the connection or\na file handle was successfully read until reaching its end (EOF).\n\nNote that this event should not be confused with the `end()` method.\nThis event defines a successful end *reading* from a source stream, while\nthe `end()` method defines *writing* a successful end to a destination\nstream.\n\n#### error event\n\nThe `error` event will be emitted once a fatal error occurs, usually while\ntrying to read from this stream.\nThe event receives a single `Exception` argument for the error instance.\n\n```php\n$server-\u003eon('error', function (Exception $e): void {\n    echo 'Error: ' . $e-\u003egetMessage() . PHP_EOL;\n});\n```\n\nThis event SHOULD be emitted once the stream detects a fatal error, such\nas a fatal transmission error or after an unexpected `data` or premature\n`end` event.\nIt SHOULD NOT be emitted after a previous `error`, `end` or `close` event.\nIt MUST NOT be emitted if this is not a fatal error condition, such as\na temporary network issue that did not cause any data to be lost.\n\nAfter the stream errors, it MUST close the stream and SHOULD thus be\nfollowed by a `close` event and then switch to non-readable mode, see\nalso `close()` and `isReadable()`.\n\nMany common streams (such as a TCP/IP connection or a file-based stream)\nonly deal with data transmission and do not make assumption about data\nboundaries (such as unexpected `data` or premature `end` events).\nIn other words, many lower-level protocols (such as TCP/IP) may choose\nto only emit this for a fatal transmission error once and will then\nclose (terminate) the stream in response.\n\nIf this stream is a `DuplexStreamInterface`, you should also notice\nhow the writable side of the stream also implements an `error` event.\nIn other words, an error may occur while either reading or writing the\nstream which should result in the same error processing.\n\n#### close event\n\nThe `close` event will be emitted once the stream closes (terminates).\n\n```php\n$stream-\u003eon('close', function (): void {\n    echo 'CLOSED';\n});\n```\n\nThis event SHOULD be emitted once or never at all, depending on whether\nthe stream ever terminates.\nIt SHOULD NOT be emitted after a previous `close` event.\n\nAfter the stream is closed, it MUST switch to non-readable mode,\nsee also `isReadable()`.\n\nUnlike the `end` event, this event SHOULD be emitted whenever the stream\ncloses, irrespective of whether this happens implicitly due to an\nunrecoverable error or explicitly when either side closes the stream.\nIf you only want to detect a *successful* end, you should use the `end`\nevent instead.\n\nMany common streams (such as a TCP/IP connection or a file-based stream)\nwill likely choose to emit this event after reading a *successful* `end`\nevent or after a fatal transmission `error` event.\n\nIf this stream is a `DuplexStreamInterface`, you should also notice\nhow the writable side of the stream also implements a `close` event.\nIn other words, after receiving this event, the stream MUST switch into\nnon-writable AND non-readable mode, see also `isWritable()`.\nNote that this event should not be confused with the `end` event.\n\n#### isReadable()\n\nThe `isReadable(): bool` method can be used to\ncheck whether this stream is in a readable state (not closed already).\n\nThis method can be used to check if the stream still accepts incoming\ndata events or if it is ended or closed already.\nOnce the stream is non-readable, no further `data` or `end` events SHOULD\nbe emitted.\n\n```php\nassert($stream-\u003eisReadable() === false);\n\n$stream-\u003eon('data', assertNeverCalled());\n$stream-\u003eon('end', assertNeverCalled());\n```\n\nA successfully opened stream always MUST start in readable mode.\n\nOnce the stream ends or closes, it MUST switch to non-readable mode.\nThis can happen any time, explicitly through `close()` or\nimplicitly due to a remote close or an unrecoverable transmission error.\nOnce a stream has switched to non-readable mode, it MUST NOT transition\nback to readable mode.\n\nIf this stream is a `DuplexStreamInterface`, you should also notice\nhow the writable side of the stream also implements an `isWritable()`\nmethod. Unless this is a half-open duplex stream, they SHOULD usually\nhave the same return value.\n\n#### pause()\n\nThe `pause(): void` method can be used to\npause reading incoming data events.\n\nRemoves the data source file descriptor from the event loop. This\nallows you to throttle incoming data.\n\nUnless otherwise noted, a successfully opened stream SHOULD NOT start\nin paused state.\n\nOnce the stream is paused, no futher `data` or `end` events SHOULD\nbe emitted.\n\n```php\n$stream-\u003epause();\n\n$stream-\u003eon('data', assertShouldNeverCalled());\n$stream-\u003eon('end', assertShouldNeverCalled());\n```\n\nThis method is advisory-only, though generally not recommended, the\nstream MAY continue emitting `data` events.\n\nYou can continue processing events by calling `resume()` again.\n\nNote that both methods can be called any number of times, in particular\ncalling `pause()` more than once SHOULD NOT have any effect.\n\nSee also `resume()`.\n\n#### resume()\n\nThe `resume(): void` method can be used to\nresume reading incoming data events.\n\nRe-attach the data source after a previous `pause()`.\n\n```php\n$stream-\u003epause();\n\nLoop::addTimer(1.0, function () use ($stream): void {\n    $stream-\u003eresume();\n});\n```\n\nNote that both methods can be called any number of times, in particular\ncalling `resume()` without a prior `pause()` SHOULD NOT have any effect.\n \nSee also `pause()`.\n\n#### pipe()\n\nThe `pipe(WritableStreamInterface $dest, array $options = []): WritableStreamInterface` method can be used to\npipe all the data from this readable source into the given writable destination.\n\nAutomatically sends all incoming data to the destination.\nAutomatically throttles the source based on what the destination can handle.\n\n```php\n$source-\u003epipe($dest);\n```\n\nSimilarly, you can also pipe an instance implementing `DuplexStreamInterface`\ninto itself in order to write back all the data that is received.\nThis may be a useful feature for a TCP/IP echo service:\n\n```php\n$connection-\u003epipe($connection);\n```\n\nThis method returns the destination stream as-is, which can be used to\nset up chains of piped streams:\n\n```php\n$source-\u003epipe($decodeGzip)-\u003epipe($filterBadWords)-\u003epipe($dest);\n```\n\nBy default, this will call `end()` on the destination stream once the\nsource stream emits an `end` event. This can be disabled like this:\n\n```php\n$source-\u003epipe($dest, ['end' =\u003e false]);\n```\n\nNote that this only applies to the `end` event.\nIf an `error` or explicit `close` event happens on the source stream,\nyou'll have to manually close the destination stream:\n\n```php\n$source-\u003epipe($dest);\n$source-\u003eon('close', function () use ($dest): void {\n    $dest-\u003eend('BYE!');\n});\n```\n\nIf the source stream is not readable (closed state), then this is a NO-OP.\n\n```php\n$source-\u003eclose();\n$source-\u003epipe($dest); // NO-OP\n```\n\nIf the destinantion stream is not writable (closed state), then this will simply\nthrottle (pause) the source stream:\n\n```php\n$dest-\u003eclose();\n$source-\u003epipe($dest); // calls $source-\u003epause()\n```\n\nSimilarly, if the destination stream is closed while the pipe is still\nactive, it will also throttle (pause) the source stream:\n\n```php\n$source-\u003epipe($dest);\n$dest-\u003eclose(); // calls $source-\u003epause()\n```\n\nOnce the pipe is set up successfully, the destination stream MUST emit\na `pipe` event with this source stream an event argument.\n\n#### close()\n\nThe `close(): void` method can be used to\nclose the stream (forcefully).\n\nThis method can be used to (forcefully) close the stream.\n\n```php\n$stream-\u003eclose();\n```\n\nOnce the stream is closed, it SHOULD emit a `close` event.\nNote that this event SHOULD NOT be emitted more than once, in particular\nif this method is called multiple times.\n\nAfter calling this method, the stream MUST switch into a non-readable\nmode, see also `isReadable()`.\nThis means that no further `data` or `end` events SHOULD be emitted.\n\n```php\n$stream-\u003eclose();\nassert($stream-\u003eisReadable() === false);\n\n$stream-\u003eon('data', assertNeverCalled());\n$stream-\u003eon('end', assertNeverCalled());\n```\n\nIf this stream is a `DuplexStreamInterface`, you should also notice\nhow the writable side of the stream also implements a `close()` method.\nIn other words, after calling this method, the stream MUST switch into\nnon-writable AND non-readable mode, see also `isWritable()`.\nNote that this method should not be confused with the `end()` method.\n\n### WritableStreamInterface\n\nThe `WritableStreamInterface` is responsible for providing an interface for\nwrite-only streams and the writable side of duplex streams.\n\nBesides defining a few methods, this interface also implements the\n`EventEmitterInterface` which allows you to react to certain events.\n\nThe event callback functions MUST be a valid `callable` that obeys strict\nparameter definitions and MUST accept event parameters exactly as documented.\nThe event callback functions MUST NOT throw an `Exception`.\nThe return value of the event callback functions will be ignored and has no\neffect, so for performance reasons you're recommended to not return any\nexcessive data structures.\n\nEvery implementation of this interface MUST follow these event semantics in\norder to be considered a well-behaving stream.\n\n\u003e Note that higher-level implementations of this interface may choose to\n  define additional events with dedicated semantics not defined as part of\n  this low-level stream specification. Conformance with these event semantics\n  is out of scope for this interface, so you may also have to refer to the\n  documentation of such a higher-level implementation.\n\n#### drain event\n\nThe `drain` event will be emitted whenever the write buffer became full\npreviously and is now ready to accept more data.\n\n```php\n$stream-\u003eon('drain', function () use ($stream): void {\n    echo 'Stream is now ready to accept more data';\n});\n```\n\nThis event SHOULD be emitted once every time the buffer became full\npreviously and is now ready to accept more data.\nIn other words, this event MAY be emitted any number of times, which may\nbe zero times if the buffer never became full in the first place.\nThis event SHOULD NOT be emitted if the buffer has not become full\npreviously.\n\nThis event is mostly used internally, see also `write()` for more details.\n\n#### pipe event\n\nThe `pipe` event will be emitted whenever a readable stream is `pipe()`d\ninto this stream.\nThe event receives a single `ReadableStreamInterface` argument for the\nsource stream.\n\n```php\n$stream-\u003eon('pipe', function (ReadableStreamInterface $source) use ($stream): void {\n    echo 'Now receiving piped data';\n\n    // explicitly close target if source emits an error\n    $source-\u003eon('error', function () use ($stream): void {\n        $stream-\u003eclose();\n    });\n});\n\n$source-\u003epipe($stream);\n```\n\nThis event MUST be emitted once for each readable stream that is\nsuccessfully piped into this destination stream.\nIn other words, this event MAY be emitted any number of times, which may\nbe zero times if no stream is ever piped into this stream.\nThis event MUST NOT be emitted if either the source is not readable\n(closed already) or this destination is not writable (closed already).\n\nThis event is mostly used internally, see also `pipe()` for more details.\n\n#### error event\n\nThe `error` event will be emitted once a fatal error occurs, usually while\ntrying to write to this stream.\nThe event receives a single `Exception` argument for the error instance.\n\n```php\n$stream-\u003eon('error', function (Exception $e): void {\n    echo 'Error: ' . $e-\u003egetMessage() . PHP_EOL;\n});\n```\n\nThis event SHOULD be emitted once the stream detects a fatal error, such\nas a fatal transmission error.\nIt SHOULD NOT be emitted after a previous `error` or `close` event.\nIt MUST NOT be emitted if this is not a fatal error condition, such as\na temporary network issue that did not cause any data to be lost.\n\nAfter the stream errors, it MUST close the stream and SHOULD thus be\nfollowed by a `close` event and then switch to non-writable mode, see\nalso `close()` and `isWritable()`.\n\nMany common streams (such as a TCP/IP connection or a file-based stream)\nonly deal with data transmission and may choose\nto only emit this for a fatal transmission error once and will then\nclose (terminate) the stream in response.\n\nIf this stream is a `DuplexStreamInterface`, you should also notice\nhow the readable side of the stream also implements an `error` event.\nIn other words, an error may occur while either reading or writing the\nstream which should result in the same error processing.\n\n#### close event\n\nThe `close` event will be emitted once the stream closes (terminates).\n\n```php\n$stream-\u003eon('close', function (): void {\n    echo 'CLOSED';\n});\n```\n\nThis event SHOULD be emitted once or never at all, depending on whether\nthe stream ever terminates.\nIt SHOULD NOT be emitted after a previous `close` event.\n\nAfter the stream is closed, it MUST switch to non-writable mode,\nsee also `isWritable()`.\n\nThis event SHOULD be emitted whenever the stream closes, irrespective of\nwhether this happens implicitly due to an unrecoverable error or\nexplicitly when either side closes the stream.\n\nMany common streams (such as a TCP/IP connection or a file-based stream)\nwill likely choose to emit this event after flushing the buffer from\nthe `end()` method, after receiving a *successful* `end` event or after\na fatal transmission `error` event.\n\nIf this stream is a `DuplexStreamInterface`, you should also notice\nhow the readable side of the stream also implements a `close` event.\nIn other words, after receiving this event, the stream MUST switch into\nnon-writable AND non-readable mode, see also `isReadable()`.\nNote that this event should not be confused with the `end` event.\n\n#### isWritable()\n\nThe `isWritable(): bool` method can be used to\ncheck whether this stream is in a writable state (not closed already).\n\nThis method can be used to check if the stream still accepts writing\nany data or if it is ended or closed already.\nWriting any data to a non-writable stream is a NO-OP:\n\n```php\nassert($stream-\u003eisWritable() === false);\n\n$stream-\u003ewrite('end'); // NO-OP\n$stream-\u003eend('end'); // NO-OP\n```\n\nA successfully opened stream always MUST start in writable mode.\n\nOnce the stream ends or closes, it MUST switch to non-writable mode.\nThis can happen any time, explicitly through `end()` or `close()` or\nimplicitly due to a remote close or an unrecoverable transmission error.\nOnce a stream has switched to non-writable mode, it MUST NOT transition\nback to writable mode.\n\nIf this stream is a `DuplexStreamInterface`, you should also notice\nhow the readable side of the stream also implements an `isReadable()`\nmethod. Unless this is a half-open duplex stream, they SHOULD usually\nhave the same return value.\n\n#### write()\n\nThe `write(mixed $data): bool` method can be used to\nwrite some data into the stream.\n\nA successful write MUST be confirmed with a boolean `true`, which means\nthat either the data was written (flushed) immediately or is buffered and\nscheduled for a future write. Note that this interface gives you no\ncontrol over explicitly flushing the buffered data, as finding the\nappropriate time for this is beyond the scope of this interface and left\nup to the implementation of this interface.\n\nMany common streams (such as a TCP/IP connection or file-based stream)\nmay choose to buffer all given data and schedule a future flush by using\nan underlying EventLoop to check when the resource is actually writable.\n\nIf a stream cannot handle writing (or flushing) the data, it SHOULD emit\nan `error` event and MAY `close()` the stream if it can not recover from\nthis error.\n\nIf the internal buffer is full after adding `$data`, then `write()`\nSHOULD return `false`, indicating that the caller should stop sending\ndata until the buffer drains.\nThe stream SHOULD send a `drain` event once the buffer is ready to accept\nmore data.\n\nSimilarly, if the stream is not writable (already in a closed state)\nit MUST NOT process the given `$data` and SHOULD return `false`,\nindicating that the caller should stop sending data.\n\nThe given `$data` argument MAY be of mixed type, but it's usually\nrecommended it SHOULD be a `string` value or MAY use a type that allows\nrepresentation as a `string` for maximum compatibility.\n\nMany common streams (such as a TCP/IP connection or a file-based stream)\nwill only accept the raw (binary) payload data that is transferred over\nthe wire as chunks of `string` values.\n\nDue to the stream-based nature of this, the sender may send any number\nof chunks with varying sizes. There are no guarantees that these chunks\nwill be received with the exact same framing the sender intended to send.\nIn other words, many lower-level protocols (such as TCP/IP) transfer the\ndata in chunks that may be anywhere between single-byte values to several\ndozens of kilobytes. You may want to apply a higher-level protocol to\nthese low-level data chunks in order to achieve proper message framing.\n\n#### end()\n\nThe `end(mixed $data = null): void` method can be used to\nsuccessfully end the stream (after optionally sending some final data).\n\nThis method can be used to successfully end the stream, i.e. close\nthe stream after sending out all data that is currently buffered.\n\n```php\n$stream-\u003ewrite('hello');\n$stream-\u003ewrite('world');\n$stream-\u003eend();\n```\n\nIf there's no data currently buffered and nothing to be flushed, then\nthis method MAY `close()` the stream immediately.\n\nIf there's still data in the buffer that needs to be flushed first, then\nthis method SHOULD try to write out this data and only then `close()`\nthe stream.\nOnce the stream is closed, it SHOULD emit a `close` event.\n\nNote that this interface gives you no control over explicitly flushing\nthe buffered data, as finding the appropriate time for this is beyond the\nscope of this interface and left up to the implementation of this\ninterface.\n\nMany common streams (such as a TCP/IP connection or file-based stream)\nmay choose to buffer all given data and schedule a future flush by using\nan underlying EventLoop to check when the resource is actually writable.\n\nYou can optionally pass some final data that is written to the stream\nbefore ending the stream. If a non-`null` value is given as `$data`, then\nthis method will behave just like calling `write($data)` before ending\nwith no data.\n\n```php\n// shorter version\n$stream-\u003eend('bye');\n\n// same as longer version\n$stream-\u003ewrite('bye');\n$stream-\u003eend();\n```\n\nAfter calling this method, the stream MUST switch into a non-writable\nmode, see also `isWritable()`.\nThis means that no further writes are possible, so any additional\n`write()` or `end()` calls have no effect.\n\n```php\n$stream-\u003eend();\nassert($stream-\u003eisWritable() === false);\n\n$stream-\u003ewrite('nope'); // NO-OP\n$stream-\u003eend(); // NO-OP\n```\n\nIf this stream is a `DuplexStreamInterface`, calling this method SHOULD\nalso end its readable side, unless the stream supports half-open mode.\nIn other words, after calling this method, these streams SHOULD switch\ninto non-writable AND non-readable mode, see also `isReadable()`.\nThis implies that in this case, the stream SHOULD NOT emit any `data`\nor `end` events anymore.\nStreams MAY choose to use the `pause()` method logic for this, but\nspecial care may have to be taken to ensure a following call to the\n`resume()` method SHOULD NOT continue emitting readable events.\n\nNote that this method should not be confused with the `close()` method.\n\n#### close()\n\nThe `close(): void` method can be used to\nclose the stream (forcefully).\n\nThis method can be used to forcefully close the stream, i.e. close\nthe stream without waiting for any buffered data to be flushed.\nIf there's still data in the buffer, this data SHOULD be discarded.\n\n```php\n$stream-\u003eclose();\n```\n\nOnce the stream is closed, it SHOULD emit a `close` event.\nNote that this event SHOULD NOT be emitted more than once, in particular\nif this method is called multiple times.\n\nAfter calling this method, the stream MUST switch into a non-writable\nmode, see also `isWritable()`.\nThis means that no further writes are possible, so any additional\n`write()` or `end()` calls have no effect.\n\n```php\n$stream-\u003eclose();\nassert($stream-\u003eisWritable() === false);\n\n$stream-\u003ewrite('nope'); // NO-OP\n$stream-\u003eend(); // NO-OP\n```\n\nNote that this method should not be confused with the `end()` method.\nUnlike the `end()` method, this method does not take care of any existing\nbuffers and simply discards any buffer contents.\nLikewise, this method may also be called after calling `end()` on a\nstream in order to stop waiting for the stream to flush its final data.\n\n```php\n$stream-\u003eend();\nLoop::addTimer(1.0, function () use ($stream): void {\n    $stream-\u003eclose();\n});\n```\n\nIf this stream is a `DuplexStreamInterface`, you should also notice\nhow the readable side of the stream also implements a `close()` method.\nIn other words, after calling this method, the stream MUST switch into\nnon-writable AND non-readable mode, see also `isReadable()`.\n\n### DuplexStreamInterface\n\nThe `DuplexStreamInterface` is responsible for providing an interface for\nduplex streams (both readable and writable).\n\nIt builds on top of the existing interfaces for readable and writable streams\nand follows the exact same method and event semantics.\nIf you're new to this concept, you should look into the\n`ReadableStreamInterface` and `WritableStreamInterface` first.\n\nBesides defining a few methods, this interface also implements the\n`EventEmitterInterface` which allows you to react to the same events defined\non the `ReadbleStreamInterface` and `WritableStreamInterface`.\n\nThe event callback functions MUST be a valid `callable` that obeys strict\nparameter definitions and MUST accept event parameters exactly as documented.\nThe event callback functions MUST NOT throw an `Exception`.\nThe return value of the event callback functions will be ignored and has no\neffect, so for performance reasons you're recommended to not return any\nexcessive data structures.\n\nEvery implementation of this interface MUST follow these event semantics in\norder to be considered a well-behaving stream.\n\n\u003e Note that higher-level implementations of this interface may choose to\n  define additional events with dedicated semantics not defined as part of\n  this low-level stream specification. Conformance with these event semantics\n  is out of scope for this interface, so you may also have to refer to the\n  documentation of such a higher-level implementation.\n\nSee also [`ReadableStreamInterface`](#readablestreaminterface) and\n[`WritableStreamInterface`](#writablestreaminterface) for more details.\n\n## Creating streams\n\nReactPHP uses the concept of \"streams\" throughout its ecosystem, so that\nmany higher-level consumers of this package only deal with\n[stream usage](#stream-usage).\nThis implies that stream instances are most often created within some\nhigher-level components and many consumers never actually have to deal with\ncreating a stream instance.\n\n* Use [react/socket](https://github.com/reactphp/socket)\n  if you want to accept incoming or establish outgoing plaintext TCP/IP or\n  secure TLS socket connection streams.\n* Use [react/http](https://github.com/reactphp/http)\n  if you want to receive an incoming HTTP request body streams.\n* Use [react/child-process](https://github.com/reactphp/child-process)\n  if you want to communicate with child processes via process pipes such as\n  STDIN, STDOUT, STDERR etc.\n* Use experimental [react/filesystem](https://github.com/reactphp/filesystem)\n  if you want to read from / write to the filesystem.\n* See also the last chapter for [more real-world applications](#more).\n\nHowever, if you are writing a lower-level component or want to create a stream\ninstance from a stream resource, then the following chapter is for you.\n\n\u003e Note that the following examples use `fopen()` and `stream_socket_client()`\n  for illustration purposes only.\n  These functions SHOULD NOT be used in a truly async program because each call\n  may take several seconds to complete and would block the EventLoop otherwise.\n  Additionally, the `fopen()` call will return a file handle on some platforms\n  which may or may not be supported by all EventLoop implementations.\n  As an alternative, you may want to use higher-level libraries listed above.\n\n### ReadableResourceStream\n\nThe `ReadableResourceStream` is a concrete implementation of the\n[`ReadableStreamInterface`](#readablestreaminterface) for PHP's stream resources.\n\nThis can be used to represent a read-only resource like a file stream opened in\nreadable mode or a stream such as `STDIN`:\n\n```php\n$stream = new ReadableResourceStream(STDIN);\n$stream-\u003eon('data', function (string $chunk): void {\n    echo $chunk;\n});\n$stream-\u003eon('end', function (): void {\n    echo 'END';\n});\n```\n\nSee also [`ReadableStreamInterface`](#readablestreaminterface) for more details.\n\nThe first parameter given to the constructor MUST be a valid stream resource\nthat is opened in reading mode (e.g. `fopen()` mode `r`).\nOtherwise, it will throw an `InvalidArgumentException`:\n\n```php\n// throws InvalidArgumentException\n$stream = new ReadableResourceStream(false);\n```\n\nSee also the [`DuplexResourceStream`](#readableresourcestream) for read-and-write\nstream resources otherwise.\n\nInternally, this class tries to enable non-blocking mode on the stream resource\nwhich may not be supported for all stream resources.\nMost notably, this is not supported by pipes on Windows (STDIN etc.).\nIf this fails, it will throw a `RuntimeException`:\n\n```php\n// throws RuntimeException on Windows\n$stream = new ReadableResourceStream(STDIN);\n```\n\nOnce the constructor is called with a valid stream resource, this class will\ntake care of the underlying stream resource.\nYou SHOULD only use its public API and SHOULD NOT interfere with the underlying\nstream resource manually.\n\nThis class takes an optional `LoopInterface|null $loop` parameter that can be used to\npass the event loop instance to use for this object. You can use a `null` value\nhere in order to use the [default loop](https://github.com/reactphp/event-loop#loop).\nThis value SHOULD NOT be given unless you're sure you want to explicitly use a\ngiven event loop instance.\n\nThis class takes an optional `int|null $readChunkSize` parameter that controls\nthe maximum buffer size in bytes to read at once from the stream.\nYou can use a `null` value here in order to apply its default value.\nThis value SHOULD NOT be changed unless you know what you're doing.\nThis can be a positive number which means that up to X bytes will be read\nat once from the underlying stream resource. Note that the actual number\nof bytes read may be lower if the stream resource has less than X bytes\ncurrently available.\nThis can be `-1` which means \"read everything available\" from the\nunderlying stream resource.\nThis should read until the stream resource is not readable anymore\n(i.e. underlying buffer drained), note that this does not neccessarily\nmean it reached EOF.\n\n```php\n$stream = new ReadableResourceStream(STDIN, null, 8192);\n```\n\n\u003e PHP bug warning: If the PHP process has explicitly been started without a\n  `STDIN` stream, then trying to read from `STDIN` may return data from\n  another stream resource. This does not happen if you start this with an empty\n  stream like `php test.php \u003c /dev/null` instead of `php test.php \u003c\u0026-`.\n  See [#81](https://github.com/reactphp/stream/issues/81) for more details.\n\n\u003e Changelog: As of v1.2.0 the `$loop` parameter can be omitted (or skipped with a\n  `null` value) to use the [default loop](https://github.com/reactphp/event-loop#loop).\n\n### WritableResourceStream\n\nThe `WritableResourceStream` is a concrete implementation of the\n[`WritableStreamInterface`](#writablestreaminterface) for PHP's stream resources.\n\nThis can be used to represent a write-only resource like a file stream opened in\nwritable mode or a stream such as `STDOUT` or `STDERR`:\n\n```php\n$stream = new WritableResourceStream(STDOUT);\n$stream-\u003ewrite('hello!');\n$stream-\u003eend();\n```\n\nSee also [`WritableStreamInterface`](#writablestreaminterface) for more details.\n\nThe first parameter given to the constructor MUST be a valid stream resource\nthat is opened for writing.\nOtherwise, it will throw an `InvalidArgumentException`:\n\n```php\n// throws InvalidArgumentException\n$stream = new WritableResourceStream(false);\n```\n\nSee also the [`DuplexResourceStream`](#readableresourcestream) for read-and-write\nstream resources otherwise.\n\nInternally, this class tries to enable non-blocking mode on the stream resource\nwhich may not be supported for all stream resources.\nMost notably, this is not supported by pipes on Windows (STDOUT, STDERR etc.).\nIf this fails, it will throw a `RuntimeException`:\n\n```php\n// throws RuntimeException on Windows\n$stream = new WritableResourceStream(STDOUT);\n```\n\nOnce the constructor is called with a valid stream resource, this class will\ntake care of the underlying stream resource.\nYou SHOULD only use its public API and SHOULD NOT interfere with the underlying\nstream resource manually.\n\nAny `write()` calls to this class will not be performed instantly, but will\nbe performed asynchronously, once the EventLoop reports the stream resource is\nready to accept data.\nFor this, it uses an in-memory buffer string to collect all outstanding writes.\nThis buffer has a soft-limit applied which defines how much data it is willing\nto accept before the caller SHOULD stop sending further data.\n\nThis class takes an optional `LoopInterface|null $loop` parameter that can be used to\npass the event loop instance to use for this object. You can use a `null` value\nhere in order to use the [default loop](https://github.com/reactphp/event-loop#loop).\nThis value SHOULD NOT be given unless you're sure you want to explicitly use a\ngiven event loop instance.\n\nThis class takes an optional `int|null $writeBufferSoftLimit` parameter that controls\nthis maximum buffer size in bytes.\nYou can use a `null` value here in order to apply its default value.\nThis value SHOULD NOT be changed unless you know what you're doing.\n\n```php\n$stream = new WritableResourceStream(STDOUT, null, 8192);\n```\n\nThis class takes an optional `int|null $writeChunkSize` parameter that controls\nthis maximum buffer size in bytes to write at once to the stream.\nYou can use a `null` value here in order to apply its default value.\nThis value SHOULD NOT be changed unless you know what you're doing.\nThis can be a positive number which means that up to X bytes will be written\nat once to the underlying stream resource. Note that the actual number\nof bytes written may be lower if the stream resource has less than X bytes\ncurrently available.\nThis can be `-1` which means \"write everything available\" to the\nunderlying stream resource.\n\n```php\n$stream = new WritableResourceStream(STDOUT, null, null, 8192);\n```\n\nSee also [`write()`](#write) for more details.\n\n\u003e Changelog: As of v1.2.0 the `$loop` parameter can be omitted (or skipped with a\n  `null` value) to use the [default loop](https://github.com/reactphp/event-loop#loop).\n\n### DuplexResourceStream\n\nThe `DuplexResourceStream` is a concrete implementation of the\n[`DuplexStreamInterface`](#duplexstreaminterface) for PHP's stream resources.\n\nThis can be used to represent a read-and-write resource like a file stream opened\nin read and write mode mode or a stream such as a TCP/IP connection:\n\n```php\n$conn = stream_socket_client('tcp://google.com:80');\n$stream = new DuplexResourceStream($conn);\n$stream-\u003ewrite('hello!');\n$stream-\u003eend();\n```\n\nSee also [`DuplexStreamInterface`](#duplexstreaminterface) for more details.\n\nThe first parameter given to the constructor MUST be a valid stream resource\nthat is opened for reading *and* writing.\nOtherwise, it will throw an `InvalidArgumentException`:\n\n```php\n// throws InvalidArgumentException\n$stream = new DuplexResourceStream(false);\n```\n\nSee also the [`ReadableResourceStream`](#readableresourcestream) for read-only\nand the [`WritableResourceStream`](#writableresourcestream) for write-only\nstream resources otherwise.\n\nInternally, this class tries to enable non-blocking mode on the stream resource\nwhich may not be supported for all stream resources.\nMost notably, this is not supported by pipes on Windows (STDOUT, STDERR etc.).\nIf this fails, it will throw a `RuntimeException`:\n\n```php\n// throws RuntimeException on Windows\n$stream = new DuplexResourceStream(STDOUT);\n```\n\nOnce the constructor is called with a valid stream resource, this class will\ntake care of the underlying stream resource.\nYou SHOULD only use its public API and SHOULD NOT interfere with the underlying\nstream resource manually.\n\nThis class takes an optional `LoopInterface|null $loop` parameter that can be used to\npass the event loop instance to use for this object. You can use a `null` value\nhere in order to use the [default loop](https://github.com/reactphp/event-loop#loop).\nThis value SHOULD NOT be given unless you're sure you want to explicitly use a\ngiven event loop instance.\n\nThis class takes an optional `int|null $readChunkSize` parameter that controls\nthe maximum buffer size in bytes to read at once from the stream.\nYou can use a `null` value here in order to apply its default value.\nThis value SHOULD NOT be changed unless you know what you're doing.\nThis can be a positive number which means that up to X bytes will be read\nat once from the underlying stream resource. Note that the actual number\nof bytes read may be lower if the stream resource has less than X bytes\ncurrently available.\nThis can be `-1` which means \"read everything available\" from the\nunderlying stream resource.\nThis should read until the stream resource is not readable anymore\n(i.e. underlying buffer drained), note that this does not neccessarily\nmean it reached EOF.\n\n```php\n$conn = stream_socket_client('tcp://google.com:80');\n$stream = new DuplexResourceStream($conn, null, 8192);\n```\n\nAny `write()` calls to this class will not be performed instantly, but will\nbe performed asynchronously, once the EventLoop reports the stream resource is\nready to accept data.\nFor this, it uses an in-memory buffer string to collect all outstanding writes.\nThis buffer has a soft-limit applied which defines how much data it is willing\nto accept before the caller SHOULD stop sending further data.\n\nThis class takes another optional `WritableStreamInterface|null $buffer` parameter\nthat controls this write behavior of this stream.\nYou can use a `null` value here in order to apply its default value.\nThis value SHOULD NOT be changed unless you know what you're doing.\n\nIf you want to change the write buffer soft limit, you can pass an instance of\n[`WritableResourceStream`](#writableresourcestream) like this:\n\n```php\n$conn = stream_socket_client('tcp://google.com:80');\n$buffer = new WritableResourceStream($conn, null, 8192);\n$stream = new DuplexResourceStream($conn, null, null, $buffer);\n```\n\nSee also [`WritableResourceStream`](#writableresourcestream) for more details.\n\n\u003e Changelog: As of v1.2.0 the `$loop` parameter can be omitted (or skipped with a\n  `null` value) to use the [default loop](https://github.com/reactphp/event-loop#loop).\n\n### ThroughStream\n\nThe `ThroughStream` implements the\n[`DuplexStreamInterface`](#duplexstreaminterface) and will simply pass any data\nyou write to it through to its readable end.\n\n```php\n$through = new ThroughStream();\n$through-\u003eon('data', $this-\u003eexpectCallableOnceWith('hello'));\n\n$through-\u003ewrite('hello');\n```\n\nSimilarly, the [`end()` method](#end) will end the stream and emit an\n[`end` event](#end-event) and then [`close()`](#close-1) the stream.\nThe [`close()` method](#close-1) will close the stream and emit a\n[`close` event](#close-event).\nAccordingly, this is can also be used in a [`pipe()`](#pipe) context like this:\n\n```php\n$through = new ThroughStream();\n$source-\u003epipe($through)-\u003epipe($dest);\n```\n\nOptionally, its constructor accepts any callable function which will then be\nused to *filter* any data written to it. This function receives a single data\nargument as passed to the writable side and must return the data as it will be\npassed to its readable end:\n\n```php\n$through = new ThroughStream('strtoupper');\n$source-\u003epipe($through)-\u003epipe($dest);\n```\n\nNote that this class makes no assumptions about any data types. This can be\nused to convert data, for example for transforming any structured data into\na newline-delimited JSON (NDJSON) stream like this:\n\n```php\n$through = new ThroughStream(function (mixed $data): string {\n    return json_encode($data) . PHP_EOL;\n});\n$through-\u003eon('data', $this-\u003eexpectCallableOnceWith(\"[2, true]\\n\"));\n\n$through-\u003ewrite([2, true]);\n```\n\nThe callback function is allowed to throw an `Exception`. In this case,\nthe stream will emit an `error` event and then [`close()`](#close-1) the stream.\n\n```php\n$through = new ThroughStream(function (mixed $data): string {\n    if (!is_string($data)) {\n        throw new \\UnexpectedValueException('Only strings allowed');\n    }\n    return $data;\n});\n$through-\u003eon('error', $this-\u003eexpectCallableOnce()));\n$through-\u003eon('close', $this-\u003eexpectCallableOnce()));\n$through-\u003eon('data', $this-\u003eexpectCallableNever()));\n\n$through-\u003ewrite(2);\n```\n\n### CompositeStream\n\nThe `CompositeStream` implements the\n[`DuplexStreamInterface`](#duplexstreaminterface) and can be used to create a\nsingle duplex stream from two individual streams implementing\n[`ReadableStreamInterface`](#readablestreaminterface) and\n[`WritableStreamInterface`](#writablestreaminterface) respectively.\n\nThis is useful for some APIs which may require a single\n[`DuplexStreamInterface`](#duplexstreaminterface) or simply because it's often\nmore convenient to work with a single stream instance like this:\n\n```php\n$stdin = new ReadableResourceStream(STDIN);\n$stdout = new WritableResourceStream(STDOUT);\n\n$stdio = new CompositeStream($stdin, $stdout);\n\n$stdio-\u003eon('data', function (string $chunk) use ($stdio): void {\n    $stdio-\u003ewrite('You said: ' . $chunk);\n});\n```\n\nThis is a well-behaving stream which forwards all stream events from the\nunderlying streams and forwards all streams calls to the underlying streams.\n\nIf you `write()` to the duplex stream, it will simply `write()` to the\nwritable side and return its status.\n\nIf you `end()` the duplex stream, it will `end()` the writable side and will\n`pause()` the readable side.\n\nIf you `close()` the duplex stream, both input streams will be closed.\nIf either of the two input streams emits a `close` event, the duplex stream\nwill also close.\nIf either of the two input streams is already closed while constructing the\nduplex stream, it will `close()` the other side and return a closed stream.\n\n## Usage\n\nThe following example can be used to pipe the contents of a source file into\na destination file without having to ever read the whole file into memory:\n\n```php\n$source = new React\\Stream\\ReadableResourceStream(fopen('source.txt', 'r'));\n$dest = new React\\Stream\\WritableResourceStream(fopen('destination.txt', 'w'));\n\n$source-\u003epipe($dest);\n```\n\n\u003e Note that this example uses `fopen()` for illustration purposes only.\n  This should not be used in a truly async program because the filesystem is\n  inherently blocking and each call could potentially take several seconds.\n  See also [creating streams](#creating-streams) for more sophisticated\n  examples.\n\n## Install\n\nThe recommended way to install this library is [through Composer](https://getcomposer.org).\n[New to Composer?](https://getcomposer.org/doc/00-intro.md)\n\nOnce released, this project will follow [SemVer](https://semver.org/).\nAt the moment, this will install the latest development version:\n\n```bash\ncomposer require react/stream:^3@dev\n```\n\nSee also the [CHANGELOG](CHANGELOG.md) for details about version upgrades.\n\nThis project aims to run on any platform and thus does not require any PHP\nextensions and supports running on PHP 7.1 through current PHP 8+.\nIt's *highly recommended to use the latest supported PHP version* for this project.\n\n## Tests\n\nTo run the test suite, you first need to clone this repo and then install all\ndependencies [through Composer](https://getcomposer.org):\n\n```bash\ncomposer install\n```\n\nTo run the test suite, go to the project root and run:\n\n```bash\nvendor/bin/phpunit\n```\n\nThe test suite also contains a number of functional integration tests that rely\non a stable internet connection.\nIf you do not want to run these, they can simply be skipped like this:\n\n```bash\nvendor/bin/phpunit --exclude-group internet\n```\n\nOn top of this, we use PHPStan on max level to ensure type safety across the project:\n\n```bash\nvendor/bin/phpstan\n```\n\n## License\n\nMIT, see [LICENSE file](LICENSE).\n\n## More\n\n* See [creating streams](#creating-streams) for more information on how streams\n  are created in real-world applications.\n* See our [users wiki](https://github.com/reactphp/react/wiki/Users) and the\n  [dependents on Packagist](https://packagist.org/packages/react/stream/dependents)\n  for a list of packages that use streams in real-world applications.\n","funding_links":["https://github.com/sponsors/reactphp","https://github.com/sponsors/clue","https://github.com/sponsors/WyriHaximus","https://opencollective.com/reactphp"],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Freactphp%2Fstream","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Freactphp%2Fstream","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Freactphp%2Fstream/lists"}