PHP code example of rikudou / activity-pub

1. Go to this page and download the library: Download rikudou/activity-pub library. Choose the download type require.

2. Extract the ZIP file and open the index.php.

3. Add this code to the index.php.
    
        
<?php
require_once('vendor/autoload.php');

/* Start to develop here. Best regards https://php-download.com/ */

    

rikudou / activity-pub example snippets




use Rikudou\ActivityPub\Vocabulary\Extended\Object\Note;
use Rikudou\ActivityPub\Dto\Source\MarkdownSource;

$note = new Note();
$note->id = 'https://example.com/notes/123';
$note->content = 'Hello <strong>there</strong>!';
$note->attributedTo = 'https://example.com/user/some-actor';
$note->to = 'https://example.com/user/some-other-actor';
$note->inReplyTo = 'https://example.com/notes/120';
$note->published = new DateTimeImmutable();
$note->source = new MarkdownSource('Hello **there**');

echo json_encode($note, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);



use Rikudou\ActivityPub\Vocabulary\Extended\Object\Note;

$note = new Note();
$note->id = '123';



use Rikudou\ActivityPub\Enum\ValidatorMode;
use Rikudou\ActivityPub\Vocabulary\Extended\Object\Note;

$note = new Note();
$note->validatorMode = ValidatorMode::None;
$note->id = '123';

echo json_encode($note, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);



use Rikudou\ActivityPub\Enum\ValidatorMode;
use Rikudou\ActivityPub\GlobalSettings;
use Rikudou\ActivityPub\Vocabulary\Extended\Object\Note;

GlobalSettings::$validatorMode = ValidatorMode::None;

$note = new Note();
$note->id = '123';

echo json_encode($note, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);



use Rikudou\ActivityPub\Vocabulary\Extended\Object\Note;
use function Rikudou\ActivityPub\runInNoValidationContext;

T | JSON_UNESCAPED_SLASHES);



use Rikudou\ActivityPub\Vocabulary\Extended\Object\Note;

$note = new Note();
$note->id = 'https://example.com/note/1';
$note->set('customProperty', 'customValue');

echo json_encode($note, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES), PHP_EOL;



use Rikudou\ActivityPub\Vocabulary\Extended\Object\Note;
use function Rikudou\ActivityPub\runInNoValidationContext;

', 'customValue'));

echo json_encode($note, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES), PHP_EOL;



use Rikudou\ActivityPub\Vocabulary\Extended\Object\Note;
use Rikudou\ActivityPub\Vocabulary\Parser\DefaultTypeParser;

$parser = new DefaultTypeParser();

$json = <<<'JSON'
{
    "type": "Note",
    "@context": "https://www.w3.org/ns/activitystreams",
    "id": "https://example.com/notes/123",
    "attributedTo": "https://example.com/user/some-actor",
    "content": "Hello <strong>there</strong>!",
    "inReplyTo": "https://example.com/notes/120",
    "published": "2025-01-06T22:52:11+01:00",
    "to": [
        "https://example.com/user/some-other-actor"
    ],
    "source": {
        "content": "Hello **there**",
        "mediaType": "text/markdown"
    }
}
JSON;

$note = $parser->parseJson($json);

// all the following assertions are true
assert($note instanceof Note);
assert($note->context === "https://www.w3.org/ns/activitystreams");
assert($note->id === "https://example.com/notes/123");
assert((string) $note->attributedTo === "https://example.com/user/some-actor");
assert($note->content === "Hello <strong>there</strong>!");
assert((string) $note->inReplyTo === "https://example.com/notes/120");
assert($note->published->format('c') === "2025-01-06T22:52:11+01:00");
assert(count($note->to) === 1);
assert((string) $note->to[0] === "https://example.com/user/some-other-actor");
assert($note->source->content === "Hello **there**");
assert($note->source->mediaType === "text/markdown");



use Rikudou\ActivityPub\Vocabulary\Core\BaseObject;

final class Cat extends BaseObject
{
    public string $type {
        get => 'Cat';
    }
}




use Rikudou\ActivityPub\Vocabulary\Core\BaseObject;

final class Cat extends BaseObject
{
    public string $type {
        get => 'Cat';
    }

    public ?int $lives = null;
}



$cat = new Cat();
$cat->id = 'https://example.com/meow';
$cat->lives = 9;

echo json_encode($cat, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT), PHP_EOL;



