File: //usr/share/php/CM4all/Passage.php
<?php
class PassageMessage {
public string $command;
public array $args = array();
public array $headers = array();
public ?string $body = null;
public function __construct(string $command) {
$this->command = $command;
}
public function serialize(): string {
$s = $this->command;
if (!empty($this->args)) {
$s .= ' ';
// TODO quote?
$s .= implode(' ', $this->args);
}
if (!empty($this->headers)) {
foreach ($this->headers as $name => $value) {
// TODO quote?
$s .= "\n$name:$value";
}
}
if (!is_null($this->body)) {
$s .= "\0";
$s .= $this->body;
}
return $s;
}
}
function cm4all_passage(string|PassageMessage $message): PassageMessage {
if (extension_loaded('sockets') === false)
throw new ErrorException("The 'sockets' extension is required but missing");
$socket = socket_create(AF_UNIX, SOCK_SEQPACKET, 0);
if ($socket === false)
throw new RuntimeException("Failed to create socket: " . socket_strerror(socket_last_error()));
$socketPath = '/run/cm4all/passage/socket';
$result = socket_connect($socket, $socketPath);
if ($result === false) {
$error = socket_last_error($socket);
socket_close($socket);
throw new RuntimeException("Failed to connect to the socket: " . socket_strerror($error));
}
if ($message instanceof PassageMessage)
$message = $message->serialize();
$bytesSent = socket_send($socket, $message, strlen($message), 0);
if ($bytesSent === false) {
$error = socket_last_error($socket);
socket_close($socket);
throw new RuntimeException("Failed to send data: " . socket_strerror($error));
}
$response = '';
$bytesReceived = socket_recv($socket, $response, 65536, MSG_WAITALL);
if ($bytesReceived === false) {
$error = socket_last_error($socket);
socket_close($socket);
throw new RuntimeException("Failed to receive data: " . socket_strerror($error));
}
socket_close($socket);
// extract the body (after the null byte)
$response = explode("\0", $response, 2);
$body = @$response[1];
$response = $response[0];
// extract the first line
$response = explode("\n", $response, 2);
$line = explode(' ', $response[0], 2);
$response = @$response[1];
// extract the command from the first line
$msg = new PassageMessage($line[0]);
$msg->body = $body;
// extract the args from the first line
if (@$line[1])
// TDDO allow quoted strings
$msg->args = explode(' ', $line[1]);
// extract headers from the remaining lines
foreach (explode("\n", $response) as $line) {
$line = explode(':', $line, 2);
if (strlen($line[0]) && isset($line[1]))
// TDDO allow quoted strings
$msg->headers[$line[0]] = $line[1];
}
return $msg;
}