Skip to content

Commit 30ed0ab

Browse files
committed
[Streams] Avoid locking stdin when no input
1 parent 48ab41e commit 30ed0ab

5 files changed

Lines changed: 175 additions & 5 deletions

File tree

src/IO/FStat.php

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
<?php
2+
3+
/**
4+
* This file is part of the yannoff/console library
5+
*
6+
* (c) Yannoff (https://github.com/yannoff)
7+
*
8+
* @project yannoff/console
9+
* @link https://github.com/yannoff/console
10+
* @license https://github.com/yannoff/console/blob/master/LICENSE
11+
*
12+
* For the full copyright and license information, please view the LICENSE
13+
* file that was distributed with this source code.
14+
*/
15+
16+
namespace Yannoff\Component\Console\IO;
17+
18+
/**
19+
* Class FStat
20+
* Handle the inode file statistics
21+
*
22+
* @source https://stackoverflow.com/a/11327451
23+
*
24+
* @package Yannoff\Component\Console\IO
25+
*/
26+
class FStat
27+
{
28+
/**
29+
* Octal mode values from libc headers
30+
*/
31+
const S_IFMT = 0170000;
32+
const S_IFIFO = 0010000;
33+
const S_IFCHR = 0020000;
34+
const S_IFDIR = 0040000;
35+
const S_IFBLK = 0060000;
36+
const S_IFREG = 0100000;
37+
const S_IFLNK = 0120000;
38+
const S_IFSOCK = 0140000;
39+
40+
/**
41+
* Get the handle current protection mode
42+
*
43+
* @param resource $handle
44+
*
45+
* @return int
46+
*/
47+
protected static function imode($handle)
48+
{
49+
$stat = fstat($handle);
50+
return $stat['mode'] & self::S_IFMT;
51+
}
52+
53+
/**
54+
* Check whether the handle is a FIFO
55+
*
56+
* @param resource $handle
57+
*
58+
* @return bool
59+
*/
60+
public static function isFifo($handle)
61+
{
62+
return self::imode($handle) == self::S_IFIFO;
63+
}
64+
65+
/**
66+
* Check whether the handle is a character
67+
*
68+
* @param resource $handle
69+
*
70+
* @return bool
71+
*/
72+
public static function isChar($handle)
73+
{
74+
return self::imode($handle) == self::S_IFCHR;
75+
}
76+
77+
/**
78+
* Check whether the handle is a directory
79+
*
80+
* @param resource $handle
81+
*
82+
* @return bool
83+
*/
84+
public static function isDir($handle)
85+
{
86+
return self::imode($handle) == self::S_IFDIR;
87+
}
88+
89+
/**
90+
* Check whether the handle is a block device
91+
*
92+
* @param resource $handle
93+
*
94+
* @return bool
95+
*/
96+
public static function isBlock($handle)
97+
{
98+
return self::imode($handle) == self::S_IFBLK;
99+
}
100+
101+
/**
102+
* Check whether the handle is a regular file redirect
103+
*
104+
* @param resource $handle
105+
*
106+
* @return bool
107+
*/
108+
public static function isRegularFile($handle)
109+
{
110+
return self::imode($handle) == self::S_IFREG;
111+
}
112+
113+
/**
114+
* Check whether the handle is a symlink
115+
*
116+
* @param resource $handle
117+
*
118+
* @return bool
119+
*/
120+
public static function isLink($handle)
121+
{
122+
return self::imode($handle) == self::S_IFLNK;
123+
}
124+
125+
/**
126+
* Check whether the handle is a socket
127+
*
128+
* @param resource $handle
129+
*
130+
* @return bool
131+
*/
132+
public static function isSocket($handle)
133+
{
134+
return self::imode($handle) == self::S_IFSOCK;
135+
}
136+
}

src/IO/Stream/IOReader.php

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,11 @@ interface IOReader extends IOStream
2626
/**
2727
* Fetch contents from the input stream
2828
*
29+
* @param bool $interactive Whether to accept contents from user input or not
30+
* If no contents are provided via a pipe or a regular file redirect, controls whether
31+
* the terminal should wait for user input - until a terminating sequence is emitted
32+
*
2933
* @return string|false The contents or **false** in case of failure
3034
*/
31-
public function read();
35+
public function read($interactive = false);
3236
}

src/IO/Stream/StandardInput.php

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,15 @@ class StandardInput extends Wrapper implements IOReader
3737
/**
3838
* {@inheritdoc}
3939
*/
40-
public function read()
40+
public function read($interactive = false)
4141
{
42-
return stream_get_contents($this->handle);
42+
// Unless explicitly forced using the $interactive parameter,
43+
// assume we only want to read from stdin when it's a piped
44+
// input or a regular file redirect
45+
if ($interactive || $this->isPiped() || $this->isFile()) {
46+
return stream_get_contents($this->handle);
47+
}
48+
49+
return '';
4350
}
4451
}

src/IO/Stream/Wrapper.php

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
use Yannoff\Component\Console\Exception\IO\StreamLogicException;
1919
use Yannoff\Component\Console\Exception\UndefinedConstantException;
2020
use Yannoff\Component\Console\IO\ConstantAccessor;
21+
use Yannoff\Component\Console\IO\FStat;
2122

2223
/**
2324
* Class Wrapper
@@ -90,6 +91,26 @@ public function getOpenMode()
9091
return $this->constant('MODE');
9192
}
9293

94+
/**
95+
* Check whether the stream is a FIFO
96+
*
97+
* @return bool
98+
*/
99+
public function isPiped()
100+
{
101+
return FStat::isFifo($this->handle);
102+
}
103+
104+
/**
105+
* Check whether the stream is a regular file redirect
106+
*
107+
* @return bool
108+
*/
109+
public function isFile()
110+
{
111+
return FStat::isRegularFile($this->handle);
112+
}
113+
93114
/**
94115
* Generic getter for Wrapper child classes constants
95116
*

src/IO/StreamAwareTrait.php

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,12 +73,14 @@ public function ioerror($message = '', $ending = Formatter::LF)
7373
/**
7474
* Read contents from the standard input
7575
*
76+
* @param bool $interactive Whether to accept user input
77+
*
7678
* @return string|false The contents or **false** in case of failure
7779
*/
78-
public function ioread()
80+
public function ioread($interactive = false)
7981
{
8082
$reader = StreamInitializer::getReader($this, IOStream::STDIN);
8183

82-
return $reader->read();
84+
return $reader->read($interactive);
8385
}
8486
}

0 commit comments

Comments
 (0)