Add dedicated API for apps' bootstrapping process

Signed-off-by: Christoph Wurst <christoph@winzerhof-wurst.at>
This commit is contained in:
Christoph Wurst 2020-05-07 21:10:30 +02:00
parent d1b03f5adf
commit 69571fb536
No known key found for this signature in database
GPG Key ID: CC42AC2A7F0E56D8
14 changed files with 1046 additions and 1 deletions

View File

@ -35,7 +35,6 @@ use OCA\TwoFactorBackupCodes\Listener\RegistryUpdater;
use OCA\TwoFactorBackupCodes\Notifications\Notifier; use OCA\TwoFactorBackupCodes\Notifications\Notifier;
use OCP\AppFramework\App; use OCP\AppFramework\App;
use OCP\Authentication\TwoFactorAuth\IRegistry; use OCP\Authentication\TwoFactorAuth\IRegistry;
use OCP\EventDispatcher\IEventDispatcher;
use OCP\Notification\IManager; use OCP\Notification\IManager;
use OCP\Util; use OCP\Util;

View File

@ -644,6 +644,10 @@ class OC {
OC\Log\ErrorHandler::register($debug); OC\Log\ErrorHandler::register($debug);
} }
/** @var \OC\AppFramework\Bootstrap\Coordinator $bootstrapCoordinator */
$bootstrapCoordinator = \OC::$server->query(\OC\AppFramework\Bootstrap\Coordinator::class);
$bootstrapCoordinator->runRegistration();
\OC::$server->getEventLogger()->start('init_session', 'Initialize session'); \OC::$server->getEventLogger()->start('init_session', 'Initialize session');
OC_App::loadApps(['session']); OC_App::loadApps(['session']);
if (!self::$CLI) { if (!self::$CLI) {

View File

@ -23,6 +23,9 @@ return array(
'OCP\\AppFramework\\ApiController' => $baseDir . '/lib/public/AppFramework/ApiController.php', 'OCP\\AppFramework\\ApiController' => $baseDir . '/lib/public/AppFramework/ApiController.php',
'OCP\\AppFramework\\App' => $baseDir . '/lib/public/AppFramework/App.php', 'OCP\\AppFramework\\App' => $baseDir . '/lib/public/AppFramework/App.php',
'OCP\\AppFramework\\AuthPublicShareController' => $baseDir . '/lib/public/AppFramework/AuthPublicShareController.php', 'OCP\\AppFramework\\AuthPublicShareController' => $baseDir . '/lib/public/AppFramework/AuthPublicShareController.php',
'OCP\\AppFramework\\Bootstrap\\IBootContext' => $baseDir . '/lib/public/AppFramework/Bootstrap/IBootContext.php',
'OCP\\AppFramework\\Bootstrap\\IBootstrap' => $baseDir . '/lib/public/AppFramework/Bootstrap/IBootstrap.php',
'OCP\\AppFramework\\Bootstrap\\IRegistrationContext' => $baseDir . '/lib/public/AppFramework/Bootstrap/IRegistrationContext.php',
'OCP\\AppFramework\\Controller' => $baseDir . '/lib/public/AppFramework/Controller.php', 'OCP\\AppFramework\\Controller' => $baseDir . '/lib/public/AppFramework/Controller.php',
'OCP\\AppFramework\\Db\\DoesNotExistException' => $baseDir . '/lib/public/AppFramework/Db/DoesNotExistException.php', 'OCP\\AppFramework\\Db\\DoesNotExistException' => $baseDir . '/lib/public/AppFramework/Db/DoesNotExistException.php',
'OCP\\AppFramework\\Db\\Entity' => $baseDir . '/lib/public/AppFramework/Db/Entity.php', 'OCP\\AppFramework\\Db\\Entity' => $baseDir . '/lib/public/AppFramework/Db/Entity.php',
@ -538,6 +541,9 @@ return array(
'OC\\AllConfig' => $baseDir . '/lib/private/AllConfig.php', 'OC\\AllConfig' => $baseDir . '/lib/private/AllConfig.php',
'OC\\AppConfig' => $baseDir . '/lib/private/AppConfig.php', 'OC\\AppConfig' => $baseDir . '/lib/private/AppConfig.php',
'OC\\AppFramework\\App' => $baseDir . '/lib/private/AppFramework/App.php', 'OC\\AppFramework\\App' => $baseDir . '/lib/private/AppFramework/App.php',
'OC\\AppFramework\\Bootstrap\\BootContext' => $baseDir . '/lib/private/AppFramework/Bootstrap/BootContext.php',
'OC\\AppFramework\\Bootstrap\\Coordinator' => $baseDir . '/lib/private/AppFramework/Bootstrap/Coordinator.php',
'OC\\AppFramework\\Bootstrap\\RegistrationContext' => $baseDir . '/lib/private/AppFramework/Bootstrap/RegistrationContext.php',
'OC\\AppFramework\\DependencyInjection\\DIContainer' => $baseDir . '/lib/private/AppFramework/DependencyInjection/DIContainer.php', 'OC\\AppFramework\\DependencyInjection\\DIContainer' => $baseDir . '/lib/private/AppFramework/DependencyInjection/DIContainer.php',
'OC\\AppFramework\\Http' => $baseDir . '/lib/private/AppFramework/Http.php', 'OC\\AppFramework\\Http' => $baseDir . '/lib/private/AppFramework/Http.php',
'OC\\AppFramework\\Http\\Dispatcher' => $baseDir . '/lib/private/AppFramework/Http/Dispatcher.php', 'OC\\AppFramework\\Http\\Dispatcher' => $baseDir . '/lib/private/AppFramework/Http/Dispatcher.php',

View File

@ -52,6 +52,9 @@ class ComposerStaticInit53792487c5a8370acc0b06b1a864ff4c
'OCP\\AppFramework\\ApiController' => __DIR__ . '/../../..' . '/lib/public/AppFramework/ApiController.php', 'OCP\\AppFramework\\ApiController' => __DIR__ . '/../../..' . '/lib/public/AppFramework/ApiController.php',
'OCP\\AppFramework\\App' => __DIR__ . '/../../..' . '/lib/public/AppFramework/App.php', 'OCP\\AppFramework\\App' => __DIR__ . '/../../..' . '/lib/public/AppFramework/App.php',
'OCP\\AppFramework\\AuthPublicShareController' => __DIR__ . '/../../..' . '/lib/public/AppFramework/AuthPublicShareController.php', 'OCP\\AppFramework\\AuthPublicShareController' => __DIR__ . '/../../..' . '/lib/public/AppFramework/AuthPublicShareController.php',
'OCP\\AppFramework\\Bootstrap\\IBootContext' => __DIR__ . '/../../..' . '/lib/public/AppFramework/Bootstrap/IBootContext.php',
'OCP\\AppFramework\\Bootstrap\\IBootstrap' => __DIR__ . '/../../..' . '/lib/public/AppFramework/Bootstrap/IBootstrap.php',
'OCP\\AppFramework\\Bootstrap\\IRegistrationContext' => __DIR__ . '/../../..' . '/lib/public/AppFramework/Bootstrap/IRegistrationContext.php',
'OCP\\AppFramework\\Controller' => __DIR__ . '/../../..' . '/lib/public/AppFramework/Controller.php', 'OCP\\AppFramework\\Controller' => __DIR__ . '/../../..' . '/lib/public/AppFramework/Controller.php',
'OCP\\AppFramework\\Db\\DoesNotExistException' => __DIR__ . '/../../..' . '/lib/public/AppFramework/Db/DoesNotExistException.php', 'OCP\\AppFramework\\Db\\DoesNotExistException' => __DIR__ . '/../../..' . '/lib/public/AppFramework/Db/DoesNotExistException.php',
'OCP\\AppFramework\\Db\\Entity' => __DIR__ . '/../../..' . '/lib/public/AppFramework/Db/Entity.php', 'OCP\\AppFramework\\Db\\Entity' => __DIR__ . '/../../..' . '/lib/public/AppFramework/Db/Entity.php',
@ -567,6 +570,9 @@ class ComposerStaticInit53792487c5a8370acc0b06b1a864ff4c
'OC\\AllConfig' => __DIR__ . '/../../..' . '/lib/private/AllConfig.php', 'OC\\AllConfig' => __DIR__ . '/../../..' . '/lib/private/AllConfig.php',
'OC\\AppConfig' => __DIR__ . '/../../..' . '/lib/private/AppConfig.php', 'OC\\AppConfig' => __DIR__ . '/../../..' . '/lib/private/AppConfig.php',
'OC\\AppFramework\\App' => __DIR__ . '/../../..' . '/lib/private/AppFramework/App.php', 'OC\\AppFramework\\App' => __DIR__ . '/../../..' . '/lib/private/AppFramework/App.php',
'OC\\AppFramework\\Bootstrap\\BootContext' => __DIR__ . '/../../..' . '/lib/private/AppFramework/Bootstrap/BootContext.php',
'OC\\AppFramework\\Bootstrap\\Coordinator' => __DIR__ . '/../../..' . '/lib/private/AppFramework/Bootstrap/Coordinator.php',
'OC\\AppFramework\\Bootstrap\\RegistrationContext' => __DIR__ . '/../../..' . '/lib/private/AppFramework/Bootstrap/RegistrationContext.php',
'OC\\AppFramework\\DependencyInjection\\DIContainer' => __DIR__ . '/../../..' . '/lib/private/AppFramework/DependencyInjection/DIContainer.php', 'OC\\AppFramework\\DependencyInjection\\DIContainer' => __DIR__ . '/../../..' . '/lib/private/AppFramework/DependencyInjection/DIContainer.php',
'OC\\AppFramework\\Http' => __DIR__ . '/../../..' . '/lib/private/AppFramework/Http.php', 'OC\\AppFramework\\Http' => __DIR__ . '/../../..' . '/lib/private/AppFramework/Http.php',
'OC\\AppFramework\\Http\\Dispatcher' => __DIR__ . '/../../..' . '/lib/private/AppFramework/Http/Dispatcher.php', 'OC\\AppFramework\\Http\\Dispatcher' => __DIR__ . '/../../..' . '/lib/private/AppFramework/Http/Dispatcher.php',

View File

@ -0,0 +1,48 @@
<?php
declare(strict_types=1);
/**
* @copyright 2020 Christoph Wurst <christoph@winzerhof-wurst.at>
*
* @author 2020 Christoph Wurst <christoph@winzerhof-wurst.at>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace OC\AppFramework\Bootstrap;
use OCP\AppFramework\Bootstrap\IBootContext;
use OCP\AppFramework\IAppContainer;
use OCP\IServerContainer;
class BootContext implements IBootContext {
/** @var IAppContainer */
private $appContainer;
public function __construct(IAppContainer $appContainer) {
$this->appContainer = $appContainer;
}
public function getAppContainer(): IAppContainer {
return $this->appContainer;
}
public function getServerContainer(): IServerContainer {
return $this->appContainer->getServer();
}
}

View File

@ -0,0 +1,130 @@
<?php
declare(strict_types=1);
/**
* @copyright 2020 Christoph Wurst <christoph@winzerhof-wurst.at>
*
* @author 2020 Christoph Wurst <christoph@winzerhof-wurst.at>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace OC\AppFramework\Bootstrap;
use OC_App;
use OCP\AppFramework\App;
use OCP\AppFramework\Bootstrap\IBootstrap;
use OCP\AppFramework\QueryException;
use OCP\EventDispatcher\IEventDispatcher;
use OCP\ILogger;
use OCP\IServerContainer;
use function class_exists;
use function class_implements;
use function in_array;
class Coordinator {
/** @var IServerContainer */
private $serverContainer;
/** @var IEventDispatcher */
private $eventDispatcher;
/** @var ILogger */
private $logger;
public function __construct(IServerContainer $container,
IEventDispatcher $eventListener,
ILogger $logger) {
$this->serverContainer = $container;
$this->eventDispatcher = $eventListener;
$this->logger = $logger;
}
public function runRegistration(): void {
$context = new RegistrationContext($this->logger);
$apps = [];
foreach (OC_App::getEnabledApps() as $appId) {
/*
* First, we have to enable the app's autoloader
*
* @todo use $this->appManager->getAppPath($appId) here
*/
$path = OC_App::getAppPath($appId);
if ($path === false) {
// Ignore
continue;
}
OC_App::registerAutoloading($appId, $path);
/*
* Next we check if there is an application class and it implements
* the \OCP\AppFramework\Bootstrap\IBootstrap interface
*/
$appNameSpace = App::buildAppNamespace($appId);
$applicationClassName = $appNameSpace . '\\AppInfo\\Application';
if (class_exists($applicationClassName) && in_array(IBootstrap::class, class_implements($applicationClassName), true)) {
try {
/** @var IBootstrap|App $application */
$apps[$appId] = $application = $this->serverContainer->query($applicationClassName);
$application->register($context->for($appId));
} catch (QueryException $e) {
// Weird, but ok
}
}
}
/**
* Now that all register methods have been called, we can delegate the registrations
* to the actual services
*/
$context->delegateCapabilityRegistrations($apps);
$context->delegateEventListenerRegistrations($this->eventDispatcher);
$context->delegateContainerRegistrations($apps);
$context->delegateMiddlewareRegistrations($apps);
}
public function bootApp(string $appId): void {
$appNameSpace = App::buildAppNamespace($appId);
$applicationClassName = $appNameSpace . '\\AppInfo\\Application';
if (!class_exists($applicationClassName)) {
// Nothing to boot
return;
}
/*
* Now it is time to fetch an instance of the App class. For classes
* that implement \OCP\AppFramework\Bootstrap\IBootstrap this means
* the instance was already created for register, but any other
* (legacy) code will now do their magic via the constructor.
*/
try {
/** @var App $application */
$application = $this->serverContainer->query($applicationClassName);
if ($application instanceof IBootstrap) {
/** @var BootContext $context */
$context = new BootContext($application->getContainer());
$application->boot($context);
}
} catch (QueryException $e) {
$this->logger->logException($e, [
'message' => "Could not boot $appId" . $e->getMessage(),
]);
return;
}
}
}

View File

@ -0,0 +1,295 @@
<?php
declare(strict_types=1);
/**
* @copyright 2020 Christoph Wurst <christoph@winzerhof-wurst.at>
*
* @author 2020 Christoph Wurst <christoph@winzerhof-wurst.at>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace OC\AppFramework\Bootstrap;
use Closure;
use OCP\AppFramework\App;
use OCP\AppFramework\Bootstrap\IRegistrationContext;
use OCP\EventDispatcher\IEventDispatcher;
use OCP\ILogger;
use Throwable;
class RegistrationContext {
/** @var array[] */
private $capabilities = [];
/** @var array[] */
private $services = [];
/** @var array[] */
private $aliases = [];
/** @var array[] */
private $parameters = [];
/** @var array[] */
private $eventListeners = [];
/** @var array[] */
private $middlewares = [];
/** @var ILogger */
private $logger;
public function __construct(ILogger $logger) {
$this->logger = $logger;
}
public function for(string $appId): IRegistrationContext {
return new class($appId, $this) implements IRegistrationContext {
/** @var string */
private $appId;
/** @var RegistrationContext */
private $context;
public function __construct(string $appId, RegistrationContext $context) {
$this->appId = $appId;
$this->context = $context;
}
public function registerCapability(string $capability): void {
$this->context->registerCapability(
$this->appId,
$capability
);
}
public function registerService(string $name, callable $factory, bool $shared = true): void {
$this->context->registerService(
$this->appId,
$name,
$factory,
$shared
);
}
public function registerServiceAlias(string $alias, string $target): void {
$this->context->registerServiceAlias(
$this->appId,
$alias,
$target
);
}
public function registerParameter(string $name, $value): void {
$this->context->registerParameter(
$this->appId,
$name,
$value
);
}
public function registerEventListener(string $event, string $listener, int $priority = 0): void {
$this->context->registerEventListener(
$this->appId,
$event,
$listener,
$priority
);
}
public function registerMiddleware(string $class): void {
$this->context->registerMiddleware(
$this->appId,
$class
);
}
};
}
public function registerCapability(string $appId, string $capability): void {
$this->capabilities[] = [
'appId' => $appId,
'capability' => $capability
];
}
public function registerService(string $appId, string $name, callable $factory, bool $shared = true): void {
$this->services[] = [
"appId" => $appId,
"name" => $name,
"factory" => $factory,
"sharred" => $shared,
];
}
public function registerServiceAlias(string $appId, string $alias, string $target): void {
$this->aliases[] = [
"appId" => $appId,
"alias" => $alias,
"target" => $target,
];
}
public function registerParameter(string $appId, string $name, $value): void {
$this->parameters[] = [
"appId" => $appId,
"name" => $name,
"value" => $value,
];
}
public function registerEventListener(string $appId, string $event, string $listener, int $priority = 0): void {
$this->eventListeners[] = [
"appId" => $appId,
"event" => $event,
"listener" => $listener,
"priority" => $priority,
];
}
public function registerMiddleware(string $appId, string $class): void {
$this->middlewares[] = [
"appId" => $appId,
"class" => $class,
];
}
/**
* @param App[] $apps
*/
public function delegateCapabilityRegistrations(array $apps): void {
foreach ($this->capabilities as $registration) {
try {
$apps[$registration['appId']]
->getContainer()
->registerCapability($registration['capability']);
} catch (Throwable $e) {
$appId = $registration['appId'];
$this->logger->logException($e, [
'message' => "Error during capability registration of $appId: " . $e->getMessage(),
'level' => ILogger::ERROR,
]);
}
}
}
public function delegateEventListenerRegistrations(IEventDispatcher $eventDispatcher): void {
foreach ($this->eventListeners as $registration) {
try {
if (isset($registration['priority'])) {
$eventDispatcher->addServiceListener(
$registration['event'],
$registration['listener'],
$registration['priority']
);
} else {
$eventDispatcher->addListener(
$registration['event'],
$registration['listener']
);
}
} catch (Throwable $e) {
$appId = $registration['appId'];
$this->logger->logException($e, [
'message' => "Error during event listener registration of $appId: " . $e->getMessage(),
'level' => ILogger::ERROR,
]);
}
}
}
/**
* @param App[] $apps
*/
public function delegateContainerRegistrations(array $apps): void {
foreach ($this->services as $registration) {
try {
/**
* Register the service and convert the callable into a \Closure if necessary
*/
$apps[$registration['appId']]
->getContainer()
->registerService(
$registration['name'],
Closure::fromCallable($registration['factory']),
$registration['shared'] ?? true
);
} catch (Throwable $e) {
$appId = $registration['appId'];
$this->logger->logException($e, [
'message' => "Error during service registration of $appId: " . $e->getMessage(),
'level' => ILogger::ERROR,
]);
}
}
foreach ($this->aliases as $registration) {
try {
$apps[$registration['appId']]
->getContainer()
->registerAlias(
$registration['alias'],
$registration['target']
);
} catch (Throwable $e) {
$appId = $registration['appId'];
$this->logger->logException($e, [
'message' => "Error during service alias registration of $appId: " . $e->getMessage(),
'level' => ILogger::ERROR,
]);
}
}
foreach ($this->parameters as $registration) {
try {
$apps[$registration['appId']]
->getContainer()
->registerParameter(
$registration['name'],
$registration['value']
);
} catch (Throwable $e) {
$appId = $registration['appId'];
$this->logger->logException($e, [
'message' => "Error during service alias registration of $appId: " . $e->getMessage(),
'level' => ILogger::ERROR,
]);
}
}
}
/**
* @param App[] $apps
*/
public function delegateMiddlewareRegistrations(array $apps): void {
foreach ($this->middlewares as $middleware) {
try {
$apps[$middleware['appId']]
->getContainer()
->registerMiddleWare($middleware['class']);
} catch (Throwable $e) {
$appId = $middleware['appId'];
$this->logger->logException($e, [
'message' => "Error during capability registration of $appId: " . $e->getMessage(),
'level' => ILogger::ERROR,
]);
}
}
}
}

View File

@ -149,6 +149,9 @@ class OC_App {
// in case someone calls loadApp() directly // in case someone calls loadApp() directly
self::registerAutoloading($app, $appPath); self::registerAutoloading($app, $appPath);
/** @var \OC\AppFramework\Bootstrap\Coordinator $coordinator */
$coordinator = \OC::$server->query(\OC\AppFramework\Bootstrap\Coordinator::class);
$coordinator->bootApp($app);
if (is_file($appPath . '/appinfo/app.php')) { if (is_file($appPath . '/appinfo/app.php')) {
\OC::$server->getEventLogger()->start('load_app_' . $app, 'Load app: ' . $app); \OC::$server->getEventLogger()->start('load_app_' . $app, 'Load app: ' . $app);
try { try {

View File

@ -0,0 +1,55 @@
<?php
declare(strict_types=1);
/**
* @copyright 2020 Christoph Wurst <christoph@winzerhof-wurst.at>
*
* @author 2020 Christoph Wurst <christoph@winzerhof-wurst.at>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace OCP\AppFramework\Bootstrap;
use OCP\AppFramework\IAppContainer;
use OCP\IServerContainer;
/**
* @since 20.0.0
*/
interface IBootContext {
/**
* Get hold of the app's container
*
* Useful to register and query app-specific services
*
* @return IAppContainer
* @since 20.0.0
*/
public function getAppContainer(): IAppContainer;
/**
* Get hold of the server DI container
*
* Useful to register and query system-wide services
*
* @return IServerContainer
* @since 20.0.0
*/
public function getServerContainer(): IServerContainer;
}

View File

@ -0,0 +1,46 @@
<?php
declare(strict_types=1);
/**
* @copyright 2020 Christoph Wurst <christoph@winzerhof-wurst.at>
*
* @author 2020 Christoph Wurst <christoph@winzerhof-wurst.at>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace OCP\AppFramework\Bootstrap;
/**
* @since 20.0.0
*/
interface IBootstrap {
/**
* @param IRegistrationContext $context
*
* @since 20.0.0
*/
public function register(IRegistrationContext $context): void;
/**
* @param IBootContext $context
*
* @since 20.0.0
*/
public function boot(IBootContext $context): void;
}

View File

@ -0,0 +1,108 @@
<?php
declare(strict_types=1);
/**
* @copyright 2020 Christoph Wurst <christoph@winzerhof-wurst.at>
*
* @author 2020 Christoph Wurst <christoph@winzerhof-wurst.at>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace OCP\AppFramework\Bootstrap;
use OCP\AppFramework\IAppContainer;
use OCP\EventDispatcher\IEventDispatcher;
use OCP\IContainer;
/**
* The context object passed to IBootstrap::register
*
* @since 20.0.0
* @see IBootstrap::register()
*/
interface IRegistrationContext {
/**
* @param string $capability
* @see IAppContainer::registerCapability
*
* @since 20.0.0
*/
public function registerCapability(string $capability): void;
/**
* Register a service
*
* @param string $name
* @param callable $factory
* @param bool $shared
*
* @return void
* @see IContainer::registerService()
*
* @since 20.0.0
*/
public function registerService(string $name, callable $factory, bool $shared = true): void;
/**
* @param string $alias
* @param string $target
*
* @return void
* @see IContainer::registerAlias()
*
* @since 20.0.0
*/
public function registerServiceAlias(string $alias, string $target): void;
/**
* @param string $name
* @param mixed $value
*
* @return void
* @see IContainer::registerParameter()
*
* @since 20.0.0
*/
public function registerParameter(string $name, $value): void;
/**
* Register a service listener
*
* This is equivalent to calling IEventDispatcher::addServiceListener
*
* @param string $event preferably the fully-qualified class name of the Event sub class to listen for
* @param string $listener fully qualified class name (or ::class notation) of a \OCP\EventDispatcher\IEventListener that can be built by the DI container
* @param int $priority
*
* @see IEventDispatcher::addServiceListener()
*
* @since 20.0.0
*/
public function registerEventListener(string $event, string $listener, int $priority = 0): void;
/**
* @param string $class
*
* @return void
* @see IAppContainer::registerMiddleWare()
*
* @since 20.0.0
*/
public function registerMiddleware(string $class): void;
}

View File

@ -0,0 +1,67 @@
<?php
declare(strict_types=1);
/**
* @copyright 2020 Christoph Wurst <christoph@winzerhof-wurst.at>
*
* @author 2020 Christoph Wurst <christoph@winzerhof-wurst.at>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace lib\AppFramework\Bootstrap;
use OC\AppFramework\Bootstrap\BootContext;
use OCP\AppFramework\IAppContainer;
use OCP\IServerContainer;
use PHPUnit\Framework\MockObject\MockObject;
use Test\TestCase;
class BootContextTest extends TestCase {
/** @var IAppContainer|MockObject */
private $appContainer;
/** @var BootContext */
private $context;
protected function setUp(): void {
parent::setUp();
$this->appContainer = $this->createMock(IAppContainer::class);
$this->context = new BootContext(
$this->appContainer
);
}
public function testGetAppContainer(): void {
$container = $this->context->getAppContainer();
$this->assertSame($this->appContainer, $container);
}
public function testGetServerContainer(): void {
$serverContainer = $this->createMock(IServerContainer::class);
$this->appContainer->method('getServer')
->willReturn($serverContainer);
$container = $this->context->getServerContainer();
$this->assertSame($serverContainer, $container);
}
}

View File

@ -0,0 +1,116 @@
<?php
declare(strict_types=1);
/**
* @copyright 2020 Christoph Wurst <christoph@winzerhof-wurst.at>
*
* @author 2020 Christoph Wurst <christoph@winzerhof-wurst.at>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace lib\AppFramework\Bootstrap;
use OC\AppFramework\Bootstrap\Coordinator;
use OCP\App\IAppManager;
use OCP\AppFramework\App;
use OCP\AppFramework\Bootstrap\IBootContext;
use OCP\AppFramework\Bootstrap\IBootstrap;
use OCP\AppFramework\Bootstrap\IRegistrationContext;
use OCP\AppFramework\QueryException;
use OCP\EventDispatcher\IEventDispatcher;
use OCP\ILogger;
use OCP\IServerContainer;
use PHPUnit\Framework\MockObject\MockObject;
use Test\TestCase;
class CoordinatorTest extends TestCase {
/** @var IAppManager|MockObject */
private $appManager;
/** @var IServerContainer|MockObject */
private $serverContainer;
/** @var IEventDispatcher|MockObject */
private $eventDispatcher;
/** @var ILogger|MockObject */
private $logger;
/** @var Coordinator */
private $coordinator;
protected function setUp(): void {
parent::setUp();
$this->appManager = $this->createMock(IAppManager::class);
$this->serverContainer = $this->createMock(IServerContainer::class);
$this->eventDispatcher = $this->createMock(IEventDispatcher::class);
$this->logger = $this->createMock(ILogger::class);
$this->coordinator = new Coordinator(
$this->serverContainer,
$this->eventDispatcher,
$this->logger
);
}
public function testBootAppNotLoadable(): void {
$appId = 'settings';
$this->serverContainer->expects($this->once())
->method('query')
->with(\OCA\Settings\AppInfo\Application::class)
->willThrowException(new QueryException(""));
$this->logger->expects($this->once())
->method('logException');
$this->coordinator->bootApp($appId);
}
public function testBootAppNotBootable(): void {
$appId = 'settings';
$mockApp = $this->createMock(\OCA\Settings\AppInfo\Application::class);
$this->serverContainer->expects($this->once())
->method('query')
->with(\OCA\Settings\AppInfo\Application::class)
->willReturn($mockApp);
$this->coordinator->bootApp($appId);
}
public function testBootApp(): void {
$appId = 'settings';
$mockApp = new class extends App implements IBootstrap {
public function __construct() {
parent::__construct('test', []);
}
public function register(IRegistrationContext $context): void {
}
public function boot(IBootContext $context): void {
}
};
$this->serverContainer->expects($this->once())
->method('query')
->with(\OCA\Settings\AppInfo\Application::class)
->willReturn($mockApp);
$this->coordinator->bootApp($appId);
}
}

View File

@ -0,0 +1,162 @@
<?php
declare(strict_types=1);
/**
* @copyright 2020 Christoph Wurst <christoph@winzerhof-wurst.at>
*
* @author 2020 Christoph Wurst <christoph@winzerhof-wurst.at>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace lib\AppFramework\Bootstrap;
use OC\AppFramework\Bootstrap\RegistrationContext;
use OCP\AppFramework\App;
use OCP\AppFramework\IAppContainer;
use OCP\EventDispatcher\IEventDispatcher;
use OCP\ILogger;
use PHPUnit\Framework\MockObject\MockObject;
use Test\TestCase;
class RegistrationContextTest extends TestCase {
/** @var ILogger|MockObject */
private $logger;
/** @var RegistrationContext */
private $context;
protected function setUp(): void {
parent::setUp();
$this->logger = $this->createMock(ILogger::class);
$this->context = new RegistrationContext(
$this->logger
);
}
public function testRegisterCapability(): void {
$app = $this->createMock(App::class);
$name = 'abc';
$container = $this->createMock(IAppContainer::class);
$app->method('getContainer')
->willReturn($container);
$container->expects($this->once())
->method('registerCapability')
->with($name);
$this->logger->expects($this->never())
->method('logException');
$this->context->for('myapp')->registerCapability($name);
$this->context->delegateCapabilityRegistrations([
'myapp' => $app,
]);
}
public function testRegisterEventListener(): void {
$event = 'abc';
$service = 'def';
$dispatcher = $this->createMock(IEventDispatcher::class);
$dispatcher->expects($this->once())
->method('addServiceListener')
->with($event, $service, 0);
$this->logger->expects($this->never())
->method('logException');
$this->context->for('myapp')->registerEventListener($event, $service);
$this->context->delegateEventListenerRegistrations($dispatcher);
}
public function testRegisterService(): void {
$app = $this->createMock(App::class);
$service = 'abc';
$factory = function () {
return 'def';
};
$container = $this->createMock(IAppContainer::class);
$app->method('getContainer')
->willReturn($container);
$container->expects($this->once())
->method('registerService')
->with($service, $factory, true);
$this->logger->expects($this->never())
->method('logException');
$this->context->for('myapp')->registerService($service, $factory);
$this->context->delegateContainerRegistrations([
'myapp' => $app,
]);
}
public function testRegisterServiceAlias(): void {
$app = $this->createMock(App::class);
$alias = 'abc';
$target = 'def';
$container = $this->createMock(IAppContainer::class);
$app->method('getContainer')
->willReturn($container);
$container->expects($this->once())
->method('registerAlias')
->with($alias, $target);
$this->logger->expects($this->never())
->method('logException');
$this->context->for('myapp')->registerServiceAlias($alias, $target);
$this->context->delegateContainerRegistrations([
'myapp' => $app,
]);
}
public function testRegisterParameter(): void {
$app = $this->createMock(App::class);
$name = 'abc';
$value = 'def';
$container = $this->createMock(IAppContainer::class);
$app->method('getContainer')
->willReturn($container);
$container->expects($this->once())
->method('registerParameter')
->with($name, $value);
$this->logger->expects($this->never())
->method('logException');
$this->context->for('myapp')->registerParameter($name, $value);
$this->context->delegateContainerRegistrations([
'myapp' => $app,
]);
}
public function testRegisterMiddleware(): void {
$app = $this->createMock(App::class);
$name = 'abc';
$container = $this->createMock(IAppContainer::class);
$app->method('getContainer')
->willReturn($container);
$container->expects($this->once())
->method('registerMiddleware')
->with($name);
$this->logger->expects($this->never())
->method('logException');
$this->context->for('myapp')->registerMiddleware($name);
$this->context->delegateMiddlewareRegistrations([
'myapp' => $app,
]);
}
}