data types and standard library
Streams
A stream is a handle PHP can read from or write to progressively. Files, temporary memory buffers, request bodies, process pipes, network connections, and output buffers can all be treated as streams.
Streams matter because not every piece of data should be loaded fully into memory. Imports, exports, downloads, uploaded files, and large reports are safer when processed in chunks or lines.
Use php://temp for examples and tests
php://temp gives you a stream without creating a named file in your project.
<?php
declare(strict_types=1);
$stream = fopen('php://temp', 'r+');
fwrite($stream, "first line\nsecond line\n");
rewind($stream);
while (($line = fgets($stream)) !== false) {
echo trim($line) . PHP_EOL;
}
fclose($stream);
// Prints:
// first line
// second line
The same functions, fwrite(), rewind(), fgets(), and fclose(), are used with real files too.
Check that a stream opened successfully
fopen() can fail. Check the result before using it.
<?php
declare(strict_types=1);
$stream = fopen('php://temp', 'r+');
if (!is_resource($stream)) {
throw new RuntimeException('Could not open stream.');
}
fwrite($stream, "ready\n");
rewind($stream);
echo fgets($stream);
fclose($stream);
// Prints:
// ready
This guard is especially important for filesystem paths and remote resources where permissions or availability can change.
Write functions that accept streams
PHP has no resource type declaration, so validate stream parameters inside the function.
<?php
declare(strict_types=1);
function countLines($stream): int
{
if (!is_resource($stream)) {
throw new InvalidArgumentException('Expected a stream resource.');
}
$count = 0;
while (($line = fgets($stream)) !== false) {
$count++;
}
return $count;
}
$stream = fopen('php://temp', 'r+');
fwrite($stream, "alpha\nbeta\ngamma\n");
rewind($stream);
echo countLines($stream) . PHP_EOL;
fclose($stream);
// Prints:
// 3
This kind of function can work with a temporary stream in tests and a real uploaded file in production.
Copy streams without loading everything
Use stream_copy_to_stream() when moving data between streams.
<?php
declare(strict_types=1);
$source = fopen('php://temp', 'r+');
$destination = fopen('php://temp', 'r+');
fwrite($source, "export contents\n");
rewind($source);
stream_copy_to_stream($source, $destination);
rewind($destination);
echo stream_get_contents($destination);
fclose($source);
fclose($destination);
// Prints:
// export contents
This pattern is useful for downloads, generated exports, file transformations, and moving uploaded content into storage.
Read fixed-size chunks
Line-based reading is useful for text. Chunk-based reading is useful for binary files or large payloads.
<?php
declare(strict_types=1);
$stream = fopen('php://temp', 'r+');
fwrite($stream, 'abcdef');
rewind($stream);
while (!feof($stream)) {
$chunk = fread($stream, 2);
if ($chunk === '') {
break;
}
echo '[' . $chunk . ']';
}
echo PHP_EOL;
fclose($stream);
// Prints:
// [ab][cd][ef]
Do not assume one read returns the whole file. Stream code should be comfortable with partial reads.
Know the common PHP stream wrappers
PHP stream wrappers let you use stream functions with special sources and destinations.
<?php
declare(strict_types=1);
$input = fopen('php://temp', 'r+');
$output = fopen('php://memory', 'r+');
echo is_resource($input) && is_resource($output) ? 'streams opened' : 'failed';
echo PHP_EOL;
fclose($input);
fclose($output);
// Prints:
// streams opened
You will often see php://input for raw HTTP request bodies, php://output for streaming responses, php://temp for temporary buffering, and php://memory for in-memory data.
What to remember
Streams let PHP process data progressively. Check handles before use, close what you open, prefer line or chunk processing for large data, and write stream-based functions when the same logic should work for files, uploads, and test buffers.
Practice
Task: Copy a report between streams
Write a small example that copies report data from one stream to another.
Requirements
- Use
declare(strict_types=1);. - Open two
php://tempstreams. - Check both handles are valid stream resources.
- Write at least two lines to the source stream.
- Rewind the source stream.
- Copy the source stream into the destination stream with
stream_copy_to_stream(). - Rewind the destination stream.
- Print the destination contents.
- Close both streams.
- Include the expected output as comments in the same PHP code block.
The example should show progressive stream handling rather than copying through a normal string variable.
Show solution
<?php
declare(strict_types=1);
$source = fopen('php://temp', 'r+');
$destination = fopen('php://temp', 'r+');
if (!is_resource($source) || !is_resource($destination)) {
throw new RuntimeException('Could not open temporary streams.');
}
fwrite($source, "Daily total: 120\n");
fwrite($source, "Weekly total: 560\n");
rewind($source);
stream_copy_to_stream($source, $destination);
rewind($destination);
echo stream_get_contents($destination);
fclose($source);
fclose($destination);
// Prints:
// Daily total: 120
// Weekly total: 560
The source and destination could be real files, upload streams, or response streams. The code works with stream handles, so it does not depend on a specific path or on loading a whole file into a normal string first.