Validation error on CollectionPage
Opened this issue · 4 comments
I am trying to build out Mastodon support for an application, and running into a validation error I cannot explain.
Here's the code:
$server new Server([
'ontologies' => [
// I'm building out the ontology class based on the error messages I get.
'mastodon' => MastodonOntology::class,
]
]);
// This works (after I create an extended Person and a PropertyValue)
$actor = $this->server->actor('crell@phpc.social');
$outbox = $this->server->outbox('crell@phpc.social');
$pages = [];
// This fails.
$page = $outbox->getPage($outbox->get()->first);
Specifically, the error comes from Util::hasProperties()
, which is called with an array for $item
instead of an object. (The method really needs better native types.) That makes the property_exists()
call fail with a type error. After some debugging, I have determined it's being called with this array:
[
'type' => 'CollectionPage',
'next' => 'https://phpc.social/users/Crell/statuses/...',
'partOf' => 'https://phpc.social/users/Crell/statuses/...',
'items' => [],
]
Which... doesn't make sense, as CollectionPage
is a built-in supported type, so I have no idea how it's still an array at that point.
I've hit a wall on debugging at this point. Any help on what's going on? There's like 10 stack layers between my code and where the error is, and I have on idea where the issue is.
(I'm quite open to submitting the Mastodon ontology as a PR once I have it working, if there would be interest.)
Hello Crell,
As far as I can see, there are 2 points:
-
I need to have a look at your
MastodonOntology::class
definitions for a deeper analysis -
If you need some flexibility for debugging, you can disable "strict" ActivityPub typing with the following Server configurations:
// Create a server instance that never fails (useful to discover new attributes or types)
$server = new Server([
// Creates AbstractObject when a type is not locally defined
'instance' => [
'types' => 'include'
],
]);
$handle = 'crell@phpc.social';
$outbox = $server->outbox($handle);
$pages = [];
$page = $outbox->getPage($outbox->get()->first);
// $page is an object with a toArray() method
// print_r($page->toArray());
$pages[] = $page;
while ($page->next !== null) {
$page = $outbox->getPage($page->next);
$pages[] = $page;
}
// Now we can work with pages
foreach ($pages as $page) {
foreach ($page->orderedItems as $item) {
// Do something here
}
}
If you want to have informations about the types
server parameters: https://landrok.github.io/activitypub/activitypub-server-usage.html
And 3rd point, it would be very interesting for the community to have a MastodonOntology, so if you need, we can work on a PR.
Hope this helps
Here's what I've got for the Ontology so far. Note that the docs seem a bit inconsistent about whether it's preferred to make a class or just define arrays, so since I'm a fan of classes and types I'm making a class and then deriving the arrays off of them. (The Ontology class is generic so it would work for anything, making it a good candidate for a utility of its own.) This is mainly reverse-engineered from the error messages, and most of the types are me making educated guesses since the Mastodon docs don't have a type in many cases.
use ActivityPhp\Type\OntologyInterface;
class MastodonOntology implements OntologyInterface
{
public static function getDefinition(): array
{
$types = [Person::class, PropertyValue::class, Note::class, Hashtag::class];
$ret = [];
foreach ($types as $type) {
$rClass = new \ReflectionClass($type);
$ret[$rClass->getShortName()] = self::getFields($rClass);
}
return $ret;
}
private static function getFields(\ReflectionClass $rClass): array
{
$props = $rClass->getProperties();
return array_map(static fn (\ReflectionProperty $p) => $p->getName(), $props);
}
}
use ActivityPhp\Type\Extended\Actor\Person as BasePerson;
class Person extends BasePerson
{
protected string $featured;
protected string $featuredTags;
protected bool $manuallyApprovesFollowers;
protected bool $discoverable;
protected array $devices;
}
use ActivityPhp\Type\Extended\Object\Note as BaseNote;
class Note extends BaseNote
{
protected bool $sensitive;
protected string $atomUri;
protected string $inReplyToAtomUri;
// Not sure what this is.
protected $conversation;
}
use ActivityPhp\Type;
class Hashtag extends Type
{
protected string $href;
}
use ActivityPhp\Type;
// I think this is for a single record within the extra properties collection; it's where in the Mastodon UI you put the "your website" links, etc.
class PropertyValue extends Type
{
protected string $value;
}
Enabling types => include does allow the process to finish and run to completion, and appears to work(!). I'd still rather have a properly defined ontology, though, so I know what the API even is. 😄
Also, is there a reason there are no @property
declarations on the type classes? Since the properties are protected trying to access them directly is going to generate errors in static analyzers (like my IDE), even if they technically work.
Any further thoughts here?