summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Readme.md5
-rw-r--r--composer.json7
-rw-r--r--composer.lock2
-rw-r--r--matrix-specification/AccountData.php16
-rw-r--r--matrix-specification/Enums/AuthenticationType.php19
-rw-r--r--matrix-specification/Enums/ErrorCode.php52
-rw-r--r--matrix-specification/Enums/EventType.php17
-rw-r--r--matrix-specification/Enums/LoginType.php14
-rw-r--r--matrix-specification/Enums/MembershipState.php17
-rw-r--r--matrix-specification/Enums/PresenceState.php15
-rw-r--r--matrix-specification/Enums/UserIdentifierType.php15
-rw-r--r--matrix-specification/Errors/Error.php41
-rw-r--r--matrix-specification/Errors/RateLimitError.php20
-rw-r--r--matrix-specification/Events/ClientEvent.php32
-rw-r--r--matrix-specification/Events/ClientEventWithoutRoomId.php34
-rw-r--r--matrix-specification/Events/Event.php17
-rw-r--r--matrix-specification/Events/PresenceEvent.php42
-rw-r--r--matrix-specification/Events/SenderEvent.php20
-rw-r--r--matrix-specification/Events/UnsignedData.php31
-rw-r--r--matrix-specification/Requests/ClientLoginGetRequest.php6
-rw-r--r--matrix-specification/Requests/ClientLoginPostRequest.php53
-rw-r--r--matrix-specification/Requests/ClientSyncGetRequest.php8
-rw-r--r--matrix-specification/Requests/RateLimited.php8
-rw-r--r--matrix-specification/Requests/RequiresAuthentication.php8
-rw-r--r--matrix-specification/Responses/ClientLoginGetResponse.php21
-rw-r--r--matrix-specification/Responses/ClientSyncGetResponse.php8
-rw-r--r--matrix-specification/Responses/LoginFlow.php31
-rw-r--r--matrix-specification/UserIdentifier.php45
28 files changed, 599 insertions, 5 deletions
diff --git a/Readme.md b/Readme.md
index 243eacb..d0c9e00 100644
--- a/Readme.md
+++ b/Readme.md
@@ -1,4 +1,7 @@
-Matrix Specification: https://spec.matrix.org/v1.15/
+Matrix Specification
+- https://spec.matrix.org/v1.15/
+- https://spec.matrix.org/v1.16/
+- https://spec.matrix.org/legacy/client_server/r0.6.1
# TODO: check if access_token is expired BEFORE using it to fetch the user
diff --git a/composer.json b/composer.json
index f187ddf..14c1b94 100644
--- a/composer.json
+++ b/composer.json
@@ -8,10 +8,10 @@
}
],
"require": {
+ "psr/log": "^3.0",
"symfony/dotenv": "^7.3",
"symfony/http-foundation": "^7.3",
- "symfony/routing": "^7.3",
- "psr/log": "^3.0"
+ "symfony/routing": "^7.3"
},
"require-dev": {
"guzzlehttp/guzzle": "^7.9",
@@ -20,7 +20,8 @@
"autoload": {
"psr-4": {
"App\\": "src/",
- "Tests\\": "tests/"
+ "Tests\\": "tests/",
+ "Matrix\\": "matrix-specification"
}
},
"scripts": {
diff --git a/composer.lock b/composer.lock
index bb1e9cb..c80924c 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
- "content-hash": "4ddf0e945485466529ae4e7507dfb9fa",
+ "content-hash": "b8c9f3de980f8dc287642ead631cec2e",
"packages": [
{
"name": "psr/log",
diff --git a/matrix-specification/AccountData.php b/matrix-specification/AccountData.php
new file mode 100644
index 0000000..b29e7fe
--- /dev/null
+++ b/matrix-specification/AccountData.php
@@ -0,0 +1,16 @@
+<?php
+
+namespace Matrix;
+
+use Matrix\Events\Event;
+
+class AccountData
+{
+ /**
+ * @param Event[] $events
+ */
+ public function __construct(
+ private array $events
+ )
+ {}
+}
diff --git a/matrix-specification/Enums/AuthenticationType.php b/matrix-specification/Enums/AuthenticationType.php
new file mode 100644
index 0000000..e335eed
--- /dev/null
+++ b/matrix-specification/Enums/AuthenticationType.php
@@ -0,0 +1,19 @@
+<?php
+
+namespace Matrix\Enums;
+
+enum AuthenticationType: string implements \JsonSerializable
+{
+ case DUMMY = "m.login.dummy";
+ case EMAIL_IDENTITY = "m.login.email.identity";
+ case MSISDN = "m.login.msisdn";
+ case PASSWORD = "m.login.password";
+ case RECAPTCHA = "m.login.recaptcha";
+ case REGISTRATION_TOKEN = "m.login.registration_token";
+ case SSO = "m.login.sso";
+
+ public function jsonSerialize(): string
+ {
+ return $this->value;
+ }
+}
diff --git a/matrix-specification/Enums/ErrorCode.php b/matrix-specification/Enums/ErrorCode.php
new file mode 100644
index 0000000..1a22f64
--- /dev/null
+++ b/matrix-specification/Enums/ErrorCode.php
@@ -0,0 +1,52 @@
+<?php
+
+namespace Matrix\Enums;
+
+enum ErrorCode: string implements \JsonSerializable
+{
+ case FORBIDDEN = "M_FORBIDDEN";
+ case UNKNOWN_TOKEN = "M_UNKNOWN_TOKEN";
+ case MISSING_TOKEN = "M_MISSING_TOKEN";
+ case USER_LOCKED = "M_USER_LOCKED";
+ case USER_SUSPENDED = "M_USER_SUSPENDED";
+ case BAD_JSON = "M_BAD_JSON";
+ case NOT_JSON = "M_NOT_JSON";
+ case NOT_FOUND = "M_NOT_FOUND";
+ case LIMIT_EXCEEDED = "M_LIMIT_EXCEEDED";
+ case UNRECOGNIZED = "M_UNRECOGNIZED";
+ case UNKNOWN = "M_UNKNOWN";
+
+ case UNAUTHORIZED = "M_UNAUTHORIZED";
+ case USER_DEACTIVATED = "M_USER_DEACTIVATED";
+ case USER_IN_USE = "M_USER_IN_USE";
+ case INVALID_USERNAME = "M_INVALID_USERNAME";
+ case ROOM_IN_USE = "M_ROOM_IN_USE";
+ case INVALID_ROOM_STATE = "M_INVALID_ROOM_STATE";
+
+ case THREEPID_IN_USE = "M_THREEPID_IN_USE";
+ case THREEPID_NOT_FOUND = "M_THREEPID_NOT_FOUND";
+ case THREEPID_AUTH_FAILED = "M_THREEPID_AUTH_FAILED";
+ case THREEPID_DENIED = "M_THREEPID_DENIED";
+ case THREEPID_MEDIUM_NOT_SUPPORTED = "M_THREEPID_MEDIUM_NOT_SUPPORTED";
+
+ case SERVER_NOT_TRUSTED = "M_SERVER_NOT_TRUSTED";
+ case UNSUPPORTED_ROOM_VERSION = "M_UNSUPPORTED_ROOM_VERSION";
+ case INCOMPATIBLE_ROOM_VERSION = "M_INCOMPATIBLE_ROOM_VERSION";
+ case BAD_STATE = "M_BAD_STATE";
+ case GUEST_ACCESS_FORBIDDEN = "M_GUEST_ACCESS_FORBIDDEN";
+
+ case CAPTCHA_NEEDED = "M_CAPTCHA_NEEDED";
+ case CAPTCHA_INVALID = "M_CAPTCHA_INVALID";
+
+ case MISSING_PARAM = "M_MISSING_PARAM";
+ case INVALID_PARAM = "M_INVALID_PARAM";
+ case TOO_LARGE = "M_TOO_LARGE";
+ case EXCLUSIVE = "M_EXCLUSIVE";
+ case RESOURCE_LIMIT_EXCEEDED = "M_RESOURCE_LIMIT_EXCEEDED";
+ case CANNOT_LEAVE_SERVER_NOTICE_ROOM = "M_CANNOT_LEAVE_SERVER_NOTICE_ROOM";
+
+ public function jsonSerialize(): string
+ {
+ return $this->value;
+ }
+}
diff --git a/matrix-specification/Enums/EventType.php b/matrix-specification/Enums/EventType.php
new file mode 100644
index 0000000..bf805b2
--- /dev/null
+++ b/matrix-specification/Enums/EventType.php
@@ -0,0 +1,17 @@
+<?php
+
+namespace Matrix\Enums;
+
+enum EventType: string implements \JsonSerializable
+{
+ case PRESENCE = "m.presence";
+
+ case ROOM_MEMBER = "m.room.member";
+ case ROOM_MESSAGE = "m.room.message";
+ case ROOM_NAME = "m.room.name";
+
+ public function jsonSerialize(): string
+ {
+ return $this->value;
+ }
+}
diff --git a/matrix-specification/Enums/LoginType.php b/matrix-specification/Enums/LoginType.php
new file mode 100644
index 0000000..b268cf5
--- /dev/null
+++ b/matrix-specification/Enums/LoginType.php
@@ -0,0 +1,14 @@
+<?php
+
+namespace Matrix\Enums;
+
+enum LoginType: string implements \JsonSerializable
+{
+ case PASSWORD = "m.login.password";
+ case TOKEN = "m.login.token";
+
+ public function jsonSerialize(): string
+ {
+ return $this->value;
+ }
+}
diff --git a/matrix-specification/Enums/MembershipState.php b/matrix-specification/Enums/MembershipState.php
new file mode 100644
index 0000000..750f4c0
--- /dev/null
+++ b/matrix-specification/Enums/MembershipState.php
@@ -0,0 +1,17 @@
+<?php
+
+namespace Matrix\Enums;
+
+enum MembershipState: string implements \JsonSerializable
+{
+ case BAN = "ban";
+ case INVITE = "invite";
+ case JOIN = "join";
+ case KNOCK = "knock";
+ case LEAVE = "leave";
+
+ public function jsonSerialize(): string
+ {
+ return $this->value;
+ }
+}
diff --git a/matrix-specification/Enums/PresenceState.php b/matrix-specification/Enums/PresenceState.php
new file mode 100644
index 0000000..bc97ae9
--- /dev/null
+++ b/matrix-specification/Enums/PresenceState.php
@@ -0,0 +1,15 @@
+<?php
+
+namespace Matrix\Enums;
+
+enum PresenceState: string implements \JsonSerializable
+{
+ case OFFLINE = "offline";
+ case ONLINE = "online";
+ case UNAVAILABLE = "unavailable";
+
+ public function jsonSerialize(): string
+ {
+ return $this->value;
+ }
+}
diff --git a/matrix-specification/Enums/UserIdentifierType.php b/matrix-specification/Enums/UserIdentifierType.php
new file mode 100644
index 0000000..d4be36e
--- /dev/null
+++ b/matrix-specification/Enums/UserIdentifierType.php
@@ -0,0 +1,15 @@
+<?php
+
+namespace Matrix\Enums;
+
+enum UserIdentifierType: string implements \JsonSerializable
+{
+ case USER = "m.id.user";
+ case THIRDPARTY = "m.id.thirdparty";
+ case PHONE = "m.id.phone";
+
+ public function jsonSerialize(): string
+ {
+ return $this->value;
+ }
+}
diff --git a/matrix-specification/Errors/Error.php b/matrix-specification/Errors/Error.php
new file mode 100644
index 0000000..2adc642
--- /dev/null
+++ b/matrix-specification/Errors/Error.php
@@ -0,0 +1,41 @@
+<?php
+
+namespace Matrix\Errors;
+
+use Matrix\Enums\ErrorCode;
+
+abstract class Error extends \RuntimeException implements \JsonSerializable
+{
+ public function __construct(
+ private ErrorCode $errorCode,
+ string $message,
+ int $httpCode
+ )
+ {
+ parent::__construct($message, $httpCode);
+ }
+
+ public function getErrorCode(): ErrorCode
+ {
+ return $this->errorCode;
+ }
+
+ public function getHttpCode(): int
+ {
+ return $this->getCode();
+ }
+
+ /**
+ * @return array<string, mixed>
+ */
+ abstract public function getAdditionalData(): array;
+
+ public function jsonSerialize(): array
+ {
+ return [
+ "errcode" => $this->getErrorCode(),
+ "error" => $this->getMessage(),
+ ...$this->getAdditionalData(),
+ ];
+ }
+}
diff --git a/matrix-specification/Errors/RateLimitError.php b/matrix-specification/Errors/RateLimitError.php
new file mode 100644
index 0000000..2f4193c
--- /dev/null
+++ b/matrix-specification/Errors/RateLimitError.php
@@ -0,0 +1,20 @@
+<?php
+
+namespace Matrix\Errors;
+
+use Matrix\Enums\ErrorCode;
+
+class RateLimitError extends Error
+{
+ public function __construct(private int $retryAfterMilliseconds)
+ {
+ parent::__construct(ErrorCode::LIMIT_EXCEEDED, "Too many requests", 429);
+ }
+
+ public function getAdditionalData(): array
+ {
+ return [
+ "retry_after_ms" => $this->retryAfterMilliseconds,
+ ];
+ }
+}
diff --git a/matrix-specification/Events/ClientEvent.php b/matrix-specification/Events/ClientEvent.php
new file mode 100644
index 0000000..be1e354
--- /dev/null
+++ b/matrix-specification/Events/ClientEvent.php
@@ -0,0 +1,32 @@
+<?php
+
+namespace Matrix\Events;
+
+use Matrix\Enums\EventType;
+
+class ClientEvent extends ClientEventWithoutRoomId
+{
+ public function __construct(
+ array $content,
+ string $eventId,
+ int $originServerTimestamp,
+ protected string $roomId,
+ string $sender,
+ string $stateKey,
+ EventType $type,
+ ?UnsignedData $unsigned = null,
+ )
+ {
+ parent::__construct($content, $eventId, $originServerTimestamp, $sender, $stateKey, $type);
+ }
+
+ public function jsonSerialize(): array
+ {
+ $clientEvent = parent::jsonSerialize();
+ $clientEvent += [
+ "room_id" => $this->roomId,
+ ];
+
+ return $clientEvent;
+ }
+}
diff --git a/matrix-specification/Events/ClientEventWithoutRoomId.php b/matrix-specification/Events/ClientEventWithoutRoomId.php
new file mode 100644
index 0000000..0410d5b
--- /dev/null
+++ b/matrix-specification/Events/ClientEventWithoutRoomId.php
@@ -0,0 +1,34 @@
+<?php
+
+namespace Matrix\Events;
+
+use Matrix\Enums\EventType;
+
+class ClientEventWithoutRoomId extends SenderEvent
+{
+ public function __construct(
+ array $content,
+ protected string $eventId,
+ protected int $originServerTimestamp,
+ string $sender,
+ protected string $stateKey,
+ EventType $type,
+ protected ?UnsignedData $unsigned = null,
+ )
+ {
+ parent::__construct($content, $sender, $type);
+ }
+
+ public function jsonSerialize(): array
+ {
+ return [
+ "content" => $this->content ?: new \stdClass,
+ "event_id" => $this->eventId,
+ "origin_server_ts" => $this->originServerTimestamp,
+ "sender" => $this->sender,
+ "state_key" => $this->stateKey,
+ "type" => $this->type,
+ "unsigned" => $this->unsigned ?? new \stdClass,
+ ];
+ }
+}
diff --git a/matrix-specification/Events/Event.php b/matrix-specification/Events/Event.php
new file mode 100644
index 0000000..fe85f48
--- /dev/null
+++ b/matrix-specification/Events/Event.php
@@ -0,0 +1,17 @@
+<?php
+
+namespace Matrix\Events;
+
+use Matrix\Enums\EventType;
+
+abstract class Event implements \JsonSerializable
+{
+ /**
+ * @param array<string, mixed> $content
+ */
+ public function __construct(
+ protected array $content,
+ protected EventType $type,
+ )
+ {}
+}
diff --git a/matrix-specification/Events/PresenceEvent.php b/matrix-specification/Events/PresenceEvent.php
new file mode 100644
index 0000000..f71d4bd
--- /dev/null
+++ b/matrix-specification/Events/PresenceEvent.php
@@ -0,0 +1,42 @@
+<?php
+
+namespace Matrix\Events;
+
+use Matrix\Enums\EventType;
+use Matrix\Enums\PresenceState;
+
+class PresenceEvent extends SenderEvent
+{
+ public function __construct(
+ string $sender,
+ PresenceState $presence,
+ string $avatarUrl = "",
+ bool $currentlyActive = false,
+ string $displayName = "",
+ int $lastActiveAgo = 0,
+ string $statusMessage = "",
+ )
+ {
+ parent::__construct(
+ [
+ "avatar_url" => $avatarUrl,
+ "currently_active" => $currentlyActive,
+ "display_name" => $displayName,
+ "last_active_ago" => $lastActiveAgo,
+ "presence" => $presence,
+ "status_msg" => $statusMessage,
+ ],
+ $sender,
+ EventType::PRESENCE
+ );
+ }
+
+ public function jsonSerialize(): array
+ {
+ return [
+ "content" => $this->content,
+ "sender" => $this->sender,
+ "type" => $this->type,
+ ];
+ }
+}
diff --git a/matrix-specification/Events/SenderEvent.php b/matrix-specification/Events/SenderEvent.php
new file mode 100644
index 0000000..f3d4ceb
--- /dev/null
+++ b/matrix-specification/Events/SenderEvent.php
@@ -0,0 +1,20 @@
+<?php
+
+namespace Matrix\Events;
+
+use Matrix\Enums\EventType;
+
+abstract class SenderEvent extends Event
+{
+ /**
+ * @param array<string, mixed> $content
+ */
+ public function __construct(
+ array $content,
+ protected string $sender,
+ EventType $type,
+ )
+ {
+ parent::__construct($content, $type);
+ }
+}
diff --git a/matrix-specification/Events/UnsignedData.php b/matrix-specification/Events/UnsignedData.php
new file mode 100644
index 0000000..924af29
--- /dev/null
+++ b/matrix-specification/Events/UnsignedData.php
@@ -0,0 +1,31 @@
+<?php
+
+namespace Matrix\Events;
+
+use Matrix\Enums\MembershipState;
+
+/**
+ * @see https://spec.matrix.org/v1.16/client-server-api/#definition-clientevent_unsigneddata
+ */
+class UnsignedData implements \JsonSerializable
+{
+ public function __construct(
+ private ?int $age = null,
+ private ?MembershipState $membership = null,
+ private ?array $previousContent = null,
+ private ?ClientEvent $redactedBecause = null,
+ private ?string $transactionId = null,
+ )
+ {}
+
+ public function jsonSerialize(): array
+ {
+ return array_filter([
+ "age" => $this->age,
+ "membership" => $this->membership,
+ "prev_content" => $this->previousContent,
+ "redacted_because" => $this->redactedBecause,
+ "transaction_id" => $this->transactionId,
+ ], "is_null");
+ }
+}
diff --git a/matrix-specification/Requests/ClientLoginGetRequest.php b/matrix-specification/Requests/ClientLoginGetRequest.php
new file mode 100644
index 0000000..73cc24c
--- /dev/null
+++ b/matrix-specification/Requests/ClientLoginGetRequest.php
@@ -0,0 +1,6 @@
+<?php
+
+namespace Matrix\Requests;
+
+class ClientLoginGetRequest implements RateLimited
+{}
diff --git a/matrix-specification/Requests/ClientLoginPostRequest.php b/matrix-specification/Requests/ClientLoginPostRequest.php
new file mode 100644
index 0000000..d17c558
--- /dev/null
+++ b/matrix-specification/Requests/ClientLoginPostRequest.php
@@ -0,0 +1,53 @@
+<?php
+
+namespace Matrix\Requests;
+
+use Matrix\Enums\LoginType;
+use Matrix\UserIdentifier;
+
+class ClientLoginPostRequest implements RateLimited, \JsonSerializable
+{
+ public function __construct(
+ private LoginType $type,
+ private ?string $deviceId = null,
+ private ?UserIdentifier $identifier = null,
+ private ?string $initialDeviceDisplayName = null,
+ private ?string $password = null,
+ private ?bool $refreshToken = null,
+ private ?string $token = null,
+ )
+ {
+ if ($type == LoginType::PASSWORD && is_null($password)) {
+ throw new \InvalidArgumentException("password is required when using LoginType password");
+ }
+
+ if ($type == LoginType::TOKEN && is_null($token)) {
+ throw new \InvalidArgumentException("token is required when using LoginType token");
+ }
+ }
+
+ public function jsonSerialize(): array
+ {
+ $request = [
+ "device_id" => $this->deviceId,
+ "identifier" => $this->identifier,
+ "initial_device_display_name" => $this->initialDeviceDisplayName,
+ "refresh_token" => $this->refreshToken,
+ "type" => $this->type,
+ ];
+
+ $request += match ($this->type) {
+ LoginType::PASSWORD => [
+ "password" => $this->password,
+ ],
+
+ LoginType::TOKEN => [
+ "token" => $this->token,
+ ],
+
+ default => [],
+ };
+
+ return array_filter($request, "is_null");
+ }
+}
diff --git a/matrix-specification/Requests/ClientSyncGetRequest.php b/matrix-specification/Requests/ClientSyncGetRequest.php
new file mode 100644
index 0000000..2921fcf
--- /dev/null
+++ b/matrix-specification/Requests/ClientSyncGetRequest.php
@@ -0,0 +1,8 @@
+<?php
+
+namespace Matrix\Requests;
+
+class ClientSyncGetRequest
+{
+ # TODO
+}
diff --git a/matrix-specification/Requests/RateLimited.php b/matrix-specification/Requests/RateLimited.php
new file mode 100644
index 0000000..9f917a4
--- /dev/null
+++ b/matrix-specification/Requests/RateLimited.php
@@ -0,0 +1,8 @@
+<?php
+
+namespace Matrix\Requests;
+
+interface RateLimited
+{
+ # TODO
+}
diff --git a/matrix-specification/Requests/RequiresAuthentication.php b/matrix-specification/Requests/RequiresAuthentication.php
new file mode 100644
index 0000000..c494f13
--- /dev/null
+++ b/matrix-specification/Requests/RequiresAuthentication.php
@@ -0,0 +1,8 @@
+<?php
+
+namespace Matrix\Requests;
+
+interface RequiresAuthentication
+{
+ public function authenticateUser(): bool;
+}
diff --git a/matrix-specification/Responses/ClientLoginGetResponse.php b/matrix-specification/Responses/ClientLoginGetResponse.php
new file mode 100644
index 0000000..e0ccc26
--- /dev/null
+++ b/matrix-specification/Responses/ClientLoginGetResponse.php
@@ -0,0 +1,21 @@
+<?php
+
+namespace Matrix\Responses;
+
+class ClientLoginGetResponse implements \JsonSerializable
+{
+ /**
+ * @param LoginFlow[] $loginFlows
+ */
+ public function __construct(
+ private array $loginFlows,
+ )
+ {}
+
+ public function jsonSerialize(): array
+ {
+ return [
+ "flows" => $this->loginFlows,
+ ];
+ }
+}
diff --git a/matrix-specification/Responses/ClientSyncGetResponse.php b/matrix-specification/Responses/ClientSyncGetResponse.php
new file mode 100644
index 0000000..27464c1
--- /dev/null
+++ b/matrix-specification/Responses/ClientSyncGetResponse.php
@@ -0,0 +1,8 @@
+<?php
+
+namespace Matrix\Responses;
+
+class ClientSyncGetResponse
+{
+ # TODO
+}
diff --git a/matrix-specification/Responses/LoginFlow.php b/matrix-specification/Responses/LoginFlow.php
new file mode 100644
index 0000000..6ddb703
--- /dev/null
+++ b/matrix-specification/Responses/LoginFlow.php
@@ -0,0 +1,31 @@
+<?php
+
+namespace Matrix\Responses;
+
+use Matrix\Enums\LoginType;
+
+class LoginFlow implements \JsonSerializable
+{
+ public function __construct(
+ private LoginType $type,
+ private ?bool $getLoginToken = null,
+ )
+ {}
+
+ public function jsonSerialize(): array
+ {
+ $loginFlow = [
+ "type" => $this->type,
+ ];
+
+ $loginFlow += match ($this->type) {
+ LoginType::TOKEN => [
+ "get_login_token" => $this->getLoginToken,
+ ],
+
+ default => [],
+ };
+
+ return $loginFlow;
+ }
+}
diff --git a/matrix-specification/UserIdentifier.php b/matrix-specification/UserIdentifier.php
new file mode 100644
index 0000000..63f939a
--- /dev/null
+++ b/matrix-specification/UserIdentifier.php
@@ -0,0 +1,45 @@
+<?php
+
+namespace Matrix;
+
+use Matrix\Enums\UserIdentifierType;
+
+class UserIdentifier implements \JsonSerializable
+{
+ public function __construct(
+ private UserIdentifierType $type,
+ private ?string $user = null,
+ private ?string $thirdPartyMedium = null,
+ private ?string $thirdPartyAddress = null,
+ private ?string $phoneCountry = null,
+ private ?string $phoneNumber = null,
+ )
+ {}
+
+ public function jsonSerialize(): array
+ {
+ $userIdentifier = [
+ "type" => $this->type,
+ ];
+
+ $userIdentifier += match ($this->type) {
+ UserIdentifierType::USER => [
+ "user" => $this->user,
+ ],
+
+ UserIdentifierType::THIRDPARTY => [
+ "medium" => $this->thirdPartyMedium,
+ "address" => $this->thirdPartyAddress,
+ ],
+
+ UserIdentifierType::PHONE => [
+ "country" => $this->phoneCountry,
+ "phone" => $this->phoneNumber,
+ ],
+
+ default => [],
+ };
+
+ return $userIdentifier;
+ }
+}