← All docs

Streams

Stream resources, wrappers, contexts, filters, sockets, TLS, and process pipes.

Resource model

Streams are PHP resource values. File handles, standard streams, directory streams, socket streams, process pipes, stream contexts, and stream filters all use the resource runtime tag instead of plain integers.

fopen() returns resource|false: successful opens produce a stream resource, while failed opens emit a suppressible runtime warning and return false. Passing that false value to a stream builtin is a fatal runtime TypeError, so code should guard failed opens before using the handle.

STDIN, STDOUT, and STDERR are stream resources. gettype(STDIN) returns "resource", is_resource(STDIN) returns true, and get_resource_type(STDIN) returns "stream".

Basic stream I/O

FunctionSignatureDescription
fopen()fopen($filename, $mode, $use_include_path = false, $context = null): resource|falseOpen a file, wrapper URL, socket-like wrapper, or temporary/memory stream. Modes r, w, a, r+, w+, and a+ are supported. The optional args are evaluated in source order; v1 consumers still read options from the single global stream-context slot.
fclose()fclose(resource $handle): boolClose a stream. Closing a phar:// write stream finalizes the archive, and closing a filtered stream runs pending filter cleanup such as user-filter onClose().
fread()fread(resource $handle, $length): stringRead up to $length bytes. Attached read filters and user-wrapper stream_read() methods are honored.
fwrite()fwrite(resource $handle, $data): intWrite bytes and return the byte count. Attached write filters and user-wrapper stream_write() methods are honored.
fprintf()fprintf(resource $handle, string $format, ...$values): intFormat like sprintf() and write to the stream.
vfprintf()vfprintf(resource $handle, string $format, array $values): intLike fprintf(), with format values supplied as an array.
fscanf()fscanf(resource $handle, string $format): arrayRead one line and parse it with the sscanf() engine. v1 supports the two-argument array-returning form and conversions %d, %f, %s, and %%.
fgets()fgets(resource $handle, int $length = null): string|falseRead a line until newline, EOF, or $length; returns false at EOF.
fgetc()fgetc(resource $handle): string|falseRead one byte, or false at EOF/failure.
feof()feof(resource $handle): boolReport whether the stream is at EOF.
fseek()fseek(resource $handle, $offset [, $whence]): intSeek a stream. User wrappers route through stream_seek().
ftell()ftell(resource $handle): intReturn the current stream position. User wrappers route through stream_tell().
rewind()rewind(resource $handle): boolSeek to the start of the stream.
fgetcsv()fgetcsv(resource $handle [, $sep]): arrayRead and parse one CSV line. User wrappers are read through stream_read().
fputcsv()fputcsv(resource $handle, $fields [, $sep]): intFormat and write one CSV line. User wrappers are written through stream_write().
readline()readline([$prompt]): stringRead a line from standard input.
readfile()readfile($filename): int|falseOpen a path or wrapper URL, stream it to stdout, and return copied bytes; returns false when open fails.
fpassthru()fpassthru(resource $handle): intStream the remaining bytes of an open handle to stdout, returning -1 on read failure.
stream_get_contents()stream_get_contents(resource $handle, ?int $length = null, int $offset = -1): string|falseRead remaining bytes from the stream. $offset >= 0 seeks there first (seekable streams / user wrappers via stream_seek()) and returns false if that seek fails; a finite $length reads at most that many bytes (a null/negative $length reads to EOF). The bounded form loops through fread until it fills $length, reaches EOF, or receives an empty read.
stream_copy_to_stream()stream_copy_to_stream(resource $from, resource $to, ?int $length = null, int $offset = -1): int|falseCopy bytes from one stream to another, returning the count. $offset >= 0 seeks the source first (seekable streams / user wrappers via stream_seek()) and returns false if that seek fails; a finite $length copies at most that many bytes (a null/negative $length copies to EOF). The bounded form drives a chunked read/write loop and clamps wrapper chunks that exceed the requested count.
stream_get_line()stream_get_line(resource $handle, int $length [, string $ending]): stringRead up to $length bytes, stopping at and consuming $ending when supplied.
flock()flock(resource $handle, int $op, &$would_block = null): boolAdvisory locking. LOCK_SH, LOCK_EX, LOCK_UN, and LOCK_NB are supported; user wrappers route through stream_lock(int $operation).
tmpfile()tmpfile(): resource|falseCreate an anonymous temporary stream backed by a /tmp/elephc-XXXXXX file that is immediately unlinked.
fstat()fstat(resource $handle): array|falseReturn the same stat shape as stat(), but for an open stream. User wrappers route through stream_stat().
ftruncate()ftruncate(resource $handle, $size): boolTruncate or extend a stream. User wrappers route through stream_truncate(int $new_size).
fflush()fflush(resource $handle): boolFlush buffered output. elephc streams are unbuffered, so this maps to fsync().
fsync()fsync(resource $handle): boolFlush data and metadata to durable storage.
fdatasync()fdatasync(resource $handle): boolFlush data only. On macOS, this falls back to fsync().

