ashikkaiser/microtikapi

适用于您的应用程序的现代 Mikrotik RouterOS API PHP 客户端(支持 Laravel)

dev-master 2021-07-02 19:24 UTC

This package is auto-updated.

Last update: 2024-09-29 05:56:42 UTC


README

Latest Stable Version Build Status Total Downloads License Code Climate Code Coverage Scrutinizer CQ

RouterOS API 客户端

composer require ashikkaiser/microtikapi

这个库部分基于这个旧项目,但它有很多创新来简化开发。此外,该项目设计为符合 PSR 标准与 PHP7 一起工作。

您可以使用此库与 6.43 版本之前的 RouterOS 固件以及 6.43 版本之后的版本一起使用,它将在连接阶段自动检测。

最低要求

  • php >= 7.2
  • ext-sockets

Laravel 框架支持

RouterOS API 客户端针对作为正常 Laravel 包的使用进行了优化,所有功能都可通过 \RouterOS 门面访问,要访问客户端对象,您需要使用

$config = new \RouterOS\Config([
    'host' => '192.168.1.3',
    'user' => 'admin',
    'pass' => 'admin',
    'port' => 8728,
]);
$client = new \RouterOS\Client($config);

调用门面并将参数数组传递给 client 方法

$client = \RouterOS::client([
    'host' => '192.168.1.3',
    'user' => 'admin',
    'pass' => 'admin',
    'port' => 8728,
]);

您还可以获取从 routeros-api.php 文件中获取的所有配置的数组

$config = \RouterOS::config([
    'host' => '192.168.1.3',
    'user' => 'admin',
    'pass' => 'admin',
    'port' => 8728,
]);

dump($config);

$client = \RouterOS::client($config);

Laravel 安装

默认情况下,该包将自动注册其服务提供程序,但如果您是低于 5.5 版本的 Laravel 的快乐所有者,那么在您的项目(当然是在完成 composer require 之后)中,将您的包添加到 config/app.phpproviders 块中

'providers' => [
    // ...
    RouterOS\Laravel\ServiceProvider::class,
],

可选地,如果需要更改任何默认设置,请发布配置文件

php artisan vendor:publish --provider="RouterOS\\Laravel\\ServiceProvider"

如何使用

基本示例,通过命令行的类似操作是 /ip hotspot ip-binding print

use \RouterOS\Client;
use \RouterOS\Query;

// Initiate client with config object
$client = new Client([
    'host' => '192.168.1.3',
    'user' => 'admin',
    'pass' => 'admin'
]);

// Create "where" Query object for RouterOS
$query =
    (new Query('/ip/hotspot/ip-binding/print'))
        ->where('mac-address', '00:00:00:00:40:29');

// Send query and read response from RouterOS
$response = $client->query($query)->read();

var_dump($response);

基本示例用于更新/创建/删除类型的查询

use \RouterOS\Client;
use \RouterOS\Query;

// Initiate client with config object
$client = new Client([
    'host' => '192.168.1.3',
    'user' => 'admin',
    'pass' => 'admin'
]);

// Send "equal" query with details about IP address which should be created
$query =
    (new Query('/ip/hotspot/ip-binding/add'))
        ->equal('mac-address', '00:00:00:00:40:29')
        ->equal('type', 'bypassed')
        ->equal('comment', 'testcomment');

// Send query and read response from RouterOS (ordinary answer from update/create/delete queries has empty body)
$response = $client->query($query)->read();

var_dump($response);

如果您需要从路由器导出所有设置

use \RouterOS\Client;

// Initiate client with config object
$client = new Client([
    'host'     => '192.168.1.3',
    'user'     => 'admin',
    'pass'     => 'admin',
    'ssh_port' => 22222,
]);

// Execute export command via ssh, because API /export method has a bug
$response = $client->export();

print_r($response);

带有 "where" 条件、"操作" 和 "标签" 的示例

use \RouterOS\Query;

/**
 * Simple "where" query will be generated by default 
 */

$client->query('/ip/address/print')->read();

/**
 * Send advanced "where" query with parameters to RouterOS 
 */

// If only one "where" condition
$client->query('/queue/simple/print', ['target', '192.168.1.1/32']);

// If multiple "where" conditions and need merge (operation "|") results
$client->query('/interface/print', [
    ['type', 'ether'],  // same as ['type', '=', 'ether']
    ['type', 'vlan'],   // same as ['type', '=', 'vlan']
], '|');

/**
 * Or in OOP style
 */

// If you need create query for "create/update/delete" operations
$query = new Query('/ip/hotspot/ip-binding/add');
$query->equal('mac-address', '00:00:00:00:40:29');
$query->equal('type', 'bypassed');
$query->equal('comment', 'testcomment');

// If multiple "where" conditions and need merge (operation "|") results
$query = new Query('/interface/print');
$query->where('type', 'ether');
$query->where('type', 'vlan');
$query->operations('|');

// If multiple "where" conditions and need append tag
$query = new Query('/interface/set');
$query->where('disabled', 'no');
$query->where('.id', 'ether1');
$query->tag(4);

/**
 * Write Query object to RouterOS and read response from it
 */

$response = $client->query($query)->read();

您可以在这里找到所有可用示例。

如何配置客户端

您只需创建一个 Client 类对象,其中包含以数组格式指定的所需参数

use \RouterOS\Client;

$client = new Client([
    'host' => '192.168.1.3',
    'user' => 'admin',
    'pass' => 'admin'
]);
ℹ️ Config 和 Client 类使用的高级示例
use \RouterOS\Config;
use \RouterOS\Client;

/**
 * You can create object of Config class
 */

$config = new Config();

// Then set parameters of config
$config->set('host', '192.168.1.3');
$config->set('user', 'admin');
$config->set('pass', 'admin');

// By the way, `->set()` method is support inline style of syntax
$config
    ->set('host', '192.168.1.3')
    ->set('user', 'admin')
    ->set('pass', 'admin');

/**
 * Or just create preconfigured Config object
 */

$config = new Config([
    'host' => '192.168.1.3',
    'user' => 'admin',
    'pass' => 'admin'
]);

/**
 * Then send Config object to Client constructor
 */

$client = new Client($config);

可用的配置参数列表

如何启用对旧登录方案(RouterOS pre-6.43)的支持

从 0.8.1 版本开始,这不再重要,固件版本将自动检测。

<?php
require_once __DIR__ . '/vendor/autoload.php';

use \RouterOS\Client;

// Initiate client with config object
$client = new Client([
    'host'   => '192.168.1.3',
    'user'   => 'admin',
    'pass'   => 'admin',
    'legacy' => true // you need set `legacy` parameter with `true` value
]);

// Your code below...

如何编写查询

您可以为您的路由器编写任何查询,为此您需要创建一个 "Query" 对象,其第一个参数是所需的命令,然后您可以向 "Query" 对象添加命令的属性。

有关属性和创建这些属性应使用的 "单词" 的更多信息,请参阅这里

有关 "表达式"、"where"、"equal" 以及您查询的其他过滤/修改的更多信息,请参阅这里

Query 类的简单使用示例

use \RouterOS\Query;

// Get all installed packages (it may be enabled or disabled)
$query = new Query('/system/package/getall');

// Send "equal" query with details about IP address which should be created
$query =
    (new Query('/ip/hotspot/ip-binding/add'))
        ->equal('mac-address', '00:00:00:00:40:29')
        ->equal('type', 'bypassed')
        ->equal('comment', 'testcomment');

// Set where interface is disabled and ID is ether1 (with tag 4)
$query = 
    (new Query('/interface/set'))
        ->where('disabled', 'no')
        ->where('.id', 'ether1')
        ->tag(4);

// Get all ethernet and VLAN interfaces
$query = 
    (new Query('/interface/print'))
        ->where('type', 'ether')
        ->where('type', 'vlan')
        ->operations('|');

// Get all routes that have non-empty comment
$query =
    (new Query('/ip/route/print'))
        ->where('comment', '>', null);
ℹ️ Query 类使用的高级示例
use \RouterOS\Query;
use \RouterOS\Client;

// Initiate connection to RouterOS
$client = new Client([
    'host'   => '192.168.1.3',
    'user'   => 'admin',
    'pass'   => 'admin'
]);

/**
 * Execute query directly through "->query()" method of Client class 
 */

// If your query has no "where" conditions
$client->query('/ip/arp/print');

// If you have only one where condition, you may use one dimensional array as second parameter of query method
$client->query('/queue/simple/print', ['target', '192.168.1.250/32']);

// If you need set few where conditions then need use multi dimensional array
$client->query('/interface/bridge/add', [
    ['name', 'vlan100-bridge'],
    ['vlan-filtering', 'no']
]);

/**
 * By some reason you may need restrict scope of RouterOS response,
 * for this need to use third attribute of "->query()" method
 */

// Get all ethernet and VLAN interfaces
$client->query('/interface/print', [
    ['type', 'ether'],
    ['type', 'vlan']
], '|');

/** 
 * If you want set tag of your query then you need to use fourth 
 * attribute of "->query()" method, but third attribute may be null
 */

// Enable interface (tag is 4)
$client->query('/interface/set', [
    ['disabled', 'no'],
    ['.id', 'ether1']
], null, 4);

/**
 * Or in OOP style  
 */

