From 50b5a6a0ada4b44814b4c197e4fecd231bbfa5ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20L=C3=BCck?= Date: Wed, 1 Nov 2017 02:00:04 +0100 Subject: [PATCH 1/2] Explicitly end input stream on CTRL+D --- examples/01-periodic.php | 10 ++++++++-- src/Readline.php | 9 +++++++++ src/Stdio.php | 2 +- tests/ReadlineTest.php | 18 ++++++++++++++++++ 4 files changed, 36 insertions(+), 3 deletions(-) diff --git a/examples/01-periodic.php b/examples/01-periodic.php index dee02a6..af75072 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->on('line', function ($line) use ($stdio, $loop, $timer) { $stdio->writeln('you just said: ' . $line . ' (' . 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..3322047 100644 --- a/src/Readline.php +++ b/src/Readline.php @@ -47,6 +47,7 @@ public function __construct(ReadableStreamInterface $input, WritableStreamInterf "\n" => 'onKeyEnter', "\x7f" => 'onKeyBackspace', "\t" => 'onKeyTab', + "\x04" => 'closeInput', // CTRL+D "\033[A" => 'onKeyUp', "\033[B" => 'onKeyDown', @@ -838,6 +839,14 @@ public function pipe(WritableStreamInterface $dest, array $options = array()) return $dest; } + public function closeInput() + { + if ($this->linebuffer !== '') { + $this->processLine(); + } + $this->handleEnd(); + } + public function close() { if ($this->closed) { 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..6fc2a57 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")); From 339f7c6de4b183e3ef598cc75bfbe5d1f04f0384 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20L=C3=BCck?= Date: Wed, 1 Nov 2017 12:23:36 +0100 Subject: [PATCH 2/2] Emit final data when input ends (EOF without EOL) --- examples/01-periodic.php | 4 ++-- src/Readline.php | 15 ++++++--------- tests/ReadlineTest.php | 13 +++++++++++++ 3 files changed, 21 insertions(+), 11 deletions(-) diff --git a/examples/01-periodic.php b/examples/01-periodic.php index af75072..508a1a3 100644 --- a/examples/01-periodic.php +++ b/examples/01-periodic.php @@ -16,8 +16,8 @@ }); // react to commands the user entered -$stdio->on('line', function ($line) use ($stdio, $loop, $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) . ')'); $loop->cancelTimer($timer); $stdio->end(); diff --git a/src/Readline.php b/src/Readline.php index 3322047..0317cae 100644 --- a/src/Readline.php +++ b/src/Readline.php @@ -47,7 +47,8 @@ public function __construct(ReadableStreamInterface $input, WritableStreamInterf "\n" => 'onKeyEnter', "\x7f" => 'onKeyBackspace', "\t" => 'onKeyTab', - "\x04" => 'closeInput', // CTRL+D + + "\x04" => 'handleEnd', // CTRL+D "\033[A" => 'onKeyUp', "\033[B" => 'onKeyDown', @@ -804,6 +805,10 @@ public function strwidth($str) /** @internal */ public function handleEnd() { + if ($this->linebuffer !== '') { + $this->processLine(); + } + if (!$this->closed) { $this->emit('end'); $this->close(); @@ -839,14 +844,6 @@ public function pipe(WritableStreamInterface $dest, array $options = array()) return $dest; } - public function closeInput() - { - if ($this->linebuffer !== '') { - $this->processLine(); - } - $this->handleEnd(); - } - public function close() { if ($this->closed) { diff --git a/tests/ReadlineTest.php b/tests/ReadlineTest.php index 6fc2a57..d675414 100644 --- a/tests/ReadlineTest.php +++ b/tests/ReadlineTest.php @@ -981,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());