use Rikudou\ActivityPub\Attribute\RequiredProperty;
use Rikudou\ActivityPub\Enum\ValidatorMode;
use Rikudou\ActivityPub\Vocabulary\Core\BaseObject;

final class Cat extends BaseObject
{
    public string $type {
        get => 'Cat';
    }

    #[RequiredProperty(ValidatorMode::Lax)]
    public ?int $lives = null;
}



$cat = new Cat();
$cat->id = 'https://example.com/meow';

echo json_encode($cat, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT), PHP_EOL;

// Uncaught Rikudou\ActivityPub\Exception\MissingRequiredPropertyException: The property "Cat:lives" is 



use Rikudou\ActivityPub\Attribute\RequiredProperty;
use Rikudou\ActivityPub\Enum\ValidatorMode;
use Rikudou\ActivityPub\GlobalSettings;
use Rikudou\ActivityPub\Vocabulary\Core\BaseObject;
use Rikudou\ActivityPub\Vocabulary\Core\Link;
use Rikudou\ActivityPub\Vocabulary\Extended\Activity\Announce;
use Rikudou\ActivityPub\Vocabulary\Extended\Activity\Create;
use Rikudou\ActivityPub\Vocabulary\Extended\Actor\Person;

final class Cat extends BaseObject
{
    public string $type {
        get => 'Cat';
    }

    #[RequiredProperty(ValidatorMode::Lax)]
    public ?int $lives = null;
}

$cat = new Cat();
$cat->id = 'https://example.com/meow';
$cat->lives = 9;
$cat->name = 'Meowth';

$me = new Person();
$me->id = 'https://example.com/me';
$me->name = 'James';
$me->inbox = 'https://example.com/inbox';
$me->outbox = 'https://example.com/outbox';
$me->following = 'https://example.com/following';
$me->followers = 'https://example.com/following';

$create = new Create();
$create->id = 'https://example.com/create/meow';
$create->actor = $me;
$create->object = $cat;
$create->to = Link::publicAudienceLink(); // a special link that indicates that the target is public

$announcer = new Person();
$announcer->id = 'https://example.com/not-me';
$announcer->name = 'Jessie';
$announcer->inbox = 'https://example.com/inbox-jessie';
$announcer->outbox = 'https://example.com/outbox-jessie';
$announcer->following = 'https://example.com/following-jessie';
$announcer->followers = 'https://example.com/following-jessie';

$announce = new Announce();
$announce->id = 'https://example.com/announce/create/meow';
$announce->to = $create->to;
$announce->actor = $announcer;
$announce->object = $create;

echo json_encode($announce, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT), PHP_EOL;



use Rikudou\ActivityPub\Attribute\RequiredProperty;
use Rikudou\ActivityPub\Enum\ValidatorMode;
use Rikudou\ActivityPub\Vocabulary\Core\BaseObject;
use Rikudou\ActivityPub\Vocabulary\Parser\DefaultTypeParser;

final class Cat extends BaseObject
{
    public string $type {
        get => 'Cat';
    }

    #[RequiredProperty(ValidatorMode::Lax)]
    public ?int $lives = null;
}

$parser = new DefaultTypeParser();

$parser->registerType('Cat', Cat::class);

$catJson = <<<'JSON'
{
    "type": "Cat",
    "lives": 9,
    "@context": "https://www.w3.org/ns/activitystreams",
    "id": "https://example.com/meow",
    "name": "Meowth"
}
JSON;

$reconstructedCat = $parser->parseJson($catJson);

// all the following are true
assert($reconstructedCat instanceof Cat);
assert($reconstructedCat->lives === 9);
assert($reconstructedCat->name === 'Meowth');
assert($reconstructedCat->id === 'https://example.com/meow');




use Rikudou\ActivityPub\Dto\KeyPair;
use Rikudou\ActivityPub\Dto\PublicKey;
use Rikudou\ActivityPub\Server\KeyGenerator\OpenSslActorKeyGenerator;
use Rikudou\ActivityPub\Vocabulary\Contract\ActivityPubActor;
use Rikudou\ActivityPub\Vocabulary\Extended\Actor\Person;

function storePrivateKeyInDatabase(ActivityPubActor $actor, KeyPair $keyPair): void
{
    $privateKey = $keyPair->privateKey;
    // todo store it somewhere securely
}

// create a minimal valid Actor object
$me = new Person();
$me->id = 'https://example.com/person/1';
$me->inbox = 'https://example.com/person/1/inbox';
$me->outbox = 'https://example.com/person/1/outbox';
$me->following = 'https://example.com/person/1/following';
$me->followers = 'https://example.com/person/1/followers';

