Browse Source

small fixes, readme update

tags/0.1
pasha 7 years ago
parent
commit
d89dcb69ab
  1. 60
      README.md
  2. 73
      src/Client.php
  3. 59
      src/Interfaces/ClientInterface.php

60
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
<?php
require_once __DIR__ . '/vendor/autoload.php';
error_reporting(E_ALL);
use \RouterOS\Config;
use \RouterOS\Client;
use \RouterOS\Query;
/**
* Set the params
*/
$config = new Config();
$config->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

73
src/Client.php

@ -3,6 +3,7 @@
namespace RouterOS; namespace RouterOS;
use RouterOS\Exceptions\Exception; use RouterOS\Exceptions\Exception;
use RouterOS\Interfaces\ClientInterface;
class Client implements Interfaces\ClientInterface class Client implements Interfaces\ClientInterface
{ {
@ -10,7 +11,7 @@ class Client implements Interfaces\ClientInterface
* Socket resource * Socket resource
* @var resource|null * @var resource|null
*/ */
private static $_socket;
private $_socket;
/** /**
* Code of error * Code of error
@ -46,7 +47,7 @@ class Client implements Interfaces\ClientInterface
* @param string $string * @param string $string
* @return 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: // 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 // https://wiki.mikrotik.com/wiki/Manual:API#API_words
@ -89,25 +90,23 @@ class Client implements Interfaces\ClientInterface
* *
* @param Query $query * @param Query $query
* @param string|null $tag * @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 // Send commands via loop to router
foreach ($query->getQuery() as $command) { foreach ($query->getQuery() as $command) {
$command = trim($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 tag is not empty, send to socket
if (null !== $tag) { 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 // Write zero-terminator
fwrite(self::$_socket, \chr(0));
fwrite($this->_socket, \chr(0));
return $this; return $this;
} }
@ -116,9 +115,9 @@ class Client implements Interfaces\ClientInterface
* Read answer from server after query was executed * Read answer from server after query was executed
* *
* @param bool $parse * @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 // By default response is empty
$response = []; $response = [];
@ -130,7 +129,7 @@ class Client implements Interfaces\ClientInterface
while (true) { while (true) {
// Read the first byte of input which gives us some or all of the length // Read the first byte of input which gives us some or all of the length
// of the remaining reply. // 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 // If the first bit is set then we need to remove the first four bits, shift left 8
// and then read another byte in. // and then read another byte in.
@ -139,21 +138,21 @@ class Client implements Interfaces\ClientInterface
// and then read in yet another byte. // and then read in yet another byte.
if ($byte & 128) { if ($byte & 128) {
if (($byte & 192) === 128) { if (($byte & 192) === 128) {
$length = (($byte & 63) << 8) + \ord(fread(self::$_socket, 1));
$length = (($byte & 63) << 8) + \ord(fread($this->_socket, 1));
} else { } else {
if (($byte & 224) === 192) { 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 { } else {
if (($byte & 240) === 224) { 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 { } 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; $retlen = 0;
while ($retlen < $length) { while ($retlen < $length) {
$toread = $length - $retlen; $toread = $length - $retlen;
$_ .= fread(self::$_socket, $toread);
$_ .= fread($this->_socket, $toread);
$retlen = \strlen($_); $retlen = \strlen($_);
} }
$response[] = $_; $response[] = $_;
@ -181,7 +180,7 @@ class Client implements Interfaces\ClientInterface
} }
// Get status about latest operation // 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 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)) { if ((!$status['unread_bytes']) || (!$status['unread_bytes'] && $done)) {
@ -199,13 +198,13 @@ class Client implements Interfaces\ClientInterface
* @param array $response Response data * @param array $response Response data
* @return array Array with parsed data * @return array Array with parsed data
*/ */
public function parseResponse(array $response): array
private function parseResponse(array $response): array
{ {
$parsed = []; $parsed = [];
$current = null; $current = null;
$single = null; $single = null;
foreach ($response as $x) { foreach ($response as $x) {
if (\in_array($x, array('!fatal', '!re', '!trap'))) {
if (\in_array($x, ['!fatal', '!re', '!trap'])) {
if ($x === '!re') { if ($x === '!re') {
$current =& $parsed[]; $current =& $parsed[];
} else { } else {
@ -234,16 +233,16 @@ class Client implements Interfaces\ClientInterface
* *
* @return bool * @return bool
*/ */
public function login(): bool
private function login(): bool
{ {
// For the first we need get hash with salt // For the first we need get hash with salt
$query = new Query('/login'); $query = new Query('/login');
$response = $this->write($query)->read(); $response = $this->write($query)->read();
// Now need use this hash for authorization // 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 // Execute query and get response
$response = $this->write($query)->read(false); $response = $this->write($query)->read(false);
@ -269,10 +268,8 @@ class Client implements Interfaces\ClientInterface
// If socket is active // If socket is active
if ($this->getSocket()) { if ($this->getSocket()) {
echo 'z';
// If we logged in then exit from loop // If we logged in then exit from loop
if ($this->login()) {
if (true === $this->login()) {
break; break;
} }
@ -297,7 +294,7 @@ class Client implements Interfaces\ClientInterface
private function setSocket($socket): bool private function setSocket($socket): bool
{ {
if (\is_resource($socket)) { if (\is_resource($socket)) {
self::$_socket = $socket;
$this->_socket = $socket;
return true; return true;
} }
return false; return false;
@ -310,8 +307,8 @@ class Client implements Interfaces\ClientInterface
*/ */
public function getSocket() public function getSocket()
{ {
return \is_resource(self::$_socket)
? self::$_socket
return \is_resource($this->_socket)
? $this->_socket
: false; : false;
} }
@ -359,9 +356,9 @@ class Client implements Interfaces\ClientInterface
* *
* @return bool * @return bool
*/ */
public function closeSocket(): bool
private function closeSocket(): bool
{ {
fclose(self::$_socket);
fclose($this->_socket);
return true; return true;
} }

59
src/Interfaces/ClientInterface.php

@ -2,12 +2,69 @@
namespace RouterOS\Interfaces; namespace RouterOS\Interfaces;
use RouterOS\Query;
interface ClientInterface interface ClientInterface
{ {
/**
* Default port number
*/
const PORT = 8728; const PORT = 8728;
/**
* Default ssl port number
*/
const PORT_SSL = 8729; const PORT_SSL = 8729;
/**
* Do not use SSL by default
*/
const SSL = false; const SSL = false;
const TIMEOUT = 1;
/**
* Max timeout for answer from router
*/
const TIMEOUT = 10;
/**
* Count of reconnect attempts
*/
const ATTEMPTS = 10; const ATTEMPTS = 10;
/**
* Delay between attempts
*/
const ATTEMPTS_DELAY = 1; 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;
} }
Loading…
Cancel
Save