61 private \PrefixedLogger $logger;
67 public function __construct(
70 $this->logger = new \PrefixedLogger($logger,
"Console Reader Daemon");
71 $this->prepareSubprocess();
74 private function prepareSubprocess() :
void{
75 $server = stream_socket_server(
"tcp://127.0.0.1:0");
76 if($server ===
false){
77 throw new \RuntimeException(
"Failed to open console reader socket server");
79 $address = Utils::assumeNotFalse(stream_socket_get_name($server,
false),
"stream_socket_get_name() shouldn't return false here");
83 $sub = Utils::assumeNotFalse(proc_open(
84 [PHP_BINARY,
'-dopcache.enable_cli=0',
'-r', sprintf(
'require base64_decode("%s", true);', base64_encode(Path::join(__DIR__,
'ConsoleReaderChildProcess.php'))), $address],
86 2 => fopen(
"php://stderr",
"w"),
89 ),
"Something has gone horribly wrong");
91 $client = stream_socket_accept($server, 15);
92 if($client ===
false){
95 stream_socket_shutdown($server, STREAM_SHUT_RDWR);
97 $this->subprocess = $sub;
98 $this->socket = $client;
101 private function shutdownSubprocess() :
void{
105 proc_terminate($this->subprocess);
106 proc_close($this->subprocess);
107 stream_socket_shutdown($this->socket, STREAM_SHUT_RDWR);
110 public function readLine() : ?
string{
111 $r = [$this->socket];
114 if(stream_select($r, $w, $e, 0, 0) === 1){
115 $command = fgets($this->socket);
116 if($command ===
false){
117 $this->logger->debug(
"Lost connection to subprocess, restarting (maybe the child process was killed from outside?)");
118 $this->shutdownSubprocess();
119 $this->prepareSubprocess();
123 $command = preg_replace(
"#\\x1b\\x5b([^\\x1b]*\\x7e|[\\x40-\\x50])#",
"", trim($command)) ??
throw new AssumptionFailedError(
"This regex is assumed to be valid");
124 $command = preg_replace(
'/[[:cntrl:]]/',
'', $command) ??
throw new AssumptionFailedError(
"This regex is assumed to be valid");
126 return $command !==
"" ? $command :
null;
132 public function quit() :
void{
133 $this->shutdownSubprocess();