From a321c4cdae45892e2538e8bc0ec01e5965f46e02 Mon Sep 17 00:00:00 2001 From: Michael Wallner Date: Fri, 8 Mar 2013 11:18:33 +0100 Subject: [PATCH] initial checkin --- lib/atick/Ticker.php | 152 +++++++++++++++++++++++++++++++++ tests/lib/atick/TickerTest.php | 69 +++++++++++++++ tests/setup.inc | 5 ++ 3 files changed, 226 insertions(+) create mode 100644 lib/atick/Ticker.php create mode 100644 tests/lib/atick/TickerTest.php create mode 100644 tests/setup.inc diff --git a/lib/atick/Ticker.php b/lib/atick/Ticker.php new file mode 100644 index 0000000..cdc3364 --- /dev/null +++ b/lib/atick/Ticker.php @@ -0,0 +1,152 @@ + + * execAsync("SELECT * FROM foo", function ($rs) { + * var_dump($rs); + * }); + * + * $ticker = new \atick\Ticker; + * $ticker->register(); + * $ticker->read($conn->socket, function($fd) use ($conn) { + * $conn->poll(); + * if ($conn->busy) { + * return false; + * } + * $conn->getResult(); + * return true; + * }); + * + * while (count($ticker)); + * ?> + * + * + * And an example without ticks: + * + * execAsync("SELECT * FROM foo", function ($r) { + * var_dump($r); + * }); + * + * $ticker = new \atick\Ticker; + * $ticker->read($conn->socket, function($fd) use ($conn) { + * $conn->poll(); + * if ($conn->busy) { + * return false; + * } + * $conn->getResult(); + * return true; + * }); + * + * while($ticker()); + * ?> + * + */ +class Ticker implements \Countable +{ + /** + * @var array + */ + protected $read = array(); + + /** + * @var array + */ + protected $write = array(); + + /** + * Register the ticker as tick function + * @return \atick\Ticker + */ + function register() { + register_tick_function(array($this, "__invoke")); + return $this; + } + + /** + * Unregister the ticker as tick function + * @return \atick\Ticker + */ + function unregister() { + unregister_tick_function(array($this, "__invoke")); + return $this; + } + + /** + * The tick handler; calls atick\Ticker::wait(0) + * @return int + */ + function __invoke() { + return $this->wait(0); + } + + /** + * Wait for read/write readiness on the watched fds + * @param float $timeout + * @return int count of wached fds + */ + function wait($timeout = 1) { + $r = $w = $e = array(); + foreach ($this->read as $s) { + $r[] = $s[0]; + } + foreach ($this->write as $s) { + $w[] = $s[0]; + } + $s = (int) $timeout; + $u = (int) (($timeout - $s) * 1000000); + if (($r || $w) && stream_select($r, $w, $e, $s, $u)) { + foreach ($r as $s) { + if ($this->read[(int)$s][1]($s)) { + unset($this->read[(int)$s]); + } + } + foreach ($w as $s) { + if ($this->write[(int)$s][1]($s)) { + unset($this->write[(int)$s]); + } + } + } + return $this->count(); + } + + /** + * @implements \Countable + * @return int + */ + function count() { + return count($this->read) + count($this->write); + } + + /** + * Attach a read handler; let the callback return true, to stop watching the fd. + * @param resource $fd + * @param callable $cb + * @return \atick\Ticker + */ + function read($fd, callable $cb) { + $this->read[(int)$fd] = array($fd, $cb); + return $this; + } + + /** + * Attach a write handler; let the callback return true, to stop watching the fd. + * @param int $fd + * @param callable $cb + * @return \atick\Ticker + */ + function write($fd, callable $cb) { + $this->write[(int)$fd] = array($fd, $cb); + return $this; + } +} diff --git a/tests/lib/atick/TickerTest.php b/tests/lib/atick/TickerTest.php new file mode 100644 index 0000000..5d07985 --- /dev/null +++ b/tests/lib/atick/TickerTest.php @@ -0,0 +1,69 @@ +ticker = new Ticker; + } + + public function testRegister() { + $this->ticker->register(); + $this->ticker->unregister(); + $this->ticker->register(); + $this->ticker->unregister(); + } + + public function testTicks() { + $file = fopen(__FILE__, "r"); + stream_set_blocking($file, false); + $read = 0; + + declare(ticks=1); + $this->ticker->register(); + $this->ticker->read($file, function ($file) use (&$read) { + do { + $data = fread($file, 4096); + $read += strlen($data); + } while (strlen($data)); + return feof($file); + }); + + $dummy = "This test is do tiny, "; + $dummy.= "we don't have to do much."; + + $this->assertEquals(filesize(__FILE__), $read); + } + + public function testBasic() { + $r = $w = false; + $this->assertCount(0, $this->ticker); + + $file = fopen(__FILE__, "r"); + stream_set_blocking($file, false); + + $this->ticker->read($file, function ($file) use (&$r) { + return $r; + }); + $this->assertCount(1, $this->ticker); + $this->ticker->write($file, function ($file) use (&$w) { + return $w; + }); + $this->assertCount(2, $this->ticker); + + $this->assertSame(2, $this->ticker->wait()); + $r = true; + $this->assertSame(1, $this->ticker->wait()); + $w = true; + $this->assertSame(0, $this->ticker->wait()); + } + +} diff --git a/tests/setup.inc b/tests/setup.inc new file mode 100644 index 0000000..e91234c --- /dev/null +++ b/tests/setup.inc @@ -0,0 +1,5 @@ +