stream_set_chunk_size($stream, $size) returns the previous chunk size for the stream. The first call reports the default 8192; later calls report the value set by the previous call. v1 tracks the value per fd but does not yet change read granularity.

stream_set_read_buffer() and stream_set_write_buffer() return 0. elephc streams are unbuffered, so the accepted buffer size does not change behavior.

Built-in wrappers

WrapperDescription
fileNormal filesystem streams.
php://stdin, php://stdout, php://stderrStandard descriptors 0, 1, and 2. php://input aliases stdin, and php://output aliases stdout.
php://memory, php://tempSeekable in-memory streams backed by an anonymous temporary buffer. php://temp/maxmemory:N is accepted and ignored.
php://filterOpens an underlying resource and attaches one built-in filter at open time, for example php://filter/read=string.toupper/resource=php://temp.
data://RFC 2397 inline payload streams. Base64 and percent-decoded payloads are supported. The URI must be a string literal.
phar://Read or write PHAR entries. Literal reads happen at compile time and embed the entry in the binary; non-literal reads happen at runtime. Native PHAR, tar-based PHAR, and zip-based PHAR containers are readable; native PHAR gzip/bzip2 entries and ZIP deflate entries are decoded transparently.
ftp://Anonymous binary passive FTP read streams. fopen() requires a literal URL; file_get_contents() also accepts runtime string URLs. Credentials in the URL are ignored in v1.
ftps://Explicit FTP over TLS using AUTH TLS, with TLS on both control and data channels. fopen() requires a literal URL; file_get_contents() also accepts runtime string URLs.
http://HTTP/1.0 GET read streams. fopen() requires a literal URL; file_get_contents() also accepts runtime string URLs. v1 does not follow redirects and buffers up to 1 MiB.
https://Same as http://, but over TLS through the elephc-tls static library. Programs using it auto-link -lelephc_tls; programs that do not use TLS pay no extra link cost.
compress.zlib://Read-only wrapper that opens the underlying file and applies zlib.inflate.
compress.bzip2://Read-only wrapper that opens the underlying file and decompresses it through libbz2.
glob://Directory-style wrapper for iterating paths matching a glob pattern through opendir() / readdir().

phar:// write streams buffer one uncompressed entry in memory. fclose() and file_put_contents("phar://archive.phar/entry", $data) insert or replace that entry in a SHA1-signed PHAR archive while preserving existing entries. file_put_contents() and write-mode fopen() also accept runtime-built phar:// URLs. Native PHAR, tar-based PHAR, and zip-based PHAR containers are writable; ZIP writes preserve stored/deflated entries and compression controls can rewrite ZIP entries between stored and deflated forms. ZIP entries written with a streaming data descriptor are read transparently, and ZIP64 archives (over 65535 entries, or sizes/offsets over 4 GiB) are both read and written. Traditional-PKWARE (ZipCrypto) encrypted ZIP entries can be read and written after calling the setZipPassword(string $password) compiler extension on a Phar/PharData object: once a password is set, encrypted entries are decrypted on read and zip entries are encrypted on write (the stub is encrypted too; the .phar/signature.bin entry stays in the clear). ZipCrypto is cryptographically weak — it is kept for compatibility with legacy archives, not as a real confidentiality mechanism. Phar and PharData expose a baseline OOP surface with constructors, format/compression/signature constants, addFromString(), delete(), compressFiles(), decompressFiles(), mixed metadata/string stub accessors, path helpers, and ArrayAccess read/write/isset over the same phar:// paths. ArrayAccess reads return PharFileInfo objects with getContent() for payload reads and setMetadata()/getMetadata()/hasMetadata()/delMetadata() for per-file metadata. foreach over a Phar / PharData object visits entries scanned from the archive at construction time plus entries written through that object, yielding entryName => PharFileInfo. unlink("phar://archive/entry") and unset($phar["entry"]) remove entries while preserving sibling entries. Native PHAR compression controls support Phar::GZ, Phar::BZ2, and Phar::NONE; ZIP compression controls support Phar::GZ and Phar::NONE.

