Actually, even if this works, scrap it… it will be very inefficient, and pose a relatively heavy burden on the router and server alike.
Here’s an alternative that makes a separate request for each interface using the monitor-traffic command:
<?php
namespace PEAR2\Net\RouterOS;
require_once 'PEAR2_Net_RouterOS-1.0.0b3.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->setCharset($config['db']['charset']);
$client = new 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']
);
//The interval is part of the command.
//Make sure the socket timeout is larger than the value of "interval".
$monitorRequest = new 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(Response::TYPE_DATA)) {
$insertStatement[1]['tiimestamp'] = date('Y-m-d H:i:s');
$insertStatement[1]['user'] = substr(strstr($response->getArgument('name'), '>', true), 7/*strlen('<pppoe-')*/);
$insertStatement[1]['rx'] = $response->getArgument('rx-bits-per-second');
$insertStatement[1]['tx'] = $response->getArgument('tx-bits-per-second');
$insertStatement[0]->execute();
}
};
//If new users appear since we've started gathering stats, start collecting stats on them as well.
$client->sendAsync(
new Request('/interface/listen .proplist=.dead,type,name', null, 'listener'),
function ($response, $client) {
if ($response->getType(Response::TYPE_DATA)
&& 'yes' !== $response->getArgument('.dead')
&& 'pppoe-in' === $response->getArgument('type')
) {
$client->sendAsync(
$monitorRequest->setArgument(
'interface', $name = $response->getArgument('name')
)->setTag($name),
$monitorListener
);
}
}
);
//Start collecting stats on existing interfaces.
foreach (
$client->sendSync(
new Request(
'/interface/print .proplist=name',
Query::where('type', 'pppoe-in')
)
)->getAllOfType(Response::TYPE_DATA) as $response
) {
$client->sendAsync(
$monitorRequest->setArgument(
'interface', $name = $response->getArgument('name')
)->setTag($name),
$monitorListener
);
}
//Activate event loop, i.e. the actual monitoring.
$client->loop();
} catch (\Exception $e) {
die($e);
}
Using this approach, the server only ever performs requests at the start, and if new users appear, whereas previously, it did a request every 3 seconds. Not only does doing that make the server faster (because it doesn’t spend time sending requests), it also lightens the router load a little (since it doesn’t need to reinitialize the stats gathering process; it reuses the already initialized monitor).
And another thing… what’s with the users database and table? I’ve altered the script from the previous one, where now it takes all pppoe-in interfaces, and monitors them. After all, they’re the only ones that can actually be monitored for sure.