landrok / activitypub

A PHP implementation of ActivityPub protocol based upon the ActivityStreams 2.0 data format.
MIT License
260 stars 32 forks source link
activitypub activitystreams federated federated-social-web protocol

ActivityPhp

Build Status Maintainability Test Coverage

ActivityPhp is an implementation of ActivityPub layers in PHP.

It provides two layers:

As the two layers are implemented, it aims to be an ActivityPub conformant Federated Server

All normalized types are implemented too. If you need to create a new one, just extend existing types.

See the full documentation or an overview below.

Table of contents


Requirements


Install

composer require landrok/activitypub

ActivityStreams Core Types

All core types are provided:

use ActivityPhp\Type\Core\Activity;
use ActivityPhp\Type\Core\Collection;
use ActivityPhp\Type\Core\CollectionPage;
use ActivityPhp\Type\Core\IntransitiveActivity;
use ActivityPhp\Type\Core\Link;
use ActivityPhp\Type\Core\ObjectType;
use ActivityPhp\Type\Core\OrderedCollection;
use ActivityPhp\Type\Core\OrderedCollectionPage;

ActivityStreams Extended Types

All extended types are provided:

Actor types

use ActivityPhp\Type\Extended\Actor\Application;
use ActivityPhp\Type\Extended\Actor\Group;
use ActivityPhp\Type\Extended\Actor\Organization;
use ActivityPhp\Type\Extended\Actor\Person;
use ActivityPhp\Type\Extended\Actor\Service;

Activity types

use ActivityPhp\Type\Extended\Activity\Accept;
use ActivityPhp\Type\Extended\Activity\Add;
use ActivityPhp\Type\Extended\Activity\Announce;
use ActivityPhp\Type\Extended\Activity\Arrive;
use ActivityPhp\Type\Extended\Activity\Block;
use ActivityPhp\Type\Extended\Activity\Create;
use ActivityPhp\Type\Extended\Activity\Delete;
use ActivityPhp\Type\Extended\Activity\Dislike;
use ActivityPhp\Type\Extended\Activity\Flag;
use ActivityPhp\Type\Extended\Activity\Follow;
use ActivityPhp\Type\Extended\Activity\Ignore;
use ActivityPhp\Type\Extended\Activity\Invite;
use ActivityPhp\Type\Extended\Activity\Join;
use ActivityPhp\Type\Extended\Activity\Leave;
use ActivityPhp\Type\Extended\Activity\Like;
use ActivityPhp\Type\Extended\Activity\Listen;
use ActivityPhp\Type\Extended\Activity\Move;
use ActivityPhp\Type\Extended\Activity\Offer;
use ActivityPhp\Type\Extended\Activity\Question;
use ActivityPhp\Type\Extended\Activity\Read;
use ActivityPhp\Type\Extended\Activity\Reject;
use ActivityPhp\Type\Extended\Activity\Remove;
use ActivityPhp\Type\Extended\Activity\TentativeAccept;
use ActivityPhp\Type\Extended\Activity\TentativeReject;
use ActivityPhp\Type\Extended\Activity\Travel;
use ActivityPhp\Type\Extended\Activity\Undo;
use ActivityPhp\Type\Extended\Activity\Update;
use ActivityPhp\Type\Extended\Activity\View;

Object types

use ActivityPhp\Type\Extended\Object\Article;
use ActivityPhp\Type\Extended\Object\Audio;
use ActivityPhp\Type\Extended\Object\Document;
use ActivityPhp\Type\Extended\Object\Event;
use ActivityPhp\Type\Extended\Object\Image;
use ActivityPhp\Type\Extended\Object\Mention;
use ActivityPhp\Type\Extended\Object\Note;
use ActivityPhp\Type\Extended\Object\Page;
use ActivityPhp\Type\Extended\Object\Place;
use ActivityPhp\Type\Extended\Object\Profile;
use ActivityPhp\Type\Extended\Object\Relationship;
use ActivityPhp\Type\Extended\Object\Tombstone;
use ActivityPhp\Type\Extended\Object\Video;

