diff --git a/src/Worker.php b/src/Worker.php index f145d9316..110c0db8a 100644 --- a/src/Worker.php +++ b/src/Worker.php @@ -534,6 +534,29 @@ class Worker */ protected ?string $workerId = null; + /** + * Constructor. + * + * @param string|null $socketName + * @param array $socketContext + */ + public function __construct(?string $socketName = null, array $socketContext = []) + { + // Save all worker instances. + $this->workerId = spl_object_hash($this); + $this->context = new stdClass(); + static::$workers[$this->workerId] = $this; + static::$pidMap[$this->workerId] = []; + + // Context for socket. + if ($socketName) { + $this->socketName = $socketName; + $socketContext['socket']['backlog'] ??= static::DEFAULT_BACKLOG; + $this->socketContext = stream_context_create($socketContext); + } + + } + /** * Run all worker instances. * @@ -543,9 +566,10 @@ public static function runAll(): void { try { static::checkSapiEnv(); - self::initStdOut(); + static::initStdOut(); static::init(); static::parseCommand(); + static::checkPortAvailable(); static::lock(); static::daemonize(); static::initWorkers(); @@ -625,7 +649,7 @@ protected static function checkSapiEnv(): void * * @return void */ - private static function initStdOut(): void + protected static function initStdOut(): void { $defaultStream = fn () => defined('STDOUT') ? STDOUT : (@fopen('php://stdout', 'w') ?: fopen('php://output', 'w')); static::$outputStream ??= $defaultStream(); //@phpstan-ignore-line @@ -2021,6 +2045,7 @@ public static function getGracefulStop(): bool } /** + * * Write statistics data to disk. * * @return void @@ -2275,51 +2300,6 @@ public static function safeEcho(string $msg, bool $decorated = false): void restore_error_handler(); } - /** - * Constructor. - * - * @param string|null $socketName - * @param array $socketContext - */ - public function __construct(?string $socketName = null, array $socketContext = []) - { - // Save all worker instances. - $this->workerId = spl_object_hash($this); - $this->context = new stdClass(); - static::$workers[$this->workerId] = $this; - static::$pidMap[$this->workerId] = []; - - // Context for socket. - if ($socketName) { - $this->socketName = $socketName; - $socketContext['socket']['backlog'] ??= static::DEFAULT_BACKLOG; - $this->socketContext = stream_context_create($socketContext); - } - - // Try to turn reusePort on. - /*if (\DIRECTORY_SEPARATOR === '/' // if linux - && $socketName - && \version_compare(php_uname('r'), '3.9', 'ge') // if kernel >=3.9 - && \strtolower(\php_uname('s')) !== 'darwin' // if not Mac OS - && strpos($socketName,'unix') !== 0 // if not unix socket - && strpos($socketName,'udp') !== 0) { // if not udp socket - - $address = \parse_url($socketName); - if (isset($address['host']) && isset($address['port'])) { - try { - \set_error_handler(static fn (): bool => true); - // If address not in use, turn reusePort on automatically. - $server = stream_socket_server("tcp://{$address['host']}:{$address['port']}"); - if ($server) { - $this->reusePort = true; - fclose($server); - } - \restore_error_handler(); - } catch (\Throwable $e) {} - } - }*/ - } - /** * Listen. */ @@ -2335,17 +2315,17 @@ public function listen(): void // Flag. $flags = $this->transport === 'udp' ? STREAM_SERVER_BIND : STREAM_SERVER_BIND | STREAM_SERVER_LISTEN; - $errno = 0; - $errmsg = ''; + $errNo = 0; + $errMsg = ''; // SO_REUSEPORT. if ($this->reusePort) { stream_context_set_option($this->socketContext, 'socket', 'so_reuseport', 1); } // Create an Internet or Unix domain server socket. - $this->mainSocket = stream_socket_server($localSocket, $errno, $errmsg, $flags, $this->socketContext); + $this->mainSocket = stream_socket_server($localSocket, $errNo, $errMsg, $flags, $this->socketContext); if (!$this->mainSocket) { - throw new RuntimeException($errmsg); + throw new RuntimeException($errMsg); } if ($this->transport === 'ssl') { @@ -2392,6 +2372,38 @@ public function unlisten(): void } } + /** + * Check port available. + * + * @return void + */ + protected static function checkPortAvailable(): void + { + foreach (static::$workers as $worker) { + $socketName = $worker->getSocketName(); + if (DIRECTORY_SEPARATOR === '/' // if linux + && static::$status === static::STATUS_STARTING // only for starting status + && $worker->transport === 'tcp' // if tcp socket + && !str_starts_with($socketName, 'unix') // if not unix socket + && !str_starts_with($socketName, 'udp')) { // if not udp socket + + $address = parse_url($socketName); + if (isset($address['host']) && isset($address['port'])) { + $address = "tcp://{$address['host']}:{$address['port']}"; + $server = null; + set_error_handler(function ($code, $msg) { + throw new RuntimeException($msg); + }); + $server = stream_socket_server($address, $code, $msg); + if ($server) { + fclose($server); + } + restore_error_handler(); + } + } + } + } + /** * Parse local socket address. */