phpfile-get-contentsflockfile-put-contents

Can PHP "file_get_contents()" function read a file while it's being written?


How does PHP "file_get_contents()" function handles the request if a given file is being written? Does it wait until the writing process is finished and then read the file, or, does it just fail to read the file?

Does "file_get_contents()" function use "flock()" function by default?


Solution

  • I did some tests and, indeed, "file_get_contents()" does not make use of "flock()" and there is no way to use "LOCK_SH" flag as an alternative!

    If a file is being written, "file_get_contents()" does not wait in queue, it reads the file then it returns an empty string!

    I wrote "flock_file_get_contents()" function as an alternative, if a file is being written, it waits in queue until the write process is over then it reads the file!

    Better solutions are always welcome!

    [OBS]:

    [Edited]:

    enter image description here

    <?php
    
    $Overwrite_Sleep = 10;       //seconds
    $flock_GFC_Sleep = 2;       //seconds
    
    $File = basename($_SERVER['SCRIPT_FILENAME'], '.php') . ".txt";
    
    if (isset($_POST["Request"])){
    
        if($_POST["Request"] == "Get_File_Content"){echo file_get_contents($File);
        }else if ($_POST["Request"] == "flock_Get_File_Content"){echo flock_file_get_contents($File);
        }else{
    
            flock_file_put_contents($File, function($F_P){
    
            $File_Content = file_get_contents($F_P);
            $File_Content = str_replace("O", "A", $File_Content);
            $File_Content = str_replace("p", "t", $File_Content);
            $File_Content = str_replace("0", "", $File_Content);
    
                //the below is basically what "file_put_contents()" does, except the "sleep()" part
     
            $File_Handle = fopen($F_P, 'w');
    
            fwrite($File_Handle, $File_Content . sleep($GLOBALS["Overwrite_Sleep"]));       //the write process takes some time to finish
                
            fclose($File_Handle);
    
            }, $Return_Message);
    
        echo ($Return_Message == 1) ? "File Overwritten" : "Failed: " . $Return_Message;
        }
    
    return;
    }
    
    file_put_contents($File, "Oops");
    
    function flock_file_get_contents($File){     //______________________________________________
    
        $File_Handle = fopen("$File", "r");
        if(flock($File_Handle, LOCK_SH)){       //on success, this script execution waits here until older scripts in queue unlock the file
    
        $File_Content = file_get_contents($File);
    
        sleep($GLOBALS["flock_GFC_Sleep"]);
    
        flock($File_Handle, LOCK_UN);       //unlock the file so new scripts in queue can continue execution
    
        }else{$File_Content = ["flock"=>"Fail"];}
        fclose($File_Handle);
    
    return $File_Content;
    }
    
    function flock_file_put_contents($File, $Do_This, &$Return_Var = ""){      //__________________________________________
    
            $File_Handle = fopen($File, 'r');
            if (flock($File_Handle, LOCK_EX)) {     //on success, this script execution waits here until older scripts in queue unlock the file
     
            $Do_This($File);
    
            $Return_Message = 1;
    
            flock($File_Handle, LOCK_UN);       //unlock the file so new scripts in queue can continue execution
    
            }else{$Return_Message = "flock() Failed";}
            fclose($File_Handle);
    
    $Return_Var = $Return_Message;
    
    return $Return_Message;
    }
    
    ?>
    
    <!DOCTYPE html>
    
    <div style="  float:left;">
    <input type="button" value="Overwrite File (Wait <?php echo $Overwrite_Sleep ?> Seconds)"  onclick='Test("Overwrite_File", this, Call_Count++)'>
    <br>
    <div id="Response_Overwrite"></div>
    </div>
    
    <div style="  float:left; margin-left: 50px; ">
    <input type="button" value="file_get_contents()"  onclick='Test("Get_File_Content", this, Call_Count++)'>
    <br>
    <div id="Response_Get_Content"></div>
    </div>
    
    <div style="  float:left; margin-left: 50px; ">
    <input type="button" value="flock_file_get_contents() - Wait <?php echo $flock_GFC_Sleep ?> seconds"  onclick='Test("flock_Get_File_Content", this, Call_Count++)'>
    <br>
    <div id="Response_flock_Get_Content"></div>
    </div>
    
    <br style=" clear: both;"><br><br>
    
    <?php
    echo $_SERVER['SCRIPT_FILENAME'] . "<br><br>";
    echo __FILE__ . "<br><br>"; 
    
    echo basename(__FILE__) . "<br><br>"; 
    echo basename(__FILE__, '.php') . "<br><br>";
    
    echo basename($_SERVER['SCRIPT_FILENAME']) . "<br><br>"; 
    echo basename($_SERVER['SCRIPT_FILENAME'], '.php') . "<br><br>";
    ?>
    
    
    <script>
    
    Call_Count = 1;
    
    function Test(Option, El, Call_Count){      //____________________________
    
        //El.disabled = true;
    
        //if (Option === "Overwrite_File"){document.getElementById("Response_Overwrite").innerHTML += "(Please Wait <?php echo $Overwrite_Sleep ?> Seconds) ";
        //}else if (Option === "flock_Get_File_Content"){document.getElementById("Response_flock_Get_Content").innerHTML += 'File Content is: ';
        //}else{document.getElementById("Response_Get_Content").innerHTML += 'File Content is: '}
    
    var http = new XMLHttpRequest();
    http.open('POST', "");      //blank url (send to same page)
    
    http.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');     //necessary to send "Request" POST key below to php
    
        http.onreadystatechange = function(){
            if (this.readyState === 4) {     //"4", request finished and response is ready!
    
                if (Option === "Overwrite_File"){document.getElementById("Response_Overwrite").innerHTML += this.responseText + " (Call " + Call_Count + ")<br>";
                }else if (Option === "flock_Get_File_Content"){
                document.getElementById("Response_flock_Get_Content").innerHTML += '"' + this.responseText + '" (Call ' + Call_Count + ')<br>';
                }else{document.getElementById("Response_Get_Content").innerHTML += '"' + this.responseText + '" (Call ' + Call_Count + ')<br>';}
    
            El.disabled = false;
            }
        };
    
    http.send('Request=' + Option);
    }
    
    </script>