diff --git a/examples/01-periodic.php b/examples/01-periodic.php index dee02a6..508a1a3 100644 --- a/examples/01-periodic.php +++ b/examples/01-periodic.php @@ -16,10 +16,16 @@ }); // react to commands the user entered -$stdio->on('line', function ($line) use ($stdio, $timer) { - $stdio->writeln('you just said: ' . $line . ' (' . strlen($line) . ')'); +$stdio->on('data', function ($line) use ($stdio, $loop, $timer) { + $stdio->writeln('you just said: ' . addcslashes($line, "\0..\37") . ' (' . strlen($line) . ')'); - $timer->cancel(); + $loop->cancelTimer($timer); + $stdio->end(); +}); + +// cancel periodic timer if STDIN closed +$stdio->on('end', function () use ($stdio, $loop, $timer) { + $loop->cancelTimer($timer); $stdio->end(); }); diff --git a/src/Readline.php b/src/Readline.php index 6a3da14..0317cae 100644 --- a/src/Readline.php +++ b/src/Readline.php @@ -48,6 +48,8 @@ public function __construct(ReadableStreamInterface $input, WritableStreamInterf "\x7f" => 'onKeyBackspace', "\t" => 'onKeyTab', + "\x04" => 'handleEnd', // CTRL+D + "\033[A" => 'onKeyUp', "\033[B" => 'onKeyDown', "\033[C" => 'onKeyRight', @@ -803,6 +805,10 @@ public function strwidth($str) /** @internal */ public function handleEnd() { + if ($this->linebuffer !== '') { + $this->processLine(); + } + if (!$this->closed) { $this->emit('end'); $this->close(); diff --git a/src/Stdio.php b/src/Stdio.php index 8218866..cb17df4 100644 --- a/src/Stdio.php +++ b/src/Stdio.php @@ -241,7 +241,7 @@ public function handleError(\Exception $e) /** @internal */ public function handleEnd() { - $this->emit('end', array()); + $this->emit('end'); } /** @internal */ diff --git a/tests/ReadlineTest.php b/tests/ReadlineTest.php index 1eb7e8e..d675414 100644 --- a/tests/ReadlineTest.php +++ b/tests/ReadlineTest.php @@ -223,6 +223,24 @@ public function testDataEventWillBeEmittedForEmptyLine() $this->input->emit('data', array("\n")); } + public function testEndInputWithoutDataOnCtrlD() + { + $this->readline->on('data', $this->expectCallableNever()); + $this->readline->on('end', $this->expectCallableOnce()); + $this->readline->on('close', $this->expectCallableOnce()); + + $this->input->emit('data', array("\x04")); + } + + public function testEndInputWithIncompleteLineOnCtrlD() + { + $this->readline->on('data', $this->expectCallableOnceWith('hello')); + $this->readline->on('end', $this->expectCallableOnce()); + $this->readline->on('close', $this->expectCallableOnce()); + + $this->input->emit('data', array("hello\x04")); + } + public function testWriteSimpleCharWritesOnce() { $this->output->expects($this->once())->method('write')->with($this->equalTo("\r\033[K" . "k")); @@ -963,9 +981,22 @@ public function testEmitErrorWillEmitErrorAndClose() public function testEmitEndWillEmitEndAndClose() { + $this->readline->on('data', $this->expectCallableNever()); + $this->readline->on('end', $this->expectCallableOnce()); + $this->readline->on('close', $this->expectCallableOnce()); + + $this->input->emit('end'); + + $this->assertFalse($this->readline->isReadable()); + } + + public function testEmitEndAfterDataWillEmitDataAndEndAndClose() + { + $this->readline->on('data', $this->expectCallableOnce('hello')); $this->readline->on('end', $this->expectCallableOnce()); $this->readline->on('close', $this->expectCallableOnce()); + $this->input->emit('data', array('hello')); $this->input->emit('end'); $this->assertFalse($this->readline->isReadable());