I have following code, it saves some php code into a file, then load it, run again, sometimes the require method returns int, why does this happen?
<?php
$f = function() use($a){
$cachePath = '/tmp/t.php';
$code = '<?php';
$code .= "nn";
$code .= 'return ' . var_export([], true) . ';';
file_put_contents($cachePath, $code, LOCK_EX);
if (file_exists($cachePath)) {
// Sometime the following line returns int,why?
$result = require($cachePath);
if (!is_array($result)) {
var_dump($result, $cachePath, file_get_contents($cachePath));
exit("ok");
}
var_dump($result);
}
};
for($i=0;$i<1000000;$i++) {
$f();
}
2
Answers
This is standard behaviour of
require
, compare with the docs ofinclude
, this behaviour is same between those two:As you can see, an integer (1) is returned on the happy path when the return is not overridden.
This makes sense for your example in so far, the file exists (therefore no fatal error), but as the file has just been created new, it might have been truncated yet only, that is, it is empty.
Therefore the return is not overridden and you can see int(1).
Another explanation is naturally you have overridden with an integer, which is also possible as multiple processes could write to the same file, but for the way you wrote the example, this is less likely. I only mention it, as this is another valid explanation.
Include if Exists
Example how you can levitate the race-condition, as you’re looking for the
$result
, not (only) if the file exists:The thinking behind it is, that we only do little error handling, like checking the file exists as otherwise it could not be included (include() would only emit a warning and passes on with $result = false), and then if the $result loading did work with the is_array() test.
That is we design for error, but we know what we’re looking for, that is $result being an array.
This is often called a transaction or transactional operation.
In this new example, we would not even enter the if-body when the $result array is empty, e.g. contains no data.
On program processing level this is likely what we’re interested in and the file existing or not or being empty or not or even wrong written are all error cases it needs to "eat" and to invalidate $result.
Define errors out of existence.
Handling Parse Errors (for Include-If-Exists)
Since PHP 7.0 we can use include() and in the unfortunate event the returning include file has been half-written, we would see a PHP Parse Error which can be caught: