Files
DomFramework/fork.php
2017-12-22 12:37:23 +00:00

232 lines
7.1 KiB
PHP

<?php
/** Manage the fork of children in Posix mode
*/
class fork
{
/** The PID list of childs
*/
private $pidList = array ();
/** The constructor check if the posix functions exists
*/
public function __construct ()
{
if (! function_exists ("pcntl_fork"))
throw new \Exception ("Can't fork as PHP doesn't have the pcntl_fork",
500);
}
/** Return the number of active PID
*/
public function childCount ()
{
return count ($this->pidList);
}
/** Return the list of the active PID
*/
public function childList ()
{
return $this->pidList;
}
/** Create a child
* If some parameters are provided, the called child method will receive them
* This function fork and return the child PID
* @param callable $callable The callback method to use in child
* @param mixed|null $params The params to provide to child method
* @return The child PID
*/
public function startChild ($callable, $params = array ())
{
$pid = pcntl_fork ();
if ($pid === -1)
throw new \Exception ("Can't fork the child", 500);
elseif ($pid)
{
// The parent
$this->pidList[$pid] = $pid;
return $pid;
}
// Call the child method
$args = func_get_args ();
unset ($args[0]);
call_user_func_array ($callable, $args);
exit;
}
/** Create a detached child. The terminal is closed. All the displayed
* messages from the child are silently dropped.
* If some parameters are provided, the called child method will receive them
* This function fork and return the child PID
* @param callable $callable The callback method to use in child
* @param mixed|null $params The params to provide to child method
* @return The child PID
*/
public function startDetachedChild ($callable, $params = array ())
{
$pid = pcntl_fork ();
if ($pid === -1)
throw new \Exception ("Can't fork the child", 500);
elseif ($pid)
{
// The parent
$this->pidList[$pid] = $pid;
return $pid;
}
// Call the child method
$sid = posix_setsid();
// Will catch all the text messages from the application to not crash if
// there is an "echo"
ob_start ();
// Close the file handlers STDOUT/STDIN
fclose (STDIN);
fclose (STDOUT);
fclose (STDERR);
$args = func_get_args ();
unset ($args[0]);
call_user_func_array ($callable, $args);
exit;
}
/** Wait the end of one child
* Return the PID of the dead child
*/
public function waitEndChild ()
{
while (1)
{
$stoppedPid = pcntl_wait ($status, WNOHANG);
if ($stoppedPid > 0)
{
unset ($this->pidList[$stoppedPid]);
return $stoppedPid;
}
usleep (100);
}
}
/** Stop (SIGTERM) a specific child.
* If the $maxWait parameter is set, wait the dead of the child for $maxWait
* seconds. If $maxWait is not set, do not wait the child, only send it the
* signal
* @param integer $pid The PID of the child to stop
* @param integer|null $maxWait The maximum time to wait the child if set
* @return $pid if the child is dead correctely. Return false if the child is
* not dead in the $maxWait time
*/
public function stopChild ($pid, $maxWait = null)
{
return $this->sendSignalToChild (SIGTERM, $pid, $maxWait);
}
/** Kill (SIGKILL) a specific child.
* If the $maxWait parameter is set, wait the dead of the child for $maxWait
* seconds. If $maxWait is not set, do not wait the child, only send it the
* signal
* @param integer $pid The PID of the child to stop
* @param integer|null $maxWait The maximum time to wait the child if set
* @return $pid if the child is dead correctely. Return false if the child is
* not dead in the $maxWait time
*/
public function killChild ($pid, $maxWait = null)
{
return $this->sendSignalToChild (SIGKILL, $pid, $maxWait);
}
/** Send a signal to a specific child.
* If the $maxWait parameter is set, wait the dead of the child for $maxWait
* seconds. If $maxWait is not set, do not wait the child, only send it the
* signal
* @param integer $signal The signal to send to child (SIGTERM or SIGKILL)
* @param integer $pid The PID of the child to stop
* @param integer|null $maxWait The maximum time to wait the child if set
* @return $pid if the child is dead correctely. Return false if the child is
* not dead in the $maxWait time
*/
private function sendSignalToChild ($signal, $pid, $maxWait = null)
{
if (! key_exists ($pid, $this->pidList))
throw new \Exception ("Can't stop/kill pid '$pid' : not in the fork list",
500);
posix_kill ($pid, SIGKILL);
if ($maxWait !== null)
{
$startWait = time ();
while (time () < $startWait + $maxWait)
{
if (pcntl_waitpid ($pid, $status, WNOHANG) > 0)
{
unset ($this->pidList[$pid]);
return $pid;
}
usleep (100);
}
return false;
}
return $pid;
}
/** Stop all the existing children.
* If the $maxWait parameter is set, wait the dead of the child for $maxWait
* seconds. If $maxWait is not set, do not wait the child, only send it the
* signal
* @param integer|null $maxWait The maximum time to wait the child if set
* @return true if all the children are dead correctely. Return false if at
* least one child isnot dead in the $maxWait time
*/
public function stopAll ($maxWait = null)
{
return $this->sendSigToAll (SIGTERM, $maxWait);
}
/** Kill all the existing children.
* If the $maxWait parameter is set, wait the dead of the child for $maxWait
* seconds. If $maxWait is not set, do not wait the child, only send it the
* signal
* @param integer|null $maxWait The maximum time to wait the child if set
* @return true if all the children are dead correctely. Return false if at
* least one child isnot dead in the $maxWait time
*/
public function killAll ($maxWait = null)
{
return $this->sendSigToAll (SIGKILL, $maxWait);
}
/** Send a stop or kill signal to all the existing children.
* If the $maxWait parameter is set, wait the dead of the child for $maxWait
* seconds. If $maxWait is not set, do not wait the child, only send it the
* signal
* @param integer $signal The signal to send to child (SIGTERM or SIGKILL)
* @param integer|null $maxWait The maximum time to wait the child if set
* @return true if all the children are dead correctely. Return false if at
* least one child isnot dead in the $maxWait time
*/
private function sendSigToAll ($signal, $maxWait = null)
{
foreach ($this->pidList as $pid)
{
posix_kill ($pid, $signal);
}
if ($maxWait !== null)
{
$startWait = time ();
while (time () < $startWait + $maxWait)
{
$pid = pcntl_wait ($status, WNOHANG);
if ($pid > 0)
{
unset ($this->pidList[$pid]);
if (empty ($this->pidList))
return true;
}
usleep (100);
}
return false;
}
return true;
}
}