Skip to content

Commit

Permalink
Merge pull request #107 from SimonFrings/nullable
Browse files Browse the repository at this point in the history
Improve PHP 8.4+ support by avoiding implicitly nullable types
  • Loading branch information
clue authored Jul 4, 2024
2 parents f7ebf4e + 4172f6e commit 9434a04
Show file tree
Hide file tree
Showing 5 changed files with 84 additions and 6 deletions.
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
"clue/term-react": "^1.0 || ^0.1.1",
"clue/utf8-react": "^1.0 || ^0.1",
"react/event-loop": "^1.2",
"react/stream": "^1.2"
"react/stream": "^1.4"
},
"require-dev": {
"clue/arguments": "^2.0",
Expand Down
15 changes: 13 additions & 2 deletions src/Readline.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,12 @@ class Readline extends EventEmitter implements ReadableStreamInterface
private $autocomplete = null;
private $autocompleteSuggestions = 8;

public function __construct(ReadableStreamInterface $input, WritableStreamInterface $output, EventEmitterInterface $base = null)
/**
* @param ReadableStreamInterface $input
* @param WritableStreamInterface $output
* @param ?EventEmitterInterface $base
*/
public function __construct(ReadableStreamInterface $input, WritableStreamInterface $output, $base = null)
{
$this->input = $input;
$this->output = $output;
Expand All @@ -49,6 +54,10 @@ public function __construct(ReadableStreamInterface $input, WritableStreamInterf
// push input through control code parser
$parser = new ControlCodeParser($input);

if ($base !== null && !$base instanceof EventEmitterInterface) { // manual type check to support legacy PHP < 7.1
throw new \InvalidArgumentException('Argument #3 ($base) expected null|Evenement\EventEmitterInterface');
}

$that = $this;
$codes = array(
"\n" => 'onKeyEnter', // ^J
Expand Down Expand Up @@ -778,9 +787,11 @@ public function onKeyDown()
/**
* Will be invoked for character(s) that could not otherwise be processed by the sequencer
*
* @param string $chars
* @param ?EventEmitterInterface $base
* @internal
*/
public function onFallback($chars, EventEmitterInterface $base = null)
public function onFallback($chars, $base = null)
{
// check if there's any special key binding for any of the chars
$buffer = '';
Expand Down
22 changes: 19 additions & 3 deletions src/Stdio.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,24 @@ class Stdio extends EventEmitter implements DuplexStreamInterface
* @param ?WritableStreamInterface $output
* @param ?Readline $readline
*/
public function __construct(LoopInterface $loop = null, ReadableStreamInterface $input = null, WritableStreamInterface $output = null, Readline $readline = null)
public function __construct($loop = null, $input = null, $output = null, $readline = null)
{
if ($loop !== null && !$loop instanceof LoopInterface) { // manual type check to support legacy PHP < 7.1
throw new \InvalidArgumentException('Argument #1 ($loop) expected null|React\EventLoop\LoopInterface');
}

if ($input !== null && !$input instanceof ReadableStreamInterface) { // manual type check to support legacy PHP < 7.1
throw new \InvalidArgumentException('Argument #2 ($input) expected null|React\Stream\ReadableStreamInterface');
}

if ($output !== null && !$output instanceof WritableStreamInterface) { // manual type check to support legacy PHP < 7.1
throw new \InvalidArgumentException('Argument #3 ($output) expected null|React\Stream\WritableStreamInterface');
}

if ($readline !== null && !$readline instanceof Readline) { // manual type check to support legacy PHP < 7.1
throw new \InvalidArgumentException('Argument #4 ($readline) expected null|Clue\React\Stdio\Readline');
}

if ($input === null) {
$input = $this->createStdin($loop); // @codeCoverageIgnore
}
Expand Down Expand Up @@ -546,7 +562,7 @@ private function restoreTtyMode()
* @return ReadableStreamInterface
* @codeCoverageIgnore this is covered by functional tests with/without ext-readline
*/
private function createStdin(LoopInterface $loop = null)
private function createStdin($loop = null)
{
// STDIN not defined ("php -a") or already closed (`fclose(STDIN)`)
// also support starting program with closed STDIN ("example.php 0<&-")
Expand Down Expand Up @@ -586,7 +602,7 @@ private function createStdin(LoopInterface $loop = null)
* @return WritableStreamInterface
* @codeCoverageIgnore this is covered by functional tests
*/
private function createStdout(LoopInterface $loop = null)
private function createStdout($loop = null)
{
// STDOUT not defined ("php -a") or already closed (`fclose(STDOUT)`)
// also support starting program with closed STDOUT ("example.php >&-")
Expand Down
6 changes: 6 additions & 0 deletions tests/ReadlineTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,12 @@ public function setUpReadline()
$this->readline = new Readline($this->input, $this->output);
}

public function testCtorThrowsExceptionForInvalidBase()
{
$this->setExpectedException('InvalidArgumentException', 'Argument #3 ($base) expected null|Evenement\EventEmitterInterface');
new Readline($this->input, $this->output, 'invalid');
}

public function testSettersReturnSelf()
{
$this->assertSame($this->readline, $this->readline->setEcho(true));
Expand Down
45 changes: 45 additions & 0 deletions tests/StdioTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,51 @@ public function testCtorReadlineArgWillBeReturnedBygetReadline()
$this->assertSame($readline, $stdio->getReadline());
}

public function testCtorThrowsExceptionForInvalidLoop()
{
$input = $this->getMockBuilder('React\Stream\ReadableStreamInterface')->getMock();
$output = $this->getMockBuilder('React\Stream\WritableStreamInterface')->getMock();

//$readline = $this->getMockBuilder('Clue\React\Stdio\Readline')->disableOriginalConstructor()->getMock();
$readline = new Readline($input, $output);

$this->setExpectedException('InvalidArgumentException', 'Argument #1 ($loop) expected null|React\EventLoop\LoopInterface');
new Stdio('invalid', $input, $output, $readline);
}

public function testCtorThrowsExceptionForInvalidInput()
{
$input = $this->getMockBuilder('React\Stream\ReadableStreamInterface')->getMock();
$output = $this->getMockBuilder('React\Stream\WritableStreamInterface')->getMock();

//$readline = $this->getMockBuilder('Clue\React\Stdio\Readline')->disableOriginalConstructor()->getMock();
$readline = new Readline($input, $output);

$this->setExpectedException('InvalidArgumentException', 'Argument #2 ($input) expected null|React\Stream\ReadableStreamInterface');
new Stdio($this->loop, 'invalid', $output, $readline);
}

public function testCtorThrowsExceptionForInvalidOutput()
{
$input = $input = $this->getMockBuilder('React\Stream\ReadableStreamInterface')->getMock();
$output = $this->getMockBuilder('React\Stream\WritableStreamInterface')->getMock();

//$readline = $this->getMockBuilder('Clue\React\Stdio\Readline')->disableOriginalConstructor()->getMock();
$readline = new Readline($input, $output);

$this->setExpectedException('InvalidArgumentException', 'Argument #3 ($output) expected null|React\Stream\WritableStreamInterface');
new Stdio($this->loop, $input, 'invalid', $readline);
}

public function testCtorThrowsExceptionForInvalidReadline()
{
$input = $input = $this->getMockBuilder('React\Stream\ReadableStreamInterface')->getMock();
$output = $this->getMockBuilder('React\Stream\WritableStreamInterface')->getMock();

$this->setExpectedException('InvalidArgumentException', 'Argument #4 ($readline) expected null|Clue\React\Stdio\Readline');
new Stdio($this->loop, $input, $output, 'invalid');
}

public function testWriteEmptyStringWillNotWriteToOutput()
{
$input = $this->getMockBuilder('React\Stream\ReadableStreamInterface')->getMock();
Expand Down

0 comments on commit 9434a04

Please sign in to comment.