flysystem-offload/src/Plugin.php

166 lines
6.0 KiB
PHP

<?php
declare(strict_types=1);
namespace FlysystemOffload;
use FlysystemOffload\Admin\HealthCheck;
use FlysystemOffload\Config\ConfigLoader;
use FlysystemOffload\Filesystem\FilesystemFactory;
use FlysystemOffload\Media\MediaHooks;
use FlysystemOffload\Settings\SettingsPage;
use FlysystemOffload\StreamWrapper\FlysystemStreamWrapper;
use League\Flysystem\FilesystemOperator;
use League\Flysystem\Visibility;
final class Plugin {
private static bool $bootstrapped = false;
private static ?Plugin $instance = null;
private array $config;
private FilesystemOperator $filesystem;
private MediaHooks $mediaHooks;
private ?SettingsPage $settingsPage;
private HealthCheck $healthCheck;
private function __construct(
array $config,
FilesystemOperator $filesystem,
MediaHooks $mediaHooks,
?SettingsPage $settingsPage,
HealthCheck $healthCheck
) {
$this->config = $config;
$this->filesystem = $filesystem;
$this->mediaHooks = $mediaHooks;
$this->settingsPage = $settingsPage;
$this->healthCheck = $healthCheck;
}
public static function bootstrap(): void {
if (self::$bootstrapped) {
return;
}
$configDirectory = \defined('FLYSYSTEM_OFFLOAD_CONFIG_PATH')
? FLYSYSTEM_OFFLOAD_CONFIG_PATH
: \dirname(__DIR__) . '/config';
$configLoader = new ConfigLoader($configDirectory);
$config = self::normaliseConfig($configLoader->load());
$filesystemFactory = new FilesystemFactory();
$filesystem = $filesystemFactory->make($config);
FlysystemStreamWrapper::register(
$filesystem,
$config['stream']['protocol'],
$config['stream']['root_prefix'],
$config['visibility']
);
$mediaHooks = new MediaHooks($filesystem, $config);
$mediaHooks->register();
$settingsPage = null;
if (! empty($config['admin']['enabled']) && \is_admin()) {
$settingsPage = new SettingsPage($filesystem, $config);
$settingsPage->register();
}
$healthCheck = new HealthCheck($filesystem, $config);
$healthCheck->register();
self::$instance = new self($config, $filesystem, $mediaHooks, $settingsPage, $healthCheck);
self::$bootstrapped = true;
}
public static function instance(): ?self {
return self::$instance;
}
public function config(): array {
return $this->config;
}
public function filesystem(): FilesystemOperator {
return $this->filesystem;
}
private static function normaliseConfig(array $config): array {
$defaults = [
'driver' => 's3',
'visibility' => Visibility::PUBLIC,
'cache_ttl' => 900,
'stream' => [
'protocol' => 'flysystem',
'root_prefix' => '',
'host' => 'uploads',
],
'uploads' => [
'base_url' => '',
'delete_remote' => true,
'prefer_local_for_missing' => false,
'cache_control' => 'public, max-age=31536000, immutable',
'expires' => null,
'expires_ttl' => 31536000,
],
'admin' => [
'enabled' => false,
],
's3' => [
'acl_public' => 'public-read',
'acl_private' => 'private',
'default_options' => [],
],
];
$config = array_replace_recursive($defaults, $config);
$config['visibility'] = self::normaliseVisibility((string) ($config['visibility'] ?? Visibility::PUBLIC));
$config['stream']['protocol'] = self::sanitizeProtocol((string) $config['stream']['protocol']);
$config['stream']['root_prefix'] = self::normalizePathSegment((string) $config['stream']['root_prefix']);
$config['stream']['host'] = self::normalizePathSegment((string) $config['stream']['host']) ?: 'uploads';
if (empty($config['uploads']['base_url'])) {
$config['uploads']['base_url'] = rtrim(content_url('uploads'), '/');
} else {
$config['uploads']['base_url'] = rtrim((string) $config['uploads']['base_url'], '/');
}
$config['uploads']['delete_remote'] = (bool) $config['uploads']['delete_remote'];
$config['uploads']['prefer_local_for_missing'] = (bool) $config['uploads']['prefer_local_for_missing'];
$config['uploads']['cache_control'] = trim((string) $config['uploads']['cache_control']);
$config['uploads']['expires'] = $config['uploads']['expires']
? trim((string) $config['uploads']['expires'])
: null;
$config['uploads']['expires_ttl'] = max(0, (int) ($config['uploads']['expires_ttl'] ?? 0));
$config['s3']['acl_public'] = (string) ($config['s3']['acl_public'] ?? 'public-read');
$config['s3']['acl_private'] = (string) ($config['s3']['acl_private'] ?? 'private');
$config['s3']['default_options'] = is_array($config['s3']['default_options'])
? $config['s3']['default_options']
: [];
return $config;
}
private static function normaliseVisibility(string $visibility): string {
$visibility = \strtolower($visibility);
return $visibility === Visibility::PRIVATE
? Visibility::PRIVATE
: Visibility::PUBLIC;
}
private static function sanitizeProtocol(string $protocol): string {
$protocol = \preg_replace('/[^A-Za-z0-9_\-]/', '', $protocol) ?? 'flysystem';
$protocol = \strtolower($protocol);
return $protocol !== '' ? $protocol : 'flysystem';
}
private static function normalizePathSegment(string $segment): string {
return \trim($segment, " \t\n\r\0\x0B/");
}
}