diff options
author | Evgeny Zinoviev <me@ch1p.io> | 2021-02-26 03:40:02 +0300 |
---|---|---|
committer | Evgeny Zinoviev <me@ch1p.io> | 2021-02-26 03:40:02 +0300 |
commit | ada8a13ce8a3410f4260601d213404bff5fa1cc7 (patch) | |
tree | a3ae5a714cf1fc5ebe5ce76175746b4b0c8a7abe /src |
initial
Diffstat (limited to 'src')
-rw-r--r-- | src/Client.php | 153 | ||||
-rw-r--r-- | src/Message.php | 36 | ||||
-rw-r--r-- | src/RequestMessage.php | 40 | ||||
-rw-r--r-- | src/ResponseMessage.php | 43 |
4 files changed, 272 insertions, 0 deletions
diff --git a/src/Client.php b/src/Client.php new file mode 100644 index 0000000..54427f4 --- /dev/null +++ b/src/Client.php @@ -0,0 +1,153 @@ +<?php + +namespace jobd; + + +class Client { + + const WORKER_PORT = 13596; + const MASTER_PORT = 13597; + + const EOT = "\4"; + + protected $host; + protected $port; + protected $password; + protected $sock; + + /** + * JobdClient constructor. + * @param int $port + * @param string $host + * @param string $password + */ + public function __construct(int $port, string $host = '127.0.0.1', string $password = '') { + $this->port = $port; + $this->host = $host; + $this->password = $password; + + $this->sock = fsockopen($this->host, $this->port); + if (!$this->sock) + throw new \Exception("Failed to connect to {$this->host}:{$this->port}"); + } + + /** + * @return mixed + */ + public function ping() { + $this->send(new RequestMessage('ping')); + return $this->recv(); + } + + /** + * @param array $targets + * @return mixed + */ + public function poke(array $targets) { + $this->send(new RequestMessage('poke', ['targets' => $targets])); + return $this->recv(); + } + + /** + * @return mixed + */ + public function status() { + $this->send(new RequestMessage('status')); + return $this->recv(); + } + + public function poll(array $targets) { + $this->send(new RequestMessage('poll', ['targets' => $targets])); + return $this->recv(); + } + + /** + * @param int $id + * @return mixed + */ + public function runManual(int $id) { + $this->send(new RequestMessage('run-manual', ['id' => $id])); + return $this->recv(); + } + + /** + * @param RequestMessage $request + */ + public function send(RequestMessage $request) { + if ($this->password) + $request->setPassword($this->password); + + $serialized = $request->serialize(); + + fwrite($this->sock, $serialized . self::EOT); + } + + /** + * @return mixed + */ + public function recv() { + $messages = []; + $buf = ''; + while (!feof($this->sock)) { + $buf .= fread($this->sock, 1024); + $buflen = strlen($buf); + if ($buflen > 0 && $buf[$buflen-1] == self::EOT) + break; + } + + $offset = 0; + $eot_pos = 0; + do { + $eot_pos = strpos($buf, self::EOT, $offset); + if ($eot_pos !== false) { + $message = substr($buf, $offset, $eot_pos); + $messages[] = $message; + + $offset = $eot_pos + 1; + } + } while ($eot_pos !== false && $offset < $buflen-1); + + if (empty($message)) + throw new \Exception("Malformed response: no messages found. Response: {$buf}"); + + if (count($messages) > 1) + trigger_error(__METHOD__.": received more than one message"); + + return self::parseMessage($messages[0]); + } + + protected static function parseMessage(string $raw_string) { + $raw = json_decode($raw_string, true); + if (!is_array($raw) || count($raw) != 2) + throw new \Exception("Malformed response: {$raw_string}"); + + list($type, $data) = $raw; + + switch ($type) { + case Message::REQUEST: + if (!$data || !is_array($data) || !isset($data['type']) || !is_string($data['type'])) + throw new \Exception('Malformed REQUEST message'); + + $message = new RequestMessage($data['type'], $data['data'] ?? null); + if (isset($data['password'])) + $message->setPassword($data['password']); + + return $message; + + case Message::RESPONSE: + if (!is_array($data) || count($data) < 2) + throw new \Exception('Malformed RESPONSE message'); + + $message = new ResponseMessage(...$data); + return $message; + } + } + + /** + * @return bool + */ + public function close() { + return fclose($this->sock); + } + +}
\ No newline at end of file diff --git a/src/Message.php b/src/Message.php new file mode 100644 index 0000000..647afad --- /dev/null +++ b/src/Message.php @@ -0,0 +1,36 @@ +<?php + +namespace jobd; + + +abstract class Message { + + const REQUEST = 0; + const RESPONSE = 1; + + protected $type; + + /** + * Message constructor. + * @param int $type + */ + public function __construct(int $type) { + $this->type = $type; + } + + /** + * @return array + */ + abstract protected function getContent(): array; + + /** + * @return string + */ + public function serialize(): string { + return json_encode([ + $this->type, + $this->getContent() + ]); + } + +}
\ No newline at end of file diff --git a/src/RequestMessage.php b/src/RequestMessage.php new file mode 100644 index 0000000..46dfba3 --- /dev/null +++ b/src/RequestMessage.php @@ -0,0 +1,40 @@ +<?php + +namespace jobd; + +class RequestMessage extends Message { + + protected $requestType; + protected $requestData; + protected $password; + + /** + * Request constructor. + * @param string $request_type + * @param null $request_data + */ + public function __construct(string $request_type, $request_data = null) { + parent::__construct(Message::REQUEST); + + $this->requestData = $request_data; + $this->requestType = $request_type; + } + + /** + * @param string $password + */ + public function setPassword(string $password) { + $this->password = $password; + } + + /** + * @return string[] + */ + public function getContent(): array { + $request = ['type' => $this->requestType]; + if (!is_null($this->requestData)) + $request['data'] = $this->requestData; + return $request; + } + +}
\ No newline at end of file diff --git a/src/ResponseMessage.php b/src/ResponseMessage.php new file mode 100644 index 0000000..fa54c29 --- /dev/null +++ b/src/ResponseMessage.php @@ -0,0 +1,43 @@ +<?php + +namespace jobd; + +class ResponseMessage extends Message { + + protected $error; + protected $data; + + /** + * Response constructor. + * @param null $error + * @param null $data + */ + public function __construct($error = null, $data = null) { + parent::__construct(Message::RESPONSE); + + $this->error = $error; + $this->data = $data; + } + + /** + * @return array + */ + public function getContent(): array { + return [$this->error, $this->data]; + } + + /** + * @return mixed + */ + public function getError() { + return $this->error; + } + + /** + * @return mixed + */ + public function getData() { + return $this->data; + } + +}
\ No newline at end of file |