API PHP class

Take it, edit it and use it as you need.
No support.

CLASS

<?php

//
// RouterOS API class
// Author: Denis Basta
//

class routeros_api {

	var $debug = false;			// Show debug information
	var $error_no;				// Variable for storing connection error number, if any
	var $error_str;				// Variable for storing connection error text, if any

	var $attempts = 5;			// Connection attempt count
	var $connected = false;		// Connection state
	var $delay = 3;				// Delay between connection attempts in seconds
	var $port = 8728;			// Port to connect to
	var $timeout = 3;			// Connection attempt timeout and data read timeout

	var $socket;				// Variable for storing socket resource

	/**************************************************
	 *
	 *************************************************/

	function debug($text) {

		if ($this->debug)
			echo $text . "\n";

	}

	/**************************************************
	 *
	 *************************************************/

	function encode_length($length) {

		if ($length < 0x80) {

			$length = chr($length);

		}
		else
		if ($length < 0x4000) {

			$length |= 0x8000;

			$length = chr( ($length >> 8) & 0xFF) . chr($length & 0xFF);

		}
		else
		if ($length < 0x200000) {

			$length |= 0xC00000;

			$length = chr( ($length >> 8) & 0xFF) . chr( ($length >> 8) & 0xFF) . chr($length & 0xFF);

		}
		else
		if ($length < 0x10000000) {

			$length |= 0xE0000000;

			$length = chr( ($length >> 8) & 0xFF) . chr( ($length >> 8) & 0xFF) . chr( ($length >> 8) & 0xFF) . chr($length & 0xFF);

		}
		else
		if ($length >= 0x10000000)
			$length = chr(0xF0) . chr( ($length >> 8) & 0xFF) . chr( ($length >> 8) & 0xFF) . chr( ($length >> 8) & 0xFF) . chr($length & 0xFF);

		return $length;

	}

	/**************************************************
	 *
	 *************************************************/

	function connect($ip, $login, $password) {

		for ($ATTEMPT = 1; $ATTEMPT <= $this->attempts; $ATTEMPT++) {

			$this->connected = false;

			$this->debug('Connection attempt #' . $ATTEMPT . ' to ' . $ip . ':' . $this->port . '...');

			if ($this->socket = @fsockopen($ip, $this->port, $this->error_no, $this->error_str, $this->timeout) ) {

				socket_set_timeout($this->socket, $this->timeout);

				$this->write('/login');

				$RESPONSE = $this->read(false);

				if ($RESPONSE[0] == '!done') {

					if (preg_match_all('/[^=]+/i', $RESPONSE[1], $MATCHES) ) {

						if ($MATCHES[0][0] == 'ret' && strlen($MATCHES[0][1]) == 32) {

							$this->write('/login', false);
							$this->write('=name=' . $login, false);
							$this->write('=response=00' . md5(chr(0) . $password . pack('H*', $MATCHES[0][1]) ) );

							$RESPONSE = $this->read(false);

							if ($RESPONSE[0] == '!done') {

								$this->connected = true;

								break;

							}

						}

					}

				}

				fclose($this->socket);

			}

			sleep($this->delay);

		}

		if ($this->connected)
			$this->debug('Connected...');
		else
			$this->debug('Error...');

		return $this->connected;

	}

	/**************************************************
	 *
	 *************************************************/

	function disconnect() {

		fclose($this->socket);

		$this->connected = false;

		$this->debug('Disconnected...');

	}

	/**************************************************
	 *
	 *************************************************/

	function parse_response($response) {

		if (is_array($response) ) {

			$PARSED = array();
			$CURRENT = null;

			for ($i = 0, $imax = count($response); $i < $imax; $i++) {

				if (in_array($response[$i], array('!fatal', '!re', '!trap') ) ) {

					if ($response[$i] == '!re')
						$CURRENT = &$PARSED[];
					else
						$CURRENT = &$PARSED[$response[$i]][];

				}
				else
				if ($response[$i] != '!done') {

					if (preg_match_all('/[^=]+/i', $response[$i], $MATCHES) )
						$CURRENT[$MATCHES[0][0]] = (isset($MATCHES[0][1]) ? $MATCHES[0][1] : '');

				}

			}

			return $PARSED;

		}
		else
			return array();

	}

	/**************************************************
	 *
	 *************************************************/

