rosavox/lib/alveolata/server/functions.php
2025-05-23 07:33:29 +00:00

276 lines
5.2 KiB
PHP

<?php
namespace alveolata\server;
require_once(DIR_ALVEOLATA . '/log/functions.php');
/**
* @author Christian Fraß <frass@greenscale.de>
*/
class struct_subject/*<type_input,type_output>*/
{
/**
* @var int
* @author Christian Fraß <frass@greenscale.de>
*/
public $port;
/**
* @var function<type_input,type_output>
* @author Christian Fraß <frass@greenscale.de>
*/
public $handler;
/**
* whether a global handler for the operating systems interrupt signal (SIGINT) shall be installed for stopping the
* server gracfully
*
* @var boolean
* @author Christian Fraß <frass@greenscale.de>
*/
public $install_killhandler;
/**
* procedure to be executed on start
*
* @var function<tuple<>,void>
* @author Christian Fraß <frass@greenscale.de>
*/
public $on_starting;
/**
* procedure to be executed on stop
*
* @var function<tuple<>,void>
* @author Christian Fraß <frass@greenscale.de>
*/
public $on_stopping;
/**
* procedure to be executed on start
*
* @var function<tuple<>,void>
* @author Christian Fraß <frass@greenscale.de>
*/
public $on_started;
/**
* procedure to be executed on stop
*
* @var function<tuple<>,void>
* @author Christian Fraß <frass@greenscale.de>
*/
public $on_stopped;
/**
* @var any
* @author Christian Fraß <frass@greenscale.de>
*/
public $socket;
/**
* whether the server is supposed to shutdown
*
* @var boolean
* @author Christian Fraß <frass@greenscale.de>
*/
public $run;
/**
* @author Christian Fraß <frass@greenscale.de>
*/
public function __construct(
int $port,
\Closure $handler,
bool $install_killhandler,
\Closure $on_starting,
\Closure $on_stopping,
\Closure $on_started,
\Closure $on_stopped
)
{
$this->port = $port;
$this->handler = $handler;
$this->install_killhandler = $install_killhandler;
$this->on_starting = $on_starting;
$this->on_stopping = $on_stopping;
$this->on_started = $on_started;
$this->on_stopped = $on_stopped;
$this->socket = null;
$this->run = false;
}
}
/**
* @param int $port
* @param function<type_input,type_output> $handler
* @param boolean $install_killhandler
* @param function<tuple<>,void> [$on_starting]
* @param function<tuple<>,void> [$on_stopping]
* @param function<tuple<>,void> [$on_started]
* @param function<tuple<>,void> [$on_stopped]
* @return struct_subject<type_input,type_output>
* @author Christian Fraß <frass@greenscale.de>
*/
function make(
int $port,
\Closure $handler,
bool $install_killhandler = false,
\Closure $on_starting = null,
\Closure $on_stopping = null,
\Closure $on_started = null,
\Closure $on_stopped = null
) : struct_subject
{
if ($on_starting === null) {
$on_starting = function () {
\alveolata\log\info('starting server …');
};
}
if ($on_stopping === null) {
$on_stopping = function () {
\alveolata\log\info('stopping server …');
};
}
if ($on_started === null) {
$on_started = function () {
\alveolata\log\info('server started');
};
}
if ($on_stopped === null) {
$on_stopped = function () {
\alveolata\log\info('server stopped');
};
}
return (
new struct_subject(
$port,
$handler,
$install_killhandler,
$on_starting,
$on_stopping,
$on_started,
$on_stopped
)
);
}
/**
* @param struct_subject<type_input,type_output> $subject
* @author Christian Fraß <frass@greenscale.de>
*/
function killhandler(
struct_subject/*<type_input,type_output>*/ $subject
) : void
{
\alveolata\log\warning(
'killhandler for server won\'t work'
);
/*
\pcntl_signal(
SIGINT,
function ($signo) use (&$subject) {stop($subject);}
);
*/
}
/**
* @param struct_subject<type_input,type_output> $subject
* @author Christian Fraß <frass@greenscale.de>
*/
function stop/*<type_input,type_output>*/(
struct_subject/*<type_input,type_output>*/ $subject
) : void
{
($subject->on_stopping)();
if (! ($subject->socket === null)) {
\socket_shutdown($subject->socket);
}
$subject->run = false;
}
/**
* @param struct_subject<type_input, type_output> $subject
* @author Christian Fraß <frass@greenscale.de>
*/
function start/*<type_input, type_output>*/(
struct_subject/*<type_input, type_output>*/ $subject
) : void
{
$conf = [
'host' => 'localhost',
'max_connections' => 7,
'buffer_length' => 2048,
];
($subject->on_starting)();
if ($subject->install_killhandler) {
killhandler($subject);
}
$subject->run = true;
$socket = \socket_create(
AF_INET,
SOCK_STREAM,
SOL_TCP
);
// \socket_set_nonblock($socket);
\socket_bind(
$socket,
$conf['host'],
$subject->port
);
\socket_listen(
$socket,
$conf['max_connections']
);
($subject->on_started)();
while ($subject->run) {
$subject->socket = $request = \socket_accept(
$socket
);
/*if ($request === false) {
usleep(125000);
}
else {*/
$input = \socket_read(
$request,
$conf['buffer_length']
);
$output = ($subject->handler)($input);
$bytes = \socket_write(
$request,
$output,
strlen($output)
);
$successful = ($bytes !== false);
if (! $successful) {
\error_log('could not write to socket; shutting down server');
break;
}
/*}*/
}
\socket_close(
$request
);
\socket_close(
$socket
);
($subject->on_stopped)();
}
?>