// instantiate a specific implementation of the KeyGenerator interface, there's currently only this one
$keyGenerator = new OpenSslActorKeyGenerator();

// generate the private and public key-pair
// if you provide an instance of actor as the first parameter, it will automatically create
$keyPair = $keyGenerator->generate($me);

// alternatively, you can assign the public key manually
$keyPair = $keyGenerator->generate();
$me->publicKey = new PublicKey(
    // adding #main-key is a convention, and it's not really important what exactly is there, important is that you can fetch
    // the public key at that URL and that it's unique (thus it cannot be the same as the owner id)
    id: $me->id . '#main-key',
    owner: $me->id,
    publicKeyPem: $keyPair->publicKey,
);



use GuzzleHttp\Psr7\Utils;
use Psr\Http\Client\ClientInterface;
use Psr\Http\Message\RequestFactoryInterface;
use Rikudou\ActivityPub\Server\Signing\RequestSigner;
use Rikudou\ActivityPub\Vocabulary\Contract\ActivityPubActivity;
use Rikudou\ActivityPub\Vocabulary\Contract\ActivityPubActor;

        private ClientInterface $httpClient,
    ) {
    }

    public function sendOutgoingActivity(
        ActivityPubActor $actor,
        #[SensitiveParameter]
        string $actorPrivateKey,
        ActivityPubActivity $activity,
    ): void {
        // you should send the activity to other fields as well, this is just for illustration
        $recipients = $activity->to;

        foreach ($recipients as $recipient) {
            // for simplicity, let's assume this is always an actor, but it can also be a link
            assert($recipient instanceof ActivityPubActor);

            // you need to specify at least the request method,
            $request = $this->requestFactory
                ->createRequest('POST', $recipient->inbox)
                ->withBody(
                    Utils::streamFor(
                        json_encode($activity),
                    ),
                )
            ;

            // now let's sign it!
            $request = $this->requestSigner->signRequest(
                $request,
                $actor->publicKey->id,
                $actorPrivateKey,
            );

            $response = $this->httpClient->sendRequest($request);
            if ($response->getStatusCode() < 200 || $response->getStatusCode() >= 300) {
                // todo handle bad responses in some way
            }
        }
    }
}



use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Rikudou\ActivityPub\Server\Signing\RequestValidator;

class IncomingActivityHandler
{
    public function __construct(
        // Just like before, an interface that's implemented by RequestSignerAndValidator
        private RequestValidator $requestValidator,
    ) {
    }

    public function handle(
        ServerRequestInterface $incomingRequest,
    ): ResponseInterface {
        if ($incomingRequest->getMethod() !== 'POST') {
            // todo return 405
        }

        if (!$this->requestValidator->isRequestValid($incomingRequest)) {
            // todo return 403 or something
        }

        // todo handle the request
    }
}




use Psr\Http\Message\ResponseInterface;
use Rikudou\ActivityPub\Exception\ActivityPubException;
use Rikudou\ActivityPub\Exception\ResourceException;
use Rikudou\ActivityPub\Exception\WebFingerException;
use Rikudou\ActivityPub\Server\ObjectFetcher\ObjectFetcher;
use Rikudou\ActivityPub\Server\ObjectFetcher\WebFinger;
use Rikudou\ActivityPub\Vocabulary\Extended\Actor\Person;
use Rikudou\ActivityPub\Vocabulary\Extended\Object\Article;

class SomeController
{
    public function __construct(
        private WebFinger $webFinger,
        private ObjectFetcher $objectFetcher,
    ) {
    }

    public function someMethod(): ResponseInterface
    {
        $account = '[email protected]';

        try {
            $webFingerResponse = $this->webFinger->find($account);
            $object = $this->objectFetcher->fetch($webFingerResponse);
            assert($object instanceof Person);
            // todo do something with the object
        } catch (WebFingerException $e) {
            // todo handle that something went wrong
        } catch (ResourceException $e) {
            // todo handle that something went wrong with fetching the person
        } catch (ActivityPubException $e) {
            // todo handle all other exceptions thrown by the package
        }
    }

    public function anotherMethod(): ResponseInterface
    {
        $resource = 'https://example.com/posts/1';
        $object = $this->objectFetcher->fetch($resource);
        assert($object instanceof Article);
    }
}