Damn, I really went overboard with that one, didn’t I
.
I wrote that before realizing why using “namespace” in user code is a bad idea (and should only be done in library code instead). Also, turns out that “print” with “follow” argument is a perfectly valid command, pretty much equivalent to “listen”, except it can take queries, and includes the current ones too.
Here:
<?php
use PEAR2\Net\RouterOS;
require_once 'PEAR2_Net_RouterOS-1.0.0b5.phar';
$config = array(
'db' => array(
'hostname' => '127.0.0.1',
'username' => 'root',
'password' => '',
'charset' => 'latin2',
'dst-db' => 'statsDb',
'src-db' => 'usersDb',
'dst-table' => 'users',
'src-table' => 'stats'
),
'router' => array(
'hostname' => '192.168.0.1',
'username' => 'admin',
'password' => ''
),
'misc' => array(
'interval' => 3
)
);
try {
//Estabilishing connections. This is where stuff is most likely to fail.
$mysqli = new mysqli($config['db']['hostname'], $config['db']['username'], $config['db']['password'], $config['db']['dst-db']);
$mysqli->set_charset($config['db']['charset']);
$client = new RouterOS\Client($config['router']['hostname'], $config['router']['username'], $config['router']['password']);
//Because we'll be doing a lot of queries, we'll use a prepared statement.
$insertStatement = array(
$mysqli->prepare(
"INSERET INTO {$config['db']['dst-table']}
(username, timestamp, rxrate, txrate, nasshortname)
VALUES (?, ?, ?, ?, '{$config['router']['hostname']}')"
),
array(
'user' => null,
'timestamp' => null,
'rx' => null,
'tx' => null
)
);
$insertStatement[0]->bind_param(
'ssss',
$insertStatement[1]['user'],
$insertStatement[1]['timestamp'],
$insertStatement[1]['rx'],
$insertStatement[1]['tx']
);
$monitorRequest = new RouterOS\Request(
'/interface/monitor-traffic .proplist=rx-bits-per-second,tx-bits-per-second'
);
$monitorRequest->setArgument('interval', $config['misc']['interval']);
//This is the fun part. Upon receiving a response from the monitor,
//the insert statement will be executed about it.
$monitorListener = function ($response) use (&$insertStatement) {
if ($response->getType(RouterOS\Response::TYPE_DATA)) {
$insertStatement[1]['tiimestamp'] = date('Y-m-d H:i:s');
$insertStatement[1]['user'] = substr(strstr($response->getTag(), '>', true), 7/*strlen('<pppoe-')*/);
$insertStatement[1]['rx'] = $response('rx-bits-per-second');
$insertStatement[1]['tx'] = $response('tx-bits-per-second');
$insertStatement[0]->execute();
}
};
//Start collecting stats on existing interfaces.
//If new users appear since we've started gathering stats, start collecting stats on them as well.
$client->sendAsync(
new RouterOS\Request(
'/interface/print .proplist=.dead,name follow',
RouterOS\Query::where('type', 'pppoe-in'),
'listener'
),
function ($response, $client) use ($monitorRequest, $monitorListener) {
if ($response->getType(RouterOS\Response::TYPE_DATA)
&& 'yes' !== $response('.dead')
) {
$client->sendAsync(
$monitorRequest
->setArgument('interface', $name = $response('name'))
->setTag($name),
$monitorListener
);
}
}
);
//Activate event loop, i.e. the actual monitoring.
$client->loop();
} catch (Exception $e) {
die($e);
}
And if you want the version that attempts to reconnect, wrap the above in
<?php
use PEAR2\Net\RouterOS;
use PEAR2\Net\Transmitter;
require_once 'PEAR2_Net_RouterOS-1.0.0b5.phar';
$config = array(...);
while (true) {
try {
...
} catch (RouterOS\Exception $er) {
sleep(10);
} catch (Transmitter\Exception $et) {
sleep(10);
} catch (Exception $e) {
die($e);
}
}
(BTW, the note abut “interval” having to be smaller than default_socket_timeout no longer applies; It may be any value now)