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/FetchableModel.php | |
| parent | b00185ddbac9ac3de975a3954f2ede2f24458f6a (diff) | |
implement remote object fetching!
this is most of the tricky parts of inbound federation :3
Diffstat (limited to 'WpfTest/Model/FetchableModel.php')
| -rw-r--r-- | WpfTest/Model/FetchableModel.php | 78 |
1 files changed, 78 insertions, 0 deletions
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 |