// Get all ethernet and VLAN interfaces
$query = new Query('/interface/print');
$query->where('type', 'ether');
$query->where('type', 'vlan');
$query->operations('|');

// Enable interface (tag is 4)
$query = new Query('/interface/set');
$query->equal('disabled', 'no');
$query->equal('.id', 'ether1');
$query->tag(4);

// Or

$query = new Query('/interface/set');
$query->add('=disabled=no');
$query->add('=.id=ether1');
$query->add('.tag=4');

// Or
    
$query = new Query('/interface/set', [
    '=disabled=no',
    '=.id=ether1',
    '.tag=4'
]);

// Or

$query = new Query([
    '/interface/set',
    '=disabled=no',
    '=.id=ether1',
    '.tag=4'
]);

/**
 * Write Query object to RouterOS and read response from it
 */

$response = $client->query($query)->read();

作为迭代器读取响应

默认情况下,此客户端的原始解决方案未针对处理大量结果进行优化,仅针对来自 RouterOS API 的少量行响应。

但某些路由器可能在其防火墙列表中拥有(例如)30,000 多条记录。具体针对此类任务,已添加了一个名为 readAsIterator 的方法,该方法将路由器获取到的结果转换为资源,稍后可以进行操作。

除了使用任何 array_* 函数外,您还可以将响应视为数组

$response = $client->query($query)->readAsIterator();
var_dump($response);

// The following for loop allows you to skip elements for which
// $iterator->current() throws an exception, rather than breaking
// the loop.
for ($response->rewind(); $response->valid(); $response->next()) {
    try {
        $value = $response->current();
    } catch (Exception $exception) {
        continue;
    }

    # ...
}

简短的方法

您可以将代码简化,并在一行中发送和从套接字读取

/** 
 * Execute query and read response in ordinary mode 
 */
$response = $client->query($query)->read();
var_dump($response);

// Or
$response = $client->q($query)->r();
var_dump($response);

// Single method analog of lines above is
$response = $client->qr($query);
var_dump($response);

/**
 * Execute query and read response as Iterator 
 */
$response = $client->query($query)->readAsIterator();
var_dump($response);

// Or
$response = $client->q($query)->ri();
var_dump($response);

// Single method analog of lines above is
$response = $client->qri($query);
var_dump($response);

/**
 * By the way, you can send few queries to your router without result: 
 */
$client->query($query1)->query($query2)->query($query3);

// Or
$client->q($query1)->q($query2)->q($query3);

已知问题

无法建立套接字会话,操作超时

此错误表示库无法连接到您的路由器,这可能意味着路由器已关闭(然后需要打开),或API服务未启用。

转到 Mikrotik Router OS -> IP -> Services 并启用 api 服务。

或者通过命令行

/ip service enable api 

如何通过API更新/删除/创建某些内容?

您需要使用 ->equal() 方法,而不是 Query 类的 ->where() 方法

// Create query which should remove security profile
$query = new \RouterOS\Query('/interface/wireless/security-profiles/remove');

// It will generate queries, which stared from "?" symbol:
$query->where('.id', '*1');

/*
// Sample with ->where() method
RouterOS\Query Object
(
    [_attributes:RouterOS\Query:private] => Array
        (
            [0] => ?.id=*1
        )

    [_operations:RouterOS\Query:private] => 
    [_tag:RouterOS\Query:private] => 
    [_endpoint:RouterOS\Query:private] => /interface/wireless/security-profiles/remove
)
*/

// So, as you can see, instead of `->where()` need to use `->equal()`
// It will generate queries, which stared from "=" symbol:
$query->equal('.id', '*1');

/*
// Sample with ->equal() method
RouterOS\Query Object
(
    [_attributes:RouterOS\Query:private] => Array
        (
            [0] => =.id=*1
        )

    [_operations:RouterOS\Query:private] => 
    [_tag:RouterOS\Query:private] => 
    [_endpoint:RouterOS\Query:private] => /interface/wireless/security-profiles/remove
)
*/

未定义字符(任何非英语语言)

RouterOS 不支持国家语言,只支持英语(以及 RouterOS 的 API)。

您可以通过网页尝试重现它,例如将注释添加到系统中的任何元素,然后保存并重新加载页面,您将看到不可读的字符。

测试

您可以使用我的包含 RouterOS 的 Docker 容器项目的其他项目,在您的计算机上运行单元测试,为此您只需要有 ExpectDockerDocker Compose

然后克隆包含 RouterOS 的 Docker 仓库,并执行 docker-compose up -d,然后您需要通过 preconf.tcl 脚本预配置虚拟路由器,脚本位于 routeros-api-php 的根目录。

./preconf.tcl 12223
./preconf.tcl 22223

然后您可以运行测试

./vendor/bin/phpunit

链接