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\Module;
|
|
|
|
|
|
|
2021-07-04 15:45:30 +08:00
|
|
|
|
use Jelix\Version\VersionComparator;
|
|
|
|
|
|
use ZM\Config\ZMConfig;
|
|
|
|
|
|
use ZM\Console\Console;
|
|
|
|
|
|
use ZM\Exception\ModulePackException;
|
|
|
|
|
|
use ZM\Exception\ZMException;
|
|
|
|
|
|
use ZM\Store\LightCache;
|
|
|
|
|
|
use ZM\Utils\DataProvider;
|
|
|
|
|
|
use ZM\Utils\Manager\ModuleManager;
|
|
|
|
|
|
|
2021-06-16 00:17:30 +08:00
|
|
|
|
class ModuleUnpacker
|
|
|
|
|
|
{
|
|
|
|
|
|
private $module;
|
|
|
|
|
|
|
2022-03-15 18:05:33 +08:00
|
|
|
|
private $module_config;
|
2021-07-04 15:45:30 +08:00
|
|
|
|
|
2022-03-15 18:05:33 +08:00
|
|
|
|
private $light_cache;
|
2021-07-04 15:45:30 +08:00
|
|
|
|
|
|
|
|
|
|
private $unpack_data_files = [];
|
|
|
|
|
|
|
2022-03-15 18:05:33 +08:00
|
|
|
|
public function __construct(array $module)
|
|
|
|
|
|
{
|
2021-06-16 00:17:30 +08:00
|
|
|
|
$this->module = $module;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 解包模块
|
2022-03-20 16:51:48 +08:00
|
|
|
|
*
|
|
|
|
|
|
* @param mixed $ignore_depends
|
2021-07-04 15:45:30 +08:00
|
|
|
|
* @throws ModulePackException
|
|
|
|
|
|
* @throws ZMException
|
2021-06-16 00:17:30 +08:00
|
|
|
|
*/
|
2022-03-15 18:05:33 +08:00
|
|
|
|
public function unpack(bool $override_light_cache = false, bool $override_data_files = false, bool $override_source = false, $ignore_depends = false): array
|
|
|
|
|
|
{
|
2021-07-04 15:45:30 +08:00
|
|
|
|
$this->checkConfig();
|
2021-09-01 14:14:00 +08:00
|
|
|
|
$this->checkDepends($ignore_depends);
|
2021-07-04 15:45:30 +08:00
|
|
|
|
$this->checkLightCacheStore();
|
|
|
|
|
|
$this->checkZMDataStore();
|
|
|
|
|
|
|
|
|
|
|
|
$this->mergeComposer();
|
|
|
|
|
|
$this->copyZMDataStore($override_data_files);
|
|
|
|
|
|
$this->copyLightCacheStore($override_light_cache);
|
|
|
|
|
|
|
|
|
|
|
|
$this->mergeGlobalConfig();
|
|
|
|
|
|
$this->copySource($override_source);
|
2021-06-16 00:17:30 +08:00
|
|
|
|
return $this->module;
|
|
|
|
|
|
}
|
2021-07-04 15:45:30 +08:00
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 检查模块配置文件是否正确地放在phar包的位置中
|
|
|
|
|
|
*/
|
2022-03-15 18:05:33 +08:00
|
|
|
|
private function checkConfig()
|
|
|
|
|
|
{
|
|
|
|
|
|
$config = 'phar://' . $this->module['phar-path'] . '/' . $this->module['module-root-path'] . '/zm.json';
|
2021-07-04 15:45:30 +08:00
|
|
|
|
$this->module_config = json_decode(file_get_contents($config), true);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 检查模块依赖关系
|
2022-03-20 16:51:48 +08:00
|
|
|
|
*
|
|
|
|
|
|
* @param mixed $ignore_depends
|
2021-07-04 15:45:30 +08:00
|
|
|
|
* @throws ModulePackException
|
|
|
|
|
|
* @throws ZMException
|
|
|
|
|
|
*/
|
2022-03-15 18:05:33 +08:00
|
|
|
|
private function checkDepends($ignore_depends = false)
|
|
|
|
|
|
{
|
2021-07-04 15:45:30 +08:00
|
|
|
|
$configured = ModuleManager::getConfiguredModules();
|
2022-03-15 18:05:33 +08:00
|
|
|
|
$depends = $this->module_config['depends'] ?? [];
|
2021-07-04 15:45:30 +08:00
|
|
|
|
foreach ($depends as $k => $v) {
|
2021-09-01 14:14:00 +08:00
|
|
|
|
if (!isset($configured[$k]) && !$ignore_depends) {
|
2022-03-15 18:05:33 +08:00
|
|
|
|
throw new ModulePackException(zm_internal_errcode('E00064') . '模块 ' . $this->module_config['name'] . " 依赖的模块 {$k} 不存在");
|
2021-07-04 15:45:30 +08:00
|
|
|
|
}
|
2022-03-15 18:05:33 +08:00
|
|
|
|
$current_ver = $configured[$k]['version'] ?? '1.0';
|
2021-09-01 14:14:00 +08:00
|
|
|
|
if (!VersionComparator::compareVersionRange($current_ver, $v) && !$ignore_depends) {
|
2022-03-15 18:05:33 +08:00
|
|
|
|
throw new ModulePackException(zm_internal_errcode('E00063') . '模块 ' . $this->module_config['name'] . " 依赖的模块 {$k} 版本依赖不符合条件(现有版本: " . $current_ver . ', 需求版本: ' . $v . ')');
|
2021-07-04 15:45:30 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 检查 light-cache-store 项是否合规
|
|
|
|
|
|
* @throws ModulePackException
|
|
|
|
|
|
*/
|
2022-03-15 18:05:33 +08:00
|
|
|
|
private function checkLightCacheStore()
|
|
|
|
|
|
{
|
|
|
|
|
|
if (isset($this->module_config['light-cache-store'])) {
|
|
|
|
|
|
$file = json_decode(file_get_contents('phar://' . $this->module['phar-path'] . '/light_cache_store.json'), true);
|
|
|
|
|
|
if ($file === null) {
|
|
|
|
|
|
throw new ModulePackException(zm_internal_errcode('E00065') . '模块系统检测到打包的模块文件中未含有 `light_cache_store.json` 文件');
|
|
|
|
|
|
}
|
2021-07-04 15:45:30 +08:00
|
|
|
|
$this->light_cache = $file;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* @throws ModulePackException
|
|
|
|
|
|
*/
|
2022-03-15 18:05:33 +08:00
|
|
|
|
private function checkZMDataStore()
|
|
|
|
|
|
{
|
|
|
|
|
|
if (is_array($this->module_config['zm-data-store'] ?? null)) {
|
|
|
|
|
|
foreach ($this->module_config['zm-data-store'] as $v) {
|
|
|
|
|
|
if (!file_exists('phar://' . $this->module['phar-path'] . '/zm_data/' . $v)) {
|
|
|
|
|
|
throw new ModulePackException(zm_internal_errcode('E00067') . '压缩包损坏,内部找不到待解压的 zm_data 原始数据');
|
2021-07-04 15:45:30 +08:00
|
|
|
|
}
|
2022-03-15 18:05:33 +08:00
|
|
|
|
$file = 'phar://' . $this->module['phar-path'] . '/zm_data/' . $v;
|
2021-07-04 15:45:30 +08:00
|
|
|
|
if (is_dir($file)) {
|
|
|
|
|
|
$all = DataProvider::scanDirFiles($file, true, true);
|
|
|
|
|
|
foreach ($all as $single) {
|
2022-03-15 18:05:33 +08:00
|
|
|
|
$this->unpack_data_files[$file . '/' . $single] = DataProvider::getDataFolder() . $v . '/' . $single;
|
2021-07-04 15:45:30 +08:00
|
|
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
|
|
$this->unpack_data_files[$file] = DataProvider::getDataFolder() . $v;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2022-03-15 18:05:33 +08:00
|
|
|
|
private function mergeComposer()
|
|
|
|
|
|
{
|
|
|
|
|
|
$composer_file = DataProvider::getWorkingDir() . '/composer.json';
|
|
|
|
|
|
if (!file_exists($composer_file)) {
|
|
|
|
|
|
throw new ModulePackException(zm_internal_errcode('E00068'));
|
|
|
|
|
|
}
|
2021-07-04 15:45:30 +08:00
|
|
|
|
$composer = json_decode(file_get_contents($composer_file), true);
|
2022-03-20 16:51:48 +08:00
|
|
|
|
if (isset($this->module_config['unpack']['composer-autoload-items'])) {
|
|
|
|
|
|
$autoload = $this->module_config['unpack']['composer-autoload-items'];
|
2022-03-15 18:05:33 +08:00
|
|
|
|
if (isset($autoload['psr-4'])) {
|
|
|
|
|
|
Console::info('Adding extended autoload psr-4 for composer');
|
|
|
|
|
|
$composer['autoload']['psr-4'] = isset($composer['autoload']['psr-4']) ? array_merge($composer['autoload']['psr-4'], $autoload['psr-4']) : $autoload['psr-4'];
|
2021-07-04 15:45:30 +08:00
|
|
|
|
}
|
2022-03-15 18:05:33 +08:00
|
|
|
|
if (isset($autoload['files'])) {
|
|
|
|
|
|
Console::info('Adding extended autoload file for composer');
|
|
|
|
|
|
$composer['autoload']['files'] = isset($composer['autoload']['files']) ? array_merge($composer['autoload']['files'], $autoload['files']) : $autoload['files'];
|
2021-07-04 15:45:30 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2022-03-20 16:51:48 +08:00
|
|
|
|
|
2022-03-15 18:05:33 +08:00
|
|
|
|
if (isset($this->module_config['composer-extend-require'])) {
|
|
|
|
|
|
foreach ($this->module_config['composer-extend-require'] as $k => $v) {
|
|
|
|
|
|
Console::info('Adding extended required composer library: ' . $k);
|
|
|
|
|
|
if (!isset($composer[$k])) {
|
|
|
|
|
|
$composer[$k] = $v;
|
|
|
|
|
|
}
|
2021-07-04 15:45:30 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
file_put_contents($composer_file, json_encode($composer, 64 | 128 | 256));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2022-03-15 18:05:33 +08:00
|
|
|
|
* @param mixed $override_data
|
2021-07-04 15:45:30 +08:00
|
|
|
|
* @throws ModulePackException
|
|
|
|
|
|
*/
|
2022-03-15 18:05:33 +08:00
|
|
|
|
private function copyZMDataStore($override_data)
|
|
|
|
|
|
{
|
2021-07-04 15:45:30 +08:00
|
|
|
|
foreach ($this->unpack_data_files as $k => $v) {
|
|
|
|
|
|
$pathinfo = pathinfo($v);
|
2022-03-15 18:05:33 +08:00
|
|
|
|
if (!is_dir($pathinfo['dirname'])) {
|
|
|
|
|
|
@mkdir($pathinfo['dirname'], 0755, true);
|
|
|
|
|
|
}
|
2021-07-04 15:45:30 +08:00
|
|
|
|
if (is_file($v) && $override_data !== true) {
|
2022-03-15 18:05:33 +08:00
|
|
|
|
Console::info('Skipping zm_data file (not overwriting): ' . $v);
|
2021-07-04 15:45:30 +08:00
|
|
|
|
continue;
|
|
|
|
|
|
}
|
2022-03-15 18:05:33 +08:00
|
|
|
|
Console::info('Copying zm_data file: ' . $v);
|
2021-07-04 15:45:30 +08:00
|
|
|
|
if (copy($k, $v) !== true) {
|
2022-03-15 18:05:33 +08:00
|
|
|
|
throw new ModulePackException(zm_internal_errcode('E00068') . 'Cannot copy file: ' . $v);
|
2021-07-04 15:45:30 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2022-03-15 18:05:33 +08:00
|
|
|
|
private function copyLightCacheStore($override)
|
|
|
|
|
|
{
|
2021-07-04 15:45:30 +08:00
|
|
|
|
$r = ZMConfig::get('global', 'light_cache') ?? [
|
2022-03-20 16:20:14 +08:00
|
|
|
|
'size' => 512, // 最多允许储存的条数(需要2的倍数)
|
|
|
|
|
|
'max_strlen' => 32768, // 单行字符串最大长度(需要2的倍数)
|
|
|
|
|
|
'hash_conflict_proportion' => 0.6, // Hash冲突率(越大越好,但是需要的内存更多)
|
2022-03-15 18:05:33 +08:00
|
|
|
|
'persistence_path' => DataProvider::getDataFolder() . '_cache.json',
|
|
|
|
|
|
'auto_save_interval' => 900,
|
|
|
|
|
|
];
|
2021-07-04 15:45:30 +08:00
|
|
|
|
LightCache::init($r);
|
|
|
|
|
|
foreach (($this->light_cache ?? []) as $k => $v) {
|
2022-03-15 18:05:33 +08:00
|
|
|
|
if (LightCache::isset($k) && $override !== true) {
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
2021-07-04 15:45:30 +08:00
|
|
|
|
LightCache::addPersistence($k);
|
|
|
|
|
|
LightCache::set($k, $v);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2022-03-15 18:05:33 +08:00
|
|
|
|
private function mergeGlobalConfig()
|
|
|
|
|
|
{
|
|
|
|
|
|
if ($this->module['unpack']['global-config-override'] !== false) {
|
|
|
|
|
|
$prompt = !is_string($this->module['unpack']['global-config-override']) ? '请根据模块提供者提供的要求进行修改 global.php 中对应的配置项' : $this->module['unpack']['global-config-override'];
|
|
|
|
|
|
Console::warning('模块作者要求用户手动修改 global.php 配置文件中的项目:');
|
|
|
|
|
|
Console::warning('*' . $prompt);
|
|
|
|
|
|
echo Console::setColor('请输入修改模式,y(使用vim修改)/e(自行使用其他编辑器修改后确认)/N(默认暂不修改):[y/e/N] ', 'gold');
|
2021-07-04 15:45:30 +08:00
|
|
|
|
$r = strtolower(trim(fgets(STDIN)));
|
|
|
|
|
|
switch ($r) {
|
2022-03-15 18:05:33 +08:00
|
|
|
|
case 'y':
|
|
|
|
|
|
system('vim ' . escapeshellarg(DataProvider::getWorkingDir() . '/config/global.php') . ' > `tty`');
|
|
|
|
|
|
Console::info('已使用 vim 修改!');
|
2021-07-04 15:45:30 +08:00
|
|
|
|
break;
|
2022-03-15 18:05:33 +08:00
|
|
|
|
case 'e':
|
|
|
|
|
|
echo Console::setColor('请修改后文件点击回车即可继续 [Enter] ', 'gold');
|
2021-07-04 15:45:30 +08:00
|
|
|
|
fgets(STDIN);
|
|
|
|
|
|
break;
|
2022-03-15 18:05:33 +08:00
|
|
|
|
case 'n':
|
|
|
|
|
|
Console::info('暂不修改 global.php');
|
2021-07-04 15:45:30 +08:00
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2022-03-15 18:05:33 +08:00
|
|
|
|
private function copySource(bool $override_source)
|
|
|
|
|
|
{
|
|
|
|
|
|
$origin_base = 'phar://' . $this->module['phar-path'] . '/' . $this->module['module-root-path'];
|
2021-07-04 15:45:30 +08:00
|
|
|
|
$dir = DataProvider::scanDirFiles($origin_base, true, true);
|
2022-03-15 18:05:33 +08:00
|
|
|
|
$base = DataProvider::getSourceRootDir() . '/' . $this->module['module-root-path'];
|
2021-07-04 15:45:30 +08:00
|
|
|
|
foreach ($dir as $v) {
|
2022-03-15 18:05:33 +08:00
|
|
|
|
$info = pathinfo($base . '/' . $v);
|
|
|
|
|
|
if (!is_dir($info['dirname'])) {
|
|
|
|
|
|
@mkdir($info['dirname'], 0755, true);
|
2021-07-04 15:45:30 +08:00
|
|
|
|
}
|
2022-03-15 18:05:33 +08:00
|
|
|
|
if (is_file($base . '/' . $v) && $override_source !== true) {
|
|
|
|
|
|
Console::info('Skipping source file (not overwriting): ' . $v);
|
2021-07-04 15:45:30 +08:00
|
|
|
|
continue;
|
|
|
|
|
|
}
|
2022-03-15 18:05:33 +08:00
|
|
|
|
Console::info('Releasing source file: ' . $this->module['module-root-path'] . '/' . $v);
|
2021-07-04 15:45:30 +08:00
|
|
|
|
|
2022-03-15 18:05:33 +08:00
|
|
|
|
if (copy($origin_base . '/' . $v, $base . '/' . $v) !== true) {
|
|
|
|
|
|
throw new ModulePackException(zm_internal_errcode('E00068') . 'Cannot copy file: ' . $v);
|
2021-07-04 15:45:30 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2022-03-15 18:05:33 +08:00
|
|
|
|
}
|