aboutsummaryrefslogtreecommitdiffhomepage
path: root/WpfTest
diff options
context:
space:
mode:
authorwinter2024-12-09 21:31:54 +0000
committerwinter2024-12-09 21:31:54 +0000
commit4c7bde1400820f36caf8b2a5374007384c3018f3 (patch)
tree83ed26ff368117b8e392f7bc28736482077fc2f4 /WpfTest
parent50edbd3907c26a2d4d616421de9bb51a4d083c1b (diff)
rename to Digitigrade / PawPub
:3
Diffstat (limited to 'WpfTest')
-rw-r--r--WpfTest/Db.php42
-rw-r--r--WpfTest/Db/Migrator.php52
-rw-r--r--WpfTest/GlobalConfig.php44
-rw-r--r--WpfTest/HttpResponseStatus.php7
-rw-r--r--WpfTest/HttpResponseStatus/BadRequest.php18
-rw-r--r--WpfTest/HttpResponseStatus/InternalServerError.php23
-rw-r--r--WpfTest/HttpResponseStatus/NoHandlerFound.php18
-rw-r--r--WpfTest/HttpResponseStatus/NotFound.php18
-rw-r--r--WpfTest/Model.php219
-rw-r--r--WpfTest/Model/Actor.php90
-rw-r--r--WpfTest/Model/ActorEndpoints.php27
-rw-r--r--WpfTest/Model/FetchableModel.php78
-rw-r--r--WpfTest/Model/Note.php95
-rw-r--r--WpfTest/Model/NoteAttachment.php23
-rw-r--r--WpfTest/Model/NotePrivacy.php39
-rw-r--r--WpfTest/Model/NotePrivacyInteractors.php9
-rw-r--r--WpfTest/Model/NotePrivacyScope.php9
-rw-r--r--WpfTest/RemoteFetchable.php21
-rw-r--r--WpfTest/Router.php98
-rw-r--r--WpfTest/Singleton.php18
20 files changed, 0 insertions, 948 deletions
diff --git a/WpfTest/Db.php b/WpfTest/Db.php
deleted file mode 100644
index 799332f..0000000
--- a/WpfTest/Db.php
+++ /dev/null
@@ -1,42 +0,0 @@
-<?php
-namespace WpfTest;
-
-use WpfTest\Model\Actor;
-
-class Db extends Singleton {
- private const INIT_SCHEMA_PATH = __DIR__ . '/../schema.sql';
-
- private \PDO $conn;
-
- protected function __construct() {
- $conf = GlobalConfig::getInstance();
- $this->conn = new \PDO($conf->getDbConnection(), $conf->getDbUser(), $conf->getDbPassword(), [
- \PDO::ATTR_PERSISTENT => true,
- ]);
- }
-
- public function runInitialSchema() {
- $this->conn->exec(file_get_contents(self::INIT_SCHEMA_PATH));
- }
-
- public function getDbVersion(): int {
- $result = $this->conn->query('SELECT db_version FROM db_version');
- return $result->fetch()['db_version'];
- }
-
- /**
- * Changes the database schema version
- *
- * CAREFUL! this shouldn't be used unless you absolutely know why
- * @param int $newVersion the new version number
- * @return void
- */
- public function setDbVersion(int $newVersion) {
- $stmt = $this->conn->prepare('UPDATE db_version SET db_version = ?');
- $stmt->execute([$newVersion]);
- }
-
- public function getPdo(): \PDO {
- return $this->conn;
- }
-} \ No newline at end of file
diff --git a/WpfTest/Db/Migrator.php b/WpfTest/Db/Migrator.php
deleted file mode 100644
index ea7f92a..0000000
--- a/WpfTest/Db/Migrator.php
+++ /dev/null
@@ -1,52 +0,0 @@
-<?php
-namespace WpfTest\Db;
-
-use WpfTest\Db;
-use WpfTest\Singleton;
-
-class Migrator extends Singleton {
- private array $migrations = [];
-
- public function register(int $newVersion, callable $fn) {
- $this->migrations[$newVersion] = $fn;
- }
-
- /**
- * Runs all available migrations
- * @return void
- */
- public function migrate() {
- $db = Db::getInstance();
- $currentVersion = null;
- try {
- $currentVersion = $db->getDbVersion();
- error_log('starting on version ' . $currentVersion);
- } catch (\PDOException $e) {
- if ($e->getCode() == '42P01') { // undefined table
- error_log("initialising database because db version table doesn't exist");
- $db->runInitialSchema();
- $currentVersion = $db->getDbVersion();
- error_log('starting on version ' . $currentVersion);
- } else
- throw $e;
- }
-
- // find migrations that haven't been run yet
- $available = array_filter($this->migrations, function (int $key) use ($currentVersion) {
- return $key > $currentVersion;
- }, ARRAY_FILTER_USE_KEY);
-
- try {
- // run them in order (at least they should be in order?)
- foreach (array_keys($available) as $version) {
- $fn = $available[$version];
- $filename = basename((new \ReflectionFunction($fn))->getFileName());
- error_log("running migration $filename");
- $available[$version]($db->getPdo());
- $db->setDbVersion($version);
- }
- } finally {
- error_log('now on version ' . $db->getDbVersion());
- }
- }
-} \ No newline at end of file
diff --git a/WpfTest/GlobalConfig.php b/WpfTest/GlobalConfig.php
deleted file mode 100644
index 4601257..0000000
--- a/WpfTest/GlobalConfig.php
+++ /dev/null
@@ -1,44 +0,0 @@
-<?php
-namespace WpfTest;
-
-class GlobalConfig extends Singleton {
- private const CONFIG_INI_PATH = __DIR__ . '/../config.ini';
- private array $confData;
-
- protected function __construct() {
- $this->confData = parse_ini_file(self::CONFIG_INI_PATH, process_sections: true);
- }
-
- public function getCanonicalHost(): string {
- $host = $this->confData['instance']['hostname'];
- if (isset($this->confData['instance']['port']) && $this->confData['instance']['port'] != ($this->isHttps() ? 443 : 80))
- $host .= ':' . $this->confData['instance']['port'];
- return $host;
- }
-
- public function isHttps(): bool {
- return $this->confData['debug']['https'] ?? true;
- }
-
- public function getBasePath(): string {
- return ($this->isHttps() ? 'https://' : 'http://') . $this->getCanonicalHost();
- }
-
- public function shouldReportTraces(): bool {
- return $this->confData['debug']['send_tracebacks'] ?? false;
- }
-
- public function getDbConnection(): string {
- return 'pgsql:host=' . $this->confData['database']['host'] .
- ';port=' . $this->confData['database']['port'] .
- ';dbname=' . $this->confData['database']['database'];
- }
-
- public function getDbUser(): string {
- return $this->confData['database']['username'];
- }
-
- public function getDbPassword(): string {
- return $this->confData['database']['password'];
- }
-} \ No newline at end of file
diff --git a/WpfTest/HttpResponseStatus.php b/WpfTest/HttpResponseStatus.php
deleted file mode 100644
index c81e000..0000000
--- a/WpfTest/HttpResponseStatus.php
+++ /dev/null
@@ -1,7 +0,0 @@
-<?php
-namespace WpfTest;
-
-interface HttpResponseStatus extends \Throwable {
- public function httpCode(): int;
- public function httpReason(): string;
-} \ No newline at end of file
diff --git a/WpfTest/HttpResponseStatus/BadRequest.php b/WpfTest/HttpResponseStatus/BadRequest.php
deleted file mode 100644
index 74fab3c..0000000
--- a/WpfTest/HttpResponseStatus/BadRequest.php
+++ /dev/null
@@ -1,18 +0,0 @@
-<?php
-namespace WpfTest\HttpResponseStatus;
-
-use WpfTest\HttpResponseStatus;
-
-class BadRequest extends \Exception implements HttpResponseStatus {
- public function __construct(?string $reason) {
- $this->message = $reason ?? 'Request does not conform to expectations';
- }
-
- public function httpCode(): int {
- return 400;
- }
-
- public function httpReason(): string {
- return "Bad Request";
- }
-} \ No newline at end of file
diff --git a/WpfTest/HttpResponseStatus/InternalServerError.php b/WpfTest/HttpResponseStatus/InternalServerError.php
deleted file mode 100644
index 11b67f3..0000000
--- a/WpfTest/HttpResponseStatus/InternalServerError.php
+++ /dev/null
@@ -1,23 +0,0 @@
-<?php
-namespace WpfTest\HttpResponseStatus;
-
-use WpfTest\GlobalConfig;
-use WpfTest\HttpResponseStatus;
-
-class InternalServerError extends \Exception implements HttpResponseStatus {
- public function __construct(\Throwable $reason, ?string $context = null) {
- $this->message = $reason::class . ': ' . $reason->getMessage();
- if (isset($context)) {
- $this->message .= " [$context]";
- }
- if (GlobalConfig::getInstance()->shouldReportTraces()) {
- $this->message .= "\n" . $reason->getTraceAsString();
- }
- }
- public function httpCode(): int {
- return 500;
- }
- public function httpReason(): string {
- return "Internal Server Error";
- }
-} \ No newline at end of file
diff --git a/WpfTest/HttpResponseStatus/NoHandlerFound.php b/WpfTest/HttpResponseStatus/NoHandlerFound.php
deleted file mode 100644
index 9ceeb0b..0000000
--- a/WpfTest/HttpResponseStatus/NoHandlerFound.php
+++ /dev/null
@@ -1,18 +0,0 @@
-<?php
-namespace WpfTest\HttpResponseStatus;
-
-use WpfTest\HttpResponseStatus;
-
-class NoHandlerFound extends \Exception implements HttpResponseStatus {
- public function __construct(string $path) {
- $this->message = "No handler found for path $path";
- }
-
- public function httpCode(): int {
- return 404;
- }
-
- public function httpReason(): string {
- return "Not Found";
- }
-} \ No newline at end of file
diff --git a/WpfTest/HttpResponseStatus/NotFound.php b/WpfTest/HttpResponseStatus/NotFound.php
deleted file mode 100644
index b36d0a3..0000000
--- a/WpfTest/HttpResponseStatus/NotFound.php
+++ /dev/null
@@ -1,18 +0,0 @@
-<?php
-namespace WpfTest\HttpResponseStatus;
-
-use WpfTest\HttpResponseStatus;
-
-class NotFound extends \Exception implements HttpResponseStatus {
- public function __construct(?string $reason) {
- $this->message = $reason ?? 'Request was handled but no appropriate resource found';
- }
-
- public function httpCode(): int {
- return 404;
- }
-
- public function httpReason(): string {
- return "Not Found";
- }
-} \ No newline at end of file
diff --git a/WpfTest/Model.php b/WpfTest/Model.php
deleted file mode 100644
index 53b2d83..0000000
--- a/WpfTest/Model.php
+++ /dev/null
@@ -1,219 +0,0 @@
-<?php
-namespace WpfTest;
-
-use WpfTest\Model\FetchableModel;
-
-abstract class Model {
- private bool $saved = false;
-
- protected function setOwnerId(int $id) {
- }
-
- /**
- * @return ?string a condition for a WHERE clause if this object already
- * exists in the database, or null otherwise
- */
- protected function getUpdateWhereClause(\PDO $db): ?string {
- if (isset($this->id) && static::find($this->{'id'}) != null) {
- return 'id = ' . $this->{'id'};
- }
- return null;
- }
-
- /**
- * Converts a field or type name into the version used in the database
- */
- private static function mangleName(string $name): string {
- $chars = str_split($name);
- $chars[0] = strtolower($chars[0]);
- for ($i = 0; $i < count($chars); $i++) {
- $c = $chars[$i];
- if (ctype_upper($c)) {
- array_splice($chars, $i, 1, ['_', strtolower($c)]);
- }
- }
- return implode($chars);
- }
-
- /**
- * Converts a database column name into the corresponding field name
- */
- private static function unmangleName(string $name): string {
- $chars = str_split($name);
- for ($i = 0; $i < count($chars); $i++) {
- if ($chars[$i] == '_') {
- array_splice($chars, $i, 2, [strtoupper($chars[$i + 1])]);
- }
- }
- return implode($chars);
- }
-
- public function toDbRow(): array {
- $refl = new \ReflectionObject($this);
- $props = $refl->getProperties();
- $result = [];
- foreach ($props as $p) {
- $pName = $p->getName();
- if (!isset($this->{$pName}))
- continue;
- // we need to determine the plain value to insert ...
- $value = $this->{$pName};
- $discarded = false;
- if ($value instanceof \DateTimeInterface) {
- // datetime objects should be turned into iso 8601 timestamps
- $value = $value->format('c');
- } elseif ($value instanceof Model) {
- // models should generally be turned into their id if there is one or removed if not
- if (property_exists($value, 'id') && isset($value->id))
- $value = $value->{'id'}; // doing it like this to shut up the warning (lol)
- else
- $discarded = true;
- } elseif (is_array($value)) {
- // arrays ought to be handled separately when saving (different db table)
- $discarded = true;
- }
- if (!$discarded)
- $result[self::mangleName($pName)] = $value;
- }
- return $result;
- }
-
- public static function fromDbRow(array|false $columns): ?static {
- if ($columns === false)
- return null;
- // create the model
- $obj = new static();
- $refl = new \ReflectionObject($obj);
- // for each field of the db row ...
- foreach (array_keys($columns) as $name) {
- $value = $columns[$name];
- $name = self::unmangleName($name); // guess the appropriate field in the model
- try {
- // try to assign it directly (works for simple values)
- $obj->{$name} = $value;
- } catch (\TypeError $e) {
- // if it's not the right type we have to try to wrangle it into the right one
- // there's a few strategies for this
- $type = $refl->getProperty($name)->getType();
- if ($type == null || !is_a($type, \ReflectionNamedType::class))
- throw $e;
- $typeClass = $type->getName();
- if (enum_exists($typeClass)) {
- // if it's a backed enum we can find the enum case with the same value
- $obj->{$name} = $typeClass::from($value);
- } elseif (is_subclass_of($typeClass, Model::class) && is_int($value)) {
- // if it's another model we can try to look it up by id automatically
- $obj->{$name} = $typeClass::find($value);
- } else {
- // otherwise try to instantiate the correct class and pass it the simple value
- $obj->{$name} = new $typeClass($value);
- }
- }
- }
- // set up any referenced fields if needed
- $obj->hydrate();
- $obj->saved = true;
- return $obj;
- }
-
- /**
- * Initialises fields of the model that contain referenced / foreign keyed objects
- * which couldn't be initialised automatically
- * @return void
- */
- protected function hydrate() {
- }
-
- public static function findWhere(string $whereClause, array $parameters): ?static {
- $classNameParts = explode('\\', static::class);
- $className = $classNameParts[count($classNameParts) - 1];
- $tableName = self::mangleName($className);
-
- $stmt = Db::getInstance()->getPdo()->prepare("SELECT * FROM $tableName WHERE $whereClause LIMIT 1");
- $stmt->execute($parameters);
- return static::fromDbRow($stmt->fetch(\PDO::FETCH_ASSOC));
- }
-
- public static function findAllWhere(string $whereClause, array $parameters): array {
- $classNameParts = explode('\\', static::class);
- $className = $classNameParts[count($classNameParts) - 1];
- $tableName = self::mangleName($className);
-
- $stmt = Db::getInstance()->getPdo()->prepare("SELECT * FROM $tableName WHERE $whereClause");
- $stmt->execute($parameters);
- return array_map(function ($row) {
- return static::fromDbRow($row);
- }, $stmt->fetchAll(\PDO::FETCH_ASSOC));
- }
-
- public static function find(int $id): ?static {
- return static::findWhere('id = ?', [$id]);
- }
-
- /**
- * Inserts or updates the model's corresponding database record.
- * Also sets the model's id field if it has one.
- */
- public function save() {
- // figure out all the parameters and such for the query
- $paramData = $this->toDbRow();
- $columnNames = array_keys($paramData);
- $parameterNames = array_map(function (string $column) {
- return ":$column";
- }, $columnNames);
- $columns = implode(', ', $columnNames);
- $parameters = implode(', ', $parameterNames);
-
- $classNameParts = explode('\\', static::class);
- $className = $classNameParts[count($classNameParts) - 1];
- $tableName = self::mangleName($className);
-
- $db = Db::getInstance()->getPdo();
-
- $whereClause = $this->getUpdateWhereClause($db);
- $shouldUpdate = $whereClause != null;
- $stmt = $db->prepare(
- $shouldUpdate
- ? "UPDATE $tableName SET ($columns) = ($parameters) WHERE $whereClause"
- : "INSERT INTO $tableName ($columns) VALUES ($parameters)"
- );
-
- // pdo is really stupid and casts `false`/0 to an empty string, which breaks the query
- // so i have to cast `false`s and `0`s manually. hooray
- // i also have to convert backed enums to their value because they dont implement Stringable
- $paramData = array_map(function ($item) {
- if ($item === false || $item === 0)
- return '0';
- if (is_a($item, \BackedEnum::class))
- return $item->value;
- return $item;
- }, $paramData);
- // actually save it !!!! yay
- $stmt->execute($paramData);
-
- if (property_exists($this, 'id')) {
- $this->id = $shouldUpdate
- ? static::findWhere($whereClause, [])->id
- : (int) $db->lastInsertId();
- }
-
- // recursively save inner models
- // but not fetchable ones because they should really be saved separately
- $reflProps = (new \ReflectionObject($this))->getProperties();
- foreach ($reflProps as $reflProp) {
- assert($reflProp instanceof \ReflectionProperty);
- $reflType = $reflProp->getType();
- $propName = $reflProp->getName();
- if (!($reflType instanceof \ReflectionNamedType))
- continue;
- $type = $reflType->getName();
- if (is_subclass_of($type, Model::class) && !is_subclass_of($type, FetchableModel::class)) {
- if (property_exists($this, 'id'))
- $this->{$propName}->setOwnerId($this->id);
- $this->{$propName}->save();
- }
- }
-
- $this->saved = true;
- }
-} \ No newline at end of file
diff --git a/WpfTest/Model/Actor.php b/WpfTest/Model/Actor.php
deleted file mode 100644
index a745a96..0000000
--- a/WpfTest/Model/Actor.php
+++ /dev/null
@@ -1,90 +0,0 @@
-<?php
-namespace WpfTest\Model;
-
-use WpfTest\RemoteFetchable;
-
-class Actor extends FetchableModel implements \JsonSerializable {
- public const REL_URI = "https://pawpub.entities.org.uk/rel/actor.html";
-
- public ?int $id;
- public string $uri;
- public bool $isLocal = false;
- public \DateTimeImmutable $created;
- public ?\DateTimeImmutable $modified;
- public ?string $homepage;
- public string $handle;
- public string $displayName;
- public ?string $avatar;
- public ?string $bio;
- public ?string $pronouns;
- public bool $automated = false;
- public bool $requestToFollow = false;
- public ActorEndpoints $endpoints;
- public array $extensions = [];
-
- protected function getUpdateWhereClause(\PDO $db): ?string {
- if (self::findWhere('uri = ?', [$this->uri]) != null)
- return 'uri = ' . $db->quote($this->uri);
- if (self::findWhere('id = ?', [$this->id]) != null)
- return "id = $this->id";
- return null;
- }
-
- public static function findLocalByHandle(string $handle): ?self {
- return self::findWhere('is_local = true AND handle = ?', [$handle]);
- }
-
- public static function findByWebfinger(string $acct, bool $autoSave = true, bool $forceRefetch = false): ?self {
- // normalise uri
- if (!str_starts_with($acct, 'acct:')) {
- if (str_starts_with($acct, '@')) {
- $acct = substr($acct, 1);
- }
- $acct = "acct:$acct";
- }
- // fetch it
- $domain = explode('@', $acct, 2)[1];
- $data = @file_get_contents("https://$domain/.well-known/webfinger?resource=$acct",
- context: stream_context_create([
- 'http' => ['header' => 'Accept: application/jrd+json, application/json']
- ])
- );
- if ($data === false)
- return null;
- $data = json_decode($data);
- if ($data === null)
- return null;
- // find the right link
- if (!isset($data->links))
- return null;
- $matching = array_filter($data->links, function (\stdClass $linkObj) {
- return $linkObj->rel == self::REL_URI;
- });
- if (count($matching) == 0)
- return null;
- return self::findByUri($matching[0]->href, $autoSave, $forceRefetch);
- }
-
- public function jsonSerialize(): array {
- return [
- 'type' => 'actor',
- 'self' => path_to_uri("/user/$this->handle"),
- 'created' => $this->created?->format('c'),
- 'modified' => $this->modified?->format('c'),
- 'homepage' => path_to_uri("/$this->handle"),
- 'handle' => $this->handle,
- 'displayName' => $this->displayName,
- 'bio' => $this->bio,
- 'pronouns' => $this->pronouns,
- 'automated' => $this->automated,
- 'endpoints' => [
- 'basicFeed' => path_to_uri("/user/$this->handle/basicFeed"),
- 'fullFeed' => path_to_uri("/user/$this->handle/fullFeed")
- ]
- ];
- }
-
- public function hydrate() {
- $this->endpoints = ActorEndpoints::findWhere('actor_id = ?', [$this->id]);
- }
-} \ No newline at end of file
diff --git a/WpfTest/Model/ActorEndpoints.php b/WpfTest/Model/ActorEndpoints.php
deleted file mode 100644
index f6120d3..0000000
--- a/WpfTest/Model/ActorEndpoints.php
+++ /dev/null
@@ -1,27 +0,0 @@
-<?php
-namespace WpfTest\Model;
-
-use WpfTest\Model;
-
-class ActorEndpoints extends Model {
- public ?int $actorId;
- public string $basicFeed;
- public ?string $fullFeed;
- public ?string $follow;
- public ?string $unfollow;
- public ?string $block;
- public ?string $unblock;
- public ?string $subscribe;
- public ?string $unsubscribe;
-
- protected function setOwnerId(int $id) {
- $this->actorId = $id;
- }
-
- protected function getUpdateWhereClause($db): ?string {
- if (self::findWhere('actor_id = ?', [$this->actorId]) != null) {
- return "actor_id = $this->actorId";
- }
- return null;
- }
-} \ No newline at end of file
diff --git a/WpfTest/Model/FetchableModel.php b/WpfTest/Model/FetchableModel.php
deleted file mode 100644
index 5dce010..0000000
--- a/WpfTest/Model/FetchableModel.php
+++ /dev/null
@@ -1,78 +0,0 @@
-<?php
-namespace WpfTest\Model;
-
-use WpfTest\Model;
-use WpfTest\RemoteFetchable;
-
-abstract class FetchableModel extends Model implements RemoteFetchable {
- private static \JsonMapper $mapper;
-
- public static function __initStatic() {
- self::$mapper = new \JsonMapper();
- // this is unfortunately necessary to parse DateTime(Immutable)s properly
- self::$mapper->bStrictObjectTypeChecking = false;
- }
-
- private static function fetchFromRemote(string $uri, bool $autoSave): ?static {
- // fetch the object
- $data = @file_get_contents($uri, context: stream_context_create(['http' => [
- 'header' => 'Accept: application/json'
- ]]));
- if ($data === false)
- return null;
- $data = json_decode($data);
- if ($data === null)
- return null;
- // default everything nullable to null
- $obj = new static();
- $reflProps = (new \ReflectionClass(static::class))->getProperties();
- foreach ($reflProps as $reflProp) {
- assert($reflProp instanceof \ReflectionProperty); // for intellisense lmao
- if ($reflProp->getType()->allowsNull()) {
- $obj->{$reflProp->getName()} = null;
- }
- }
- // rename 'self' to 'uri' because of reasons
- if (property_exists($data, 'self')) {
- $data->uri = $data->self;
- unset($data->self);
- }
- // map all present fields
- self::$mapper->map($data, $obj);
- // (hopefully) recursively fetch any other fetchable models contained in this one
- foreach ($reflProps as $reflProp) {
- assert($reflProp instanceof \ReflectionProperty);
- $reflType = $reflProp->getType();
- $propName = $reflProp->getName();
- if (!($reflType instanceof \ReflectionNamedType))
- continue;
- $type = $reflType->getName();
- if (is_subclass_of($type, FetchableModel::class) && is_string($data->{$propName})) {
- // try to avoid recursion
- // TODO: make a better effort at avoiding recursion (e.g. when it's more than one step nested)
- if ($data->{$propName} == $uri) {
- $obj->{$propName} = $obj;
- } else {
- $obj->{$propName} = $type::findByUri($data->{$propName}, $autoSave);
- }
- }
- }
-
- if ($autoSave) {
- $obj->save();
- }
-
- return $obj;
- }
-
- public static function findByUri(string $uri, bool $autoSave = true, bool $forceRefetch = false): ?static {
- if (!$forceRefetch) {
- $obj = static::findWhere('uri = ?', [$uri]);
- if ($obj != null)
- return $obj;
- }
- return static::fetchFromRemote($uri, $autoSave);
- }
-}
-
-FetchableModel::__initStatic(); \ No newline at end of file
diff --git a/WpfTest/Model/Note.php b/WpfTest/Model/Note.php
deleted file mode 100644
index af2d2aa..0000000
--- a/WpfTest/Model/Note.php
+++ /dev/null
@@ -1,95 +0,0 @@
-<?php
-namespace WpfTest\Model;
-
-use WpfTest\Db;
-
-class Note extends FetchableModel implements \JsonSerializable {
- public ?int $id;
- public string $uri;
- public \DateTimeImmutable $created;
- public ?\DateTimeImmutable $modified;
- public Actor $author;
- public ?string $summary;
- public string $plainContent;
- /**
- * @var array<string, string>
- */
- public array $formattedContent = [];
- public string $language;
- /**
- * @var Actor[]
- */
- public array $mentions = [];
- public ?Note $inReplyTo;
- public ?Note $threadApex;
- public NotePrivacy $privacy;
- /**
- * @var NoteAttachment[]
- */
- public array $attachments = [];
- public array $extensions = [];
-
- protected function getUpdateWhereClause(\PDO $db): ?string {
- if (self::findWhere('uri = ?', [$this->uri]) != null)
- return 'uri = ' . $db->quote($this->uri);
- if (self::findWhere('id = ?', [$this->id]) != null)
- return "id = $this->id";
- return null;
- }
-
- protected function hydrate() {
- $this->privacy = NotePrivacy::findWhere('note_id = ?', [$this->id]);
- $this->formattedContent = $this->findFormattedContents();
- $this->mentions = $this->findMentions();
- $this->attachments = NoteAttachment::findAllWhere('note_id = ?', [$this->id]);
- }
-
- private function findFormattedContents(): array {
- $pdo = Db::getInstance()->getPdo();
- $stmt = $pdo->prepare('SELECT mimetype, body FROM note_formatted_content WHERE note_id = ?');
- $stmt->execute([$this->id]);
- return $stmt->fetchAll(\PDO::FETCH_ASSOC);
- }
-
- private function findMentions(): array {
- $pdo = Db::getInstance()->getPdo();
- $stmt = $pdo->prepare('SELECT actor_id FROM note_mention WHERE note_id = ?');
- $stmt->execute([$this->id]);
- $actors = array_map(function ($actorId) {
- return Actor::find($actorId);
- }, $stmt->fetchAll(\PDO::FETCH_COLUMN, 0));
- return $actors;
- }
-
- public static function findAllWithAuthor(Actor $author): array {
- return self::findAllWhere('author = ?', [$author->id]);
- }
-
- public function jsonSerialize(): array {
- return [
- 'type' => 'note',
- 'self' => path_to_uri("/post/$this->id"),
- 'created' => $this->created->format('c'),
- 'modified' => $this->modified?->format('c'),
- 'author' => $this->author->uri,
- 'summary' => $this->summary,
- 'plainContent' => $this->plainContent,
- 'formattedContent' => $this->formattedContent,
- 'language' => $this->language,
- 'mentions' => array_map(function (Actor $actor) {
- return $actor->uri;
- }, $this->mentions),
- 'inReplyTo' => $this->inReplyTo?->uri,
- 'threadApex' => $this->threadApex?->uri,
- 'privacy' => $this->privacy,
- 'attachments' => array_map(function (NoteAttachment $attachment) {
- return [
- 'type' => $attachment->type,
- 'href' => $attachment->href,
- 'description' => $attachment->description
- ];
- }, $this->attachments),
- //'extensions' => []
- ];
- }
-} \ No newline at end of file
diff --git a/WpfTest/Model/NoteAttachment.php b/WpfTest/Model/NoteAttachment.php
deleted file mode 100644
index 16947c5..0000000
--- a/WpfTest/Model/NoteAttachment.php
+++ /dev/null
@@ -1,23 +0,0 @@
-<?php
-namespace WpfTest\Model;
-
-use WpfTest\Model;
-
-class NoteAttachment extends Model {
- public ?int $noteId;
- public int $index;
- public string $type;
- public string $href;
- public ?string $description;
-
- protected function setOwnerId(int $id) {
- $this->noteId = $id;
- }
-
- protected function getUpdateWhereClause($db): ?string {
- if (self::findWhere('note_id = ?', [$this->noteId]) != null) {
- return "note_id = $this->noteId";
- }
- return null;
- }
-} \ No newline at end of file
diff --git a/WpfTest/Model/NotePrivacy.php b/WpfTest/Model/NotePrivacy.php
deleted file mode 100644
index b955447..0000000
--- a/WpfTest/Model/NotePrivacy.php
+++ /dev/null
@@ -1,39 +0,0 @@
-<?php
-namespace WpfTest\Model;
-
-use WpfTest\Model;
-
-class NotePrivacy extends Model implements \JsonSerializable {
- public ?int $noteId;
- public NotePrivacyScope $scope;
- /**
- * @var Actor[]
- */
- public array $alsoVisibleTo = [];
- public bool $indexable;
- public NotePrivacyInteractors $canReshare = NotePrivacyInteractors::ALL;
- public NotePrivacyInteractors $canReply = NotePrivacyInteractors::ALL;
- public NotePrivacyInteractors $canOtherInteract = NotePrivacyInteractors::ALL;
-
- protected function setOwnerId(int $id) {
- $this->noteId = $id;
- }
-
- protected function getUpdateWhereClause($db): ?string {
- if (self::findWhere('note_id = ?', [$this->noteId]) != null) {
- return "note_id = $this->noteId";
- }
- return null;
- }
-
- public function jsonSerialize(): array {
- return [
- 'scope' => $this->scope->value,
- 'alsoVisibleTo' => $this->alsoVisibleTo,
- 'indexable' => $this->indexable,
- 'canReshare' => $this->canReshare->value,
- 'canReply' => $this->canReply->value,
- 'canOtherInteract' => $this->canOtherInteract->value
- ];
- }
-} \ No newline at end of file
diff --git a/WpfTest/Model/NotePrivacyInteractors.php b/WpfTest/Model/NotePrivacyInteractors.php
deleted file mode 100644
index 917d90a..0000000
--- a/WpfTest/Model/NotePrivacyInteractors.php
+++ /dev/null
@@ -1,9 +0,0 @@
-<?php
-namespace WpfTest\Model;
-
-enum NotePrivacyInteractors: string {
- case ALL = 'all';
- case FOLLOWERS = 'followers';
- case MUTUALS = 'mutuals';
- case NONE = 'none';
-} \ No newline at end of file
diff --git a/WpfTest/Model/NotePrivacyScope.php b/WpfTest/Model/NotePrivacyScope.php
deleted file mode 100644
index 44c9344..0000000
--- a/WpfTest/Model/NotePrivacyScope.php
+++ /dev/null
@@ -1,9 +0,0 @@
-<?php
-namespace WpfTest\Model;
-
-enum NotePrivacyScope: string {
- case PUBLIC = 'public';
- case FOLLOWERS = 'followers';
- case MUTUALS = 'mutuals';
- case NONE = 'none';
-} \ No newline at end of file
diff --git a/WpfTest/RemoteFetchable.php b/WpfTest/RemoteFetchable.php
deleted file mode 100644
index 0782e7e..0000000
--- a/WpfTest/RemoteFetchable.php
+++ /dev/null
@@ -1,21 +0,0 @@
-<?php
-namespace WpfTest;
-
-interface RemoteFetchable {
- /**
- * Fetches the object by its URI from the remote server.
- * @param string $uri the URI of the remote object
- * @param bool $autoSave whether to automatically save the fetched object to the database
- * @return static
- */
- //public static function fetchFromRemote(string $uri, bool $autoSave);
-
- /**
- * Finds the object locally by its URI or fetches it remotely if not found.
- * @param string $uri the URI identifier of the object
- * @param bool $autoSave whether to automatically save the object (if fetched) to the database
- * @param bool $forceRefetch whether to forcibly refetch the object even if it's in the database
- * @return static
- */
- public static function findByUri(string $uri, bool $autoSave = true, bool $forceRefetch = false);
-} \ No newline at end of file
diff --git a/WpfTest/Router.php b/WpfTest/Router.php
deleted file mode 100644
index c975723..0000000
--- a/WpfTest/Router.php
+++ /dev/null
@@ -1,98 +0,0 @@
-<?php
-namespace WpfTest;
-
-use WpfTest\HttpResponseStatus\InternalServerError;
-use WpfTest\HttpResponseStatus\NoHandlerFound;
-
-class Router extends Singleton {
-
- private readonly string $requestPath;
- private array $routes;
-
- protected function __construct() {
- $this->requestPath = $_GET['requestPath'] ?? $_SERVER['PATH_INFO'] ?? explode('?', $_SERVER['REQUEST_URI'], 2)[0] ?? '/';
- $this->routes = [];
- }
-
- /**
- * Processes and sends a response to the current request
- * @return void
- */
- public function processRequest() {
- try {
- $this->processRequestInner();
- } catch (HttpResponseStatus $e) {
- http_response_code($e->httpCode());
- echo '<h1>' . $e->httpReason() . '</h1><pre>' . $e->getMessage() . '</pre>';
- }
- }
-
- private function processRequestInner() {
- $context = null;
- try {
- $route = $this->matchPath($this->requestPath);
- if ($route == null) {
- throw new NoHandlerFound($this->requestPath);
- }
- $context = 'handler for /' . implode('/', $route[0]);
- $route[1]($route[2]); // call handler with args
- } catch (\Exception $e) {
- if (is_a($e, HttpResponseStatus::class)) {
- throw $e;
- }
- throw new InternalServerError($e, $context);
- } catch (\Error $e) {
- throw new InternalServerError($e, $context);
- }
- }
-
- public function mount(string $pathSpec, callable $handler) {
- $this->routes[] = [$this->explodePathPieces($pathSpec), $handler];
- }
-
- private function explodePathPieces(string $pathSpec): array {
- $pieces = explode('/', $pathSpec);
- if ($pieces[0] == '')
- array_shift($pieces);
- return $pieces;
- }
-
- private function matchPath(string $requestPath): ?array {
- // find matching candidates
- $pieces = $this->explodePathPieces($requestPath);
- $candidates = [];
- foreach ($this->routes as $route) {
- $routePieces = $route[0];
- if (count($routePieces) != count($pieces))
- continue;
- $args = [];
- $matches = true;
- for ($i = 0; $i < count($pieces); $i++) {
- if (str_starts_with($routePieces[$i], ':')) { // this is a parameter
- $args[substr($routePieces[$i], 1)] = $pieces[$i];
- } elseif ($routePieces[$i] != $pieces[$i]) {
- $matches = false;
- break;
- }
- }
- if ($matches) {
- $candidates[] = [$route[0], $route[1], $args];
- }
- }
-
- // select the best matching one (has the longest path, for now)
- $bestScore = 0;
- $bestItem = null;
- foreach ($candidates as $c) {
- $score = count($c[0]) - count(array_filter($c[0], function ($item) {
- return str_starts_with($item, ':');
- })) * 0.1;
- if ($score > $bestScore) {
- $bestItem = $c;
- $bestScore = $score;
- }
- }
- return $bestItem;
- }
-
-}
diff --git a/WpfTest/Singleton.php b/WpfTest/Singleton.php
deleted file mode 100644
index b365e1e..0000000
--- a/WpfTest/Singleton.php
+++ /dev/null
@@ -1,18 +0,0 @@
-<?php
-namespace WpfTest;
-
-abstract class Singleton {
- private static array $instances;
- protected function __construct() {
- }
- /**
- * @return static the singleton instance
- */
- public static function getInstance() {
- if (!isset(self::$instances[static::class])) {
- //error_log('making a new ' . static::class);
- self::$instances[static::class] = new static();
- }
- return self::$instances[static::class];
- }
-} \ No newline at end of file