Skip to content

Commit

Permalink
Merge pull request #10 from SAREhub/future/pcntl_signal
Browse files Browse the repository at this point in the history
added PcntlSignals helper class
  • Loading branch information
Mararok authored Sep 2, 2016
2 parents 77989b6 + 3006f0a commit a8553f8
Show file tree
Hide file tree
Showing 3 changed files with 184 additions and 0 deletions.
16 changes: 16 additions & 0 deletions examples/Process/PcntlSignals/example.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php

use SAREhub\Commons\Process\PcntlSignals;

echo "PcntlSignals example\n\n";
echo "press CTRL+C to call SIGINT signal handler \n";

PcntlSignals::get()->handle(\SIGINT, function () {
echo "\nkilled via signal\n";
exit(0);
})->install();

while (true) {
PcntlSignals::get()->checkPendingSignals();
};

109 changes: 109 additions & 0 deletions src/SAREhub/Commons/Process/PcntlSignals.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
<?php

namespace SAREhub\Commons\Process;

/**
* Helper class for handling linux signals.
* Installed signals:
* SIGHUP
* SIGINT
* SIGTERM
* SIGPIPE
* SIGUSR1
*/
class PcntlSignals {

const DEFAULT_NAMESPACE = 'default';

protected $handlers = [];

private static $instance = null;

public function __construct() {

}

/**
* @return PcntlSignals
*/
public static function getGlobal() {
if (self::$instance === null) {
self::$instance = new self();
}

return self::$instance;
}

/**
* Installs pcntl signals.
*/
public function install() {
$handler = function ($signal) {
$this->dispatchSignal($signal);
};

\pcntl_signal(\SIGHUP, $handler);
\pcntl_signal(\SIGINT, $handler);
\pcntl_signal(\SIGTERM, $handler);
\pcntl_signal(\SIGPIPE, $handler);
\pcntl_signal(\SIGUSR1, $handler);
}

/**
* Registers handler for signal in selected namespace.
* @param int $signal Signal id (\SIG* constants)
* @param callable $handler Handler callback
* @param string $namespace
* @return $this
*/
public function handle($signal, callable $handler, $namespace = self::DEFAULT_NAMESPACE) {
if (empty($this->handlers[$signal])) {
$this->handlers[$signal] = [];
}

if (empty($this->handlers[$signal][$namespace])) {
$this->handlers[$signal][$namespace] = [];
}

$this->handlers[$signal][$namespace][] = $handler;

return $this;
}

/**
* Dispatch signal on registered handlers.
* @param int $signal
* @return $this
*/
public function dispatchSignal($signal) {
if (isset($this->handlers[$signal])) {
foreach ($this->handlers[$signal] as $namespaceHandlers) {
foreach ($namespaceHandlers as $handler) {
$handler();
}
}
}

return $this;
}

/**
* Returns all registered handlers.
* @return array
*/
public function getHandlers() {
return $this->handlers;
}

public function getHandlersForSignal($signal) {
return isset($this->handlers[$signal]) ? $this->handlers[$signal] : [];
}


/**
* Calls pcntl_signal_dispatch for process pending signals.
*/
public function checkPendingSignals() {
pcntl_signal_dispatch();
}
}
59 changes: 59 additions & 0 deletions tests/SAREhub/Commons/Process/PcntlSignalsTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
<?php

namespace SAREhub\Commons\Process;

use PHPUnit\Framework\TestCase;

class PcntlSignalsTest extends TestCase {

/** @var PcntlSignals */
private $signals;

protected function setUp() {
$this->signals = new PcntlSignals();
}

public function testHandle() {
$handlerMock = $this->createSignalHandler();
$this->signals->handle(2, $handlerMock);
$this->assertEquals([
2 => [
PcntlSignals::DEFAULT_NAMESPACE => [$handlerMock]
]
], $this->signals->getHandlers());
}

public function testHandleSameSignal() {
$handlerMock = $this->createSignalHandler();
$this->signals->handle(2, $handlerMock);
$handlerMock2 = $this->createSignalHandler();
$this->signals->handle(2, $handlerMock2);

$this->assertEquals([
2 => [
PcntlSignals::DEFAULT_NAMESPACE => [$handlerMock, $handlerMock2]
]
], $this->signals->getHandlers());
}

public function testGetHandlersForSignal() {
$handlerMock = $this->createSignalHandler();
$signals = new PcntlSignals();
$signals->handle(2, $handlerMock);
$signals->handle(5, $this->createSignalHandler());
$this->assertEquals([
PcntlSignals::DEFAULT_NAMESPACE => [$handlerMock]
], $signals->getHandlersForSignal(2));
}

public function testDispatchSignal() {
$handlerMock = $this->createSignalHandler();
$handlerMock->expects($this->once())->method('__invoke');
$this->signals->handle(2, $handlerMock);
$this->signals->dispatchSignal(2);
}

private function createSignalHandler() {
return $this->getMockBuilder(\stdClass::class)->setMethods(['__invoke'])->getMock();
}
}

0 comments on commit a8553f8

Please sign in to comment.