chore: 重写初始提交(清空历史,整理后全量提交)
This commit is contained in:
@@ -0,0 +1,106 @@
|
||||
<?php
|
||||
/**
|
||||
* 模板皮肤安装器(后台「安装模板」/ appmall 下载后部署的接入点)。
|
||||
*
|
||||
* 模板包 zip 由 scripts/package_template.php 产出,内部两段前缀:
|
||||
* - templates/<name>/... → 部署到 <root>/templates/<name>/
|
||||
* - static/... → 部署到 <root>/public/static/templates/<name>/
|
||||
*
|
||||
* 模板不携带 DB 迁移,安装即文件落地;与插件(Addon)的 importsql 机制解耦。
|
||||
*/
|
||||
namespace ywxapp\library;
|
||||
|
||||
class TemplateInstaller
|
||||
{
|
||||
/**
|
||||
* 从 zip 部署模板到项目。
|
||||
*
|
||||
* @param string $zipPath 模板包 zip 绝对路径
|
||||
* @param string $rootDir 项目根目录(含 templates/ 与 public/)
|
||||
* @return array 部署后的清单 ['name','version','target_addon','tpl_dir','static_dir']
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
public static function install(string $zipPath, string $rootDir): array
|
||||
{
|
||||
if (!is_file($zipPath)) {
|
||||
throw new \RuntimeException("模板包不存在: {$zipPath}");
|
||||
}
|
||||
if (!class_exists('ZipArchive')) {
|
||||
throw new \RuntimeException('ZipArchive 扩展不可用');
|
||||
}
|
||||
|
||||
$zip = new ZipArchive();
|
||||
if ($zip->open($zipPath) !== true) {
|
||||
throw new \RuntimeException("无法打开模板包: {$zipPath}");
|
||||
}
|
||||
|
||||
// 先探测模板名(templates/<name>/ 前缀)
|
||||
$name = '';
|
||||
for ($i = 0; $i < $zip->numFiles; $i++) {
|
||||
$entry = $zip->getNameIndex($i);
|
||||
if (preg_match('#^templates/([^/]+)/#', $entry, $m)) {
|
||||
$name = $m[1];
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ($name === '') {
|
||||
$zip->close();
|
||||
throw new \RuntimeException('zip 中未找到 templates/<name>/ 结构,可能不是合法模板包');
|
||||
}
|
||||
|
||||
$tplTarget = rtrim($rootDir, '/\\') . '/templates/' . $name . '/';
|
||||
$staticTarget = rtrim($rootDir, '/\\') . '/public/static/templates/' . $name . '/';
|
||||
|
||||
for ($i = 0; $i < $zip->numFiles; $i++) {
|
||||
$entry = $zip->getNameIndex($i);
|
||||
if ($entry === '' || $entry === null) {
|
||||
continue;
|
||||
}
|
||||
$prefix = 'templates/' . $name . '/';
|
||||
if (strpos($entry, $prefix) === 0) {
|
||||
self::writeEntry($zip, $entry, $tplTarget . substr($entry, strlen($prefix)));
|
||||
} elseif (strpos($entry, 'static/') === 0) {
|
||||
$rel = substr($entry, strlen('static/'));
|
||||
if ($rel === '') {
|
||||
continue;
|
||||
}
|
||||
self::writeEntry($zip, $entry, $staticTarget . $rel);
|
||||
}
|
||||
}
|
||||
$zip->close();
|
||||
|
||||
$metaFile = $tplTarget . 'template.json';
|
||||
$meta = is_file($metaFile) ? (json_decode(file_get_contents($metaFile), true) ?? []) : [];
|
||||
|
||||
return [
|
||||
'name' => $name,
|
||||
'version' => $meta['version'] ?? '',
|
||||
'title' => $meta['title'] ?? $name,
|
||||
'target_addon' => $meta['target_addon'] ?? [],
|
||||
'tpl_dir' => $tplTarget,
|
||||
'static_dir' => $staticTarget,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 将 zip 内单个条目写出到目标路径(目录建目录,文件写内容)。
|
||||
*/
|
||||
protected static function writeEntry(ZipArchive $zip, string $entry, string $target): void
|
||||
{
|
||||
if (substr($entry, -1) === '/') {
|
||||
if (!is_dir($target)) {
|
||||
@mkdir($target, 0755, true);
|
||||
}
|
||||
return;
|
||||
}
|
||||
$dir = dirname($target);
|
||||
if (!is_dir($dir)) {
|
||||
@mkdir($dir, 0755, true);
|
||||
}
|
||||
$content = $zip->getFromName($entry);
|
||||
if ($content === false) {
|
||||
return;
|
||||
}
|
||||
file_put_contents($target, $content);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user