Plugin Lifecycle Management โ
Detailed explanation of MineAdmin plugin lifecycle management, including the complete processes of installation, enabling, disabling, updating, and uninstallation.
Lifecycle Overview โ
The lifecycle of MineAdmin plugins includes the following stages:
Plugin Discovery and Loading โ
1. Plugin Discovery Mechanism โ
Core Implementation: Plugin::init()
method called in bin/hyperf.php
(GitHub)
2. Loading Process Details โ
- Scan Plugin Directory: Traverse all subdirectories under
plugin/
- Check Installation Status: Verify existence of
install.lock
file - Read Configuration: Parse
mine.json
configuration file - Load ConfigProvider: Register plugin services to Hyperf container
- Register Routes: Automatically register controller routes
- Load Middleware: Register plugin middleware
- Register Event Listeners: Load event listeners
Download Phase โ
Command Usage โ
# Download specified plugin
php bin/hyperf.php mine-extension:download --name plugin-name
# View downloadable plugin list
php bin/hyperf.php mine-extension:list
2
3
4
5
Download Process โ
- Verify AccessToken: Check
MINE_ACCESS_TOKEN
environment variable - Request Remote Repository: Fetch plugin info from MineAdmin official repository
- Download Plugin Package: Download zip package to local temp directory
- Extract Files: Unzip to
plugin/vendor/plugin-name/
directory - Verify Integrity: Check if
mine.json
exists with correct format
Implementation Principle โ
Core Service: App-Store component (GitHub) provides download functionality
// Pseudo-code example
class DownloadService
{
public function download(string $pluginName): bool
{
// 1. Verify access token
$this->validateAccessToken();
// 2. Get plugin info
$pluginInfo = $this->getPluginInfo($pluginName);
// 3. Download package
$packagePath = $this->downloadPackage($pluginInfo['download_url']);
// 4. Extract to target directory
$this->extractPackage($packagePath, $this->getPluginPath($pluginName));
return true;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Installation Phase โ
Command Usage โ
# Install plugin
php bin/hyperf.php mine-extension:install vendor/plugin-name --yes
# Force reinstall
php bin/hyperf.php mine-extension:install vendor/plugin-name --force
2
3
4
5
Installation Process Details โ
โ ๏ธ Important: Configuration file publishing, environment checks, and database migrations should be handled in
InstallScript
, not relying on ConfigProvider's publish functionality.
1. Pre-installation Checks โ
// Pre-installation check logic
class InstallChecker
{
public function check(string $pluginPath): array
{
$errors = [];
// Check plugin directory
if (!is_dir($pluginPath)) {
$errors[] = 'Plugin directory not found';
}
// Check mine.json
$configPath = $pluginPath . '/mine.json';
if (!file_exists($configPath)) {
$errors[] = 'mine.json config file not found';
}
// Check dependencies
$config = json_decode(file_get_contents($configPath), true);
foreach ($config['require'] ?? [] as $dependency => $version) {
if (!$this->isDependencyMet($dependency, $version)) {
$errors[] = "Dependency {$dependency} version {$version} not satisfied";
}
}
return $errors;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
2. Composer Dependency Installation โ
The installation process handles plugin Composer dependencies:
// Composer config in mine.json
{
"composer": {
"require": {
"hyperf/async-queue": "^3.0",
"symfony/console": "^6.0"
},
"psr-4": {
"Plugin\\Vendor\\PluginName\\": "src"
}
}
}
2
3
4
5
6
7
8
9
10
11
12
The system automatically executes:
composer require hyperf/async-queue:^3.0 symfony/console:^6.0
3. InstallScript Handling โญ โ
Best Practice: Database migrations, config publishing, and environment checks should be handled in
InstallScript
:
// Handle all installation logic in InstallScript
class InstallScript
{
public function handle(): bool
{
// 1. Environment check
if (!$this->checkEnvironment()) {
echo "Environment requirements not met\n";
return false;
}
// 2. Publish config files (not using ConfigProvider publish)
$this->publishConfig();
// 3. Run database migrations
if (!$this->runMigrations()) {
echo "Database migration failed\n";
return false;
}
// 4. Initialize data
$this->seedData();
return true;
}
private function publishConfig(): void
{
$source = __DIR__ . '/../publish/config/plugin.php';
$target = BASE_PATH . '/config/autoload/plugin.php';
if (!file_exists($target)) {
copy($source, $target);
echo "Config file published\n";
}
}
private function runMigrations(): bool
{
$migrationPath = __DIR__ . '/../Database/Migrations';
if (is_dir($migrationPath)) {
// Use Hyperf migration command
$container = \Hyperf\Context\ApplicationContext::getContainer();
$application = $container->get(\Hyperf\Contract\ApplicationInterface::class);
$input = new \Symfony\Component\Console\Input\ArrayInput([
'command' => 'migrate',
'--path' => $migrationPath,
]);
$output = new \Symfony\Component\Console\Output\BufferedOutput();
$exitCode = $application->run($input, $output);
return $exitCode === 0;
}
return true;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
4. Frontend File Copying โ
Copy files from web/
directory to frontend project:
plugin/vendor/plugin-name/web/ โ Frontend project directory
โโโ views/example.vue โ src/views/plugin/vendor/plugin-name/example.vue
โโโ components/ExampleComp.vue โ src/components/plugin/vendor/plugin-name/ExampleComp.vue
โโโ api/example.js โ src/api/plugin/vendor/plugin-name/example.js
2
3
4
5. Config File Publishing โ ๏ธ โ
Note: The
publish
functionality in ConfigProvider is unreliable in plugin system, handle manually in InstallScript:
// Not recommended: publish in ConfigProvider may not work
'publish' => [
// This approach may not execute in plugins
]
// Recommended: Manual publishing in InstallScript
protected function publishConfig(): void
{
$configs = [
[
'source' => __DIR__ . '/../publish/config/plugin.php',
'target' => BASE_PATH . '/config/autoload/plugin.php',
],
[
'source' => __DIR__ . '/../publish/config/routes.php',
'target' => BASE_PATH . '/config/routes/plugin.php',
],
];
foreach ($configs as $config) {
if (!file_exists($config['target'])) {
copy($config['source'], $config['target']);
echo "Config file published: {$config['target']}\n";
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
6. Create Installation Lock File โ
After successful installation, create install.lock
to mark installation status:
plugin/vendor/plugin-name/install.lock
File contains installation info:
{
"installed_at": "2024-01-01 12:00:00",
"version": "1.0.0",
"installer": "admin",
"checksum": "abc123..."
}
2
3
4
5
6
Enable/Disable Management โ
Plugin State Control โ
MineAdmin supports temporarily disabling plugins without uninstalling:
# Disable plugin
php bin/hyperf.php mine-extension:disable vendor/plugin-name
# Enable plugin
php bin/hyperf.php mine-extension:enable vendor/plugin-name
# Check plugin status
php bin/hyperf.php mine-extension:status vendor/plugin-name
2
3
4
5
6
7
8
State Management Mechanism โ
State information stored in install.lock
file:
{
"installed_at": "2024-01-01 12:00:00",
"version": "1.0.0",
"status": "enabled", // enabled | disabled
"disabled_at": null,
"disabled_reason": null
}
2
3
4
5
6
7
Update Phase โ
Update Check โ
# Check for plugin updates
php bin/hyperf.php mine-extension:check-updates
# Update specific plugin
php bin/hyperf.php mine-extension:update vendor/plugin-name
# Update all plugins
php bin/hyperf.php mine-extension:update-all
2
3
4
5
6
7
8
Update Process โ
Version Compatibility Handling โ
Version compatibility checked during update:
class UpdateManager
{
public function checkCompatibility(string $currentVersion, string $newVersion): bool
{
// Check major version compatibility
$current = $this->parseVersion($currentVersion);
$new = $this->parseVersion($newVersion);
// Breaking changes possible with major version change
if ($current['major'] !== $new['major']) {
return $this->checkBreakingChanges($currentVersion, $newVersion);
}
return true;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Uninstallation Phase โ
Command Usage โ
# Uninstall plugin
php bin/hyperf.php mine-extension:uninstall vendor/plugin-name --yes
# Force uninstall (ignore errors)
php bin/hyperf.php mine-extension:uninstall vendor/plugin-name --force
2
3
4
5
Uninstallation Process โ
Uninstall Script Execution โ
// UninstallScript example
class UninstallScript
{
public function handle(): bool
{
try {
// 1. Clean database
$this->cleanDatabase();
// 2. Clean config files
$this->cleanConfigFiles();
// 3. Clean cache data
$this->cleanCache();
// 4. Clean log files
$this->cleanLogs();
// 5. Execute custom cleanup
$this->customCleanup();
return true;
} catch (\Exception $e) {
logger()->error('Plugin uninstall failed: ' . $e->getMessage());
return false;
}
}
private function cleanDatabase(): void
{
// Drop plugin-related tables
DB::statement('DROP TABLE IF EXISTS plugin_example');
// Clean config data
DB::table('system_config')->where('key', 'like', 'plugin.example.%')->delete();
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
Error Handling and Rollback โ
Installation Error Rollback โ
Automatic rollback if errors occur during installation:
class InstallRollback
{
public function rollback(string $pluginPath, array $operations): void
{
foreach (array_reverse($operations) as $operation) {
try {
switch ($operation['type']) {
case 'database':
$this->rollbackDatabase($operation['data']);
break;
case 'files':
$this->rollbackFiles($operation['data']);
break;
case 'config':
$this->rollbackConfig($operation['data']);
break;
}
} catch (\Exception $e) {
logger()->error('Rollback failed: ' . $e->getMessage());
}
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
Dependency Conflict Resolution โ
Handling strategies for plugin dependency conflicts:
class DependencyResolver
{
public function resolveConflicts(array $conflicts): array
{
$solutions = [];
foreach ($conflicts as $conflict) {
$solution = match($conflict['type']) {
'version_conflict' => $this->resolveVersionConflict($conflict),
'circular_dependency' => $this->resolveCircularDependency($conflict),
'missing_dependency' => $this->resolveMissingDependency($conflict),
default => null
};
if ($solution) {
$solutions[] = $solution;
}
}
return $solutions;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
Event System โ
Various plugin lifecycle stages trigger corresponding events:
Event List โ
// Plugin lifecycle events
class PluginEvents
{
const BEFORE_INSTALL = 'plugin.before_install';
const AFTER_INSTALL = 'plugin.after_install';
const BEFORE_UNINSTALL = 'plugin.before_uninstall';
const AFTER_UNINSTALL = 'plugin.after_uninstall';
const BEFORE_UPDATE = 'plugin.before_update';
const AFTER_UPDATE = 'plugin.after_update';
const ENABLED = 'plugin.enabled';
const DISABLED = 'plugin.disabled';
}
2
3
4
5
6
7
8
9
10
11
12
Event Listener Example โ
use Hyperf\Event\Annotation\Listener;
use Hyperf\Event\Contract\ListenerInterface;
#[Listener]
class PluginInstallListener implements ListenerInterface
{
public function listen(): array
{
return [
PluginEvents::AFTER_INSTALL,
];
}
public function process(object $event): void
{
// Post-installation logic
logger()->info('Plugin installed', [
'plugin' => $event->getPluginName(),
'version' => $event->getVersion()
]);
// Clear cache
$this->clearCache($event->getPluginName());
// Send notification
$this->sendNotification($event);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
Status Queries โ
View Plugin Status โ
# View all local plugin statuses
php bin/hyperf.php mine-extension:local-list
# View available remote plugins
php bin/hyperf.php mine-extension:list
# View specific plugin details
php bin/hyperf.php mine-extension:info vendor/plugin-name
2
3
4
5
6
7
8
Status Information Structure โ
{
"name": "vendor/plugin-name",
"version": "
2
3