$fds[(int)$pipe] = $fd;
}
- while ( true ) {
- $status = proc_get_status( $proc );
- if ( !$status['running'] ) {
- break;
+ $running = true;
+ $timeout = null;
+ $numReadyPipes = 0;
+
+ while ( $running === true || $numReadyPipes !== 0 ) {
+ if ( $running ) {
+ $status = proc_get_status( $proc );
+ // If the process has terminated, switch to nonblocking selects
+ // for getting any data still waiting to be read.
+ if ( !$status['running'] ) {
+ $running = false;
+ $timeout = 0;
+ }
}
- $status = false;
$readyPipes = $pipes;
// Clear last error
// @codingStandardsIgnoreStart Generic.PHP.NoSilencedErrors.Discouraged
@trigger_error( '' );
- if ( @stream_select( $readyPipes, $emptyArray, $emptyArray, null ) === false ) {
+ $numReadyPipes = @stream_select( $readyPipes, $emptyArray, $emptyArray, $timeout );
+ if ( $numReadyPipes === false ) {
// @codingStandardsIgnoreEnd
$error = error_get_last();
if ( strncmp( $error['message'], $eintrMessage, strlen( $eintrMessage ) ) == 0 ) {
// Use the status previously collected if possible, since proc_get_status()
// just calls waitpid() which will not return anything useful the second time.
- if ( $status === false ) {
+ if ( $running ) {
$status = proc_get_status( $proc );
}
--- /dev/null
+<?php
+
+/**
+ * @group GlobalFunctions
+ * @covers ::wfShellExec
+ */
+class WfShellExecTest extends MediaWikiTestCase {
+ public function testBug67870() {
+ $command = wfIsWindows()
+ // 333 = 331 + CRLF
+ ? ( 'for /l %i in (1, 1, 1001) do @echo ' . str_repeat( '*', 331 ) )
+ : 'printf "%-333333s" "*"';
+
+ // Test several times because it involves a race condition that may randomly succeed or fail
+ for ( $i = 0; $i < 10; $i++ ) {
+ $output = wfShellExec( $command );
+ $this->assertEquals( 333333, strlen( $output ) );
+ }
+ }
+}