Skip to content

Commit

Permalink
Fix slow input on stdin
Browse files Browse the repository at this point in the history
Non-blocking reading from stdin was introduced in
80b156d because we had problems with
auto detecting whether we should read from stdin or not. Combining
`posix_isatty()` and `feof()` can get us back to blocking read.

By going back to blocking read from stdin we can avoid the extra check
introduced in 23bd023 for Windows.

We can also avoid the "read by line" and "usleep" introduced
8d669c5 and
63fafe0. They where introduced trying
to solve what is possible the same problem still reported in squizlabs#1472.

Fixes squizlabs#993.
Fixes squizlabs#1472.
  • Loading branch information
arnested committed Oct 28, 2017
1 parent fc08f50 commit 89fb342
Showing 1 changed file with 69 additions and 20 deletions.
89 changes: 69 additions & 20 deletions src/Config.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
* and provides functions to access data stored in config files.
*
* @author Greg Sherwood <gsherwood@squiz.net>
* @copyright 2006-2015 Squiz Pty Ltd (ABN 77 084 670 600)
* @copyright 2006-2017 Squiz Pty Ltd (ABN 77 084 670 600)
* @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
*/

Expand Down Expand Up @@ -326,11 +326,9 @@ public function __construct(array $cliArgs=array(), $dieOnUnknownArg=true)
$this->dieOnUnknownArg = $dieOnUnknownArg;
}

$checkStdin = false;
if (empty($cliArgs) === true) {
$cliArgs = $_SERVER['argv'];
array_shift($cliArgs);
$checkStdin = true;
}

$this->restoreDefaults();
Expand Down Expand Up @@ -362,30 +360,81 @@ public function __construct(array $cliArgs=array(), $dieOnUnknownArg=true)
} while ($currentDir !== '.' && $currentDir !== $lastDir);
}//end if

// Check for content on STDIN.
if ($checkStdin === true) {
$handle = fopen('php://stdin', 'r');
if (stream_set_blocking($handle, false) === true) {
$fileContents = '';
while (($line = fgets($handle)) !== false) {
$fileContents .= $line;
usleep(10);
}
$handle = fopen('php://stdin', 'r');

stream_set_blocking($handle, true);
fclose($handle);
if (trim($fileContents) !== '') {
$this->stdin = true;
$this->stdinContent = $fileContents;
$this->overriddenDefaults['stdin'] = true;
$this->overriddenDefaults['stdinContent'] = true;
}
// Check for content on STDIN.
if (($this->stdin === true) || (($this->isStdinATTY() === false) && (feof($handle) === false))) {
$fileContents = stream_get_contents($handle);

if (trim($fileContents) !== '') {
$this->stdin = true;
$this->stdinContent = $fileContents;
$this->overriddenDefaults['stdin'] = true;
$this->overriddenDefaults['stdinContent'] = true;
}
}

fclose($handle);

}//end __construct()


/**
* Check if STDIN is a TTY.
*
* @return bool
*/
protected function isStdinATTY()
{
// The check is slow (especially calling `tty`) so we static
// cache the result.
static $isStdin = null;

if ($isStdin !== null) {
return $isStdin;
}

// If PHP has the POSIX extensions we will use them.
if (function_exists('posix_isatty') === true) {
$isStdin = (posix_isatty(STDIN) === true);

return $isStdin;
}

// Next try is detecting whether we have `tty` installed and use that.
if (defined('PHP_WINDOWS_VERSION_PLATFORM') === true) {
$devnull = 'NUL';
$which = 'where';
} else {
$devnull = '/dev/null';
$which = 'which';
}

if (empty(trim(shell_exec("$which tty 2> $devnull"))) === false) {
exec("tty -s 2> $devnull", $output, $returnValue);
$isStdin = ($returnValue === 0);

return $isStdin;
}

// Finally we will use fstat. The solution borrowed from
// https://stackoverflow.com/questions/11327367/detect-if-a-php-script-is-being-run-interactively-or-not
// This doesn't work on Mingw/Cygwin/... using Mintty but they
// have `tty` installed.
$type = array(
'S_IFMT' => 0170000,
'S_IFIFO' => 0010000,
);

$stat = fstat(STDIN);
$mode = ($stat['mode'] & $type['S_IFMT']);
$isStdin = ($mode !== $type['S_IFIFO']);

return $isStdin;

}//end isStdinATTY()


/**
* Set the command line values.
*
Expand Down

0 comments on commit 89fb342

Please sign in to comment.