Types

Type factory

You can instanciate ActivityStreams types using their short name.

use ActivityPhp\Type;

$link = Type::create('Link');
$note = Type::create('Note');

Instanciating a type and setting properties is possible with the second parameter.

use ActivityPhp\Type;

$note = Type::create('Note', [
    'content' => 'A content for my note'
]);

Starting from an array with a 'type' key, it's even possible to directly instanciate your type.

use ActivityPhp\Type;

$array = [
    'type'    => 'Note',
    'content' => 'A content for my note'
];

$note = Type::create($array);

Properties names

Whatever be your object or link, you can get all properties names with getProperties() method.

use ActivityPhp\Type;

$link = Type::create('Link');

print_r(
    $link->getProperties()
);

Would output something like:

Array
(
    [0] => type
    [1] => id
    [2] => name
    [3] => nameMap
    [4] => href
    [5] => hreflang
    [6] => mediaType
    [7] => rel
    [8] => height
    [9] => preview
    [10] => width
)

All properties and their values

In order to dump all properties and associated values, use toArray() method.

use ActivityPhp\Type;

$link = Type::create('Link');
$link->setName('An example');
$link->setHref('http://example.com');

print_r(
    $link->toArray()
);

Would output something like:

Array
(
    [type] => Link
    [name] => An example
    [href] => http://example.com
)

Get a property

There are 3 equivalent ways to get a value.

use ActivityPhp\Type;

$note = Type::create('Note');

// Each method returns the same value
echo $note->id;
echo $note->get('id');
echo $note->getId();

Set a property

There are 3 equivalent ways to set a value.

use ActivityPhp\Type;

$note = Type::create('Note');

$note->id = 'https://example.com/custom-notes/1';
$note->set('id', 'https://example.com/custom-notes/1');
$note->setId('https://example.com/custom-notes/1');

Whenever you assign a value, the format of this value is checked.

This action is made by a validator. If rules are not respected an Exception is thrown.

When a property does not exist, an Exception is thrown in strict mode. You can define 3 different behaviours:

use ActivityPhp\Type;
use ActivityPhp\Type\TypeConfiguration;

$note = Type::create('Note');

// Ignore mode
TypeConfiguration::set('undefined_properties', 'ignore');
$note->undefinedProperty = 'https://example.com/custom-notes/1';
echo $note->undefinedProperty; // null

// Include mode
TypeConfiguration::set('undefined_properties', 'include');
$note->undefinedProperty = 'https://example.com/custom-notes/1';
echo $note->undefinedProperty; // https://example.com/custom-notes/1

// Strict mode
TypeConfiguration::set('undefined_properties', 'strict');
$note->undefinedProperty = 'https://example.com/custom-notes/1'; // Exception

Set several properties

With Type factory, you can instanciate a type and set several properties.

use ActivityPhp\Type;

$note = Type::create('Note', [
    'id'   => 'https://example.com/custom-notes/1',
    'name' => 'An important note',
]);

Create a copy

Sometimes you may use a copy in order not to affect values of the original type.

use ActivityPhp\Type;

$note = Type::create('Note', ['name' => 'Original name']);

$copy = $note->copy()->setName('Copy name');

echo $copy->name; // Copy name
echo $note->name; // Original name

You can copy and chain methods to affect only values of the copied type.


Check if a property exists

use ActivityPhp\Type;

$note = Type::create('Note');

echo $note->has('id'); // true
echo $note->has('anotherProperty'); // false

Use native types

All core and extended types are used with a classic instanciation.

use ActivityPhp\Type\Extended\Object\Note;

$note = new Note();

Same way with Type factory:

use ActivityPhp\Type;

$note = Type::create('Note');

Use your own extended types

If you need some custom attributes, you can extend predefined types.

class MyNote extends Note { // Override basic type protected $type = 'CustomNote';

// Custom property
protected $myProperty;

}


