8000 feature #17427 [Process] Allow a callback whenever the output is disa… · symfony/symfony@d55114e · GitHub
[go: up one dir, main page]

Skip to content

Commit d55114e

Browse files
committed
feature #17427 [Process] Allow a callback whenever the output is disabled (romainneutron)
This PR was merged into the 3.1-dev branch. Discussion ---------- [Process] Allow a callback whenever the output is disabled | Q | A | ------------- | --- | Bug fix? | no | New feature? | yes | BC breaks? | no | Deprecations? | no | Tests pass? | yes | Fixed tickets | Related to #17390 | License | MIT This was not allowed in the past. However, it's possible with these changes Commits ------- 02f0fc7 [Process] Allow a callback whenever the output is disabled
2 parents 8c51a94 + 02f0fc7 commit d55114e

File tree

5 files changed

+75
-39
lines changed

5 files changed

+75
-39
lines changed

src/Symfony/Component/Process/Pipes/PipesInterface.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,13 @@ public function readAndWrite($blocking, $close = false);
5353
*/
5454
public function areOpen();
5555

56+
/**
57+
* Returns if pipes are able to read output.
58+
*
59+
* @return bool
60+
*/
61+
public function haveReadSupport();
62+
5663
/**
5764
* Closes file handles and pipes.
5865
*/

src/Symfony/Component/Process/Pipes/UnixPipes.php

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,13 @@ class UnixPipes extends AbstractPipes
2727
/** @var bool */
2828
private $ptyMode;
2929
/** @var bool */
30-
private $disableOutput;
30+
private $haveReadSupport;
3131

32-
public function __construct($ttyMode, $ptyMode, $input, $disableOutput)
32+
public function __construct($ttyMode, $ptyMode, $input, $haveReadSupport)
3333
{
3434
$this->ttyMode = (bool) $ttyMode;
3535
$this->ptyMode = (bool) $ptyMode;
36-
$this->disableOutput = (bool) $disableOutput;
36+
$this->haveReadSupport = (bool) $haveReadSupport;
3737

3838
if (is_resource($input)) {
3939
$this->input = $input;
@@ -52,7 +52,7 @@ public function __destruct()
5252
*/
5353
public function getDescriptors()
5454
{
55-
if ($this->disableOutput) {
55+
if (!$this->haveReadSupport) {
5656
$nullstream = fopen('/dev/null', 'c');
5757

5858
return array(
@@ -191,6 +191,14 @@ public function readAndWrite($blocking, $close = false)
191191
return $read;
192192
}
193193

194+
/**
195+
* {@inheritdoc}
196+
*/
197+
public function haveReadSupport()
198+
{
199+
return $this->haveReadSupport;
200+
}
201+
194202
/**
195203
* {@inheritdoc}
196204
*/
@@ -209,6 +217,6 @@ public function areOpen()
209217
*/
210218
public static function create(Process $process, $input)
211219
{
212-
return new static($process->isTty(), $process->isPty(), $input, $process->isOutputDisabled());
220+
return new static($process->isTty(), $process->isPty(), $input, !$process->isOutputDisabled() || $process->hasCallback());
213221
}
214222
}

src/Symfony/Component/Process/Pipes/WindowsPipes.php

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -36,13 +36,13 @@ class WindowsPipes extends AbstractPipes
3636
Process::STDERR => 0,
3737
);
3838
/** @var bool */
39-
private $disableOutput;
39+
private $haveReadSupport;
4040

41-
public function __construct($disableOutput, $input)
41+
public function __construct($haveReadSupport, $input)
4242
{
43-
$this->disableOutput = (bool) $disableOutput;
43+
$this->haveReadSupport = (bool) $haveReadSupport;
4444

45-
if (!$this->disableOutput) {
45+
if ($this->haveReadSupport) {
4646
// Fix for PHP bug #51800: reading from STDOUT pipe hangs forever on Windows if the output is too big.
4747
// Workaround for this problem is to use temporary files instead of pipes on Windows platform.
4848
//
@@ -76,7 +76,7 @@ public function __destruct()
7676
*/
7777
public function getDescriptors()
7878
{
79-
if ($this->disableOutput) {
79+
if (!$this->haveReadSupport) {
8080
$nullstream = fopen('NUL', 'c');
8181

8282
return array(
@@ -138,6 +138,14 @@ public function readAndWrite($blocking, $close = false)
138138
return $read;
139139
}
140140

141+
/**
142+
* {@inheritdoc}
143+
*/
144+
public function haveReadSupport()
145+
{
146+
return $this->haveReadSupport;
147+
}
148+
141149
/**
142150
* {@inheritdoc}
143151
*/
@@ -168,7 +176,7 @@ public function close()
168176
*/
169177
public static function create(Process $process, $input)
170178
{
171-
return new static($process->isOutputDisabled(), $input);
179+
return new static(!$process->isOutputDisabled() || $process->hasCallback(), $input);
172180
}
173181

174182
/**

src/Symfony/Component/Process/Process.php

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -253,9 +253,6 @@ public function start(callable $callback = null)
253253
if ($this->isRunning()) {
254254
throw new RuntimeException('Process is already running');
255255
}
256-
if ($this->outputDisabled && null !== $callback) {
257-
throw new LogicException('Output has been disabled, enable it to allow the use of a callback.');
258-
}
259256

260257
$this->resetProcessData();
261258
$this->starttime = $this->lastOutputTime = microtime(true);
@@ -353,7 +350,12 @@ public function wait(callable $callback = null)
353350
$this->requireProcessIsStarted(__FUNCTION__);
354351

355352
$this->updateStatus(false);
353+
356354
if (null !== $callback) {
355+
if (!$this->processPipes->haveReadSupport()) {
356+
$this->stop(0);
357+
throw new \LogicException('Pass the callback to the Process:start method or enableOutput to use a callback with Process::wait');
358+
}
357359
$this->callback = $this->buildCallback($callback);
358360
}
359361

@@ -1200,6 +1202,18 @@ public static function isPtySupported()
12001202
return $result = (bool) @proc_open('echo 1', array(array('pty'), array('pty'), array('pty')), $pipes);
12011203
}
12021204

1205+
/**
1206+
* Returns whether a callback is used on underlying process output.
1207+
*
1208+
* @internal
1209+
*
1210+
* @return bool
1211+
*/
1212+
public function hasCallback()
1213+
{
1214+
return (bool) $this->callback;
1215+
}
1216+
12031217
/**
12041218
* Creates the descriptors needed by the proc_open.
12051219
*
@@ -1226,10 +1240,19 @@ private function getDescriptors()
12261240
*
12271241
* @return \Closure A PHP closure
12281242
*/
1229-
protected function buildCallback($callback)
1243+
protected function buildCallback(callable $callback = null)
12301244
{
1245+
if ($this->outputDisabled) {
1246+
return function ($type, $data) use ($callback) {
1247+
if (null !== $callback) {
1248+
call_user_func($callback, $type, $data);
1249+
}
1250+
};
1251+
}
1252+
12311253
$out = self::OUT;
1232-
$callback = function ($type, $data) use ($callback, $out) {
1254+
1255+
return function ($type, $data) use ($callback, $out) {
12331256
if ($out == $type) {
12341257
$this->addOutput($data);
12351258
} else {

src/Symfony/Component/Process/Tests/ProcessTest.php

Lines changed: 13 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -305,6 +305,19 @@ public function testCallbackIsExecutedForOutput()
305305
$this->assertTrue($called, 'The callback should be executed with the output');
306306
}
307307

308+
public function testCallbackIsExecutedForOutputWheneverOutputIsDisabled()
309+
{
310+
$p = $this->getProcess(sprintf('%s -r %s', self::$phpBin, escapeshellarg('echo \'foo\';')));
311+
$p->disableOutput();
312+
313+
$called = false;
314+
$p->run(function ($type, $buffer) use (&$called) {
315+
$called = $buffer === 'foo';
316+
});
317+
318+
$this->assertTrue($called, 'The callback should be executed with the output');
319+
}
320+
308321
public function testGetErrorOutput()
309322
{
310323
$p = $this->getProcess(sprintf('%s -r %s', self::$phpBin, escapeshellarg('$n = 0; while ($n < 3) { file_put_contents(\'php://stderr\', \'ERROR\'); $n++; }')));
@@ -1108,29 +1121,6 @@ public function testSetNullIdleTimeoutWhileOutputIsDisabled()
11081121
$this->assertSame($process, $process->setIdleTimeout(null));
11091122
}
11101123

1111-
/**
1112-
* @dataProvider provideStartMethods
1113-
*/
1114-
public function testStartWithACallbackAndDisabledOutput($startMethod, $exception, $exceptionMessage)
1115-
{
1116-
$p = $this->getProcess('foo');
1117-
$p->disableOutput();
1118-
$this->setExpectedException($exception, $exceptionMessage);
1119-
if ('mustRun' === $startMethod) {
1120-
$this->skipIfNotEnhancedSigchild();
1121-
}
1122-
$p->{$startMethod}(function () {});
1123-
}
1124-
1125-
public function provideStartMethods()
1126-
{
1127-
return array(
1128-
array('start', 'Symfony\Component\Process\Exception\LogicException', 'Output has been disabled, enable it to allow the use of a callback.'),
1129-
array('run', 'Symfony\Component\Process\Exception\LogicException', 'Output has been disabled, enable it to allow the use of a callback.'),
1130-
array('mustRun', 'Symfony\Component\Process\Exception\LogicException', 'Output has been disabled, enable it to allow the use of a callback.'),
1131-
);
1132-
}
1133-
11341124
/**
11351125
* @dataProvider provideOutputFetchingMethods
11361126
* @expectedException \Symfony\Component\Process\Exception\LogicException

0 commit comments

Comments
 (0)
0