Skip to content

Commit 0fd7fd4

Browse files
committed
ConsoleReader: improvements from pmmp
1 parent b3b8acd commit 0fd7fd4

File tree

1 file changed

+85
-27
lines changed

1 file changed

+85
-27
lines changed

src/iTXTech/SimpleFramework/Console/ConsoleReader.php

Lines changed: 85 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -17,16 +17,21 @@
1717
namespace iTXTech\SimpleFramework\Console;
1818

1919
use iTXTech\SimpleFramework\Thread;
20-
use iTXTech\SimpleFramework\Util\Util;
2120

2221
class ConsoleReader extends Thread{
22+
public const TYPE_STREAM = 1;
23+
public const TYPE_PIPED = 2;
24+
25+
/** @var resource */
26+
private static $stdin;
27+
2328
/** @var \Threaded */
2429
protected $buffer;
2530
private $shutdown = false;
26-
private $stdin;
31+
private $type = self::TYPE_STREAM;
32+
2733

2834
public function __construct(){
29-
$this->stdin = fopen("php://stdin", "r");
3035
$this->buffer = new \Threaded;
3136
$this->start();
3237
}
@@ -36,13 +41,81 @@ public function shutdown(){
3641
}
3742

3843
public function quit(){
44+
$wait = microtime(true) + 0.5;
45+
while(microtime(true) < $wait){
46+
if($this->isRunning()){
47+
usleep(100000);
48+
}else{
49+
parent::quit();
50+
return;
51+
}
52+
}
53+
}
54+
55+
private function initStdin(){
56+
if(is_resource(self::$stdin)){
57+
fclose(self::$stdin);
58+
}
59+
60+
self::$stdin = fopen("php://stdin", "r");
61+
if($this->isPipe(self::$stdin)){
62+
$this->type = self::TYPE_PIPED;
63+
}else{
64+
$this->type = self::TYPE_STREAM;
65+
}
66+
}
67+
68+
/**
69+
* Checks if the specified stream is a FIFO pipe.
70+
*
71+
* @param resource $stream
72+
*
73+
* @return bool
74+
*/
75+
private function isPipe($stream) : bool{
76+
return is_resource($stream) and (!stream_isatty($stream) or ((fstat($stream)["mode"] & 0170000) === 0010000));
3977
}
4078

41-
private function readLine(){
42-
$line = trim(fgets($this->stdin));
79+
/**
80+
* Reads a line from the console and adds it to the buffer. This method may block the thread.
81+
*
82+
* @return bool if the main execution should continue reading lines
83+
*/
84+
private function readLine() : bool{
85+
$line = "";
86+
if(!is_resource(self::$stdin)){
87+
$this->initStdin();
88+
}
89+
90+
switch($this->type){
91+
/** @noinspection PhpMissingBreakStatementInspection */
92+
case self::TYPE_STREAM:
93+
//stream_select doesn't work on piped streams for some reason
94+
$r = [self::$stdin];
95+
if(($count = stream_select($r, $w, $e, 0, 200000)) === 0){ //nothing changed in 200000 microseconds
96+
return true;
97+
}elseif($count === false){ //stream error
98+
$this->initStdin();
99+
}
100+
101+
case self::TYPE_PIPED:
102+
if(($raw = fgets(self::$stdin)) === false){ //broken pipe or EOF
103+
$this->initStdin();
104+
$this->synchronized(function(){
105+
$this->wait(200000);
106+
}); //prevent CPU waste if it's end of pipe
107+
return true; //loop back round
108+
}
109+
110+
$line = trim($raw);
111+
break;
112+
}
113+
43114
if($line !== ""){
44-
$this->buffer[] = $line;
115+
$this->buffer[] = preg_replace("#\\x1b\\x5b([^\\x1b]*\\x7e|[\\x40-\\x50])#", "", $line);
45116
}
117+
118+
return true;
46119
}
47120

48121
/**
@@ -52,35 +125,20 @@ private function readLine(){
52125
*/
53126
public function getLine(){
54127
if($this->buffer->count() !== 0){
55-
return $this->buffer->shift();
128+
return (string) $this->buffer->shift();
56129
}
57130

58131
return null;
59132
}
60133

61134
public function run(){
62-
while(!$this->shutdown){
63-
$r = [$this->stdin];
64-
$w = null;
65-
$e = null;
66-
if(stream_select($r, $w, $e, 0, 200000) > 0){
67-
// PHP on Windows sucks
68-
if(feof($this->stdin)){
69-
if(Util::getOS() == "win"){
70-
$this->stdin = fopen("php://stdin", "r");
71-
if(!is_resource($this->stdin)){
72-
break;
73-
}
74-
}else{
75-
break;
76-
}
77-
}
78-
$this->readLine();
79-
}
80-
}
135+
$this->registerClassLoader();
136+
$this->initStdin();
137+
while(!$this->shutdown and $this->readLine()) ;
138+
fclose(self::$stdin);
81139
}
82140

83-
public function getThreadName(){
141+
public function getThreadName() : string{
84142
return "Console";
85143
}
86144
}

0 commit comments

Comments
 (0)