diff options
author | Daniel Weipert <git@mail.dweipert.de> | 2025-08-10 10:46:41 +0200 |
---|---|---|
committer | Daniel Weipert <git@mail.dweipert.de> | 2025-08-10 10:46:41 +0200 |
commit | c14579871fa1241713128a2d0d5514af004e3371 (patch) | |
tree | 6b28c42958650d94c9aa1947a0b5c32eb869d89f |
initial commit
-rw-r--r-- | .gitignore | 2 | ||||
-rw-r--r-- | composer.json | 20 | ||||
-rw-r--r-- | composer.lock | 489 | ||||
-rw-r--r-- | docker-compose.yml | 27 | ||||
-rw-r--r-- | docker/nginx/Dockerfile | 3 | ||||
-rw-r--r-- | docker/nginx/default.conf.template | 19 | ||||
-rw-r--r-- | docker/php/Dockerfile | 7 | ||||
-rw-r--r-- | public/index.php | 7 | ||||
-rw-r--r-- | src/App.php | 19 | ||||
-rw-r--r-- | src/Controllers/KeyController.php | 22 | ||||
-rw-r--r-- | src/Controllers/ServerDiscoveryController.php | 25 | ||||
-rw-r--r-- | src/Controllers/ServerImplementationController.php | 19 | ||||
-rw-r--r-- | src/DB.php | 22 | ||||
-rw-r--r-- | src/Router.php | 52 | ||||
-rw-r--r-- | src/Singleton.php | 17 | ||||
-rw-r--r-- | src/routes.php | 26 |
16 files changed, 776 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3d5163b --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/vendor/ +.env diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..d27bea8 --- /dev/null +++ b/composer.json @@ -0,0 +1,20 @@ +{ + "name": "matrix-php/matrix-php", + "type": "project", + "autoload": { + "psr-4": { + "App\\": "src/" + } + }, + "authors": [ + { + "name": "Daniel Weipert", + "email": "git@mail.dweipert.de" + } + ], + "require": { + "symfony/routing": "^7.3", + "symfony/http-foundation": "^7.3", + "symfony/dotenv": "^7.3" + } +} diff --git a/composer.lock b/composer.lock new file mode 100644 index 0000000..192fd6b --- /dev/null +++ b/composer.lock @@ -0,0 +1,489 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", + "This file is @generated automatically" + ], + "content-hash": "7733486e3e144bf785bda75acdd2ca46", + "packages": [ + { + "name": "symfony/deprecation-contracts", + "version": "v3.6.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/deprecation-contracts.git", + "reference": "63afe740e99a13ba87ec199bb07bbdee937a5b62" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/63afe740e99a13ba87ec199bb07bbdee937a5b62", + "reference": "63afe740e99a13ba87ec199bb07bbdee937a5b62", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, + "branch-alias": { + "dev-main": "3.6-dev" + } + }, + "autoload": { + "files": [ + "function.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "A generic function and convention to trigger deprecation notices", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/deprecation-contracts/tree/v3.6.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-25T14:21:43+00:00" + }, + { + "name": "symfony/dotenv", + "version": "v7.3.2", + "source": { + "type": "git", + "url": "https://github.com/symfony/dotenv.git", + "reference": "2192790a11f9e22cbcf9dc705a3ff22a5503923a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/dotenv/zipball/2192790a11f9e22cbcf9dc705a3ff22a5503923a", + "reference": "2192790a11f9e22cbcf9dc705a3ff22a5503923a", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "conflict": { + "symfony/console": "<6.4", + "symfony/process": "<6.4" + }, + "require-dev": { + "symfony/console": "^6.4|^7.0", + "symfony/process": "^6.4|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Dotenv\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Registers environment variables from a .env file", + "homepage": "https://symfony.com", + "keywords": [ + "dotenv", + "env", + "environment" + ], + "support": { + "source": "https://github.com/symfony/dotenv/tree/v7.3.2" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-07-10T08:29:33+00:00" + }, + { + "name": "symfony/http-foundation", + "version": "v7.3.2", + "source": { + "type": "git", + "url": "https://github.com/symfony/http-foundation.git", + "reference": "6877c122b3a6cc3695849622720054f6e6fa5fa6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/6877c122b3a6cc3695849622720054f6e6fa5fa6", + "reference": "6877c122b3a6cc3695849622720054f6e6fa5fa6", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3.0", + "symfony/polyfill-mbstring": "~1.1", + "symfony/polyfill-php83": "^1.27" + }, + "conflict": { + "doctrine/dbal": "<3.6", + "symfony/cache": "<6.4.12|>=7.0,<7.1.5" + }, + "require-dev": { + "doctrine/dbal": "^3.6|^4", + "predis/predis": "^1.1|^2.0", + "symfony/cache": "^6.4.12|^7.1.5", + "symfony/clock": "^6.4|^7.0", + "symfony/dependency-injection": "^6.4|^7.0", + "symfony/expression-language": "^6.4|^7.0", + "symfony/http-kernel": "^6.4|^7.0", + "symfony/mime": "^6.4|^7.0", + "symfony/rate-limiter": "^6.4|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\HttpFoundation\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Defines an object-oriented layer for the HTTP specification", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/http-foundation/tree/v7.3.2" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-07-10T08:47:49+00:00" + }, + { + "name": "symfony/polyfill-mbstring", + "version": "v1.32.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/6d857f4d76bd4b343eac26d6b539585d2bc56493", + "reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493", + "shasum": "" + }, + "require": { + "ext-iconv": "*", + "php": ">=7.2" + }, + "provide": { + "ext-mbstring": "*" + }, + "suggest": { + "ext-mbstring": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Mbstring\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for the Mbstring extension", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "mbstring", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.32.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-12-23T08:48:59+00:00" + }, + { + "name": "symfony/polyfill-php83", + "version": "v1.32.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php83.git", + "reference": "2fb86d65e2d424369ad2905e83b236a8805ba491" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php83/zipball/2fb86d65e2d424369ad2905e83b236a8805ba491", + "reference": "2fb86d65e2d424369ad2905e83b236a8805ba491", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php83\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 8.3+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php83/tree/v1.32.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-09T11:45:10+00:00" + }, + { + "name": "symfony/routing", + "version": "v7.3.2", + "source": { + "type": "git", + "url": "https://github.com/symfony/routing.git", + "reference": "7614b8ca5fa89b9cd233e21b627bfc5774f586e4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/routing/zipball/7614b8ca5fa89b9cd233e21b627bfc5774f586e4", + "reference": "7614b8ca5fa89b9cd233e21b627bfc5774f586e4", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3" + }, + "conflict": { + "symfony/config": "<6.4", + "symfony/dependency-injection": "<6.4", + "symfony/yaml": "<6.4" + }, + "require-dev": { + "psr/log": "^1|^2|^3", + "symfony/config": "^6.4|^7.0", + "symfony/dependency-injection": "^6.4|^7.0", + "symfony/expression-language": "^6.4|^7.0", + "symfony/http-foundation": "^6.4|^7.0", + "symfony/yaml": "^6.4|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Routing\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Maps an HTTP request to a set of configuration variables", + "homepage": "https://symfony.com", + "keywords": [ + "router", + "routing", + "uri", + "url" + ], + "support": { + "source": "https://github.com/symfony/routing/tree/v7.3.2" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-07-15T11:36:08+00:00" + } + ], + "packages-dev": [], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": {}, + "prefer-stable": false, + "prefer-lowest": false, + "platform": {}, + "platform-dev": {}, + "plugin-api-version": "2.6.0" +} diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..2fd644f --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,27 @@ +services: + db: + image: postgres + ports: + - "5432:5432" + environment: + - "POSTGRES_DB=${DB_NAME}" + - "POSTGRES_USER=${DB_USER}" + - "POSTGRES_PASSWORD=${DB_PASSWORD}" + volumes: + - "db:/var/lib/postgresql/data" + + php: + build: "docker/php" + volumes: + - "./:/var/www/html" + + web: + build: "docker/nginx" + ports: + - "8080:80" + volumes: + - "./:/var/www/html" + + +volumes: + db: diff --git a/docker/nginx/Dockerfile b/docker/nginx/Dockerfile new file mode 100644 index 0000000..cad37d9 --- /dev/null +++ b/docker/nginx/Dockerfile @@ -0,0 +1,3 @@ +FROM nginx:alpine + +ADD default.conf.template /etc/nginx/templates/ diff --git a/docker/nginx/default.conf.template b/docker/nginx/default.conf.template new file mode 100644 index 0000000..c82c019 --- /dev/null +++ b/docker/nginx/default.conf.template @@ -0,0 +1,19 @@ +server { + listen 0.0.0.0:80; + + root /var/www/html/public; + index index.php; + + location / { + try_files $uri $uri/ /index.php?$args; + } + + location ~ \.php$ { + include fastcgi_params; + + fastcgi_pass php:9000; + + fastcgi_index index.php; + fastcgi_param SCRIPT_FILENAME $document_root/$fastcgi_script_name; + } +} diff --git a/docker/php/Dockerfile b/docker/php/Dockerfile new file mode 100644 index 0000000..204d790 --- /dev/null +++ b/docker/php/Dockerfile @@ -0,0 +1,7 @@ +FROM php:fpm-alpine + +RUN : \ + && apk add libpq-dev icu-dev icu-data-full \ + && docker-php-ext-install pdo_pgsql intl + +WORKDIR /var/www/html diff --git a/public/index.php b/public/index.php new file mode 100644 index 0000000..f3acbb9 --- /dev/null +++ b/public/index.php @@ -0,0 +1,7 @@ +<?php + +use App\App; + +require_once dirname(__DIR__) . "/vendor/autoload.php"; + +(new App())->run(); diff --git a/src/App.php b/src/App.php new file mode 100644 index 0000000..4c67f76 --- /dev/null +++ b/src/App.php @@ -0,0 +1,19 @@ +<?php + +namespace App; + +use Symfony\Component\Dotenv\Dotenv; + +class App +{ + public function __construct() + { + $dotenv = new Dotenv(); + $dotenv->load(dirname(__DIR__) . "/.env"); + } + + public function run(): void + { + Router::getInstance()->run()->send(); + } +} diff --git a/src/Controllers/KeyController.php b/src/Controllers/KeyController.php new file mode 100644 index 0000000..bde1642 --- /dev/null +++ b/src/Controllers/KeyController.php @@ -0,0 +1,22 @@ +<?php + +namespace App\Controllers; + +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpFoundation\JsonResponse; + +class KeyController +{ + public function server(): Response + { + return new JsonResponse([ + "server" => [ + "name" => "Matrix PHP", + "version" => "0.1.0", + ], + ]); + } + + public function query(string $serverName): Response + {} +} diff --git a/src/Controllers/ServerDiscoveryController.php b/src/Controllers/ServerDiscoveryController.php new file mode 100644 index 0000000..e5fdd31 --- /dev/null +++ b/src/Controllers/ServerDiscoveryController.php @@ -0,0 +1,25 @@ +<?php + +namespace App\Controllers; + +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpFoundation\JsonResponse; + +class ServerDiscoveryController +{ + public function server(): Response + { + return new JsonResponse([ + "m.server" => "$_ENV[DOMAIN]:443", + ]); + } + + public function client(): Response + { + return new JsonResponse([ + "m.homeserver" => [ + "base_url" => "https://$_ENV[DOMAIN]", + ], + ]); + } +} diff --git a/src/Controllers/ServerImplementationController.php b/src/Controllers/ServerImplementationController.php new file mode 100644 index 0000000..4531ce5 --- /dev/null +++ b/src/Controllers/ServerImplementationController.php @@ -0,0 +1,19 @@ +<?php + +namespace App\Controllers; + +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpFoundation\JsonResponse; + +class ServerImplementationController +{ + public function version(): Response + { + return new JsonResponse([ + "server" => [ + "name" => "Matrix PHP", + "version" => "0.1.0", + ], + ]); + } +} diff --git a/src/DB.php b/src/DB.php new file mode 100644 index 0000000..b536079 --- /dev/null +++ b/src/DB.php @@ -0,0 +1,22 @@ +<?php + +namespace App; + +class DB +{ + use Singleton; + + private \PDO $connection; + + public function __construct() + { + $driver = $_ENV['DB_DRIVER'] ?? 'pgsql'; + $host = $_ENV['DB_HOST'] ?? 'localhost'; + $port = $_ENV['DB_PORT'] ?? 5432; + $dbname = $_ENV['DB_NAME']; + $user = $_ENV['DB_USER']; + $password = $_ENV['DB_PASSWORD']; + + $this->connection = new \PDO("$driver:host=$host;port=$port;dbname=$dbname", $user, $password); + } +} diff --git a/src/Router.php b/src/Router.php new file mode 100644 index 0000000..deb852e --- /dev/null +++ b/src/Router.php @@ -0,0 +1,52 @@ +<?php + +namespace App; + +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\Routing\Loader\Configurator\RouteConfigurator; +use Symfony\Component\Routing\Matcher\UrlMatcher; +use Symfony\Component\Routing\RequestContext; +use Symfony\Component\Routing\RouteCollection; + +class Router +{ + use Singleton; + + private RouteCollection $routes; + private RouteConfigurator $configurator; + + public function __construct() + { + $this->routes = new RouteCollection(); + $this->configurator = new RouteConfigurator($this->routes, $this->routes); + + $this->addRoutes(); + } + + public function run(): Response + { + $request = Request::createFromGlobals(); + + $context = new RequestContext(); + $context->fromRequest($request); + + try { + $matcher = new UrlMatcher($this->routes, $context); + $match = $matcher->matchRequest($request); + + $class = $match["_controller"][0]; + $method = $match["_controller"][1]; + + return (new $class)->$method(); + } catch (\Exception $exception) { + return new Response("500: " . $exception->getMessage(), 500); + } + } + + private function addRoutes(): void + { + $routes = include_once(__DIR__ . "/routes.php"); + $routes($this->configurator); + } +} diff --git a/src/Singleton.php b/src/Singleton.php new file mode 100644 index 0000000..b4d4598 --- /dev/null +++ b/src/Singleton.php @@ -0,0 +1,17 @@ +<?php + +namespace App; + +trait Singleton +{ + private static self $instance; + + public static function getInstance(): self + { + if (! isset(self::$instance)) { + self::$instance = new self(); + } + + return self::$instance; + } +} diff --git a/src/routes.php b/src/routes.php new file mode 100644 index 0000000..ff16ecc --- /dev/null +++ b/src/routes.php @@ -0,0 +1,26 @@ +<?php + +namespace App; + +use App\Controllers\ServerDiscoveryController; +use App\Controllers\ServerImplementationController; +use Symfony\Component\Routing\Loader\Configurator\RouteConfigurator; + +return function (RouteConfigurator $routes): void +{ + $routes + ->add("well_known_matrix_server", "/.well-known/matrix/server") + ->controller([ServerDiscoveryController::class, "server"]); + + $routes + ->add("well_known_matrix_client", "/.well-known/matrix/client") + ->controller([ServerDiscoveryController::class, "client"]); + + $routes + ->add("matrix_federation_version", "/_matrix/federation/v1/version") + ->controller([ServerImplementationController::class, "version"]); + + # /_matrix/key/v2/server + # /_matrix/key/v2/query + # /_matrix/key/v2/query/{serverName} +}; |