	function read($parse = true) {

		$RESPONSE = array();

		while (true) {

			$LENGTH = ord(fread($this->socket, 1) );

			if ($LENGTH > 0) {

				$_ = fread($this->socket, $LENGTH);

				$RESPONSE[] = $_;

			}

			$STATUS = socket_get_status($this->socket);

			if ($LENGTH > 0)
				$this->debug('>>> [' . $LENGTH . ', ' . $STATUS['unread_bytes'] . '] ' . $_);

			if ( (!$this->connected && !$STATUS['unread_bytes']) || ($this->connected && $_ == '!done' && !$STATUS['unread_bytes']) )
				break;

		}

		if ($parse)
			$RESPONSE = $this->parse_response($RESPONSE);

		return $RESPONSE;

	}

	/**************************************************
	 *
	 *************************************************/

	function write($command, $param2 = true) {

		if ($command) {

			fwrite($this->socket, $this->encode_length(strlen($command) ) . $command);

			$this->debug('<<< [' . strlen($command) . '] ' . $command);

			if (gettype($param2) == 'integer') {

				fwrite($this->socket, $this->encode_length(strlen('.tag=' . $param2) ) . '.tag=' . $param2 . chr(0) );

				$this->debug('<<< [' . strlen('.tag=' . $param2) . '] .tag=' . $param2);

			}
			else
			if (gettype($param2) == 'boolean')
				fwrite($this->socket, ($param2 ? chr(0) : '') );

			return true;

		}
		else
			return false;

	}

}

?>

EXAMPLE

<?php

require('routeros_api.class.php');

$API = new routeros_api();

$API->debug = true;

if ($API->connect('111.111.111.111', 'LOGIN', 'PASSWORD')) {

   $API->write('/interface/getall');

   $READ = $API->read();
   $ARRAY = $API->parse_response($READ);

   print_r($ARRAY);

   $API->disconnect();

}

?>

OUTPUT

Array
(
	[0] => Array
		(
			[.id] => *1
			[name] => ether1
			[mtu] => 1500
			[type] => ether
			[running] => yes
			[dynamic] => no
			[slave] => no
			[comment] => 
			[disabled] => no
		)

	[1] => Array
		(
			[.id] => *2
			[name] => ether2
			[mtu] => 1500
			[type] => ether
			[running] => yes
			[dynamic] => no
			[slave] => no
			[comment] => 
			[disabled] => no
		)

	[2] => Array
		(
			[.id] => *3
			[name] => ether3
			[mtu] => 1500
			[type] => ether
			[running] => yes
			[dynamic] => no
			[slave] => no
			[comment] => ether3
			[disabled] => no
		)
)

routeros_api.class.zip (1.83 KB)

cool! post it in the wiki and you will get a license :slight_smile:

I’ve never done this before…
Could you please help me with it?
Where and how to make a post?
Link to a help also would be great…

P.S. Already logged in wiki…

just think of an article name, and add it to the link in your address bar, it will make a new article, for example:

http://wiki.mikrotik.com/wiki/API_PHP_class

then hit the EDIT button and paste your text. to make text appear as CODE enclose it in text

more help:

http://en.wikipedia.org/wiki/Wikipedia:Cheatsheet

Thanks!

Done!

http://wiki.mikrotik.com/wiki/API_PHP_class

what are these things?
Picture 1.png

Well, can’t say.

I’ve copy/pasted everything from the forum…

While posting article in WIKI, there is a problem, if several “(” or “)” symbols is used one just right after another…

I think I fixed it, I used to stop interpreting anything in your code. Just add the same tag to other sections too and check if all is correct

