Created
February 27, 2023 16:05
-
-
Save crazywhalecc/1405553fd77dc1ecf10f5093a598e4a8 to your computer and use it in GitHub Desktop.
GitStrategy
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace ZM\Plugin\Strategy; | |
use ZM\Utils\ZMRequest; | |
use ZM\Utils\ZMUtil; | |
class GitStrategy extends PluginInstallStrategy | |
{ | |
private string $git_api_link = 'https://api.github.com/repos/{owner}/{repo}/contents/composer.json'; | |
private string $token = ''; | |
public function install(array $option = []): bool | |
{ | |
// 应用 git_api_link | |
if (isset($option['git-api-link'])) { | |
$this->git_api_link = $option['git-api-link']; | |
} | |
if (isset($option['github-token'])) { | |
$this->token = $option['github-token']; | |
} | |
$git_url = parse_url($this->input); | |
// GitHub 做特殊处理,直接调用 API 检查 | |
$is_github = false; | |
$plugin_name = null; | |
if ($git_url['host'] === 'github.com' && ($option['github-skip-check'] ?? false) !== true) { | |
if (!$this->checkGitAPI($git_url, $plugin_name)) { | |
return false; | |
} | |
$is_github = true; | |
} | |
// 使用 Composer 管理插件,将仓库链接绑定到 composer.json | |
$composer = ZMUtil::getComposerMetadata($this->root_composer_path); | |
$origin_composer = $composer; | |
$already_has_repo = false; | |
// 不破坏原有队列,加入 GitHub 的 repo | |
if (!isset($composer['repositories'])) { | |
$composer['repositories'] = []; | |
} | |
if (is_assoc_array($composer['repositories'])) { | |
$composer['repositories'] = [$composer['repositories']]; | |
} | |
foreach ($composer['repositories'] as $v) { | |
if (($v['url'] ?? '') === $this->input) { | |
$already_has_repo = true; | |
break; | |
} | |
} | |
if (!$already_has_repo) { | |
$composer['repositories'][] = [ | |
'type' => $is_github ? 'github' : 'git', | |
'url' => $this->input, | |
'.belongs' => $plugin_name, | |
]; | |
} | |
// 写入 composer.json | |
if (ZMUtil::putComposerMetadata($this->root_composer_path, $composer) === false) { | |
$this->error = '写入 composer.json 失败'; | |
return false; | |
} | |
$env = getenv('COMPOSER_EXECUTABLE'); | |
if ($env === false) { | |
$env = 'composer'; | |
} | |
if ($plugin_name === null) { | |
$this->error = '没有从 Git 获取到插件的元信息,目前无法从 GitHub 以外的 Git 仓库下载插件,后续会更新!'; | |
ZMUtil::putComposerMetadata($this->root_composer_path, $origin_composer); | |
return false; | |
} | |
if ($plugin_name === '') { | |
$this->error = '获取插件名称失败!'; | |
ZMUtil::putComposerMetadata($this->root_composer_path, $origin_composer); | |
return false; | |
} | |
if (function_exists('pcntl_signal')) { | |
pcntl_signal(SIGINT, function () use ($origin_composer) { | |
echo "强行中断,恢复 Composer 中\n"; | |
ZMUtil::putComposerMetadata($this->root_composer_path, $origin_composer); | |
}); | |
} | |
passthru("$env require $plugin_name", $code); | |
if (function_exists('pcntl_signal')) { | |
pcntl_signal(SIGINT, SIG_IGN); | |
} | |
if ($code !== 0) { | |
$this->error = '使用 composer 引入 Git 插件出现了一些错误,请看上方错误'; | |
ZMUtil::putComposerMetadata($this->root_composer_path, $origin_composer); | |
return false; | |
} | |
return true; | |
} | |
/** | |
* 用于调用 GitHub 的 API 用作不下载就检查插件是否合规 | |
* | |
* @param array $git_url 解析后的链接 | |
* @param string|null $plugin_name | |
* @return bool | |
*/ | |
private function checkGitAPI(array $git_url, ?string &$plugin_name = null): bool | |
{ | |
[, $owner, $repo] = explode('/', $git_url['path']); | |
if (str_ends_with($repo, '.git')) { | |
$repo = substr($repo, 0, -4); | |
} | |
// 调用 HTTP 客户端获取 API 信息 | |
$header = ['User-Agent' => 'zhamao-framework']; | |
if ($this->token !== '') { | |
$header['Authorization'] = 'token ' . $this->token; | |
} | |
$api = ZMRequest::get( | |
str_replace(['{owner}', '{repo}'], [$owner, $repo], $this->git_api_link), | |
$header, | |
only_body: false | |
); | |
if ($api->getStatusCode() !== 200) { | |
$this->error = "GitHub API 请求失败[{$api->getStatusCode()}]"; | |
if ($api->getStatusCode() === 403) { | |
$this->error .= '可能是 API 滥用导致的,建议生成一个 GitHub Token。'; | |
} | |
return false; | |
} | |
// 检查插件的 composer.json 是否合规 | |
$content = json_decode($api->getBody(), true); | |
if (isset($content['message'])) { | |
$this->error = '该 GitHub 仓库中不存在 composer.json 文件!'; | |
return false; | |
} | |
$contents = implode('', array_map(fn($x) => base64_decode($x), explode("\n", $content['content']))); | |
$json = json_decode($contents, true); | |
if (!$this->checkComposerIntegrity($json)) { | |
return false; | |
} | |
$plugin_name = $json['name']; | |
$this->installed_name = $plugin_name; | |
return true; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment