Skip to content

Instantly share code, notes, and snippets.

@code-boxx
Created May 31, 2023 01:28
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save code-boxx/ab60f406c05c00fc24bf6715b56a9e41 to your computer and use it in GitHub Desktop.
Save code-boxx/ab60f406c05c00fc24bf6715b56a9e41 to your computer and use it in GitHub Desktop.
Run PHP In Background

RUN PHP IN BACKGROUND

https://code-boxx.com/php-background-process/

NOTES

  1. If you only want to run a simple background script, stick with 1a-background.php and 1b-run.php.
  2. For "advanced task management", go through the 2abcd-* scripts.

LICENSE

Copyright by Code Boxx

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

<?php
// (A) COMMAND LINE ONLY
// CREDITS : https://stackoverflow.com/questions/933367/php-how-to-best-determine-if-the-current-invocation-is-from-cli-or-web-server
function is_cli () {
if (php_sapi_name()==="cli") { return true; }
if (defined("STDIN")) { return true; }
if (array_key_exists("SHELL", $_ENV)) { return true; }
if (!array_key_exists("REQUEST_METHOD", $_SERVER)) { return true; }
if (empty($_SERVER["REMOTE_ADDR"]) && !isset($_SERVER["HTTP_USER_AGENT"]) && count($_SERVER["argv"])>0) { return true; }
return false;
}
if (!is_cli()) { exit("Please run this in the command line."); }
// (B) CREATE A DUMMY TEXT FILE
// YOU DO WHATEVER IS REQUIRED IN YOUR PROJECT...
file_put_contents(
__DIR__ . DIRECTORY_SEPARATOR . "dummy.txt",
"Background script ran at " . date("Y-m-d H:i:s")
);
<?php
// (A) SCRIPT TO RUN IN BACKGROUND
$script = __DIR__ . DIRECTORY_SEPARATOR ."1a-background.php";
// (B) RUN
// NOTE: PHP_OS_FAMILY IS AVAILABLE IN PHP 7.2+ ONLY
switch (strtolower(PHP_OS_FAMILY)) {
// (B1) UNSUPPORTED OS
default: echo "Unsupported OS"; break;
// (B2) WINDOWS
case "windows":
pclose(popen("start /B php $script", "r"));
break;
// (B3) LINUX
case "linux":
exec("php $script > /dev/null &");
break;
}
CREATE TABLE `tasks` (
`user_id` bigint(20) NOT NULL,
`process_id` varchar(32) NOT NULL,
`task_date` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
ALTER TABLE `tasks`
ADD PRIMARY KEY (`user_id`),
ADD KEY `task_date` (`task_date`);
<?php
class Background {
// (A) CONSTRUCTOR - CONNECT TO DATABASE
private $pdo = null;
private $stmt = null;
public $error = "";
function __construct () {
$this->pdo = new PDO(
"mysql:host=".DB_HOST.";dbname=".DB_NAME.";charset=".DB_CHARSET,
DB_USER, DB_PASSWORD, [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC
]);
}
// (B) DESTRUCTOR - CLOSE DATABASE CONNECTION
function __destruct () {
if ($this->stmt!==null) { $this->stmt = null; }
if ($this->pdo!==null) { $this->pdo = null; }
}
// (C) HELPER FUNCTION - EXECUTE SQL QUERY
function query ($sql, $data=null) : void {
$this->stmt = $this->pdo->prepare($sql);
$this->stmt->execute($data);
}
// (D) RUN A SCRIPT IN BACKGROUND - TESTED ON WINDOWS ONLY
function start ($uid, $script) {
// (D1) CHECK IF USER ALREADY HAS A RUNNING PROCESS
$this->query("SELECT * FROM `tasks` WHERE `user_id`=?", [$uid]);
if (is_array($this->stmt->fetch())) {
$this->error = "User already has a running process.";
return false;
}
// (D2) RUN SCRIPT & GET PROCESS ID
// start /B wmic process call create "PATH/PHP.EXE -f PATH/SCRIPT.PHP USER-ID | find ProcessId"
$processID = 0;
$fp = popen(sprintf('start /B wmic process call create "%s -f %s %u | find ProcessId"', PHP_PATH, $script, $uid), "r");
while (!feof($fp)) {
$line = fread($fp, 1024);
if (strpos($line, "ProcessId") !== false) {
preg_match('/\d+/', $line, $processID);
$processID = $processID[0];
}
}
pclose($fp);
// (D3) REGISTER ENTRY & DONE
$this->query("INSERT INTO `tasks` (`user_id`, `process_id`) VALUES (?,?)", [$uid, $processID]);
return true;
}
// (E) BACKGROUND SCRIPT HAS ENDED
function end ($uid) {
$this->query("DELETE FROM `tasks` WHERE `user_id`=?", [$uid]);
return true;
}
// (F) KILL TASK
function kill ($uid) {
// (F1) CHECK IF USER HAS PENDING TASK
$this->query("SELECT * FROM `tasks` WHERE `user_id`=?", [$uid]);
$task = $this->stmt->fetch();
if (!is_array($task)) {
$this->error = "User does not have any pending tasks.";
return false;
}
// (F2) WINDOWS KILL TASK & CLOSE ENTRY
pclose(popen("taskkill /PID ".$task["process_id"]." /F", "r"));
$this->end($uid);
return true;
}
}
// (G) SETTINGS - CHANGE TO YOUR OWN!
define("DB_HOST", "localhost");
define("DB_NAME", "test");
define("DB_CHARSET", "utf8mb4");
define("DB_USER", "root");
define("DB_PASSWORD", "");
define("PHP_PATH", "c:/xampp/php/php.exe");
// (H) NEW BACKGROUND OBJECT
$_BG = new Background();
<?php
// (A) LOAD LIBRARY
require "2b-lib-background.php";
// (B) TO RUN
echo $_BG->start(999, __DIR__ . DIRECTORY_SEPARATOR . "2d-background.php") ? "RUNNING!" : $_BG->error ;
// (C) TO "FORCE STOP"
// echo $_BG->kill(999) ? "KILLED" : $_BG->error ;
<?php
// (A) COMMAND LINE ONLY
// CREDITS : https://stackoverflow.com/questions/933367/php-how-to-best-determine-if-the-current-invocation-is-from-cli-or-web-server
function is_cli () {
if (php_sapi_name()==="cli") { return true; }
if (defined("STDIN")) { return true; }
if (array_key_exists("SHELL", $_ENV)) { return true; }
if (!array_key_exists("REQUEST_METHOD", $_SERVER)) { return true; }
if (empty($_SERVER["REMOTE_ADDR"]) && !isset($_SERVER["HTTP_USER_AGENT"]) && count($_SERVER["argv"])>0) { return true; }
return false;
}
if (!is_cli()) { exit("Please run this in the command line."); }
// (B) CREATE A DUMMY TEXT FILE
// YOU DO WHATEVER IS REQUIRED IN YOUR PROJECT...
file_put_contents(
__DIR__ . DIRECTORY_SEPARATOR . "dummy.txt",
"Background script ran at " . date("Y-m-d H:i:s")
);
// (C) REMEMBER TO "END" THE TASK IN THE DATABASE
require __DIR__ . DIRECTORY_SEPARATOR . "2b-lib-background.php";
$_BG->end($argv[1]);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment