Skip to content

Commit 8a3322e

Browse files
authored
fix: Make the PHP executable into an array (#314)
1 parent ad9f918 commit 8a3322e

7 files changed

+83
-32
lines changed

src/Input/ChildCommandFactory.php

+5-11
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818
use Webmozart\Assert\Assert;
1919
use function array_filter;
2020
use function array_map;
21-
use function explode;
2221
use function Safe\getcwd;
2322
use function sprintf;
2423

@@ -27,8 +26,11 @@
2726
*/
2827
final readonly class ChildCommandFactory
2928
{
29+
/**
30+
* @param list<string> $phpExecutable
31+
*/
3032
public function __construct(
31-
private string $phpExecutable,
33+
private array $phpExecutable,
3234
private string $scriptPath,
3335
private string $commandName,
3436
private InputDefinition $commandDefinition,
@@ -51,7 +53,7 @@ private function createBaseCommand(
5153
InputInterface $input
5254
): array {
5355
return array_filter([
54-
...$this->getEscapedPhpExecutable(),
56+
...$this->phpExecutable,
5557
$this->scriptPath,
5658
$this->commandName,
5759
...array_map(strval(...), self::getArguments($input)),
@@ -74,14 +76,6 @@ private function getForwardedOptions(InputInterface $input): array
7476
);
7577
}
7678

77-
/**
78-
* @return list<string>
79-
*/
80-
private function getEscapedPhpExecutable(): array
81-
{
82-
return explode(' ', $this->phpExecutable);
83-
}
84-
8579
/**
8680
* @return list<string|bool|int|float|null|array<string|bool|int|float|null>>
8781
*/

src/ParallelExecutorFactory.php

+12-3
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424
use Webmozarts\Console\Parallelization\Process\StandardSymfonyProcessFactory;
2525
use Webmozarts\Console\Parallelization\Process\SymfonyProcessLauncherFactory;
2626
use function chr;
27+
use function explode;
28+
use function is_string;
2729
use function Safe\getcwd;
2830
use function str_starts_with;
2931
use const DIRECTORY_SEPARATOR;
@@ -46,6 +48,7 @@ final class ParallelExecutorFactory
4648
* @param Closure(InputInterface, OutputInterface):void $runAfterLastCommand
4749
* @param Closure(InputInterface, OutputInterface, list<string>):void $runBeforeBatch
4850
* @param Closure(InputInterface, OutputInterface, list<string>):void $runAfterBatch
51+
* @param list<string> $phpExecutable
4952
* @param array<string, string> $extraEnvironmentVariables
5053
* @param Closure(): void $processTick
5154
*/
@@ -64,7 +67,7 @@ private function __construct(
6467
private Closure $runBeforeBatch,
6568
private Closure $runAfterBatch,
6669
private string $progressSymbol,
67-
private string $phpExecutable,
70+
private array $phpExecutable,
6871
private string $scriptPath,
6972
private string $workingDirectory,
7073
private ?array $extraEnvironmentVariables,
@@ -228,11 +231,17 @@ public function withProgressSymbol(string $progressSymbol): self
228231
/**
229232
* The path of the PHP executable. It is the executable that will be used
230233
* to spawn the child process(es).
234+
*
235+
* @param string|list<string> $phpExecutable
231236
*/
232-
public function withPhpExecutable(string $phpExecutable): self
237+
public function withPhpExecutable(string|array $phpExecutable): self
233238
{
239+
$normalizedExecutable = is_string($phpExecutable)
240+
? explode(' ', $phpExecutable)
241+
: $phpExecutable;
242+
234243
$clone = clone $this;
235-
$clone->phpExecutable = $phpExecutable;
244+
$clone->phpExecutable = $normalizedExecutable;
236245

237246
return $clone;
238247
}

src/Process/PhpExecutableFinder.php

+10-3
Original file line numberDiff line numberDiff line change
@@ -35,16 +35,23 @@ public static function tryToFind(): ?string
3535
return false === $phpExecutable ? null : $phpExecutable;
3636
}
3737

38-
public static function find(): string
38+
/**
39+
* @return list<string>
40+
*/
41+
public static function find(): array
3942
{
40-
$phpExecutable = self::getFinder()->find();
43+
$finder = self::getFinder();
44+
$phpExecutable = $finder->find(false);
4145

4246
Assert::notFalse(
4347
$phpExecutable,
4448
'Could not find the PHP executable.',
4549
);
4650

47-
return $phpExecutable;
51+
return array_merge(
52+
[$phpExecutable],
53+
$finder->findArguments(),
54+
);
4855
}
4956

5057
private static function getFinder(): SymfonyPhpExecutableFinder

tests/Fixtures/Command/LegacyCommand.php

+5-2
Original file line numberDiff line numberDiff line change
@@ -160,11 +160,14 @@ private static function getProgressSymbol(): string
160160
return chr(200);
161161
}
162162

163-
private static function detectPhpExecutable(): string
163+
private static function detectPhpExecutable(): array
164164
{
165165
self::$calls['static'][] = [__FUNCTION__, func_get_args()];
166166

167-
return PhpExecutableFinder::find().' -d memory_limit=-1';
167+
return [
168+
...PhpExecutableFinder::find(),
169+
'-d memory_limit=-1',
170+
];
168171
}
169172

170173
private static function getWorkingDirectory(ContainerInterface $container): string

tests/Input/ChildCommandFactoryTest.php

+9-9
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ final class ChildCommandFactoryTest extends TestCase
3232
{
3333
#[DataProvider('childProvider')]
3434
public function test_it_can_launch_configured_child_processes(
35-
string $phpExecutable,
35+
array $phpExecutable,
3636
string $scriptPath,
3737
string $commandName,
3838
InputDefinition $commandDefinition,
@@ -53,7 +53,7 @@ public function test_it_can_launch_configured_child_processes(
5353

5454
public static function childProvider(): iterable
5555
{
56-
$phpExecutable = __FILE__;
56+
$phpExecutable = [__FILE__];
5757
$scriptPath = __DIR__.'/../../bin/console';
5858
$commandName = 'import:something';
5959

@@ -110,7 +110,7 @@ public static function childProvider(): iterable
110110
$commandDefinition,
111111
$input,
112112
[
113-
$phpExecutable,
113+
$phpExecutable[0],
114114
$scriptPath,
115115
$commandName,
116116
'group2',
@@ -166,7 +166,7 @@ public static function childProvider(): iterable
166166
$commandDefinition,
167167
$input,
168168
[
169-
$phpExecutable,
169+
$phpExecutable[0],
170170
$scriptPath,
171171
$commandName,
172172
'group2',
@@ -214,7 +214,7 @@ public static function childProvider(): iterable
214214
$commandDefinition,
215215
$input,
216216
[
217-
$phpExecutable,
217+
$phpExecutable[0],
218218
$scriptPath,
219219
$commandName,
220220
'--child',
@@ -232,7 +232,7 @@ public static function childProvider(): iterable
232232
);
233233

234234
return [
235-
'',
235+
[],
236236
$scriptPath,
237237
$commandName,
238238
$commandDefinition,
@@ -255,7 +255,7 @@ public static function childProvider(): iterable
255255
);
256256

257257
return [
258-
'/path/to/php -dmemory_limit=1',
258+
['/path/to/php', '-dmemory_limit=1'],
259259
$scriptPath,
260260
$commandName,
261261
$commandDefinition,
@@ -279,7 +279,7 @@ public static function childProvider(): iterable
279279
);
280280

281281
return [
282-
'',
282+
[],
283283
$scriptPath,
284284
'',
285285
$commandDefinition,
@@ -300,7 +300,7 @@ public function test_it_cannot_create_a_factory_with_an_invalid_script_path(): v
300300
$this->expectExceptionMessage('The script file could not be found at the path "path/to/unknown" (working directory: '.$cwd.')');
301301

302302
new ChildCommandFactory(
303-
__FILE__,
303+
[__FILE__],
304304
'path/to/unknown',
305305
'import:something',
306306
new InputDefinition(),

tests/ParallelExecutorFactoryTest.php

+39-1
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ public function test_it_can_create_a_configured_executor(): void
9797
$callable6,
9898
$progressSymbol,
9999
new ChildCommandFactory(
100-
self::FILE_1,
100+
[self::FILE_1],
101101
self::FILE_2,
102102
$commandName,
103103
$definition,
@@ -111,6 +111,44 @@ public function test_it_can_create_a_configured_executor(): void
111111
self::assertEquals($expected, $executor);
112112
}
113113

114+
public function test_it_escapes_the_php_executable_when_necessary(): void
115+
{
116+
$commandName = 'import:items';
117+
$definition = new InputDefinition();
118+
$errorHandler = new FakeErrorHandler();
119+
120+
$callable0 = self::createCallable(0);
121+
$callable1 = self::createCallable(1);
122+
$callable2 = self::createCallable(2);
123+
124+
$executorWithStringPhpExecutable = ParallelExecutorFactory::create(
125+
$callable0,
126+
$callable1,
127+
$callable2,
128+
$commandName,
129+
$definition,
130+
$errorHandler,
131+
)
132+
->withPhpExecutable('/path/to/php -dmemory_limit=128M')
133+
->build();
134+
135+
$executorWithArrayPhpExecutable = ParallelExecutorFactory::create(
136+
$callable0,
137+
$callable1,
138+
$callable2,
139+
$commandName,
140+
$definition,
141+
$errorHandler,
142+
)
143+
->withPhpExecutable([
144+
'/path/to/php',
145+
'-dmemory_limit=128M',
146+
])
147+
->build();
148+
149+
self::assertEquals($executorWithArrayPhpExecutable, $executorWithStringPhpExecutable);
150+
}
151+
114152
public function test_it_sets_the_batch_size_to_the_segment_size_by_default(): void
115153
{
116154
$commandName = 'import:items';

tests/ParallelExecutorTest.php

+3-3
Original file line numberDiff line numberDiff line change
@@ -561,7 +561,7 @@ public function test_it_can_launch_configured_child_processes(): void
561561
$noop,
562562
'ø',
563563
new ChildCommandFactory(
564-
$phpExecutable,
564+
[$phpExecutable],
565565
$scriptPath,
566566
$commandName,
567567
$commandDefinition,
@@ -1188,7 +1188,7 @@ private static function createChildProcessExecutor(
11881188
$runAfterBatch,
11891189
$progressSymbol,
11901190
new ChildCommandFactory(
1191-
__FILE__,
1191+
[__FILE__],
11921192
__FILE__,
11931193
'',
11941194
new InputDefinition(),
@@ -1237,7 +1237,7 @@ private static function createMainProcessExecutor(
12371237
$runAfterBatch,
12381238
$progressSymbol,
12391239
new ChildCommandFactory(
1240-
__FILE__,
1240+
[__FILE__],
12411241
__FILE__,
12421242
'import:something',
12431243
new InputDefinition([

0 commit comments

Comments
 (0)