aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorEvgeny Zinoviev <me@ch1p.io>2021-02-26 03:40:02 +0300
committerEvgeny Zinoviev <me@ch1p.io>2021-02-26 03:40:02 +0300
commitada8a13ce8a3410f4260601d213404bff5fa1cc7 (patch)
treea3ae5a714cf1fc5ebe5ce76175746b4b0c8a7abe /src
initial
Diffstat (limited to 'src')
-rw-r--r--src/Client.php153
-rw-r--r--src/Message.php36
-rw-r--r--src/RequestMessage.php40
-rw-r--r--src/ResponseMessage.php43
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