diff options
| author | winter | 2024-12-08 23:18:35 +0000 |
|---|---|---|
| committer | winter | 2024-12-08 23:18:35 +0000 |
| commit | bda28640d6e066ae338c6f31407df274ed09f026 (patch) | |
| tree | 2a887dd4f98f79fc11efefc9585c58e144f66a00 /WpfTest/Model | |
| parent | b00185ddbac9ac3de975a3954f2ede2f24458f6a (diff) | |
implement remote object fetching!
this is most of the tricky parts of inbound federation :3
Diffstat (limited to 'WpfTest/Model')
| -rw-r--r-- | WpfTest/Model/Actor.php | 16 | ||||
| -rw-r--r-- | WpfTest/Model/ActorEndpoints.php | 12 | ||||
| -rw-r--r-- | WpfTest/Model/FetchableModel.php | 78 | ||||
| -rw-r--r-- | WpfTest/Model/Note.php | 22 | ||||
| -rw-r--r-- | WpfTest/Model/NoteAttachment.php | 12 | ||||
| -rw-r--r-- | WpfTest/Model/NotePrivacy.php | 31 | ||||
| -rw-r--r-- | WpfTest/Model/NotePrivacyInteractors.php | 10 | ||||
| -rw-r--r-- | WpfTest/Model/NotePrivacyScope.php | 10 |
8 files changed, 164 insertions, 27 deletions
diff --git a/WpfTest/Model/Actor.php b/WpfTest/Model/Actor.php index eaef99c..0b0e729 100644 --- a/WpfTest/Model/Actor.php +++ b/WpfTest/Model/Actor.php @@ -1,9 +1,9 @@ <?php namespace WpfTest\Model; -use WpfTest\Model; +use WpfTest\RemoteFetchable; -class Actor extends Model implements \JsonSerializable { +class Actor extends FetchableModel implements \JsonSerializable { public ?int $id; public string $uri; public bool $isLocal = false; @@ -20,6 +20,14 @@ class Actor extends Model implements \JsonSerializable { 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]); } @@ -42,4 +50,8 @@ class Actor extends Model implements \JsonSerializable { ] ]; } + + 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 index 03dab9e..f6120d3 100644 --- a/WpfTest/Model/ActorEndpoints.php +++ b/WpfTest/Model/ActorEndpoints.php @@ -4,6 +4,7 @@ namespace WpfTest\Model; use WpfTest\Model; class ActorEndpoints extends Model { + public ?int $actorId; public string $basicFeed; public ?string $fullFeed; public ?string $follow; @@ -12,4 +13,15 @@ class ActorEndpoints extends Model { 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 new file mode 100644 index 0000000..44106d4 --- /dev/null +++ b/WpfTest/Model/FetchableModel.php @@ -0,0 +1,78 @@ +<?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 === false) + 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 index 85b3567..af2d2aa 100644 --- a/WpfTest/Model/Note.php +++ b/WpfTest/Model/Note.php @@ -2,9 +2,8 @@ namespace WpfTest\Model; use WpfTest\Db; -use WpfTest\Model; -class Note extends Model implements \JsonSerializable { +class Note extends FetchableModel implements \JsonSerializable { public ?int $id; public string $uri; public \DateTimeImmutable $created; @@ -30,7 +29,15 @@ class Note extends Model implements \JsonSerializable { public array $attachments = []; public array $extensions = []; - protected function hydrate(array $row) { + 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(); @@ -74,14 +81,7 @@ class Note extends Model implements \JsonSerializable { }, $this->mentions), 'inReplyTo' => $this->inReplyTo?->uri, 'threadApex' => $this->threadApex?->uri, - 'privacy' => [ - 'scope' => $this->privacy->scope->name, - 'alsoVisibleTo' => $this->privacy->alsoVisibleTo, - 'indexable' => $this->privacy->indexable, - 'canReshare' => $this->privacy->canReshare->name, - 'canReply' => $this->privacy->canReply->name, - 'canOtherInteract' => $this->privacy->canOtherInteract->name - ], + 'privacy' => $this->privacy, 'attachments' => array_map(function (NoteAttachment $attachment) { return [ 'type' => $attachment->type, diff --git a/WpfTest/Model/NoteAttachment.php b/WpfTest/Model/NoteAttachment.php index b07d595..16947c5 100644 --- a/WpfTest/Model/NoteAttachment.php +++ b/WpfTest/Model/NoteAttachment.php @@ -4,8 +4,20 @@ 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 index b7ed58a..b955447 100644 --- a/WpfTest/Model/NotePrivacy.php +++ b/WpfTest/Model/NotePrivacy.php @@ -3,14 +3,37 @@ namespace WpfTest\Model; use WpfTest\Model; -class NotePrivacy extends 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; + 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 index ff4d7fa..917d90a 100644 --- a/WpfTest/Model/NotePrivacyInteractors.php +++ b/WpfTest/Model/NotePrivacyInteractors.php @@ -1,9 +1,9 @@ <?php namespace WpfTest\Model; -enum NotePrivacyInteractors { - case all; - case followers; - case mutuals; - case none; +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 index 3134c26..44c9344 100644 --- a/WpfTest/Model/NotePrivacyScope.php +++ b/WpfTest/Model/NotePrivacyScope.php @@ -1,9 +1,9 @@ <?php namespace WpfTest\Model; -enum NotePrivacyScope { - case public; - case followers; - case mutuals; - case none; +enum NotePrivacyScope: string { + case PUBLIC = 'public'; + case FOLLOWERS = 'followers'; + case MUTUALS = 'mutuals'; + case NONE = 'none'; }
\ No newline at end of file |
