require method returns int when not possible in PHP
P粉883223328
P粉883223328 2023-09-02 09:55:14
<p>I have the following code which saves some php code to a file and then loads it and runs it again and sometimes the require method returns int, why does this happen? </p> <h1>demo.php</h1> <pre class="brush:php;toolbar:false;"><?php $f = function() use($a){ $cachePath = '/tmp/t.php'; $code = '<?php'; $code .= "\n\n"; $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(); }</pre> <h1>How to reproduce? </h1> <p>Use two php processes to run the above code</p> <pre class="brush:php;toolbar:false;">php demo.php</pre></p>
P粉883223328
P粉883223328

reply all(2)
P粉351138462

This is the standard behavior of require, and the behavior of include is the same:

As you can see, when the return value is not overwritten, an integer (1) is returned on the happy path.

This makes sense for your example, so far the file exists (so no fatal error), but since the file has just been created, it might just be truncated, i.e., it's empty. p>

So the return value is not overwritten and you can see the int(1).

Another explanation is naturally that you have overwritten with an integer, which is also possible since multiple processes can write to the same file, but with the way you wrote your example it is less likely. I only mention it because it's another valid explanation.

Contains if present

Example how to suspend a race condition when you look for $result, instead of (only) when the file exists:

if (($result = @include($cachePath)) &&
    is_array($result)    
) {
   # $result is array, which is required
   # ...
}

The idea behind it is that we do very little error handling, like checking if the file exists, otherwise it can't be included (include() just emits a warning and passes it with $result = false), and then if $result Loading does work with is_array() tests.

This is what we have for errors, but we know what we are looking for, i.e. $result is an array.

This is often called a transaction or transaction operation.

In this new example, we don't even enter the if-body when the $result array is empty, i.e. does not contain any data.

On a program processing level this might be of interest to us, file presence or absence, being empty or not empty, or even being written incorrectly are all error conditions that need to be "eaten" and invalidate the $result.

The definition error does not exist.

Handling parsing errors (for Include-If-Exists)

Since PHP 7.0 we can use include() and if unfortunately the returned include file is half written we will see a PHP parsing error which can be caught :

# start the transaction
$result = null;
assert(
    is_string($cachePath) &&           # pathnames are strings,
    '' !== $cachePath &&               # never empty,
    false === strpos($cachePath, "rrreee") # and must not contain null-bytes
);
try {
    if (file_exists($cachePath)) {
        $result = include($cachePath);
    }
    # invalidate $result in case include() did technically work.
    if (!$result || !is_array($result) {
        $result = null;
    }
} catch (Throwable $t) {
    # catch all errors and exceptions,
    # the fall-through is intended to invalidate $result.
    $result = null;
} finally {
    # $result is not null, but a non-empty array if it worked.
    # $result is null, if it could not be acquired.
}

Please refer to PHP try-catch-finally to learn how to throw exceptions/exception handling work details, assert() is used to record the meaning of the input parameter $cachePath in the example.

The second example does not use the suppress operation "@", the reason is that if it were used like the previous example, and the file to be included would contain a real fatal error, the fatal error would be silenced. Nowadays, in modern PHP, this is not a big problem anymore, but using file_exists() include() - although there is a race condition due to check time vs. use time - is safe (only a warning) and fatal for non-existent files Errors will not be hidden.

As you may have seen, the more details you know, the harder it is to write code that is as future-proof as possible. We must not get lost in error handling itself, but should focus on the results and define that these errors do not exist.

That said, include() still causes the data to be loaded into memory, file_exists() is only used to "suppress" the warning, we know that, nevertheless, include() may issue a warning and may return an integer, while Not an array.


Now, since programming is hard: you might then wrap it in a loop and retry three times, for example. Why not use a for loop to count and protect the number of retries?

Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template
About us Disclaimer Sitemap
php.cn:Public welfare online PHP training,Help PHP learners grow quickly!