Plugin Lifecycle Management
Detailed introduction to MineAdmin plugin lifecycle management, covering the complete process of installation, enabling, disabling, updating, and uninstallation.
Lifecycle Overview
The lifecycle of a MineAdmin plugin 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: Iterate through all subdirectories under
plugin/ - Check Installation Status: Verify if
install.lockfile exists - Read Configuration: Parse
mine.jsonconfiguration file - Load ConfigProvider: Register plugin services into the Hyperf container
- Register Routes: Automatically register controller routes
- Load Middleware: Register plugin middleware
- Register Event Listeners: Load event listeners
Download Phase
Command Usage
# Download a specific plugin
php bin/hyperf.php mine-extension:download --name plugin-name
# View downloadable plugins list
php bin/hyperf.php mine-extension:list2
3
4
5
Download Process
- Validate AccessToken: Check the
MINE_ACCESS_TOKENenvironment variable - Request Remote Repository: Fetch plugin information from the official MineAdmin repository
- Download Plugin Package: Download the compressed package to a local temporary directory
- Extract Files: Extract to
plugin/vendor/plugin-name/directory - Verify Integrity: Check if
mine.jsonfile exists and is properly formatted
Implementation Principle
Core Service: App-Store component (GitHub) provides download functionality
// Pseudo-code example
class DownloadService
{
public function download(string $pluginName): bool
{
// 1. Validate access token
$this->validateAccessToken();
// 2. Get plugin info
$pluginInfo = $this->getPluginInfo($pluginName);
// 3. Download plugin 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 a plugin
php bin/hyperf.php mine-extension:install vendor/plugin-name --yes
# Force reinstallation
php bin/hyperf.php mine-extension:install vendor/plugin-name --force2
3
4
5
Installation Process Details
⚠️ Important Note: Configuration file publishing, environment detection, and database migrations should be handled in
InstallScript, not relying on ConfigProvider's publish functionality.
1. Prerequisite Checks
// Installation check logic
class InstallChecker
{
public function check(string $pluginPath): array
{
$errors = [];
// Check plugin directory
if (!is_dir($pluginPath)) {
$errors[] = 'Plugin directory does not exist';
}
// Check mine.json
$configPath = $pluginPath . '/mine.json';
if (!file_exists($configPath)) {
$errors[] = 'mine.json configuration file does not exist';
}
// 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 the plugin's Composer dependencies:
// composer configuration 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 will automatically execute:
composer require hyperf/async-queue:^3.0 symfony/console:^6.03. InstallScript Handling ⭐
Best Practice: Database migrations, configuration publishing, and environment detection should be handled in
InstallScript:
// Handle all installation logic in InstallScript
class InstallScript
{
public function handle(): bool
{
// 1. Environment detection
if (!$this->checkEnvironment()) {
echo "Environment requirements not met\n";
return false;
}
// 2. Publish configuration files (do not use ConfigProvider's publish)
$this->publishConfig();
// 3. Execute 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 "Configuration file published\n";
}
}
private function runMigrations(): bool
{
$migrationPath = __DIR__ . '/../Database/Migrations';
if (is_dir($migrationPath)) {
// Use Hyperf's 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 the web/ directory to the frontend project:
plugin/vendor/plugin-name/web/ → Frontend project corresponding 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.js2
3
4
5. Configuration File Publishing ⚠️
Note: The
publishfunctionality in ConfigProvider is unreliable in the plugin system and should be handled manually in InstallScript:
// Not recommended: ConfigProvider's publish may not take effect
'publish' => [
// This method may not execute in plugins
]
// Recommended: Publish manually 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 "Configuration 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. Creating the Installation Lock File
After successful installation, create the install.lock file to mark the installation status:
plugin/vendor/plugin-name/install.lockFile content contains installation information:
{
"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 Status Control
MineAdmin supports temporarily disabling plugins without uninstalling them:
# Disable a plugin
php bin/hyperf.php mine-extension:disable vendor/plugin-name
# Enable a plugin
php bin/hyperf.php mine-extension:enable vendor/plugin-name
# Check plugin status
php bin/hyperf.php mine-extension:status vendor/plugin-name2
3
4
5
6
7
8
Status Management Mechanism
Status information is stored in the 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 a specific plugin
php bin/hyperf.php mine-extension:update vendor/plugin-name
# Update all plugins
php bin/hyperf.php mine-extension:update-all2
3
4
5
6
7
8
Update Process
Version Compatibility Handling
Version compatibility is checked during updates:
class UpdateManager
{
public function checkCompatibility(string $currentVersion, string $newVersion): bool
{
// Check major version compatibility
$current = $this->parseVersion($currentVersion);
$new = $this->parseVersion($newVersion);
// Different major versions may contain breaking changes
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 a 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 --force2
3
4
5
Uninstallation Process
Uninstallation Script Execution
// UninstallScript example
class UninstallScript
{
public function handle(): bool
{
try {
// 1. Clean database
$this->cleanDatabase();
// 2. Clean configuration files
$this->cleanConfigFiles();
// 3. Clean cached data
$this->cleanCache();
// 4. Clean log files
$this->cleanLogs();
// 5. Execute custom cleanup logic
$this->customCleanup();
return true;
} catch (\Exception $e) {
logger()->error('Plugin uninstall failed: ' . $e->getMessage());
return false;
}
}
private function cleanDatabase(): void
{
// Delete plugin-related tables
DB::statement('DROP TABLE IF EXISTS plugin_example');
// Clean configuration 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
If an error occurs during the installation, the system will automatically roll back:
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 operation 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 Handling
Strategy for handling dependency conflicts between plugins:
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
Each stage of the plugin lifecycle triggers 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 installation complete', [
'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 Query
View Plugin Status
# View status of all local plugins
php bin/hyperf.php mine-extension:local-list
# View remotely available plugins
php bin/hyperf.php mine-extension:list
# View specific plugin details
php bin/hyperf.php mine-extension:info vendor/plugin-name2
3
4
5
6
7
8
Status Information Structure
{
"name": "vendor/plugin-name",
"version": "1.0.0",
"status": "enabled",
"installed_at": "2024-01-01 12:00:00",
"last_updated": "2024-01-15 10:30:00",
"dependencies": [
"vendor/dependency-plugin"
],
"dependents": [
"vendor/dependent-plugin"
],
"file_integrity": "valid",
"database_status": "migrated"
}2
3
4
5
6
7
8
9
10
11
12
13
14
15
Best Practices
1. Install Script Design
- Achieve idempotence: consistent results with multiple executions
- Provide detailed error messages
- Support transaction rollback
- Log operations
2. Uninstall Script Design
- Completely clean plugin data
- Provide option to backup important user data
- Handle dependencies
- Graceful degradation
3. Version Management
- Follow semantic versioning conventions
- Provide upgrade path descriptions
- Mark breaking changes
- Maintain a changelog
Related Documentation
- Plugin Development Guide - Development process
- Plugin Structure Description - Directory structure
- API Reference - Interface documentation
- Example Code - Practical examples