22
33namespace Clue \React \Stdio ;
44
5- use React \Stream \CompositeStream ;
5+ use Evenement \EventEmitter ;
6+ use React \Stream \DuplexStreamInterface ;
67use React \EventLoop \LoopInterface ;
78use React \Stream \ReadableStreamInterface ;
89use React \Stream \WritableStreamInterface ;
10+ use React \Stream \Util ;
911
10- class Stdio extends CompositeStream
12+ class Stdio extends EventEmitter implements DuplexStreamInterface
1113{
1214 private $ input ;
1315 private $ output ;
1416 private $ readline ;
1517
18+ private $ ending = false ;
19+ private $ closed = false ;
1620 private $ incompleteLine = '' ;
1721
1822 public function __construct (LoopInterface $ loop , ReadableStreamInterface $ input = null , WritableStreamInterface $ output = null , Readline $ readline = null )
@@ -35,19 +39,27 @@ public function __construct(LoopInterface $loop, ReadableStreamInterface $input
3539
3640 $ that = $ this ;
3741
38- // stdin emits single chars
39- $ this ->input ->on ('data ' , function ($ data ) use ($ that ) {
40- $ that ->emit ('char ' , array ($ data , $ that ));
41- });
42-
4342 // readline data emits a new line
4443 $ incomplete =& $ this ->incompleteLine ;
4544 $ this ->readline ->on ('data ' , function ($ line ) use ($ that , &$ incomplete ) {
4645 // readline emits a new line on enter, so start with a blank line
4746 $ incomplete = '' ;
4847
48+ // emit data with trailing newline in order to preserve readable API
49+ $ that ->emit ('data ' , array ($ line . PHP_EOL ));
50+
51+ // emit custom line event for ease of use
4952 $ that ->emit ('line ' , array ($ line , $ that ));
5053 });
54+
55+ // handle all input events (readline forwards all input events)
56+ $ this ->readline ->on ('error ' , array ($ this , 'handleError ' ));
57+ $ this ->readline ->on ('end ' , array ($ this , 'handleEnd ' ));
58+ $ this ->readline ->on ('close ' , array ($ this , 'handleCloseInput ' ));
59+
60+ // handle all output events
61+ $ this ->output ->on ('error ' , array ($ this , 'handleError ' ));
62+ $ this ->output ->on ('close ' , array ($ this , 'handleCloseOutput ' ));
5163 }
5264
5365 public function pause ()
@@ -60,6 +72,23 @@ public function resume()
6072 $ this ->input ->resume ();
6173 }
6274
75+ public function isReadable ()
76+ {
77+ return $ this ->input ->isReadable ();
78+ }
79+
80+ public function isWritable ()
81+ {
82+ return $ this ->output ->isWritable ();
83+ }
84+
85+ public function pipe (WritableStreamInterface $ dest , array $ options = array ())
86+ {
87+ Util::pipe ($ this , $ dest , $ options );
88+
89+ return $ dest ;
90+ }
91+
6392 public function handleBuffer ()
6493 {
6594 $ that = $ this ;
@@ -70,7 +99,7 @@ public function handleBuffer()
7099
71100 public function write ($ data )
72101 {
73- if ((string )$ data === '' ) {
102+ if ($ this -> ending || (string )$ data === '' ) {
74103 return ;
75104 }
76105
@@ -159,18 +188,33 @@ public function overwrite($data = '')
159188
160189 public function end ($ data = null )
161190 {
191+ if ($ this ->ending ) {
192+ return ;
193+ }
194+
162195 if ($ data !== null ) {
163196 $ this ->write ($ data );
164197 }
165198
199+ $ this ->ending = true ;
200+
201+ // clear readline output, close input and end output
166202 $ this ->readline ->setInput ('' )->setPrompt ('' )->clear ();
167- $ this ->input ->pause ();
203+ $ this ->input ->close ();
168204 $ this ->output ->end ();
169205 }
170206
171207 public function close ()
172208 {
173- $ this ->readline ->setInput ('' )->setPrompt ('' )->clear ();
209+ if ($ this ->closed ) {
210+ return ;
211+ }
212+
213+ $ this ->ending = true ;
214+ $ this ->closed = true ;
215+
216+ // clear readline output and then close
217+ $ this ->readline ->setInput ('' )->setPrompt ('' )->clear ()->close ();
174218 $ this ->input ->close ();
175219 $ this ->output ->close ();
176220 }
@@ -194,4 +238,33 @@ private function width($str)
194238 {
195239 return mb_strwidth ($ str , 'utf-8 ' ) - 2 * substr_count ($ str , "\x08" );
196240 }
241+
242+ /** @internal */
243+ public function handleError (\Exception $ e )
244+ {
245+ $ this ->emit ('error ' , array ($ e ));
246+ $ this ->close ();
247+ }
248+
249+ /** @internal */
250+ public function handleEnd ()
251+ {
252+ $ this ->emit ('end ' , array ());
253+ }
254+
255+ /** @internal */
256+ public function handleCloseInput ()
257+ {
258+ if (!$ this ->output ->isWritable ()) {
259+ $ this ->close ();
260+ }
261+ }
262+
263+ /** @internal */
264+ public function handleCloseOutput ()
265+ {
266+ if (!$ this ->input ->isReadable ()) {
267+ $ this ->close ();
268+ }
269+ }
197270}
0 commit comments