Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
126 lines
3.4 KiB
PHP
126 lines
3.4 KiB
PHP
<?php
|
||
|
||
declare(strict_types=1);
|
||
|
||
namespace LIT\MCP\Integrations;
|
||
|
||
use LIT\MCP\Abilities\Ability;
|
||
|
||
defined('ABSPATH') || exit;
|
||
|
||
final class IntegrationRegistry
|
||
{
|
||
/** @var list<Integration> */
|
||
private array $integrations;
|
||
|
||
/** @var list<array{Integration, list<Ability>}>|null Available integrations with their abilities. */
|
||
private ?array $loaded = null;
|
||
|
||
private bool $booted = false;
|
||
|
||
public function __construct(Integration ...$integrations)
|
||
{
|
||
$this->integrations = array_values($integrations);
|
||
}
|
||
|
||
/**
|
||
* Boots every Bootable integration once, in registry order – available or not: Plugin boots on plugins_loaded,
|
||
* before the theme (Divi) is loaded, so isAvailable() is not known yet (as the hooks were registered before
|
||
* Bootable). A second call does nothing.
|
||
*/
|
||
public function boot(): void
|
||
{
|
||
if ($this->booted) {
|
||
return;
|
||
}
|
||
|
||
$this->booted = true;
|
||
foreach ($this->integrations as $integration) {
|
||
if ($integration instanceof Bootable) {
|
||
$integration->boot();
|
||
}
|
||
}
|
||
}
|
||
|
||
/** @return list<Integration> */
|
||
public function available(): array
|
||
{
|
||
return array_values(array_filter(
|
||
$this->integrations,
|
||
static fn (Integration $integration): bool => $integration->isAvailable()
|
||
));
|
||
}
|
||
|
||
/** @return list<Ability> */
|
||
public function abilities(): array
|
||
{
|
||
$abilities = [];
|
||
foreach ($this->loaded() as [, $own]) {
|
||
array_push($abilities, ...$own);
|
||
}
|
||
|
||
return $abilities;
|
||
}
|
||
|
||
/**
|
||
* Abilities of the available integrations, grouped by integration id (registry order).
|
||
*
|
||
* @return array<string, list<Ability>>
|
||
*/
|
||
public function grouped(): array
|
||
{
|
||
$groups = [];
|
||
foreach ($this->loaded() as [$integration, $own]) {
|
||
$groups[$integration->id()] = array_merge($groups[$integration->id()] ?? [], $own);
|
||
}
|
||
|
||
return $groups;
|
||
}
|
||
|
||
/** @return array<string, string> Ability name => integration id (the ability's group). */
|
||
public function groupMap(): array
|
||
{
|
||
$map = [];
|
||
foreach ($this->grouped() as $id => $abilities) {
|
||
foreach ($abilities as $ability) {
|
||
$map[$ability->name()] = $id;
|
||
}
|
||
}
|
||
|
||
return $map;
|
||
}
|
||
|
||
/** @return array<string, string> Integration id => label, of the available integrations. */
|
||
public function labels(): array
|
||
{
|
||
$labels = [];
|
||
foreach ($this->loaded() as [$integration]) {
|
||
$labels[$integration->id()] = $integration->label();
|
||
}
|
||
|
||
return $labels;
|
||
}
|
||
|
||
/** @return list<string> Ids of all known integrations, available or not. */
|
||
public function ids(): array
|
||
{
|
||
return array_values(array_unique(array_map(
|
||
static fn (Integration $integration): string => $integration->id(),
|
||
$this->integrations
|
||
)));
|
||
}
|
||
|
||
/** @return list<array{Integration, list<Ability>}> */
|
||
private function loaded(): array
|
||
{
|
||
if ($this->loaded === null) {
|
||
$this->loaded = array_map(
|
||
static fn (Integration $integration): array => [$integration, $integration->abilities()],
|
||
$this->available()
|
||
);
|
||
}
|
||
|
||
return $this->loaded;
|
||
}
|
||
}
|