I have a php script that I want to run. But if it is already running I want any new instances to end immediately.
I have seen other similar questions to this but all the solutions do not work for me. I need it to work when opening two tabs in the same browser and loading both tabs at once. Currently all the solutions I have tried will queue the second page load instead of terminating it.
I have tried the following...
Create a file at the start and unlinking it at the end, and if it exists exit:
<?php
if(file_exists("block.txt"))
{
die('running');
}
else
{
file_put_contents("block.txt", "blocking");
//simulate some work
sleep(60);
echo "END";
unlink("block.txt");
}
?>
I have tried getting a file lock:
<?php
class Lock{
private $fp;
function __construct(){
$this->fp=fopen(__File__,'r');
if (!flock($this->fp,LOCK_EX|LOCK_NB)) {
die('already running !'.PHP_EOL);
}
}
function __destruct(){
flock($this->fp,LOCK_UN);
fclose($this->fp);
}
}
$lock=new Lock();
sleep(60);
echo "END";
?>
and I have also tried making a locking database file
<?php
$con = mysqli_connect($servername, $username, $password, $dbname);
// Check connection
if (!$con) {
die("Connection failed: " . mysqli_connect_error());
}
if(!mysqli_query($con, "LOCK TABLES Block WRITE;"))
{
die("blocked");
}
$result = mysqli_query($con, "Select Count(*) as isBlocked from Block;");
if(!$result)
{
mysqli_query($con, "UNLOCK TABLES;");
die("blocked");
}
while($row = mysqli_fetch_assoc($result))
{
$isBlocked = $row['isBlocked'];
}
mysqli_free_result($result);
if($isBlocked > 0)
{
mysqli_query($con, "UNLOCK TABLES;");
die("blocked");
}
else
{
mysqli_query($con, "INSERT INTO Block (x) VALUES (1);");
mysqli_query($con, "UNLOCK TABLES;");
}
sleep(60);
echo "END";
mysqli_query($con, "Truncate Block;");
mysqli_close($con);
?>
When I hit the page in Firefox and then try and open the page in Chrome it works. But if I try and open two tabs in Firefox or two tabs in Chrome it seems to ignore the blocking mechanism and just queues the scripts. How can I make it so that even if I am running it in a separate tab it gets blocked?
All of these examples also have a flaw. If you cancel the page load it never performs the unblocking function and you have to manually force it to unblock. Ideally I would like a solution that does not have this issue.
Try this,
Don't blindly create the lock file, upon successful start store the process id in the lockfile. Then upon subsequent instances check to see if that process id is still running and if it's not then allow an instance to start.
This will then recover from a stale lock file, and upon destruct delete the lockfile as normal.
<?php
class pid
{
protected $pidfile;
public $running = false;
public function __construct($directory = '')
{
$this->pidfile = (!empty($directory) ? $directory.'/' : null) . basename($_SERVER['PHP_SELF']) . '.pid';
if (is_writable($this->pidfile) || is_writable($directory)) {
if (file_exists($this->pidfile)) {
$pid = (int) file_get_contents($this->pidfile);
if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
$wmi = new COM("winmgmts:{impersonationLevel=impersonate}!\\\\.\oot\\cimv2");
$procs = $wmi->ExecQuery("SELECT * FROM Win32_Process WHERE ProcessId='".$pid."'");
foreach ($procs as $proc) {
$proc->Terminate();
$this->running = true;
}
} else {
if (posix_kill($pid, 0)) {
$this->running = true;
}
}
}
} else {
die("Cannot write to pid file '{$this->pidfile}'. Program execution halted.
");
}
if (!$this->running) {
file_put_contents($this->pidfile, getmypid());
}
}
public function __destruct()
{
if (!$this->running && file_exists($this->pidfile) && is_writeable($this->pidfile)) {
unlink($this->pidfile);
}
}
}
$pid = new pid(dirname(__FILE__));
if ($pid->running) {
exit;
}
// your code