mirror of
https://github.com/amphp/parallel.git
synced 2025-02-20 04:44:36 +01:00
Allow custom binary path or locate binary
This commit is contained in:
parent
cc07650c3e
commit
e3b1cfd0cf
@ -17,6 +17,9 @@ use function Amp\asyncCall;
|
||||
use function Amp\call;
|
||||
|
||||
class Process implements Context {
|
||||
/** @var string|null Cached path to located PHP binary. */
|
||||
private static $binaryPath;
|
||||
|
||||
/** @var \Amp\Process\Process */
|
||||
private $process;
|
||||
|
||||
@ -26,10 +29,11 @@ class Process implements Context {
|
||||
/**
|
||||
* @param string|array $script Path to PHP script or array with first element as path and following elements options
|
||||
* to the PHP script (e.g.: ['bin/worker', '-eOptionValue', '-nOptionValue'].
|
||||
* @param string $binary Path to PHP binary. Null will attempt to automatically locate the binary.
|
||||
* @param string $cwd Working directory.
|
||||
* @param mixed[] $env Array of environment variables.
|
||||
*/
|
||||
public function __construct($script, string $cwd = "", array $env = []) {
|
||||
public function __construct($script, string $binary = null, string $cwd = "", array $env = []) {
|
||||
$options = [
|
||||
"html_errors" => "0",
|
||||
"display_errors" => "0",
|
||||
@ -42,19 +46,35 @@ class Process implements Context {
|
||||
$script = \escapeshellarg($script);
|
||||
}
|
||||
|
||||
$options = (\PHP_SAPI === "phpdbg" ? " -b -qrr " : " ") . $this->formatOptions($options);
|
||||
$separator = \PHP_SAPI === "phpdbg" ? " -- " : " ";
|
||||
$command = \escapeshellarg(\PHP_BINARY) . $options . $separator . $script;
|
||||
|
||||
$processOptions = [];
|
||||
|
||||
if (\strncasecmp(\PHP_OS, "WIN", 3) === 0) {
|
||||
$processOptions = ["bypass_shell" => true];
|
||||
if ($binary === null) {
|
||||
$binary = \PHP_BINARY;
|
||||
}
|
||||
|
||||
// Locate PHP executable when running under non-cli SAPI and no custom binary path was provided.
|
||||
if ($binary === \PHP_BINARY && \PHP_SAPI !== "cli") {
|
||||
$binary = self::$binaryPath ?? self::locateBinary();
|
||||
} elseif (!\is_executable($binary)) {
|
||||
throw new \Error(\sprintf("The PHP binary path '%s' was not found or is not executable", $binary));
|
||||
}
|
||||
|
||||
$command = \escapeshellarg($binary) . " " . $this->formatOptions($options) . " " . $script;
|
||||
$processOptions = \strncasecmp(\PHP_OS, "WIN", 3) === 0 ? ["bypass_shell" => true] : [];
|
||||
|
||||
$this->process = new BaseProcess($command, $cwd, $env, $processOptions);
|
||||
}
|
||||
|
||||
private static function locateBinary(): string {
|
||||
$executable = \strncasecmp(\PHP_OS, "WIN", 3) === 0 ? "php.exe" : "php";
|
||||
foreach (\explode(\PATH_SEPARATOR, \getenv("PATH")) as $path) {
|
||||
$path .= \DIRECTORY_SEPARATOR . $executable;
|
||||
if (\is_executable($path)) {
|
||||
return self::$binaryPath = $path;
|
||||
}
|
||||
}
|
||||
|
||||
throw new \Error("Could not locate PHP executable binary");
|
||||
}
|
||||
|
||||
private function formatOptions(array $options) {
|
||||
$result = [];
|
||||
|
||||
|
@ -22,6 +22,9 @@ class DefaultWorkerFactory implements WorkerFactory {
|
||||
return new WorkerThread;
|
||||
}
|
||||
|
||||
return new WorkerProcess;
|
||||
return new WorkerProcess(
|
||||
\getenv("AMP_PHP_BINARY") ?:
|
||||
\defined("AMP_PHP_BINARY") ? \AMP_PHP_BINARY : null
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -9,17 +9,18 @@ use Amp\Parallel\Context\Process;
|
||||
*/
|
||||
class WorkerProcess extends AbstractWorker {
|
||||
/**
|
||||
* @param string|null $binary Path to PHP binary. Null will attempt to automatically locate the binary.
|
||||
* @param string $envClassName Name of class implementing \Amp\Parallel\Worker\Environment to instigate.
|
||||
* Defaults to \Amp\Parallel\Worker\BasicEnvironment.
|
||||
* @param mixed[] $env Array of environment variables to pass to the worker. Empty array inherits from the current
|
||||
* PHP process. See the $env parameter of \Amp\Process\Process::__construct().
|
||||
*/
|
||||
public function __construct(string $envClassName = BasicEnvironment::class, array $env = []) {
|
||||
public function __construct(string $binary = null, string $envClassName = BasicEnvironment::class, array $env = []) {
|
||||
$dir = \dirname(__DIR__, 2) . '/bin';
|
||||
$script = [
|
||||
$dir . "/worker",
|
||||
"-e" . $envClassName,
|
||||
];
|
||||
parent::__construct(new Process($script, $dir, $env));
|
||||
parent::__construct(new Process($script, $binary, $dir, $env));
|
||||
}
|
||||
}
|
||||
|
18
test/Context/ProcessTest.php
Normal file
18
test/Context/ProcessTest.php
Normal file
@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
namespace Amp\Parallel\Test\Context;
|
||||
|
||||
use Amp\Parallel\Context\Process;
|
||||
use Amp\PHPUnit\TestCase;
|
||||
use Amp\Promise;
|
||||
|
||||
class ProcessTest extends TestCase {
|
||||
public function testBasicProcess() {
|
||||
$process = new Process([
|
||||
dirname(__DIR__) . "/bin/process",
|
||||
"-sTest"
|
||||
]);
|
||||
$process->start();
|
||||
$this->assertSame("Test", Promise\wait($process->join()));
|
||||
}
|
||||
}
|
61
test/bin/process
Normal file
61
test/bin/process
Normal file
@ -0,0 +1,61 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
|
||||
use Amp\Parallel\Sync;
|
||||
|
||||
// Doesn't exist in phpdbg...
|
||||
if (function_exists("cli_set_process_title")) {
|
||||
@cli_set_process_title("process-test");
|
||||
}
|
||||
|
||||
// Redirect all output written using echo, print, printf, etc. to STDERR.
|
||||
ob_start(function ($data) {
|
||||
fwrite(STDERR, $data);
|
||||
return '';
|
||||
}, 1, PHP_OUTPUT_HANDLER_CLEANABLE | PHP_OUTPUT_HANDLER_FLUSHABLE);
|
||||
|
||||
(function () {
|
||||
$paths = [
|
||||
dirname(__DIR__, 2) . "/vendor/autoload.php",
|
||||
];
|
||||
|
||||
foreach ($paths as $path) {
|
||||
if (file_exists($path)) {
|
||||
$autoloadPath = $path;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!isset($autoloadPath)) {
|
||||
fwrite(STDERR, "Could not locate autoload.php");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
require $autoloadPath;
|
||||
})();
|
||||
|
||||
Amp\Loop::run(function () {
|
||||
$channel = new Sync\ChannelledSocket(STDIN, STDOUT);
|
||||
|
||||
try {
|
||||
$value = (function (): string {
|
||||
$options = getopt("s:");
|
||||
|
||||
if (!isset($options["s"])) {
|
||||
throw new Error("No string provided");
|
||||
}
|
||||
|
||||
return $options["s"];
|
||||
})();
|
||||
|
||||
$result = new Sync\ExitSuccess($value);
|
||||
} catch (Throwable $exception) {
|
||||
$result = new Sync\ExitFailure($exception);
|
||||
}
|
||||
|
||||
try {
|
||||
yield $channel->send($result);
|
||||
} catch (Throwable $exception) {
|
||||
exit(1); // Parent context died, simply exit.
|
||||
}
|
||||
});
|
Loading…
x
Reference in New Issue
Block a user