There are 2 ways to instanciate a type:

- A classic PHP call:

```php
$note = new MyNote();
$note->id = 'https://example.com/custom-notes/1';
$note->myProperty = 'Custom Value';

echo $note->getMyProperty(); // Custom Value
use ActivityPhp\Type;

$note = Type::create('MyNote', [
    'id' => 'https://example.com/custom-notes/1',
    'myProperty' => 'Custom Value'
]);

Extending types preserves benefits of getters, setters and their validators.


Create your own property validator

Use a custom property validator when you define custom attributes or when you want to override ActivityPub attribute default validation.

Regarding to previous example with a custom attribute $myProperty, if you try to set this property, it would be done without any check on values you're providing.

You can easily cope with that implementing a custom validator using Validator.

use ActivityPhp\Type\ValidatorInterface;
use ActivityPhp\Type\Validator;

// Create a custom validator that implements ValidatorInterface
class MyPropertyValidator implements ValidatorInterface
{
    // A public validate() method is mandatory
    public function validate($value, $container)
    {
        return true;
    }
}

// Attach this custom validator to a property
Validator::add('myProperty', MyPropertyValidator::class);

// Now all values are checked with the validate() method
// 'myProperty' is passed to the first argument
// $note is passed to the second one.

$note->myProperty = 'Custom Value';

An equivalent way is to use Type factory and addValidator() method:

use ActivityPhp\Type;

// Attach this custom validator to a property
Type::addValidator('myProperty', MyPropertyValidator::class);

Server

A server instance is an entry point of a federation.

Its purpose is to receive, send and forward activities appropriately.

A minimal approach is:

use ActivityPhp\Server;

$server = new Server();

For more configuration parameters, See the full documentation

WebFinger

WebFinger is a protocol that allows for discovery of information about people.

Given a handle, ActivityPub instances can discover profiles using this protocol.

use ActivityPhp\Server;

$server = new Server();

$handle = 'bob@example.org';

// Get a WebFinger instance
$webfinger = $server->actor($handle)->webfinger();

In this implementation, we can use an Object Identifier (URI) instead of a WebFinger handle.

use ActivityPhp\Server;

$server = new Server();

$handle = 'https://example.org/users/bob';

// Get a WebFinger instance
$webfinger = $server->actor($handle)->webfinger();

WebFinger::toArray()

Get all WebFinger data as an array.

use ActivityPhp\Server;

$server = new Server();

$handle = 'bob@example.org';

// Get a WebFinger instance
$webfinger = $server->actor($handle)->webfinger();

// Dumps all properties
print_r($webfinger->toArray());

// A one line call
print_r(
    $server->actor($handle)->webfinger()->toArray()
);

Would output something like:

Array
(
    [subject] => acct:bob@example.org
    [aliases] => Array
        (
            [0] => http://example.org/users/bob
        )
    [links] => Array
        (
            [0] => Array
                (
                    [rel] => self
                    [type] => application/activity+json
                    [href] => http://example.org/users/bob
                )
        )
)

WebFinger::getSubject()

Get a WebFinger resource.

echo $webfinger->getSubject();

// Would output 'acct:bob@example.org'

WebFinger::getProfileId()

Get ActivityPub object identifier (URI).

echo $webfinger->getProfileId();

// Would output 'http://example.org/users/bob'

WebFinger::getHandle()

Get a profile handle.

echo $webfinger->getHandle();

// Would output 'bob@example.org'

WebFinger::getAliases()

Get all aliases entries for this profile.

print_r(
    $webfinger->getAliases()
);

Would output something like:

Array
(
    [0] => http://example.org/users/bob
)

WebFinger::getLinks()

Get all links entries for this profile.


print_r(
    $webfinger->getLinks()
);

Would output something like:

Array
(
    [0] => Array
        (
            [rel] => self
            [type] => application/activity+json
            [href] => http://example.org/users/bob
        )
)

More