Stream Wrappers & php:// — Virtual File Handles
PHP's stream system lets you treat memory, network connections, compression filters, and custom protocols as ordinary file handles. The built-in php:// wrappers are especially useful.
// php://memory — a file handle backed by RAM, not disk
$handle = fopen('php://memory', 'r+');
fwrite($handle, "line one\n");
fwrite($handle, "line two\n");
rewind($handle);
echo stream_get_contents($handle); // "line one\nline two\n"
fclose($handle);
// Works with all standard file functions:
rewind($handle);
while (($line = fgets($handle)) !== false) {
echo trim($line);
}
php://temp works like php://memory but transparently spills to a temporary file if the data exceeds a threshold (default 2 MB) — the right default for processing uploads or large responses:
$handle = fopen('php://temp/maxmemory:5242880', 'r+'); // 5 MB threshold
// Use it like any file handle; PHP handles the memory/disk decision
Other built-in wrappers: php://stdin, php://stdout, php://stderr for CLI pipes; compress.zlib://file.gz to read gzipped files transparently; data:// for inline data URIs. You can also register your own stream wrapper with stream_wrapper_register() to implement custom protocols.
// Use php://memory to test I/O code without touching the filesystem
function writeCsv(iterable $rows, $handle): void
{
foreach ($rows as $row) {
fputcsv($handle, $row);
}
}
// In tests — no temp files, no cleanup, runs in microseconds
$out = fopen('php://memory', 'r+');
writeCsv([['a', 'b'], ['c', 'd']], $out);
rewind($out);
$result = stream_get_contents($out);
assert($result === "a,b\nc,d\n");
Significance: Testability
php://memory is the cleanest way to unit-test I/O code in PHP. Any function that accepts a file handle can be tested without touching the disk, without mock objects, and without temporary files to clean up. It's duck-typing for streams.