改名
This commit is contained in:
-780
@@ -1,780 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Kiri\Rpc;
|
||||
|
||||
use Exception;
|
||||
use Swoole\Coroutine\Http\Handler\Client as CoroutineClient;
|
||||
|
||||
class Client
|
||||
{
|
||||
|
||||
|
||||
// KV
|
||||
const URI_PUT = 'kv/put';
|
||||
const URI_RANGE = 'kv/range';
|
||||
const URI_DELETE_RANGE = 'kv/deleterange';
|
||||
const URI_TXN = 'kv/txn';
|
||||
const URI_COMPACTION = 'kv/compaction';
|
||||
|
||||
// Lease
|
||||
const URI_GRANT = 'lease/grant';
|
||||
const URI_REVOKE = 'kv/lease/revoke';
|
||||
const URI_KEEPALIVE = 'lease/keepalive';
|
||||
const URI_TIMETOLIVE = 'kv/lease/timetolive';
|
||||
|
||||
// Role
|
||||
const URI_AUTH_ROLE_ADD = 'auth/role/add';
|
||||
const URI_AUTH_ROLE_GET = 'auth/role/get';
|
||||
const URI_AUTH_ROLE_DELETE = 'auth/role/delete';
|
||||
const URI_AUTH_ROLE_LIST = 'auth/role/list';
|
||||
|
||||
// Authenticate
|
||||
const URI_AUTH_ENABLE = 'auth/enable';
|
||||
const URI_AUTH_DISABLE = 'auth/disable';
|
||||
const URI_AUTH_AUTHENTICATE = 'auth/authenticate';
|
||||
|
||||
// User
|
||||
const URI_AUTH_USER_ADD = 'auth/user/add';
|
||||
const URI_AUTH_USER_GET = 'auth/user/get';
|
||||
const URI_AUTH_USER_DELETE = 'auth/user/delete';
|
||||
const URI_AUTH_USER_CHANGE_PASSWORD = 'auth/user/changepw';
|
||||
const URI_AUTH_USER_LIST = 'auth/user/list';
|
||||
|
||||
const URI_AUTH_ROLE_GRANT = 'auth/role/grant';
|
||||
const URI_AUTH_ROLE_REVOKE = 'auth/role/revoke';
|
||||
|
||||
const URI_AUTH_USER_GRANT = 'auth/user/grant';
|
||||
const URI_AUTH_USER_REVOKE = 'auth/user/revoke';
|
||||
|
||||
const PERMISSION_READ = 0;
|
||||
const PERMISSION_WRITE = 1;
|
||||
const PERMISSION_READWRITE = 2;
|
||||
|
||||
const DEFAULT_HTTP_TIMEOUT = 30;
|
||||
|
||||
|
||||
/**
|
||||
* @var string host:port
|
||||
*/
|
||||
protected string $host;
|
||||
|
||||
|
||||
protected int $port;
|
||||
|
||||
/**
|
||||
* @var string api version
|
||||
*/
|
||||
protected string $version;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected array $httpOptions;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
protected bool $pretty = false;
|
||||
|
||||
/**
|
||||
* @var string|null auth token
|
||||
*/
|
||||
protected ?string $token = null;
|
||||
|
||||
|
||||
/**
|
||||
* @param string $host
|
||||
* @param int $port
|
||||
* @param string $version
|
||||
*/
|
||||
public function __construct(string $host, int $port, string $version = 'v3alpha')
|
||||
{
|
||||
$this->host = $host;
|
||||
$this->port = $port;
|
||||
$this->version = trim($version);
|
||||
}
|
||||
|
||||
public function setHttpOptions(array $options)
|
||||
{
|
||||
$this->httpOptions = $options;
|
||||
}
|
||||
|
||||
public function setPretty($enabled)
|
||||
{
|
||||
$this->pretty = $enabled;
|
||||
}
|
||||
|
||||
public function setToken($token)
|
||||
{
|
||||
$this->token = $token;
|
||||
}
|
||||
|
||||
public function clearToken()
|
||||
{
|
||||
$this->token = null;
|
||||
}
|
||||
|
||||
// region kv
|
||||
|
||||
/**
|
||||
* Put puts the given key into the key-value store.
|
||||
* A put request increments the revision of the key-value
|
||||
* store\nand generates one event in the event history.
|
||||
*
|
||||
* @param string $key
|
||||
* @param string $value
|
||||
* @param array $options 可选参数
|
||||
* int64 lease
|
||||
* bool prev_kv
|
||||
* bool ignore_value
|
||||
* bool ignore_lease
|
||||
* @return array
|
||||
* @throws
|
||||
*/
|
||||
public function put(string $key, string $value, array $options = []): array
|
||||
{
|
||||
$params = ['value' => $value];
|
||||
|
||||
$key = ltrim($key,'/');
|
||||
|
||||
$body = $this->request('POST', "/v2/keys/$key", $params, $options);
|
||||
$body = $this->decodeBodyForFields(
|
||||
$body,
|
||||
'prev_kv',
|
||||
['key', 'value',]
|
||||
);
|
||||
|
||||
if (isset($body['prev_kv']) && $this->pretty) {
|
||||
return $this->convertFields($body['prev_kv']);
|
||||
}
|
||||
|
||||
return $body;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the key or a range of keys
|
||||
*
|
||||
* @param string $key
|
||||
* @param array $options
|
||||
* string range_end
|
||||
* int limit
|
||||
* int revision
|
||||
* int sort_order
|
||||
* int sort_target
|
||||
* bool serializable
|
||||
* bool keys_only
|
||||
* bool count_only
|
||||
* int64 min_mod_revision
|
||||
* int64 max_mod_revision
|
||||
* int64 min_create_revision
|
||||
* int64 max_create_revision
|
||||
* @return array
|
||||
* @throws Exception
|
||||
*/
|
||||
public function get(string $key): array
|
||||
{
|
||||
$key = ltrim($key,'/');
|
||||
|
||||
$body = $this->request('GET', "/v2/keys/$key");
|
||||
$body = $this->decodeBodyForFields(
|
||||
$body,
|
||||
'kvs',
|
||||
['key', 'value',]
|
||||
);
|
||||
|
||||
if (isset($body['kvs']) && $this->pretty) {
|
||||
return $this->convertFields($body['kvs']);
|
||||
}
|
||||
|
||||
return $body;
|
||||
}
|
||||
|
||||
/**
|
||||
* get all keys
|
||||
*
|
||||
* @return array
|
||||
* @throws Exception
|
||||
*/
|
||||
public function getAllKeys(): array
|
||||
{
|
||||
return $this->get('/');
|
||||
}
|
||||
|
||||
/**
|
||||
* get all keys with prefix
|
||||
*
|
||||
* @param string $prefix
|
||||
* @return array
|
||||
* @throws Exception
|
||||
*/
|
||||
public function getKeysWithPrefix(string $prefix): array
|
||||
{
|
||||
$prefix = trim($prefix);
|
||||
if (!$prefix) {
|
||||
return [];
|
||||
}
|
||||
$lastIndex = strlen($prefix) - 1;
|
||||
$lastChar = $prefix[$lastIndex];
|
||||
$nextAsciiCode = ord($lastChar) + 1;
|
||||
$rangeEnd = $prefix;
|
||||
$rangeEnd[$lastIndex] = chr($nextAsciiCode);
|
||||
|
||||
return $this->get($prefix, ['range_end' => $rangeEnd]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the specified key or range of keys
|
||||
*
|
||||
* @param string $key
|
||||
* @return array
|
||||
* @throws Exception
|
||||
*/
|
||||
public function del(string $key): array
|
||||
{
|
||||
$key = ltrim($key,'/');
|
||||
$body = $this->request('DELETE', "/v2/keys/$key");
|
||||
$body = $this->decodeBodyForFields(
|
||||
$body,
|
||||
'prev_kvs',
|
||||
['key', 'value',]
|
||||
);
|
||||
if (isset($body['prev_kvs']) && $this->pretty) {
|
||||
return $this->convertFields($body['prev_kvs']);
|
||||
}
|
||||
return $body;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compact compacts the event history in the etcd key-value store.
|
||||
* The key-value\nstore should be periodically compacted
|
||||
* or the event history will continue to grow\nindefinitely.
|
||||
*
|
||||
* @param int $revision
|
||||
*
|
||||
* @param bool|false $physical
|
||||
*
|
||||
* @return array
|
||||
* @throws Exception
|
||||
*/
|
||||
public function compaction($revision, $physical = false)
|
||||
{
|
||||
$params = [
|
||||
'revision' => $revision,
|
||||
'physical' => $physical,
|
||||
];
|
||||
|
||||
return $this->request(self::URI_COMPACTION, $params);
|
||||
}
|
||||
|
||||
// endregion kv
|
||||
|
||||
// region lease
|
||||
|
||||
/**
|
||||
* LeaseGrant creates a lease which expires if the server does not receive a
|
||||
* keepAlive\nwithin a given time to live period. All keys attached to the lease
|
||||
* will be expired and\ndeleted if the lease expires.
|
||||
* Each expired key generates a delete event in the event history.",
|
||||
*
|
||||
* @param int $ttl TTL is the advisory time-to-live in seconds.
|
||||
* @param int $id ID is the requested ID for the lease.
|
||||
* If ID is set to 0, the lessor chooses an ID.
|
||||
* @return array
|
||||
*/
|
||||
public function grant($ttl, $id = 0)
|
||||
{
|
||||
$params = [
|
||||
'TTL' => $ttl,
|
||||
'ID' => $id,
|
||||
];
|
||||
|
||||
|
||||
return $this->request(self::URI_GRANT, $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* revokes a lease. All keys attached to the lease will expire and be deleted.
|
||||
*
|
||||
* @param int $id ID is the lease ID to revoke. When the ID is revoked,
|
||||
* all associated keys will be deleted.
|
||||
* @return array
|
||||
*/
|
||||
public function revoke($id)
|
||||
{
|
||||
$params = [
|
||||
'ID' => $id,
|
||||
];
|
||||
|
||||
return $this->request(self::URI_REVOKE, $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* keeps the lease alive by streaming keep alive requests
|
||||
* from the client\nto the server and streaming keep alive responses
|
||||
* from the server to the client.
|
||||
*
|
||||
* @param int $id ID is the lease ID for the lease to keep alive.
|
||||
* @return array
|
||||
*/
|
||||
public function keepAlive(int $id): array
|
||||
{
|
||||
$params = [
|
||||
'ID' => $id,
|
||||
];
|
||||
|
||||
$body = $this->request(self::URI_KEEPALIVE, $params);
|
||||
|
||||
if (!isset($body['result'])) {
|
||||
return $body;
|
||||
}
|
||||
// response "result" field, etcd bug?
|
||||
return [
|
||||
'ID' => $body['result']['ID'],
|
||||
'TTL' => $body['result']['TTL'],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* retrieves lease information.
|
||||
*
|
||||
* @param int $id ID is the lease ID for the lease.
|
||||
* @param bool|false $keys
|
||||
* @return array
|
||||
*/
|
||||
public function timeToLive(int $id, bool $keys): array
|
||||
{
|
||||
$params = [
|
||||
'ID' => $id,
|
||||
'keys' => $keys,
|
||||
];
|
||||
|
||||
$body = $this->request(self::URI_TIMETOLIVE, $params);
|
||||
|
||||
if (isset($body['keys'])) {
|
||||
$body['keys'] = array_map(function ($value) {
|
||||
return base64_decode($value);
|
||||
}, $body['keys']);
|
||||
}
|
||||
|
||||
return $body;
|
||||
}
|
||||
|
||||
// endregion lease
|
||||
|
||||
// region auth
|
||||
|
||||
/**
|
||||
* enable authentication
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function authEnable()
|
||||
{
|
||||
$body = $this->request(self::URI_AUTH_ENABLE);
|
||||
$this->clearToken();
|
||||
|
||||
return $body;
|
||||
}
|
||||
|
||||
/**
|
||||
* disable authentication
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function authDisable()
|
||||
{
|
||||
$body = $this->request(self::URI_AUTH_DISABLE);
|
||||
$this->clearToken();
|
||||
|
||||
return $body;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $user
|
||||
* @param string $password
|
||||
* @return array
|
||||
*/
|
||||
public function authenticate($user, $password)
|
||||
{
|
||||
$params = [
|
||||
'name' => $user,
|
||||
'password' => $password,
|
||||
];
|
||||
|
||||
$body = $this->request(self::URI_AUTH_AUTHENTICATE, $params);
|
||||
if ($this->pretty && isset($body['token'])) {
|
||||
return $body['token'];
|
||||
}
|
||||
|
||||
return $body;
|
||||
}
|
||||
|
||||
/**
|
||||
* add a new role.
|
||||
*
|
||||
* @param string $name
|
||||
* @return array
|
||||
*/
|
||||
public function addRole($name)
|
||||
{
|
||||
$params = [
|
||||
'name' => $name,
|
||||
];
|
||||
|
||||
$body = $this->request(self::URI_AUTH_ROLE_ADD, $params);
|
||||
|
||||
return $body;
|
||||
}
|
||||
|
||||
/**
|
||||
* get detailed role information.
|
||||
*
|
||||
* @param string $role
|
||||
* @return array
|
||||
*/
|
||||
public function getRole($role)
|
||||
{
|
||||
$params = [
|
||||
'role' => $role,
|
||||
];
|
||||
|
||||
$body = $this->request(self::URI_AUTH_ROLE_GET, $params);
|
||||
$body = $this->decodeBodyForFields(
|
||||
$body,
|
||||
'perm',
|
||||
['key', 'range_end',]
|
||||
);
|
||||
if ($this->pretty && isset($body['perm'])) {
|
||||
return $body['perm'];
|
||||
}
|
||||
|
||||
return $body;
|
||||
}
|
||||
|
||||
/**
|
||||
* delete a specified role.
|
||||
*
|
||||
* @param string $role
|
||||
* @return array
|
||||
*/
|
||||
public function deleteRole($role)
|
||||
{
|
||||
$params = [
|
||||
'role' => $role,
|
||||
];
|
||||
|
||||
return $this->request(self::URI_AUTH_ROLE_DELETE, $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* get lists of all roles
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function roleList()
|
||||
{
|
||||
$body = $this->request(self::URI_AUTH_ROLE_LIST);
|
||||
|
||||
if ($this->pretty && isset($body['roles'])) {
|
||||
return $body['roles'];
|
||||
}
|
||||
|
||||
return $body;
|
||||
}
|
||||
|
||||
/**
|
||||
* add a new user
|
||||
*
|
||||
* @param string $user
|
||||
* @param string $password
|
||||
* @return array
|
||||
*/
|
||||
public function addUser($user, $password)
|
||||
{
|
||||
$params = [
|
||||
'name' => $user,
|
||||
'password' => $password,
|
||||
];
|
||||
|
||||
return $this->request(self::URI_AUTH_USER_ADD, $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* get detailed user information
|
||||
*
|
||||
* @param string $user
|
||||
* @return array
|
||||
* @throws \GuzzleHttp\Exception\BadResponseException
|
||||
*/
|
||||
public function getUser($user)
|
||||
{
|
||||
$params = [
|
||||
'name' => $user,
|
||||
];
|
||||
|
||||
$body = $this->request(self::URI_AUTH_USER_GET, $params);
|
||||
if ($this->pretty && isset($body['roles'])) {
|
||||
return $body['roles'];
|
||||
}
|
||||
|
||||
return $body;
|
||||
}
|
||||
|
||||
/**
|
||||
* delete a specified user
|
||||
*
|
||||
* @param string $user
|
||||
* @return array
|
||||
* @throws \GuzzleHttp\Exception\BadResponseException
|
||||
*/
|
||||
public function deleteUser($user)
|
||||
{
|
||||
$params = [
|
||||
'name' => $user,
|
||||
];
|
||||
|
||||
$body = $this->request(self::URI_AUTH_USER_DELETE, $params);
|
||||
|
||||
return $body;
|
||||
}
|
||||
|
||||
/**
|
||||
* get a list of all users.
|
||||
*
|
||||
* @return array
|
||||
* @throws \GuzzleHttp\Exception\BadResponseException
|
||||
*/
|
||||
public function userList()
|
||||
{
|
||||
$body = $this->request(self::URI_AUTH_USER_LIST);
|
||||
if ($this->pretty && isset($body['users'])) {
|
||||
return $body['users'];
|
||||
}
|
||||
|
||||
return $body;
|
||||
}
|
||||
|
||||
/**
|
||||
* change the password of a specified user.
|
||||
*
|
||||
* @param string $user
|
||||
* @param string $password
|
||||
* @return array
|
||||
* @throws \GuzzleHttp\Exception\BadResponseException
|
||||
*/
|
||||
public function changeUserPassword($user, $password)
|
||||
{
|
||||
$params = [
|
||||
'name' => $user,
|
||||
'password' => $password,
|
||||
];
|
||||
|
||||
$body = $this->request(self::URI_AUTH_USER_CHANGE_PASSWORD, $params);
|
||||
|
||||
return $body;
|
||||
}
|
||||
|
||||
/**
|
||||
* grant a permission of a specified key or range to a specified role.
|
||||
*
|
||||
* @param string $role
|
||||
* @param int $permType
|
||||
* @param string $key
|
||||
* @param string|null $rangeEnd
|
||||
* @return array
|
||||
* @throws \GuzzleHttp\Exception\BadResponseException
|
||||
*/
|
||||
public function grantRolePermission($role, $permType, $key, $rangeEnd = null)
|
||||
{
|
||||
$params = [
|
||||
'name' => $role,
|
||||
'perm' => [
|
||||
'permType' => $permType,
|
||||
'key' => base64_encode($key),
|
||||
],
|
||||
];
|
||||
if ($rangeEnd !== null) {
|
||||
$params['perm']['range_end'] = base64_encode($rangeEnd);
|
||||
}
|
||||
|
||||
$body = $this->request(self::URI_AUTH_ROLE_GRANT, $params);
|
||||
|
||||
return $body;
|
||||
}
|
||||
|
||||
/**
|
||||
* revoke a key or range permission of a specified role.
|
||||
*
|
||||
* @param string $role
|
||||
* @param string $key
|
||||
* @param string|null $rangeEnd
|
||||
* @return array
|
||||
* @throws BadResponseException
|
||||
*/
|
||||
public function revokeRolePermission($role, $key, $rangeEnd = null)
|
||||
{
|
||||
$params = [
|
||||
'role' => $role,
|
||||
'key' => $key,
|
||||
];
|
||||
if ($rangeEnd !== null) {
|
||||
$params['range_end'] = $rangeEnd;
|
||||
}
|
||||
|
||||
$body = $this->request(self::URI_AUTH_ROLE_REVOKE, $params);
|
||||
|
||||
return $body;
|
||||
}
|
||||
|
||||
/**
|
||||
* grant a role to a specified user.
|
||||
*
|
||||
* @param string $user
|
||||
* @param string $role
|
||||
* @return array
|
||||
* @throws BadResponseException
|
||||
*/
|
||||
public function grantUserRole($user, $role)
|
||||
{
|
||||
$params = [
|
||||
'user' => $user,
|
||||
'role' => $role,
|
||||
];
|
||||
|
||||
$body = $this->request(self::URI_AUTH_USER_GRANT, $params);
|
||||
|
||||
return $body;
|
||||
}
|
||||
|
||||
/**
|
||||
* revoke a role of specified user.
|
||||
*
|
||||
* @param string $user
|
||||
* @param string $role
|
||||
* @return array
|
||||
* @throws \GuzzleHttp\Exception\BadResponseException
|
||||
*/
|
||||
public function revokeUserRole($user, $role)
|
||||
{
|
||||
$params = [
|
||||
'name' => $user,
|
||||
'role' => $role,
|
||||
];
|
||||
|
||||
$body = $this->request(self::URI_AUTH_USER_REVOKE, $params);
|
||||
|
||||
return $body;
|
||||
}
|
||||
|
||||
// endregion auth
|
||||
|
||||
/**
|
||||
* 发送HTTP请求
|
||||
*
|
||||
* @param string $method
|
||||
* @param string $uri
|
||||
* @param array $params 请求参数
|
||||
* @param array $options 可选参数
|
||||
* @return array
|
||||
* @throws Exception
|
||||
*/
|
||||
protected function request(string $method, string $uri, array $params = [], array $options = []): array
|
||||
{
|
||||
if ($options) {
|
||||
$params = array_merge($params, $options);
|
||||
}
|
||||
// 没有参数, 设置一个默认参数
|
||||
if (!$params) {
|
||||
$params['php-etcd-client'] = 1;
|
||||
}
|
||||
$client = new CoroutineClient('47.92.194.207', 2379);
|
||||
if (!empty($params)) {
|
||||
$client->setHeaders(['Content-Type' => 'application/x-www-form-urlencoded']);
|
||||
$client->setData(http_build_query($params));
|
||||
}
|
||||
|
||||
$client->setMethod($method);
|
||||
$client->execute($uri);
|
||||
$body = json_decode($client->body, true);
|
||||
$client->close();
|
||||
|
||||
if (!in_array($client->getStatusCode(), [200, 201])) {
|
||||
throw new Exception($client->body, $client->errCode);
|
||||
}
|
||||
return $body;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* string类型key用base64编码
|
||||
*
|
||||
* @param array $data
|
||||
* @return array
|
||||
*/
|
||||
protected function encode(array $data): array
|
||||
{
|
||||
|
||||
foreach ($data as $key => $value) {
|
||||
if (is_string($value)) {
|
||||
$data[$key] = base64_encode($value);
|
||||
}
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 指定字段base64解码
|
||||
*
|
||||
* @param array $body
|
||||
* @param string $bodyKey
|
||||
* @param array $fields 需要解码的字段
|
||||
* @return array
|
||||
*/
|
||||
protected function decodeBodyForFields(array $body, string $bodyKey, array $fields): array
|
||||
{
|
||||
if (!isset($body[$bodyKey])) {
|
||||
return $body;
|
||||
}
|
||||
$data = $body[$bodyKey];
|
||||
if (!isset($data[0])) {
|
||||
$data = array($data);
|
||||
}
|
||||
foreach ($data as $key => $value) {
|
||||
foreach ($fields as $field) {
|
||||
if (isset($value[$field])) {
|
||||
$data[$key][$field] = base64_decode($value[$field]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($body[$bodyKey][0])) {
|
||||
$body[$bodyKey] = $data;
|
||||
} else {
|
||||
$body[$bodyKey] = $data[0];
|
||||
}
|
||||
|
||||
return $body;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param array $data
|
||||
* @return mixed
|
||||
*/
|
||||
protected function convertFields(array $data): mixed
|
||||
{
|
||||
if (!isset($data[0])) {
|
||||
return $data['value'];
|
||||
}
|
||||
|
||||
$map = [];
|
||||
foreach ($data as $value) {
|
||||
$key = $value['key'];
|
||||
$map[$key] = $value['value'];
|
||||
}
|
||||
|
||||
return $map;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Kiri\Rpc;
|
||||
|
||||
|
||||
use Annotation\Target;
|
||||
use Http\Constrict\RequestInterface;
|
||||
use Http\Handler\Controller;
|
||||
use Kiri\Rpc\Annotation\JsonRpc;
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
#[Target]
|
||||
#[JsonRpc(method: 'test', version: '2.0')]
|
||||
class TestRpcService extends Controller implements OnJsonRpcInterface
|
||||
{
|
||||
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function execute(): int
|
||||
{
|
||||
return $this->request->int('a') + $this->request->int('b');
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user