Skip to content

Commit

Permalink
Create a generic input to read from any stream
Browse files Browse the repository at this point in the history
  • Loading branch information
duncan3dc committed Oct 30, 2024
1 parent dbdf2a0 commit 7b2c188
Show file tree
Hide file tree
Showing 3 changed files with 178 additions and 87 deletions.
90 changes: 3 additions & 87 deletions src/Util/Reader/Stdin.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,94 +2,10 @@

namespace League\CLImate\Util\Reader;

use League\CLImate\Exceptions\RuntimeException;
use Seld\CliPrompt\CliPrompt;

class Stdin implements ReaderInterface
class Stdin extends Stream
{
protected $stdIn = false;

/**
* Read the line typed in by the user
*
* @return string
*/
public function line()
{
return trim(fgets($this->getStdIn(), 1024));
}

/**
* Read from STDIN until EOF (^D) is reached
*
* @return string
*/
public function multiLine()
{
return trim(stream_get_contents($this->getStdIn()));
}

/**
* Read one character
*
* @param int $count
*
* @return string
*/
public function char($count = 1)
{
return fread($this->getStdIn(), $count);
}

/**
* Read the line, but hide what the user is typing
*
* @return string
*/
public function hidden()
public function __construct()
{
return CliPrompt::hiddenPrompt();
}

/**
* Return a valid STDIN, even if it previously EOF'ed
*
* Lazily re-opens STDIN after hitting an EOF
*
* @return resource
* @throws RuntimeException
*/
protected function getStdIn()
{
if ($this->stdIn && !feof($this->stdIn)) {
return $this->stdIn;
}

try {
$this->setStdIn();
} catch (\Error $e) {
throw new RuntimeException('Unable to read from STDIN', 0, $e);
}

return $this->stdIn;
}

/**
* Attempt to set the stdin property
*
* @return void
* @throws RuntimeException
*/
protected function setStdIn()
{
if ($this->stdIn !== false) {
fclose($this->stdIn);
}

$this->stdIn = fopen('php://stdin', 'r');

if (!$this->stdIn) {
throw new RuntimeException('Unable to read from STDIN');
}
parent::__construct("php://stdin");
}
}
100 changes: 100 additions & 0 deletions src/Util/Reader/Stream.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
<?php

namespace League\CLImate\Util\Reader;

use League\CLImate\Exceptions\RuntimeException;
use Seld\CliPrompt\CliPrompt;

class Stream implements ReaderInterface
{
/**
* @var string $filename The name of the file this stream represents.
*/
private $filename;

/**
* @var resource $resource The underlying stream this object represents.
*/
private $resource;


/**
* Create a new instance.
*
* @param string $filename The name of the file this stream represents
*/
public function __construct($filename)
{
$this->filename = $filename;
}


/**
* Read a line from the stream
*
* @return string
*/
public function line()
{
return trim(fgets($this->getResource(), 1024));
}


/**
* Read from the stream until EOF (^D) is reached
*
* @return string
*/
public function multiLine()
{
return trim(stream_get_contents($this->getResource()));
}


/**
* Read one character
*
* @param int $count
*
* @return string
*/
public function char($count = 1)
{
return fread($this->getResource(), $count);
}


/**
* Read the line, but hide what the user is typing
*
* @return string
*/
public function hidden()
{
return CliPrompt::hiddenPrompt();
}


/**
* Return a valid resource, even if it previously EOF'ed.
*
* @return resource
*/
private function getResource()
{
if ($this->resource && !feof($this->resource)) {
return $this->resource;
}

if ($this->resource !== null) {
fclose($this->resource);
}

$this->resource = fopen($this->filename, "r");
if (!$this->resource) {
throw new RuntimeException("Unable to read from {$this->filename}");
}

return $this->resource;
}
}
75 changes: 75 additions & 0 deletions tests/Util/Reader/StreamTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
<?php

namespace League\CLImate\Tests\Util\Reader;

use League\CLImate\Tests\TestBase;
use League\CLImate\Util\Reader\Stream;

class StreamTest extends TestBase
{
private $filename;
private $stream;
private $file;

public function setUp(): void
{
$this->filename = tempnam(sys_get_temp_dir(), "climate_");

$this->stream = new Stream($this->filename);
$this->file = new \SplFileObject($this->filename, "w");
}


public function tearDown(): void
{
unset($this->file);
unlink($this->filename);
}


public function testLine()
{
$this->file->fwrite("Line A\n");
$this->file->fwrite("Line B\n");

$response = $this->stream->line();
$this->assertSame("Line A", $response);

$response = $this->stream->line();
$this->assertSame("Line B", $response);
}


public function testMutliLine()
{
$this->file->fwrite("Line one\n");
$this->file->fwrite("Line two\n");

$response = $this->stream->multiLine();

$this->assertSame("Line one\nLine two", $response);
}


public function testChar()
{
$this->file->fwrite("123456789");

$response = $this->stream->char(6);

$this->assertSame("123456", $response);
}


public function testReopenStream()
{
$this->file->fwrite("Line 1\n");
$this->file->fwrite("Line 2\n");

$response = $this->stream->multiLine();
$this->assertSame("Line 1\nLine 2", $response);

$response = $this->stream->line();
$this->assertSame("Line 1", $response);
}
}

0 comments on commit 7b2c188

Please sign in to comment.