From d89dcb69abc06766393759234aeb647bd5d8b3e5 Mon Sep 17 00:00:00 2001 From: pasha Date: Sun, 19 Aug 2018 23:37:06 +0300 Subject: [PATCH] small fixes, readme update --- README.md | 60 +++++++++++++++++++++++++++++++ src/Client.php | 73 ++++++++++++++++++-------------------- src/Interfaces/ClientInterface.php | 59 +++++++++++++++++++++++++++++- 3 files changed, 153 insertions(+), 39 deletions(-) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..9ff35fe --- /dev/null +++ b/README.md @@ -0,0 +1,60 @@ +# RouterOS PHP7 API Client + + composer require evilfreelancer/routeros-api-php + +## Small example + +Get all IP addresses, analog via command line is `/ip address print` + +```php +host = '192.168.1.104'; +$config->user = 'admin'; +$config->pass = 'admin'; + +/** + * Initiate client with parameters + */ +$client = new Client($config); + +/** + * Build query + */ +$query = new Query('/ip/address/print'); + +/** + * Send query to socket server + */ +$request = $client->write($query); +var_dump($request); + +/** + * Read answer from server + */ +$response = $client->read(); +var_dump($response); +``` + +You can simplify your code and write then read from socket in one line: + +```php +$response = $client->write($query)->read(); +var_dump($response); +``` + +## Links + +* [Cloud Hosted Router](https://mikrotik.com/download#chr) - Virtual images of RouterOS for your hypervisor +* [RouterOS Manual:API](https://wiki.mikrotik.com/wiki/Manual:API) - In case if you are wondering what is insane diff --git a/src/Client.php b/src/Client.php index 46d0f1b..5782abf 100644 --- a/src/Client.php +++ b/src/Client.php @@ -3,6 +3,7 @@ namespace RouterOS; use RouterOS\Exceptions\Exception; +use RouterOS\Interfaces\ClientInterface; class Client implements Interfaces\ClientInterface { @@ -10,7 +11,7 @@ class Client implements Interfaces\ClientInterface * Socket resource * @var resource|null */ - private static $_socket; + private $_socket; /** * Code of error @@ -46,7 +47,7 @@ class Client implements Interfaces\ClientInterface * @param string $string * @return string */ - public function encodeLength(string $string): string + private function encodeLength(string $string): string { // Yeah, that's insane, but was more ugly, so you need read this post if you interesting a details: // https://wiki.mikrotik.com/wiki/Manual:API#API_words @@ -89,25 +90,23 @@ class Client implements Interfaces\ClientInterface * * @param Query $query * @param string|null $tag - * @return $this + * @return ClientInterface */ - public function write(Query $query, string $tag = null): self + public function write(Query $query, string $tag = null): ClientInterface { - print_r($query); - // Send commands via loop to router foreach ($query->getQuery() as $command) { $command = trim($command); - fwrite(self::$_socket, $this->encodeLength(\strlen($command)) . $command); + fwrite($this->_socket, $this->encodeLength(\strlen($command)) . $command); } // If tag is not empty, send to socket if (null !== $tag) { - fwrite(self::$_socket, $this->encodeLength(\strlen('.tag=' . $tag)) . '.tag=' . $tag); + fwrite($this->_socket, $this->encodeLength(\strlen('.tag=' . $tag)) . '.tag=' . $tag); } // Write zero-terminator - fwrite(self::$_socket, \chr(0)); + fwrite($this->_socket, \chr(0)); return $this; } @@ -116,9 +115,9 @@ class Client implements Interfaces\ClientInterface * Read answer from server after query was executed * * @param bool $parse - * @return array|string + * @return array */ - public function read($parse = true) + public function read(bool $parse = true): array { // By default response is empty $response = []; @@ -130,7 +129,7 @@ class Client implements Interfaces\ClientInterface while (true) { // Read the first byte of input which gives us some or all of the length // of the remaining reply. - $byte = \ord(fread(self::$_socket, 1)); + $byte = \ord(fread($this->_socket, 1)); // If the first bit is set then we need to remove the first four bits, shift left 8 // and then read another byte in. @@ -139,21 +138,21 @@ class Client implements Interfaces\ClientInterface // and then read in yet another byte. if ($byte & 128) { if (($byte & 192) === 128) { - $length = (($byte & 63) << 8) + \ord(fread(self::$_socket, 1)); + $length = (($byte & 63) << 8) + \ord(fread($this->_socket, 1)); } else { if (($byte & 224) === 192) { - $length = (($byte & 31) << 8) + \ord(fread(self::$_socket, 1)); - $length = ($length << 8) + \ord(fread(self::$_socket, 1)); + $length = (($byte & 31) << 8) + \ord(fread($this->_socket, 1)); + $length = ($length << 8) + \ord(fread($this->_socket, 1)); } else { if (($byte & 240) === 224) { - $length = (($byte & 15) << 8) + \ord(fread(self::$_socket, 1)); - $length = ($length << 8) + \ord(fread(self::$_socket, 1)); - $length = ($length << 8) + \ord(fread(self::$_socket, 1)); + $length = (($byte & 15) << 8) + \ord(fread($this->_socket, 1)); + $length = ($length << 8) + \ord(fread($this->_socket, 1)); + $length = ($length << 8) + \ord(fread($this->_socket, 1)); } else { - $length = \ord(fread(self::$_socket, 1)); - $length = ($length << 8) + \ord(fread(self::$_socket, 1)); - $length = ($length << 8) + \ord(fread(self::$_socket, 1)); - $length = ($length << 8) + \ord(fread(self::$_socket, 1)); + $length = \ord(fread($this->_socket, 1)); + $length = ($length << 8) + \ord(fread($this->_socket, 1)) * 3; + $length = ($length << 8) + \ord(fread($this->_socket, 1)); + $length = ($length << 8) + \ord(fread($this->_socket, 1)); } } } @@ -169,7 +168,7 @@ class Client implements Interfaces\ClientInterface $retlen = 0; while ($retlen < $length) { $toread = $length - $retlen; - $_ .= fread(self::$_socket, $toread); + $_ .= fread($this->_socket, $toread); $retlen = \strlen($_); } $response[] = $_; @@ -181,7 +180,7 @@ class Client implements Interfaces\ClientInterface } // Get status about latest operation - $status = stream_get_meta_data(self::$_socket); + $status = stream_get_meta_data($this->_socket); // If we do not have unread bytes from socket or <-same and is done, then exit from loop if ((!$status['unread_bytes']) || (!$status['unread_bytes'] && $done)) { @@ -199,13 +198,13 @@ class Client implements Interfaces\ClientInterface * @param array $response Response data * @return array Array with parsed data */ - public function parseResponse(array $response): array + private function parseResponse(array $response): array { $parsed = []; $current = null; $single = null; foreach ($response as $x) { - if (\in_array($x, array('!fatal', '!re', '!trap'))) { + if (\in_array($x, ['!fatal', '!re', '!trap'])) { if ($x === '!re') { $current =& $parsed[]; } else { @@ -234,16 +233,16 @@ class Client implements Interfaces\ClientInterface * * @return bool */ - public function login(): bool + private function login(): bool { // For the first we need get hash with salt $query = new Query('/login'); $response = $this->write($query)->read(); // Now need use this hash for authorization - $query = new Query('/login'); - $query->add('=name=' . $this->_config->user); - $query->add('=response=00' . md5(\chr(0) . $this->_config->pass . pack('H*', $response[0]))); + $query = (new Query('/login')) + ->add('=name=' . $this->_config->user) + ->add('=response=00' . md5(\chr(0) . $this->_config->pass . pack('H*', $response[0]))); // Execute query and get response $response = $this->write($query)->read(false); @@ -269,10 +268,8 @@ class Client implements Interfaces\ClientInterface // If socket is active if ($this->getSocket()) { - echo 'z'; - // If we logged in then exit from loop - if ($this->login()) { + if (true === $this->login()) { break; } @@ -297,7 +294,7 @@ class Client implements Interfaces\ClientInterface private function setSocket($socket): bool { if (\is_resource($socket)) { - self::$_socket = $socket; + $this->_socket = $socket; return true; } return false; @@ -310,8 +307,8 @@ class Client implements Interfaces\ClientInterface */ public function getSocket() { - return \is_resource(self::$_socket) - ? self::$_socket + return \is_resource($this->_socket) + ? $this->_socket : false; } @@ -359,9 +356,9 @@ class Client implements Interfaces\ClientInterface * * @return bool */ - public function closeSocket(): bool + private function closeSocket(): bool { - fclose(self::$_socket); + fclose($this->_socket); return true; } diff --git a/src/Interfaces/ClientInterface.php b/src/Interfaces/ClientInterface.php index c2edb3c..d118336 100644 --- a/src/Interfaces/ClientInterface.php +++ b/src/Interfaces/ClientInterface.php @@ -2,12 +2,69 @@ namespace RouterOS\Interfaces; +use RouterOS\Query; + interface ClientInterface { + /** + * Default port number + */ const PORT = 8728; + + /** + * Default ssl port number + */ const PORT_SSL = 8729; + + /** + * Do not use SSL by default + */ const SSL = false; - const TIMEOUT = 1; + + /** + * Max timeout for answer from router + */ + const TIMEOUT = 10; + + /** + * Count of reconnect attempts + */ const ATTEMPTS = 10; + + /** + * Delay between attempts + */ const ATTEMPTS_DELAY = 1; + + /** + * Return socket resource if is exist + * + * @return bool|resource + */ + public function getSocket(); + + /** + * Connect to socket server + * + * @return bool + */ + public function connect(): bool; + + /** + * Read answer from server after query was executed + * + * @param bool $parse + * @return array + */ + public function read(bool $parse = true): array; + + /** + * Send write query to RouterOS (with or without tag) + * + * @param Query $query + * @param string|null $tag + * @return ClientInterface + */ + public function write(Query $query, string $tag = null): ClientInterface; + }