From 48e01670609e4d828fbaeae2f058d79b6054077d Mon Sep 17 00:00:00 2001 From: Felix Becker Date: Tue, 29 Nov 2016 19:32:17 +0100 Subject: [PATCH] Support to run as TCP server & fork a child process for every connection (#183) --- README.md | 10 ++++++ bin/php-language-server.php | 70 ++++++++++++++++++++++++++++++------- 2 files changed, 68 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index dae264d..de9167b 100644 --- a/README.md +++ b/README.md @@ -148,6 +148,16 @@ Example: php bin/php-language-server.php --tcp=127.0.0.1:12345 +#### `--tcp-server=host:port` (optional) +Causes the server to use a tcp connection for communicating with the language client instead of using STDIN/STDOUT. +The server will listen on the given address for a connection. +If PCNTL is available, will fork a child process for every connection. +If not, will only accept one connection and the connection cannot be reestablished once closed, spawn a new process instead. + +Example: + + php bin/php-language-server.php --tcp-server=127.0.0.1:12345 + #### `--memory-limit=integer` (optional) Sets memory limit for language server. Equivalent to [memory-limit](http://php.net/manual/en/ini.core.php#ini.memory-limit) php.ini directive. diff --git a/bin/php-language-server.php b/bin/php-language-server.php index cad4215..7cb3178 100644 --- a/bin/php-language-server.php +++ b/bin/php-language-server.php @@ -3,7 +3,7 @@ use LanguageServer\{LanguageServer, ProtocolStreamReader, ProtocolStreamWriter}; use Sabre\Event\Loop; -$options = getopt('', ['tcp::', 'memory-limit::']); +$options = getopt('', ['tcp::', 'tcp-server::', 'memory-limit::']); ini_set('memory_limit', $options['memory-limit'] ?? -1); @@ -31,21 +31,67 @@ set_exception_handler(function (\Throwable $e) { @cli_set_process_title('PHP Language Server'); if (!empty($options['tcp'])) { + // Connect to a TCP server $address = $options['tcp']; $socket = stream_socket_client('tcp://' . $address, $errno, $errstr); if ($socket === false) { - fwrite(STDERR, "Could not connect to language client. Error $errno\n"); - fwrite(STDERR, "$errstr\n"); + fwrite(STDERR, "Could not connect to language client. Error $errno\n$errstr"); exit(1); } - $inputStream = $outputStream = $socket; + stream_set_blocking($socket, false); + $ls = new LanguageServer( + new ProtocolStreamReader($socket), + new ProtocolStreamWriter($socket) + ); + Loop\run(); +} else if (!empty($options['tcp-server'])) { + // Run a TCP Server + $address = $options['tcp-server']; + $tcpServer = stream_socket_server('tcp://' . $address, $errno, $errstr); + if ($tcpServer === false) { + fwrite(STDERR, "Could not listen on $address. Error $errno\n$errstr"); + exit(1); + } + fwrite(STDOUT, "Server listening on $address\n"); + if (!extension_loaded('pcntl')) { + fwrite(STDERR, "PCNTL is not available. Only a single connection will be accepted\n"); + } + while ($socket = stream_socket_accept($tcpServer, -1)) { + fwrite(STDOUT, "Connection accepted\n"); + stream_set_blocking($socket, false); + if (extension_loaded('pcntl')) { + // If PCNTL is available, fork a child process for the connection + // An exit notification will only terminate the child process + $pid = pcntl_fork(); + if ($pid === -1) { + fwrite(STDERR, "Could not fork\n"); + exit(1); + } else if ($pid === 0) { + // Child process + $ls = new LanguageServer( + new ProtocolStreamReader($socket), + new ProtocolStreamWriter($socket) + ); + Loop\run(); + // Just for safety + exit(0); + } + } else { + // If PCNTL is not available, we only accept one connection. + // An exit notification will terminate the server + $ls = new LanguageServer( + new ProtocolStreamReader($socket), + new ProtocolStreamWriter($socket) + ); + Loop\run(); + } + } } else { - $inputStream = STDIN; - $outputStream = STDOUT; + // Use STDIO + stream_set_blocking(STDIN, false); + $ls = new LanguageServer( + new ProtocolStreamReader(STDIN), + new ProtocolStreamWriter(STDOUT) + ); + Loop\run(); } - -stream_set_blocking($inputStream, false); - -$server = new LanguageServer(new ProtocolStreamReader($inputStream), new ProtocolStreamWriter($outputStream)); - -Loop\run();