normis, You did mistake by pasting, for ex. line if (preg_match_all(‘/[^=]+/i’, $RESPONSE[1], $MATCHES� {�� - there is something like new line on the end, but wrong interpretated under firefox@windows.

Expensive Friends of the FORUM,
Already I tried you vary mareiras more I am not obtaining to execute the commands for alterations of the functions of the MIKROTIK type.

system identity set name=test - in the terminal or ssh

in api
/system/identity/set =name=teste, does not function,

if I place

/system/identity/get functions there results giving it of the name.

As I will be able to decide this problem.

$API->write(‘/system/identity/set’,false);
$API->write(‘=name=teste’);

I dont get anything from print_r($ARRAY); Its only showing the login process and the interface data when I run the example.. then I get several of the following error :

Warning: preg_match_all() expects parameter 2 to be string, array given in routeros_api.class.php on line 177

the line in the code is the following :

if (preg_match_all('/[^=]+/i', $response[$i], $MATCHES) )
						$CURRENT[$MATCHES[0][0]] = (isset($MATCHES[0][1]) ? $MATCHES[0][1] : '');

When I echoed the $response[$i] on screen I get ‘Array’ as output when it fails with error message.

if (gettype($response[$i])!='array')

made it run without errors, but the print_r($ARRAY) still doesnt print out anything on screen.. Anyone have an idea where the real bug is ?

Post your full code…

I just copied and pasted your 1st post here into 2 different php files..

Hello All,

Fist of all I want to thank all of the users of this board for all of the information that has gotten me to this point in my project. I am designing a web interface for a client and have ran into this particular problem. Just so that everyone is on the same page I am using the PHP API class found above. When I send the following command to the Mikrotik via SSH or terminal it works without any problems.

  /ip hotspot user set mv99 password=newpassword

However when I submit this command via the API class the router replies with a !done command but does not change the password.

* Change user password via web interface */
   
   $API->write('/ip/hotspot/user/set', false);
   $API->write('=name=mv99',false);
   $API->write('=password=phptest');

Here is a an output/debug log for the code listed above showing the full transaction.

Connection attempt #1 to xxx.xxx.xxx.xxx:8728...

Router <<< Client: [6] /login
    Router >>> Client: [5, 39] !done
    Router >>> Client: [37, 1] =ret=00a61be5f4c5f0bdb48e3d710f2ce732
Router <<< Client: [6] /login
Router <<< Client: [14] =name=mvoffice
Router <<< Client: [44] =response=00aa6135b90a91bf4166629a3195a858b2
    Router >>> Client: [5, 1] !done
    
Connected...
Router <<< Client: [20] /ip/hotspot/user/set
Router <<< Client: [10] =name=mv99
Router <<< Client: [17] =password=phptest
    Router >>> Client: [5, 1] !done
Disconnected...

As best that I can tell my syntax is correct or mostly correct. If anyone out there has any suggestions as to what I may be doing wrong or some pointers feel free to let me know. I have used this API implementation to change things like the system identity, get interface lists, and to print the firewall entries. To date this is the only part of the API that I have not been able to get the desired data from.

Thank you in advance,
John Annis
The Wireless Web

in terminal you dont use the **name=**mv99 value on username, Have you tried it without that =name= in your code ?

fosben:

Thank you for the reply, I am aware that using the terminal you use the following syntax.

/ip hotspot user set mv99 password=newpassword

According to the Mikrotik API command sentences are to be formatted in the following format. I admit I most certainly could be using the wrong command arguments the API documentation lacks quite badly in this department. When looking at the available choices in the console for actions that can be preformed on hotspot users =name=mv99 is the closest option that makes sense to me. Also I have tried submitting the just the user name mv99 without the =name=, the code executed without any errors and completed with !done but it also did not change the password.

Command argument should begin with ‘=’ followed by name of argument, followed by another ‘=’, followed by value of argument.
Command Argument Examples
=address=10.0.0.1
=name=iu=c3Eeg
=disable-running-check=yes

Try this one:

  1. Receive list of all users:
$API->write(/ip/hotspot/user/getall');
  1. In PHP iterate through resulted array comparing user name, whose password should be changed, with current iteration’s [name] property.
  2. In the resulting array get [.id] property.
$API->write('/ip/hotspot/user/set', false);
$API->write('=password=phptest',false);
$API->write('=.id=' . [.id]);

This might work.

With your Excellent class and a bit of ajax, we can have a “webWinbox” or phpbox or so. :smiley:

Thanks!

Hi
I have a problem with this API class, after checking I know it is in read function.
There is problem when I send command throught API which is correctly done by mikrotik, webbrowser get halted because of cycle.
this problem I solved by adding this line of code into the read function

if (!$LENGTH) break;

BUT when this line is added I got just first part of the outputs, especialy from command getall.
So I added second variable to function read

   function read($parse = true, $zerolength = false)

and by this second variable I chose to add that line or not

$API->read(true, true)

BUT this is just workaround and I got in situation when is very difficult to prepare all possible situations and decide if there would be that line or not.

Rewriting function read is far beyond my abilities so I would very appreciate if someone have allready solved this problem, to share the solution.

Thanks Norg