setMetadata()/getMetadata()/hasMetadata()/delMetadata() and setStub()/getStub() persist into the archive file for all three families, so the global metadata and stub round-trip across fresh Phar/PharData objects and across processes (and are interchangeable with the PHP interpreter). Metadata is stored PHP-serialize()d — in the manifest metadata field for native PHAR, in a .phar/.metadata.bin entry for tar, and in the ZIP archive comment for zip; the stub is stored as the byte prefix for native PHAR and as a .phar/stub.php entry for tar/zip. setStub() requires the stub to contain __HALT_COMPILER(); (matching PHP). The reserved .phar/* control entries are hidden from the entry listing and iteration.

Per-file metadata persists the same way through the PharFileInfo returned by ArrayAccess: $phar["entry"]->setMetadata(...), getMetadata(), hasMetadata(), and delMetadata() round-trip across fresh objects and the PHP interpreter. It is stored in the per-entry manifest field for native PHAR, in a .phar/.metadata/<entry>/.metadata.bin side entry for tar, and in the per-entry ZIP central-directory file comment for zip.

Whole-archive compression is supported on tar-based PharData: compress(Phar::GZ) and compress(Phar::BZ2) write a sibling .tar.gz / .tar.bz2 and return a fresh PharData for it, while decompress() writes the plain .tar back; the compressed archives are read transparently (and are interchangeable with the PHP interpreter). Per-entry compression for native PHAR / zip stays on compressFiles() / decompressFiles().

Signatures are supported through setSignatureAlgorithm() / getSignature() across native PHAR, tar, and zip phars. setSignatureAlgorithm(Phar::MD5|Phar::SHA1|Phar::SHA256|Phar::SHA512) applies a hash signature, and setSignatureAlgorithm(Phar::OPENSSL, $privateKey) signs with RSA-SHA1 using a PEM private key (PKCS#1 or PKCS#8). Native PHARs store the signature in their trailer; tar and zip phars store it in a .phar/signature.bin control entry. The resulting signature is verifiable by the PHP interpreter (for OpenSSL, place the matching public key in <archive>.pubkey). getSignature() returns ['hash' => <uppercase hex>, 'hash_type' => 'MD5'|'SHA-1'|'SHA-256'|'SHA-512'|'OpenSSL'].

Metadata persistence covers the same scalar+array subset as serialize()/unserialize(); object metadata is not serialized.

file_get_contents($url) recognizes runtime http://, https://, ftp://, and ftps:// strings before falling back to phar:///filesystem handling. Because the scheme is not known statically, non-literal file_get_contents() conservatively links elephc-tls, elephc-phar, zlib, and libbz2.

https://, ftps://, and stream_socket_enable_crypto() use elephc-tls (rustls, the ring crypto provider, and Mozilla webpki roots). TLS contexts can override trust with ssl.cafile or ssl.capath, set ssl.peer_name, or relax verification with ssl.verify_peer = "0", ssl.allow_self_signed, or ssl.verify_peer_name = "0". Client certificates are supported when both ssl.local_cert and ssl.local_pk point at readable PEM files; encrypted keys and ssl.passphrase are not supported. ssl.ciphers and ssl.security_level are accepted as context options for source compatibility but are no-ops: rustls does not consume OpenSSL cipher-list strings and chooses its TLS 1.2/1.3 policy internally.

Stream contexts

FunctionSignatureDescription
stream_context_create()stream_context_create(array $options = [], array $params = []): resourceCreate a stream-context resource and persist $options in the single global context slot. A literal ['notification' => <closure>] in $params is captured for HTTP notification callbacks.
stream_context_get_default()stream_context_get_default(array $options = []): resourceReturn the default context resource. The optional arg is evaluated for side effects; v1 does not apply it.
stream_context_set_default()stream_context_set_default(array $options): resourceReturn the default context resource. v1 evaluates $options for side effects but does not yet walk and persist the array; use stream_context_set_option(stream_context_get_default(), ...) when code needs options stored.
stream_context_set_option()stream_context_set_option(resource $context, ...): boolAccepts PHP’s two forms: (ctx, options_array) replaces the persisted options hash, while (ctx, wrapper, option, value) sets one nested option. In the four-arg form, values are stored as strings in v1.
stream_context_set_params()stream_context_set_params(resource $context, array $params): boolCaptures a literal notification closure or first-class callable into the global notification slot and returns true.
stream_context_get_options()stream_context_get_options(resource $context): arrayReturn the persisted options hash, or an empty hash when no context has been created.
stream_context_get_params()stream_context_get_params(resource $context): arrayv1 stub: returns an empty associative array.
stream_resolve_include_path()stream_resolve_include_path(string $filename): string|falseelephc has no runtime include_path, so this is equivalent to realpath($filename): canonical path on success, false otherwise.

Active stream-context consumers:

  • fopen("http://...") reads http.method, http.header, and http.content.
  • fopen("https://...") reads the ssl trust and peer-name options.
  • fopen("ftp://...") reads ftp.resume_pos.
  • file_get_contents() over https:// reads the same ssl options; over ftp:// or ftps:// it reads ftp.resume_pos.
  • stream_socket_server() reads socket.backlog.
  • stream_socket_enable_crypto() reads TLS peer and client-certificate options.

v1 has one active context slot. Creating or setting a context overwrites the global options used by subsequent consumers.

Notification callbacks

HTTP streams can fire a context notification callback:

$ctx = stream_context_create([], [
    'notification' => function (int $code, int $severity, ?string $message,
                                int $message_code, int $bytes_transferred,
                                int $bytes_max): void {
        if ($code === STREAM_NOTIFY_CONNECT)   { echo "connected\n"; }
        if ($code === STREAM_NOTIFY_COMPLETED) { echo "done\n"; }
        if ($code === STREAM_NOTIFY_FAILURE)   { echo "failed\n"; }
    },
]);

$body = fopen('http://example.com/', 'r');

http:// fires STREAM_NOTIFY_CONNECT, STREAM_NOTIFY_COMPLETED, and STREAM_NOTIFY_FAILURE. v1 captures only literal closure or first-class callable entries. String function names, [object, method] arrays, variable callbacks, HTTPS/FTP notifications, progress/file-size/mime/redirect/auth milestones, $message, $message_code, and $bytes_max are deferred.

Filters and buckets

FunctionSignatureDescription
stream_get_filters()stream_get_filters(): arrayReturn built-in filters: string.toupper, string.tolower, string.rot13, string.strip_tags, convert.base64-encode, convert.base64-decode, convert.quoted-printable-encode, convert.quoted-printable-decode, convert.iconv.*, dechunk, zlib.deflate, zlib.inflate, bzip2.compress, and bzip2.decompress. User filters are not enumerated in v1.
stream_filter_append()stream_filter_append(resource $stream, string $filtername, int $read_write = STREAM_FILTER_ALL, mixed $params = null): resource|falseAttach a built-in or user-registered filter. STREAM_FILTER_READ, STREAM_FILTER_WRITE, and STREAM_FILTER_ALL select directions.
stream_filter_prepend()stream_filter_prepend(resource $stream, string $filtername, int $read_write = STREAM_FILTER_ALL, mixed $params = null): resourceSame behavior as append in v1’s one-filter-per-direction model.
stream_filter_remove()stream_filter_remove(resource $filter): boolRemove the attached filter and return true.
stream_filter_register()stream_filter_register(string $filter_name, string $class): boolRegister a user filter class. Up to 128 registrations are stored; a literal class name is validated at compile time.
stream_bucket_new()stream_bucket_new(resource $stream, string $data): objectCreate a stdClass-backed bucket with public data and datalen properties.
stream_bucket_make_writeable()stream_bucket_make_writeable(resource $brigade): object|nullPop the next bucket from a brigade.
stream_bucket_append()stream_bucket_append(resource $brigade, object $bucket): voidPush a bucket to the end of a brigade.
stream_bucket_prepend()stream_bucket_prepend(resource $brigade, object $bucket): voidAccepted for source compatibility; v1 currently appends like stream_bucket_append() because bucket-brigade front insertion is not lowered separately yet.

zlib.deflate and gzcompress() use system libz. bzip2.compress, bzip2.decompress, and compress.bzip2:// use libbz2. convert.iconv.* uses libc iconv and auto-links -liconv on macOS.

Compression filter params can be a bare integer or a literal array. zlib.deflate reads level (-1..9). bzip2.compress reads blocks (1..9) and work (0..250). Non-literal params keep defaults.

User filters can implement either filter(string $data): string or PHP’s four-argument filter($in, $out, &$consumed, $closing): int bucket form. Classes may extend PHP’s php_user_filter base class; the fourth stream_filter_append/prepend $params argument is available as $this->params before onCreate() runs. Optional onCreate(): bool and onClose(): void hooks are honored. v1 seeds one input bucket per dispatch; PSFS_FEED_ME does not request more input, and PSFS_ERR_FATAL does not propagate as a stream error.

User stream wrappers

FunctionSignatureDescription
stream_get_wrappers()stream_get_wrappers(): arrayReturn built-in wrappers: file, php, data, ftp, http, https, ftps, compress.zlib, compress.bzip2, phar, and glob. User wrappers are not enumerated in v1.
stream_wrapper_register()stream_wrapper_register(string $protocol, string $class, int $flags = 0): boolRegister a userspace wrapper class for $protocol:// URLs. Up to 16 registrations are stored.
stream_wrapper_unregister()stream_wrapper_unregister(string $protocol): boolRemove a user-registered wrapper; built-in wrappers cannot be unregistered in v1.
stream_wrapper_restore()stream_wrapper_restore(string $protocol): boolv1 no-op that reports success because built-in wrappers are always present.

When fopen("$protocol://...") matches a registered wrapper, elephc creates an instance through the runtime class registry. Declared property defaults are applied, but __construct is not invoked on this path.

Supported wrapper methods include stream_open, stream_read, stream_write, stream_close, stream_eof, stream_seek, stream_tell, stream_flush, stream_stat, stream_lock, stream_truncate, stream_metadata, stream_set_option, stream_cast, url_stat, and the directory methods dir_opendir, dir_readdir, dir_rewinddir, and dir_closedir.

Wrapper methods should declare return types that match their PHP contracts. stream_stat() and url_stat() are exceptions: declare them without a return type, or as mixed, when returning associative stat arrays with string keys.

Sockets and process streams

FunctionSignatureDescription
stream_get_transports()stream_get_transports(): arrayReturn recognized socket transports: tcp, udp, unix, udg, tls, ssl, sslv2, sslv3, tlsv1.0, tlsv1.1, tlsv1.2, and tlsv1.3. TLS-version names all use rustls default negotiation.
stream_socket_server()stream_socket_server($address): resource|falseBind a server socket for [tcp://]host:port, udp://host:port, unix:///path, or udg:///path. TCP and Unix-stream sockets listen; UDP and Unix-datagram sockets only bind.
stream_socket_client()stream_socket_client($address): resource|falseOpen a client stream for [tcp://]host:port, udp://host:port, unix:///path, or udg:///path.
stream_socket_accept()stream_socket_accept($socket): resource|falseAccept the next pending connection from a listening stream.
stream_socket_enable_crypto()stream_socket_enable_crypto(resource $stream, bool $enable, int $crypto_method = null, resource $session_stream = null): boolAttach TLS to an already-connected TCP fd. $enable=false unwinds the session (sends close_notify and clears the per-fd TLS handle), leaving the fd a plain TCP socket, then reports true; it is a no-op when no session is attached.
fsockopen()fsockopen(string $hostname, int $port, int &$error_code = null, string &$error_message = null, float $timeout = null): resource|falseOpen a TCP connection to $hostname:$port, writing optional by-reference error outputs. The timeout arg is evaluated but the OS default connect timeout is used in v1.
pfsockopen()pfsockopen(string $hostname, int $port, int &$error_code = null, string &$error_message = null, float $timeout = null): resource|falseAlias of fsockopen(); persistent connections are not meaningful for standalone native binaries.
stream_set_blocking()stream_set_blocking($stream, bool $enable): boolToggle O_NONBLOCK. Non-blocking read misses return an empty fread() result or false from fgetc()/fgets() without setting EOF. User wrappers route through stream_set_option(STREAM_OPTION_BLOCKING, ...).
stream_set_timeout()stream_set_timeout($stream, int $seconds, int $microseconds = 0): boolSet SO_RCVTIMEO on socket streams. User wrappers route through stream_set_option(STREAM_OPTION_READ_TIMEOUT, ...).
stream_select()stream_select(array &$read, array &$write, array &$except, int $seconds, int $microseconds = 0): intWait until stream arrays are ready and rewrite each array to its ready subset. Word-0 only: descriptors must be 0..63. User wrappers are selectable when stream_cast(STREAM_CAST_FOR_SELECT) returns a real stream resource.
stream_socket_shutdown()stream_socket_shutdown($stream, int $mode): boolShut down socket reads (0), writes (1), or both (2).
stream_socket_sendto()stream_socket_sendto($socket, string $data, int $flags = 0, string $address = ""): int|falseSend bytes to the connected peer or to an explicit datagram address.
stream_socket_recvfrom()stream_socket_recvfrom($socket, int $length, int $flags = 0, string &$address = ""): string|falseReceive bytes and optionally write back the sender address as host:port.
stream_socket_get_name()stream_socket_get_name($socket, bool $remote): string|falseReturn local or remote socket name as host:port.
stream_socket_pair()stream_socket_pair(int $domain, int $type, int $protocol): arrayCreate a pair of connected socket streams, for example STREAM_PF_UNIX, STREAM_SOCK_STREAM, 0.
popen()popen(string $command, string $mode): resource|falseOpen a pipe to a process in read ("r") or write ("w") mode.
pclose()pclose($handle): intClose a process pipe and return its termination status.

Socket addresses use [tcp://]host:port, udp://host:port, unix:///path, or udg:///path. Host names are resolved through the system resolver to IPv4.

Directory streams

Directory handles are stream resources too. opendir(), readdir(), rewinddir(), and closedir() are documented with filesystem functions in System & I/O. Registered userspace wrappers can implement dir_opendir, dir_readdir, dir_rewinddir, and dir_closedir; the glob:// wrapper exposes glob matches through the same directory-stream API.

Stream metadata and introspection

FunctionSignatureDescription
get_resource_type()get_resource_type(resource $handle): stringReturn "stream" for every resource elephc produces.
get_resource_id()get_resource_id(resource $handle): intReturn the numeric id shown in Resource id #N.
stream_isatty()stream_isatty(resource $stream): boolReport whether the stream is connected to an interactive terminal.
stream_is_local()stream_is_local(resource|string $stream): boolReturn true for local streams.
stream_supports_lock()stream_supports_lock(resource $stream): boolReturn true when a stream supports flock().
stream_get_meta_data()stream_get_meta_data(resource $stream): arrayReturn metadata keys timed_out, blocked, eof, unread_bytes, stream_type, wrapper_type, mode, seekable, and uri.

stream_get_meta_data() derives eof, seekable, blocked, and mode from the live descriptor. stream_type is "STDIO" for seekable streams and "tcp_socket" for non-seekable streams. wrapper_type is reported as "plainfile" and uri as the empty string because elephc does not track per-resource open paths in v1.