2
2
3
3
namespace Clue \React \Stdio ;
4
4
5
- use React \Stream \ReadableStream ;
6
5
use React \Stream \Stream ;
7
6
use React \EventLoop \LoopInterface ;
8
7
@@ -13,40 +12,77 @@ class Stdin extends Stream
13
12
14
13
public function __construct (LoopInterface $ loop )
15
14
{
15
+ // STDIN not defined ("php -a") or already closed (`fclose(STDIN)`)
16
+ if (!defined ('STDIN ' ) || !is_resource (STDIN )) {
17
+ parent ::__construct (fopen ('php://memory ' , 'r ' ), $ loop );
18
+ return $ this ->close ();
19
+ }
20
+
16
21
parent ::__construct (STDIN , $ loop );
17
- }
18
22
19
- public function resume ()
20
- {
21
- if ($ this ->oldMode === null ) {
23
+ // support starting program with closed STDIN ("example.php 0<&-")
24
+ // the stream is a valid resource and is not EOF, but fstat fails
25
+ if (fstat (STDIN ) === false ) {
26
+ return $ this ->close ();
27
+ }
28
+
29
+ if ($ this ->isTty ()) {
22
30
$ this ->oldMode = shell_exec ('stty -g ' );
23
31
24
32
// Disable icanon (so we can fread each keypress) and echo (we'll do echoing here instead)
25
33
shell_exec ('stty -icanon -echo ' );
26
-
27
- parent ::resume ();
28
34
}
29
35
}
30
36
31
- public function pause ()
37
+ public function close ()
38
+ {
39
+ $ this ->restore ();
40
+ parent ::close ();
41
+ }
42
+
43
+ public function __destruct ()
32
44
{
33
- if ($ this ->oldMode !== null ) {
45
+ $ this ->restore ();
46
+ }
47
+
48
+ private function restore ()
49
+ {
50
+ if ($ this ->oldMode !== null && $ this ->isTty ()) {
34
51
// Reset stty so it behaves normally again
35
52
shell_exec (sprintf ('stty %s ' , $ this ->oldMode ));
36
-
37
53
$ this ->oldMode = null ;
38
- parent ::pause ();
39
54
}
40
55
}
41
56
42
- public function close ()
57
+ /**
58
+ * @return bool
59
+ * @codeCoverageIgnore
60
+ */
61
+ private function isTty ()
43
62
{
44
- $ this ->pause ();
45
- parent ::close ();
46
- }
63
+ if (PHP_VERSION_ID >= 70200 ) {
64
+ // Prefer `stream_isatty()` (available as of PHP 7.2 only)
65
+ return stream_isatty (STDIN );
66
+ } elseif (function_exists ('posix_isatty ' )) {
67
+ // Otherwise use `posix_isatty` if available (requires `ext-posix`)
68
+ return posix_isatty (STDIN );
69
+ }
47
70
48
- public function __destruct ()
49
- {
50
- $ this ->pause ();
71
+ // otherwise try to guess based on stat file mode and device major number
72
+ // Must be special character device: ($mode & S_IFMT) === S_IFCHR
73
+ // And device major number must be allocated to TTYs (2-5 and 128-143)
74
+ // For what it's worth, checking for device gid 5 (tty) is less reliable.
75
+ // @link http://man7.org/linux/man-pages/man7/inode.7.html
76
+ // @link https://www.kernel.org/doc/html/v4.11/admin-guide/devices.html#terminal-devices
77
+ if (is_resource (STDIN )) {
78
+ $ stat = fstat (STDIN );
79
+ $ mode = isset ($ stat ['mode ' ]) ? ($ stat ['mode ' ] & 0170000 ) : 0 ;
80
+ $ major = isset ($ stat ['dev ' ]) ? (($ stat ['dev ' ] >> 8 ) & 0xff ) : 0 ;
81
+
82
+ if ($ mode === 0020000 && $ major >= 2 && $ major <= 143 && ($ major <=5 || $ major >= 128 )) {
83
+ return true ;
84
+ }
85
+ }
86
+ return false ;
51
87
}
52
88
}
0 commit comments