matrix-php-sdk/src/MatrixHttpApi.php

264 lines
8.4 KiB
PHP

<?php
namespace Aryess\PhpMatrixSdk;
use Aryess\PhpMatrixSdk\Exceptions\MatrixException;
use Aryess\PhpMatrixSdk\Exceptions\MatrixHttpLibException;
use Aryess\PhpMatrixSdk\Exceptions\MatrixRequestException;
use GuzzleHttp\Client;
/**
* Contains all raw Matrix HTTP Client-Server API calls.
* For room and sync handling, consider using MatrixClient.
*
* Examples:
* Create a client and send a message::
*
* $matrix = new MatrixHttpApi("https://matrix.org", $token="foobar");
* $response = $matrix.sync();
* $response = $matrix->sendMessage("!roomid:matrix.org", "Hello!");
*
* @package Aryess\PhpMatrixSdk
*/
class MatrixHttpApi {
const MATRIX_V2_API_PATH = "/_matrix/client/r0";
/**
* @var string
*/
private $baseUrl;
/**
* @var string|null
*/
private $token;
/**
* @var string|null
*/
private $identity;
/**
* @var int
*/
private $default429WaitMs;
/**
* @var bool
*/
private $useAuthorizationHeader;
/**
* @var int
*/
private $txnId;
/**
* @var bool
*/
private $vallidateCert;
/**
* @var Client
*/
private $client;
/**
* MatrixHttpApi constructor.
*
* @param string $baseUrl The home server URL e.g. 'http://localhost:8008'
* @param string|null $token Optional. The client's access token.
* @param string|null $identity Optional. The mxid to act as (For application services only).
* @param int $default429WaitMs Optional. Time in milliseconds to wait before retrying a request
* when server returns a HTTP 429 response without a 'retry_after_ms' key.
* @param bool $useAuthorizationHeader Optional. Use Authorization header instead of access_token query parameter.
* @throws MatrixException
*/
public function __construct(string $baseUrl, ?string $token = null, ?string $identity = null,
int $default429WaitMs = 5000, bool $useAuthorizationHeader = true) {
if (!filter_var($baseUrl, FILTER_VALIDATE_URL)) {
throw new MatrixException("Invalid homeserver url $baseUrl");
}
if (!array_get(parse_url($baseUrl), 'scheme')) {
throw new MatrixException("No scheme in homeserver url $baseUrl");
}
$this->baseUrl = $baseUrl;
$this->token = $token;
$this->identity = $identity;
$this->txnId = 0;
$this->vallidateCert = true;//FIXME: use config
$this->client = new Client();
$this->default429WaitMs = $default429WaitMs;
$this->useAuthorizationHeader = $useAuthorizationHeader;
}
public function setClient(Client $client) {
$this->client = $client;
}
/**
* @param string|null $since Optional. A token which specifies where to continue a sync from.
* @param int $timeoutMs
* @param null $filter
* @param bool $fullState
* @param string|null $setPresence
* @return array|string
* @throws MatrixException
* @throws \GuzzleHttp\Exception\GuzzleException
*/
public function sync(?string $since = null, int $timeoutMs = 30000, $filter = null,
bool $fullState = false, ?string $setPresence = null) {
$request = [
'timeout' => (int)$timeoutMs,
];
if ($since) {
$request['since'] = $since;
}
if ($filter) {
$request['filter'] = $filter;
}
if ($fullState) {
$request['full_state'] = json_encode($fullState);
}
if ($setPresence) {
$request['set_presence'] = $setPresence;
}
return $this->send('GET', "/sync", null, $request);
}
public function validateCertificate(bool $validity) {
$this->vallidateCert = $validity;
}
/**
* Performs /register.
*
* @param array $authBody Authentication Params.
* @param string $kind Specify kind of account to register. Can be 'guest' or 'user'.
* @param bool $bindEmail Whether to use email in registration and authentication.
* @param string|null $username The localpart of a Matrix ID.
* @param string|null $password The desired password of the account.
* @param string|null $deviceId ID of the client device.
* @param string|null $initialDeviceDisplayName Display name to be assigned.
* @param bool $inhibitLogin Whether to login after registration. Defaults to false.
*/
public function register(array $authBody = [], string $kind = "user", bool $bindEmail = false,
?string $username = null, ?string $password = null, ?string $deviceId = null,
?string $initialDeviceDisplayName = null, bool $inhibitLogin = false) {
$content = [
'kind' => $kind
];
if ($authBody) {
$content['auth'] = $authBody;
}
if ($username) {
$content['username'] = $username;
}
if ($password) {
$content['password'] = $password;
}
if ($deviceId) {
$content['device_id'] = $deviceId;
}
if ($initialDeviceDisplayName) {
$content['initial_device_display_name'] = $initialDeviceDisplayName;
}
if ($bindEmail) {
$content['bind_email'] = $bindEmail;
}
if ($inhibitLogin) {
$content['inhibit_login'] = $inhibitLogin;
}
return $this->send('POST', '/register', $content, ['kind' => $kind]);
}
public function getDisplayName(string $userId): ?string {
$content = $this->send("GET", "/profile/$userId/displayname");
return array_get($content, 'displayname', json_encode($content));
}
/**
* @param string $method
* @param string $path
* @param mixed $content
* @param array $queryParams
* @param array $headers
* @param string $apiPath
* @param bool $returnJson
* @return array|string
* @throws MatrixException
* @throws \GuzzleHttp\Exception\GuzzleException
*/
private function send(string $method, string $path, $content = null, array $queryParams = [], array $headers = [],
$apiPath = self::MATRIX_V2_API_PATH, $returnJson = true) {
$options = [];
if (!in_array('User-Agent', $headers)) {
$headers['User-Agent'] = 'php-matrix-sdk/0.0.1-dev'; //TODO: add version
}
$method = strtoupper($method);
if (!in_array($method, ['GET', 'POST', 'PUT', 'DELETE'])) {
throw new MatrixException("Unsupported HTTP method: $method");
}
if (!in_array('Content-Type', $headers)) {
$headers['Content-Type'] = 'application/json';
}
if ($this->useAuthorizationHeader) {
$headers['Authorization'] = sprintf('Bearer %s', $this->token);
} else {
$queryParams['access_token'] = $this->token;
}
if ($this->identity) {
$queryParams['user_id'] = $this->identity;
}
$endpoint = $this->baseUrl . $apiPath . $path;
if ($headers['Content-Type'] == "application/json" && $content != null) {
$content = json_encode($content);
}
$options = array_merge($options, [
'headers' => $headers,
'query' => $queryParams,
'body' => $content,
'verify' => $this->vallidateCert,
]);
$responseBody = '';
while (true) {
try {
$response = $this->client->request($method, $endpoint, $options);
} catch (RequestException $e) {
throw new MatrixHttpLibException($e, $method, $endpoint);
}
if ($response->getStatusCode() != 429) {
$responseBody = $response->getBody()->getContents();
break;
}
$jsonResponse = json_decode($responseBody, true);
$waitTime = array_get($jsonResponse, 'retry_after_ms');
$waitTime = $waitTime ?: array_get($jsonResponse, 'error.retry_after_ms', $this->default429WaitMs);
$waitTime /= 1000;
sleep($waitTime);
}
if ($response->getStatusCode() < 200 || $response->getStatusCode() >= 300) {
throw new MatrixRequestException($response->getStatusCode(), $responseBody);
}
return $returnJson ? json_decode($responseBody, true) : $responseBody;
}
}