2021-06-16 00:17:30 +08:00
|
|
|
|
<?php
|
|
|
|
|
|
|
2022-03-15 18:05:33 +08:00
|
|
|
|
declare(strict_types=1);
|
2021-06-16 00:17:30 +08:00
|
|
|
|
|
|
|
|
|
|
namespace ZM\Utils\Manager;
|
|
|
|
|
|
|
2021-09-01 14:14:00 +08:00
|
|
|
|
use ZM\Config\ZMConfig;
|
2021-06-16 00:17:30 +08:00
|
|
|
|
use ZM\Console\Console;
|
|
|
|
|
|
use ZM\Exception\ModulePackException;
|
|
|
|
|
|
use ZM\Exception\ZMException;
|
2021-09-01 14:14:00 +08:00
|
|
|
|
use ZM\Exception\ZMKnownException;
|
2021-06-16 00:17:30 +08:00
|
|
|
|
use ZM\Module\ModulePacker;
|
|
|
|
|
|
use ZM\Module\ModuleUnpacker;
|
|
|
|
|
|
use ZM\Utils\DataProvider;
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 模块管理器,负责打包解包模块
|
|
|
|
|
|
* Class ModuleManager
|
|
|
|
|
|
* @since 2.5
|
|
|
|
|
|
*/
|
|
|
|
|
|
class ModuleManager
|
|
|
|
|
|
{
|
2022-03-20 16:18:33 +08:00
|
|
|
|
public static function getComposer()
|
|
|
|
|
|
{
|
|
|
|
|
|
return json_decode(file_get_contents(DataProvider::getSourceRootDir() . '/composer.json'), true);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-06-16 00:17:30 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 扫描src目录下的所有已经被标注的模块
|
|
|
|
|
|
* @throws ZMException
|
|
|
|
|
|
*/
|
2022-03-15 18:05:33 +08:00
|
|
|
|
public static function getConfiguredModules(): array
|
|
|
|
|
|
{
|
2022-03-20 16:18:33 +08:00
|
|
|
|
$composer = self::getComposer();
|
2022-03-15 18:05:33 +08:00
|
|
|
|
$dir = DataProvider::getSourceRootDir() . '/src/';
|
2021-06-16 00:17:30 +08:00
|
|
|
|
$ls = DataProvider::scanDirFiles($dir, true, true);
|
|
|
|
|
|
$modules = [];
|
|
|
|
|
|
foreach ($ls as $v) {
|
|
|
|
|
|
$pathinfo = pathinfo($v);
|
2022-03-15 18:05:33 +08:00
|
|
|
|
if ($pathinfo['basename'] == 'zm.json') {
|
|
|
|
|
|
$json = json_decode(file_get_contents(realpath($dir . '/' . $v)), true);
|
|
|
|
|
|
if ($json === null) {
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (!isset($json['name'])) {
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
if ($pathinfo['dirname'] == '.') {
|
|
|
|
|
|
throw new ZMKnownException('E00052', '在/src/目录下不可以直接标记为模块(zm.json),因为命名空间不能为根空间!');
|
2021-06-16 00:17:30 +08:00
|
|
|
|
}
|
2022-03-15 18:05:33 +08:00
|
|
|
|
$json['module-path'] = realpath($dir . '/' . $pathinfo['dirname']);
|
2022-03-20 16:18:33 +08:00
|
|
|
|
|
|
|
|
|
|
$relative_path = str_replace(DataProvider::getSourceRootDir() . '/', '', $json['module-path']);
|
|
|
|
|
|
foreach (array_merge($composer['autoload']['psr-4'] ?? [], $composer['autoload-dev']['psr-4'] ?? []) as $ks => $vs) {
|
|
|
|
|
|
if (strpos($relative_path, $vs) === 0) {
|
|
|
|
|
|
$remain = trim(substr($relative_path, strlen($vs)), '/');
|
|
|
|
|
|
$remain = str_replace('/', '\\', $remain);
|
|
|
|
|
|
$json['namespace'] = $ks . $remain;
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
// $json['namespace'] = str_replace('/', '\\', $pathinfo['dirname']);
|
|
|
|
|
|
|
2022-03-15 18:05:33 +08:00
|
|
|
|
if (isset($modules[$json['name']])) {
|
|
|
|
|
|
throw new ZMKnownException('E00053', '重名模块:' . $json['name']);
|
2021-06-16 00:17:30 +08:00
|
|
|
|
}
|
2022-03-15 18:05:33 +08:00
|
|
|
|
$modules[$json['name']] = $json;
|
2021-06-16 00:17:30 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return $modules;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2022-03-15 18:05:33 +08:00
|
|
|
|
public static function getPackedModules(): array
|
|
|
|
|
|
{
|
|
|
|
|
|
$dir = ZMConfig::get('global', 'module_loader')['load_path'] ?? (ZM_DATA . 'modules');
|
2021-06-16 00:17:30 +08:00
|
|
|
|
$ls = DataProvider::scanDirFiles($dir, true, false);
|
2022-03-15 18:05:33 +08:00
|
|
|
|
if ($ls === false) {
|
|
|
|
|
|
return [];
|
|
|
|
|
|
}
|
2021-06-16 00:17:30 +08:00
|
|
|
|
$modules = [];
|
|
|
|
|
|
foreach ($ls as $v) {
|
|
|
|
|
|
$pathinfo = pathinfo($v);
|
2022-03-15 18:05:33 +08:00
|
|
|
|
if (($pathinfo['extension'] ?? '') != 'phar') {
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
$file = 'phar://' . $v;
|
|
|
|
|
|
if (!is_file($file . '/module_entry.php') || !is_file($file . '/zmplugin.json')) {
|
2021-06-16 00:17:30 +08:00
|
|
|
|
continue;
|
|
|
|
|
|
}
|
2022-03-15 18:05:33 +08:00
|
|
|
|
$module_config = json_decode(file_get_contents($file . '/zmplugin.json'), true);
|
|
|
|
|
|
if ($module_config === null) {
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (!is_file($file . '/' . $module_config['module-root-path'] . '/zm.json')) {
|
|
|
|
|
|
Console::warning(zm_internal_errcode('E00054') . '模块(插件)文件 ' . $pathinfo['basename'] . ' 无法找到模块配置文件(zm.json)!');
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
$module_file = json_decode(file_get_contents($file . '/' . $module_config['module-root-path'] . '/zm.json'), true);
|
2021-06-16 00:17:30 +08:00
|
|
|
|
if ($module_file === null) {
|
2022-03-15 18:05:33 +08:00
|
|
|
|
Console::warning(zm_internal_errcode('E000555') . '模块(插件)文件 ' . $pathinfo['basename'] . ' 无法正常读取模块配置文件(zm.json)!');
|
2021-06-16 00:17:30 +08:00
|
|
|
|
continue;
|
|
|
|
|
|
}
|
2022-03-15 18:05:33 +08:00
|
|
|
|
$module_config['phar-path'] = $v;
|
|
|
|
|
|
$module_config['name'] = $module_file['name'] ?? null;
|
|
|
|
|
|
if ($module_config['name'] === null) {
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
$module_config['module-config'] = $module_file;
|
|
|
|
|
|
$modules[$module_config['name']] = $module_config;
|
2021-06-16 00:17:30 +08:00
|
|
|
|
}
|
|
|
|
|
|
return $modules;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2022-03-21 01:24:07 +08:00
|
|
|
|
public static function getComposerModules()
|
|
|
|
|
|
{
|
|
|
|
|
|
$vendor_file = DataProvider::getSourceRootDir() . '/vendor/composer/installed.json';
|
|
|
|
|
|
$obj = json_decode(file_get_contents($vendor_file), true);
|
|
|
|
|
|
if ($obj === null) {
|
|
|
|
|
|
return [];
|
|
|
|
|
|
}
|
|
|
|
|
|
$modules = [];
|
|
|
|
|
|
foreach ($obj['packages'] as $v) {
|
|
|
|
|
|
if (isset($v['extra']['zm']['module-path'])) {
|
|
|
|
|
|
if (is_array($v['extra']['zm']['module-path'])) {
|
|
|
|
|
|
foreach ($v['extra']['zm']['module-path'] as $module_path) {
|
|
|
|
|
|
$m = self::getComposerModuleInfo($v, $module_path);
|
|
|
|
|
|
if ($m !== null) {
|
|
|
|
|
|
$modules[$m['name']] = $m;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
} elseif (is_string($v['extra']['zm']['module-path'])) {
|
|
|
|
|
|
$m = self::getComposerModuleInfo($v, $v['extra']['zm']['module-path']);
|
|
|
|
|
|
if ($m !== null) {
|
|
|
|
|
|
$modules[$m['name']] = $m;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return $modules;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-06-16 00:17:30 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 打包模块
|
|
|
|
|
|
* @param $module
|
|
|
|
|
|
* @throws ZMException
|
|
|
|
|
|
*/
|
2022-03-15 18:05:33 +08:00
|
|
|
|
public static function packModule($module): bool
|
|
|
|
|
|
{
|
2021-06-16 00:17:30 +08:00
|
|
|
|
try {
|
|
|
|
|
|
$packer = new ModulePacker($module);
|
2022-03-15 18:05:33 +08:00
|
|
|
|
if (!is_dir(DataProvider::getDataFolder())) {
|
|
|
|
|
|
throw new ModulePackException(zm_internal_errcode('E00070') . 'zm_data dir not found!');
|
|
|
|
|
|
}
|
|
|
|
|
|
$path = realpath(DataProvider::getDataFolder() . '/output');
|
|
|
|
|
|
if ($path === false) {
|
|
|
|
|
|
mkdir($path = DataProvider::getDataFolder() . '/output');
|
|
|
|
|
|
}
|
2021-09-01 14:14:00 +08:00
|
|
|
|
$packer->setOutputPath($path);
|
2021-06-16 00:17:30 +08:00
|
|
|
|
$packer->setOverride();
|
|
|
|
|
|
$packer->pack();
|
|
|
|
|
|
return true;
|
|
|
|
|
|
} catch (ModulePackException $e) {
|
|
|
|
|
|
Console::error($e->getMessage());
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2021-07-04 15:45:30 +08:00
|
|
|
|
* 解包模块
|
2021-06-16 00:17:30 +08:00
|
|
|
|
* @param $module
|
|
|
|
|
|
* @return array|false
|
|
|
|
|
|
*/
|
2022-03-15 18:05:33 +08:00
|
|
|
|
public static function unpackModule($module, array $options = [])
|
|
|
|
|
|
{
|
2021-06-16 00:17:30 +08:00
|
|
|
|
try {
|
|
|
|
|
|
$packer = new ModuleUnpacker($module);
|
2022-03-15 18:05:33 +08:00
|
|
|
|
return $packer->unpack((bool) $options['overwrite-light-cache'], (bool) $options['overwrite-zm-data'], (bool) $options['overwrite-source'], (bool) $options['ignore-depends']);
|
2021-07-04 15:45:30 +08:00
|
|
|
|
} catch (ZMException $e) {
|
2021-06-16 00:17:30 +08:00
|
|
|
|
Console::error($e->getMessage());
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2022-03-21 01:24:07 +08:00
|
|
|
|
|
|
|
|
|
|
private static function getComposerModuleInfo($v, $module_path)
|
|
|
|
|
|
{
|
|
|
|
|
|
$module_root_path = realpath(DataProvider::getSourceRootDir() . '/vendor/composer/' . $v['install-path'] . '/' . $module_path);
|
|
|
|
|
|
if ($module_root_path === false) {
|
|
|
|
|
|
Console::warning(zm_internal_errcode('E00055') . '无法找到Composer发布的插件配置路径在包 `' . $v['name'] . '` 中!');
|
|
|
|
|
|
return null;
|
|
|
|
|
|
}
|
|
|
|
|
|
$json = json_decode(file_get_contents($module_root_path . '/zm.json'), true);
|
|
|
|
|
|
if ($json === null) {
|
|
|
|
|
|
Console::warning(zm_internal_errcode('E00054') . 'Composer包内无法正常读取 ' . $v['name'] . ' 的内的配置文件(zm.json)!');
|
|
|
|
|
|
return null;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (!isset($json['name'])) {
|
|
|
|
|
|
return null;
|
|
|
|
|
|
}
|
|
|
|
|
|
$json['composer-name'] = $v['name'];
|
|
|
|
|
|
$json['module-root-path'] = realpath(DataProvider::getSourceRootDir() . '/vendor/composer/' . $v['install-path']);
|
|
|
|
|
|
$json['module-path'] = realpath($json['module-root-path'] . '/' . $module_path);
|
|
|
|
|
|
if (isset($v['autoload']['psr-4'])) {
|
|
|
|
|
|
foreach ($v['autoload']['psr-4'] as $ks => $vs) {
|
|
|
|
|
|
$vs = trim($vs, '/');
|
|
|
|
|
|
if (strpos($module_path, $vs) === 0) {
|
|
|
|
|
|
$json['namespace'] = trim($ks . str_replace('/', '\\', trim(substr($module_path, strlen($vs)), '/')), '\\');
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
if (!isset($json['namespace'])) {
|
|
|
|
|
|
Console::warning(zm_internal_errcode('E00055') . '无法获取Composer发布的模块命名空间!');
|
|
|
|
|
|
return null;
|
|
|
|
|
|
}
|
|
|
|
|
|
return $json;
|
|
|
|
|
|
}
|
2022-03-15 18:05:33 +08:00
|
|
|
|
}
|