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.lockfile - Read Configuration: Parse
mine.jsonconfiguration 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:list2
3
4
5
Download Process
- Verify AccessToken: Check
MINE_ACCESS_TOKENenvironment 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.jsonexists 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 --force2
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.03. 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.js2
3
4
5. Config File Publishing ⚠️
Note: The
publishfunctionality 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.lockFile 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-name2
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-all2
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 --force2
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-name2
3
4
5
6
7
8
Status Information Structure
{
"name": "vendor/plugin-name",
"version": "2
3