SafeStream: Safety for Files
Nette SafeStream guarantees that every file read and write operation occurs in isolation. This means that no thread will start reading a file that hasn't been fully written yet, nor will multiple threads overwrite the same file.
Installation:
composer require nette/safe-stream
What Is It Good For?
What are isolated operations actually good for? Let's start with a simple example that repeatedly writes to a file and then reads the same string from it:
$s = str_repeat('Long String', 10000);
$counter = 1000;
while ($counter--) {
file_put_contents('file', $s); // write it
$readed = file_get_contents('file'); // read it
if ($s !== $readed) { // check it
echo 'strings are different!';
}
}
It might seem that the call echo 'strings are different!'
can never occur. The opposite is true. Try running this
script in two browser tabs simultaneously. The error will occur almost immediately.
One of the tabs will read the file at a moment when the other hasn't finished writing it completely, so the content will be incomplete.
Therefore, the code is not safe if executed multiple times concurrently (i.e., in multiple threads). This is not uncommon on the internet, as servers often respond to a large number of users simultaneously. Ensuring that your application works reliably even when executed in multiple threads (thread-safe) is crucial. Otherwise, data loss and hard-to-detect errors can occur.
However, as you can see, PHP's native file read and write functions are not isolated or atomic.
How to Use SafeStream?
SafeStream creates a secure protocol through which files can be read and written in isolation using standard PHP functions. You
just need to prefix the filename with nette.safe://
:
file_put_contents('nette.safe://file', $s);
$s = file_get_contents('nette.safe://file');
SafeStream ensures that at most one thread can write to the file at a time. Other threads wait in a queue. If no thread is writing, any number of threads can read the file in parallel.
All common PHP functions can be used with the protocol, for example:
// 'r' means open for reading only
$handle = fopen('nette.safe://file.txt', 'r');
$ini = parse_ini_file('nette.safe://translations.neon');