Use a type-safe config class instead of passing in an array

This commit is contained in:
Jeremy Dormitzer 2019-01-23 18:34:25 -05:00
parent c31eaf2d18
commit 7ec09310b2
9 changed files with 364 additions and 84 deletions

View File

@ -30,32 +30,34 @@ Basic usage example:
``` php
<?php
use ActivityPub\ActivityPub;
use ActivityPub\Config\ActivityPubConfig;
// Constructing the ActivityPub instance
$activitypub = new ActivityPub( array(
// Function to determine if the current request should be associated
// with an ActivityPub actor. It should return the actor id associated
// with the current request, or false if the current request is not associated
// with the actor. This is where you can plug in your application's user
// management system:
'authFunction' => function() {
if ( current_user_is_logged_in() ) {
return get_actor_id_for_current_user();
} else {
return false;
}
},
// Database connection options, passed directly to Doctrine:
'dbOptions' => array(
'driver' => 'pdo_mysql',
'user' => 'mysql'
'password' => 'thePa$$word',
'dbname' => 'my-database',
),
// Database table name prefix for compatibility with $wpdb->prefix, etc.:
// Default: ''
'dbPrefix' => 'activitypub_',
) );
$config = ActivityPubConfig::createBuilder()
// Function to determine if the current request should be associated
// with an ActivityPub actor. It should return the actor id associated
// with the current request, or false if the current request is not associated
// with the actor. This is where you can plug in your application's user
// management system:
->setAuthFunction( function() {
if ( current_user_is_logged_in() ) {
return get_actor_id_for_current_user();
} else {
return false;
}
} )
// Database connection options, passed directly to Doctrine:
->setDbConnectionParams( array(
'driver' => 'pdo_mysql',
'user' => 'mysql'
'password' => 'thePa$$word',
'dbname' => 'my-database',
) )
// Database table name prefix for compatibility with $wpdb->prefix, etc.:
// Default: ''
->setDbPrefix( 'activitypub_' )
->build();
$activitypub = new ActivityPub( $config );
// Routing incoming ActivityPub requests to ActivityPub-PHP
if ( in_array( $_SERVER['HTTP_ACCEPT'],

View File

@ -5,6 +5,7 @@ require_once __DIR__ . '/../vendor/autoload.php';
use ActivityPub\Auth\AuthListener;
use ActivityPub\Auth\SignatureListener;
use ActivityPub\Config\ActivityPubConfig;
use ActivityPub\Config\ActivityPubModule;
use ActivityPub\Http\Router;
use Doctrine\ORM\EntityManager;
@ -27,12 +28,11 @@ class ActivityPub
/**
* Constructs a new ActivityPub instance
*
* @param array $opts Array of options. Valid keys are
* 'dbOptions', 'dbprefix', and 'isDevMode'.
* @param ActivityPubConfig $config Configuration options
*/
public function __construct( array $opts )
public function __construct( ActivityPubConfig $config )
{
$this->module = new ActivityPubModule( $opts );
$this->module = new ActivityPubModule( $config);
}
/**

View File

@ -0,0 +1,98 @@
<?php
namespace ActivityPub\Config;
use ActivityPub\Config\ActivityPubConfigBuilder;
use ActivityPub\Objects\ContextProvider;
/**
* The ActivityPubConfig is a data class to hold ActivityPub configuration options
*/
class ActivityPubConfig
{
/**
* @var array
*/
private $dbConnectionParams;
/**
* @var bool
*/
private $isDevMode;
/**
* @var string
*/
private $dbPrefix;
/**
* @var Callable
*/
private $authFunction;
/**
* @var array
*/
private $jsonLdContext;
/**
* Don't call this directly - instead, use
* ActivityPubConfig->createBuilder()->build()
*
* @param ActivityPubConfigBuilder $builder
*/
public function __construct( ActivityPubConfigBuilder $builder )
{
$this->dbConnectionParams = $builder->getDbConnectionParams();
$this->isDevMode = $builder->getIsDevMode();
$this->dbPrefix = $builder->getDbPrefix();
$this->authFunction = $builder->getAuthFunction();
$this->jsonLdContext = $builder->getJsonLdContext();
}
public function createBuilder()
{
return new ActivityPubConfigBuilder();
}
/**
* @var array
*/
public function getDbConnectionParams()
{
return $this->dbConnectionParams;
}
/**
* @var bool
*/
public function getIsDevMode()
{
return $this->isDevMode;
}
/**
* @var string
*/
public function getDbPrefix()
{
return $this->dbPrefix;
}
/**
* @var Callable
*/
public function getAuthFunction()
{
return $this->authFunction;
}
/**
* @var array
*/
public function getJsonLdContext()
{
return $this->jsonLdContext;
}
}
?>

View File

@ -0,0 +1,199 @@
<?php
namespace ActivityPub\Config;
use ActivityPub\Config\ActivityPubConfig;
use ActivityPub\Objects\ContextProvider;
/**
* The ActivityPubConfigBuilder is a builder class to create ActivityPub config data
*
* Usage:
* $config = ActivityPubConfig::createBuilder()
* ->setDbConnectionParams( array(
* 'driver' => 'pdo_sqlite',
* 'path' => __DIR__ . '/db.sqlite'
* ) )
* ->build();
*
* See the `set*` methods below for descriptions of available options.
*/
class ActivityPubConfigBuilder
{
/**
* @var array
*/
private $dbConnectionParams;
/**
* @var bool
*/
private $isDevMode;
/**
* @var string
*/
private $dbPrefix;
/**
* @var Callable
*/
private $authFunction;
/**
* @var array
*/
private $jsonLdContext;
/**
* Creates a new ActivityPubConfig instance with default values
*
* See the `set*` methods below for individual option defaults.
*/
public function __construct()
{
$this->isDevMode = false;
$this->dbPrefix = '';
$this->authFunction = function() {
return false;
};
$this->jsonLDContext = ContextProvider::DEFAULT_CONTEXT;
}
/**
* Validates and builds the config instance
*
* @return ActivityPubConfig
*/
public function build()
{
$this->validate();
return new ActivityPubConfig( $this );
}
private function validate()
{
if ( ! $this->dbConnectionParams ) {
throw new Exception( "Missing required option 'dbConnectionParams'" );
}
}
/**
* The `dbConnectionParams` are the Doctrine connection parameters,
* passed directly through to EntityManager::create(). See
* https://www.doctrine-project.org/projects/doctrine-orm/en/2.6/tutorials/getting-started.html#obtaining-the-entitymanager
*
* This option is required and has no default.
* @param array $dbConnectionParams The connection parameters
* @return ActivityPubConfigBuilder The builder instance
*/
public function setDbConnectionParams( array $dbConnectionParams )
{
$this->dbConnectionParams = $dbConnectionParams;
return $this;
}
/**
* @return array
*
*
*/
public function getDbConnectionParams()
{
return $this->dbConnectionParams;
}
/**
* If `isDevMode` is true, the Doctrine EntityManager configuration will
* be set to development mode.
*
* Default: false
* @param bool $isDevMode
* @return ActivityPubConfigBuilder The builder instance
*/
public function setIsDevMode( bool $isDevMode )
{
$this->isDevMode = $isDevMode;
return $this;
}
/**
* @return bool
*
*
*/
public function getIsDevMode()
{
return $this->isDevMode;
}
/**
* The `dbPrefix` is a string that is prepended to all SQL tables created
* by the ActivityPub library. This is useful for environments like multi-site
* WordPress installations where table prefixes are used to distinguish tables
* for different sites.
*
* Default: ''
* @param string $dbPrefix
* @return ActivityPubConfigBuilder The builder instance
*/
public function setDbPrefix( string $dbPrefix )
{
$this->dbPrefix = $dbPrefix;
return $this;
}
/**
* @return string
*/
public function getDbPrefix()
{
return $this->dbPrefix;
}
/**
* The `authFunction` is used to bridge your application's user management
* system with ActivityPub. It should be a Callable that takes no arguments
* and returns the ID of the ActivityPub actor associated with the user
* authenticated to the current request, if any. If no such actor exists,
* it should return `false`.
*
* Default: function() { return false; }, i.e. HTTP signatures are the only valid
* authentication mechanism.
* @param Callable $authFunction
* @return ActivityPubConfigBuilder The builder instance
*/
public function setAuthFunction( Callable $authFunction )
{
$this->authFunction = $authFunction;
return $this;
}
/**
* @return Callable
*/
public function getAuthFunction()
{
return $this->authFunction;
}
/**
* The `jsonLdContext` option sets a custom JSON-LD context on all
* objects created by the ActivityPub library. See https://json-ld.org/.
*
* Default: array( 'https://www.w3.org/ns/activitystreams',
* 'https://w3id.org/security/v1' )
* @param array $jsonLdContext
* @return ActivityPubConfigBuilder The builder instance
*/
public function setJsonLdContext( array $jsonLdContext )
{
$this->jsonLdContext = $jsonLdContext;
return $this;
}
public function getJsonLdContext()
{
return $this->jsonLdContext;
}
}
?>

View File

@ -6,6 +6,7 @@ use ActivityPub\Auth\AuthService;
use ActivityPub\Auth\SignatureListener;
use ActivityPub\Controllers\GetController;
use ActivityPub\Controllers\PostController;
use ActivityPub\Config\ActivityPubConfig;
use ActivityPub\Crypto\HttpSignatureService;
use ActivityPub\Database\PrefixNamingStrategy;
use ActivityPub\Http\Router;
@ -34,30 +35,16 @@ class ActivityPubModule
*/
private $injector;
public function __construct( $options )
public function __construct( ActivityPubConfig $config )
{
$defaults = array(
'isDevMode' => false,
'dbPrefix' => '',
'authFunction' => function() {
return false;
},
'context' => array(
'https://www.w3.org/ns/activitystreams',
'https://w3id.org/security/v1',
),
);
$options = array_merge( $defaults, $options );
$this->validateOptions( $options );
$this->injector = new ContainerBuilder;
$dbConfig = Setup::createAnnotationMetadataConfiguration(
array( __DIR__ . '/../Entities' ), $options['isDevMode']
array( __DIR__ . '/../Entities' ), $config->getIsDevMode()
);
$namingStrategy = new PrefixNamingStrategy( $options['dbPrefix'] );
$namingStrategy = new PrefixNamingStrategy( $config->getDbPrefix() );
$dbConfig->setNamingStrategy( $namingStrategy );
$dbParams = $options['dbOptions'];
$dbParams = $config->getDbConnectionParams();
$this->injector->register( EntityManager::class, EntityManager::class )
->setArguments( array( $dbParams, $dbConfig ) )
->setFactory( array( EntityManager::class, 'create' ) );
@ -85,12 +72,12 @@ class ActivityPubModule
->addArgument( new Reference( ObjectsService::class ) );
$this->injector->register( AuthListener::class, AuthListener::class )
->addArgument( $options['authFunction'] );
->addArgument( $config->getAuthFunction() );
$this->injector->register( AuthService::class, AuthService::class );
$this->injector->register( ContextProvider::class, ContextProvider::class )
->addArgument( $options['context'] );
->addArgument( $config->getJsonLdContext() );
$this->injector->register( CollectionsService::class, CollectionsService::class )
->addArgument( self::COLLECTION_PAGE_SIZE )
@ -127,18 +114,5 @@ class ActivityPubModule
{
return $this->injector->get( $id );
}
private function validateOptions( $options )
{
$required = array( 'dbOptions' );
$actual = array_keys( $options );
$missing = array_diff( $required, $actual );
if ( count( $missing ) > 0 ) {
throw new InvalidArgumentException(
'Missing required options: ' . print_r( $missing, t )
);
}
}
}
?>

View File

@ -2,6 +2,7 @@
namespace ActivityPub\Test;
use ActivityPub\ActivityPub;
use ActivityPub\Config\ActivityPubConfig;
use ActivityPub\Test\TestConfig\SQLiteTestCase;
use ActivityPub\Test\TestConfig\ArrayDataSet;
@ -19,12 +20,13 @@ class ActivityPubTest extends SQLiteTestCase
* @depends testItCreatesSchema
*/
public function testItUpdatesSchema() {
$activityPub = new ActivityPub(array(
'dbOptions' => array(
'driver' => 'pdo_sqlite',
'path' => $this->getDbPath(),
),
) );
$config = ActivityPubConfig::createBuilder()
->setDbConnectionParams( array(
'driver' => 'pdo_sqlite',
'path' => $this->getDbPath(),
) )
->build();
$activityPub = new ActivityPub( $config );
$activityPub->updateSchema();
$this->assertTrue( file_exists( $this->getDbPath() ) );
}

View File

@ -1,6 +1,7 @@
<?php
namespace ActivityPub\Test\Config;
use ActivityPub\Config\ActivityPubConfig;
use ActivityPub\Config\ActivityPubModule;
use ActivityPub\Http\Router;
use Doctrine\ORM\EntityManager;
@ -12,13 +13,13 @@ class ActivityPubModuleTest extends TestCase
public function setUp()
{
$opts = array(
'dbOptions' => array(
'driver' => 'pdo_sqlite',
'path' => ':memory:',
),
);
$this->module = new ActivityPubModule( $opts );
$config = ActivityPubConfig::createBuilder()
->setDbConnectionParams( array(
'driver' => 'pdo_sqlite',
'path' => ':memory:',
) )
->build();
$this->module = new ActivityPubModule( $config );
}
public function testItInjects()

View File

@ -2,6 +2,7 @@
namespace ActivityPub\Test\TestConfig;
use ActivityPub\ActivityPub;
use ActivityPub\Config\ActivityPubConfig;
use PHPUnit\Framework\TestCase;
use PHPUnit\DbUnit\TestCaseTrait;
use PHPUnit\DbUnit\Operation\Composite;
@ -22,12 +23,13 @@ abstract class SQLiteTestCase extends TestCase
if ( file_exists( $dbPath ) ) {
unlink( $dbPath );
}
$activityPub = new ActivityPub( array(
'dbOptions' => array(
'driver' => 'pdo_sqlite',
'path' => $dbPath,
),
) );
$config = ActivityPubConfig::createBuilder()
->setDbConnectionParams( array(
'driver' => 'pdo_sqlite',
'path' => $dbPath,
) )
->build();
$activityPub = new ActivityPub( $config );
$activityPub->updateSchema();
}

View File

@ -2,16 +2,18 @@
namespace ActivityPub\Test;
use ActivityPub\ActivityPub;
use ActivityPub\Config\ActivityPubConfig;
$dbPath = dirname( __FILE__ ) . '/../db.sqlite';
if ( file_exists( $dbPath ) ) {
unlink( $dbPath );
}
$activityPub = new ActivityPub( array(
'dbOptions' => array(
$config = ActivityPubConfig::createBuilder()
->setDbConnectionParams( array(
'driver' => 'pdo_sqlite',
'path' => $dbPath,
),
) );
) )
->build();
$activityPub = new ActivityPub( $config );
$activityPub->updateSchema();
?>