本腳本適用于無互聯(lián)網(wǎng)環(huán)境,若你的開發(fā)環(huán)境有互聯(lián)網(wǎng),可以退出了。
情景:之前通過互聯(lián)網(wǎng)離線導(dǎo)入了一個項目A,已經(jīng)安裝好全部依賴了,比如 webman/admin依賴。但在離線環(huán)境下,我又想在新的項目B中也安裝webman/admin,通過這個腳本,你可以輕松做到,腳本代碼會在最后全部貼出。
本工具的作用就是將之前已經(jīng)安裝好依賴的composer項目中的依賴提取出來,放到指定目錄下,供其他目錄離線安裝使用
注意,一定要有其他項目已經(jīng)安裝好依賴的,也就是說,目錄下一定要有 composer.lock和vendor目錄
因為無互聯(lián)網(wǎng),無法使用composer require
來安裝需要的包,之前的項目下載的包在vendor目錄下是沒有version信息的,但版本信息保存在了 composer.lock
文件中。
所以,本腳本的目標(biāo)就是,將 composer.lock
文件中的版本信息提取出來,寫入每個包的composer.json
文件中,并且通過下面命令,將每一個包都壓縮成zip文件。
composer archive --format=zip --dir=$dest
打成zip后,就可以搭配上在composer.json中配置離線倉庫就可以使用這些zip的包。
一個包打成zip格式之前,如果json文件中沒有版本信息的話,在使用離線安裝的時候不能做到像有互聯(lián)網(wǎng)那樣遞歸安裝依賴所依賴的其他依賴。
總體就是先提取其他項目的依賴,到指定目錄,新項目需要安裝依賴時,可以直接使用composer require XX 來安裝包,方便快捷
將以下代碼復(fù)制保存到php文件,這里叫 generate_repositories.php
<?php
main(); // 執(zhí)行main()方法
function main()
{
// 因為倉庫是離線的,composer.json 中沒有帶version信息,需要根據(jù)composer.lock中提取版本信息,寫到每個包的composer.json中去,再執(zhí)行 composer archive 操作
// 將每個庫打包成zip包放到zips目錄下
// 會將composer.lock目錄下的vendor包全部拷貝到目標(biāo)目錄下再進(jìn)行修改composer.json,源目錄下的composer.json文件保持不變
$lock_file = "D:/4.PHP/Code/wfdemo/composer.lock"; // 修改為已安裝好依賴項目根目錄下的composer.lock絕對路徑,注意\要改為/
$zip_dest_dir = "D:/4.PHP/Code/wfdemo_repos";// 修改為離線倉庫目錄,再次運行舊的不會清空
run($lock_file, $zip_dest_dir);
}
// 讀取文件內(nèi)容
function read_file($file)
{
return !is_file($file) ? '' : @file_get_contents($file);
}
// 遞歸創(chuàng)建文件夾
function mk_dirs($path, int $mode = 0777)
{
if (!is_dir(dirname($path))) {
mk_dirs(dirname($path));
}
if (!file_exists($path)) {
return mkdir($path, $mode);
}
return true;
}
// 寫文件
function write_file($file, $content)
{
$dir = dirname($file);
if (!is_dir($dir)) {
mk_dirs($dir);
}
return @file_put_contents($file, $content);
}
// 目錄拷貝
function copy_dir(string $source, string $dest, bool $overwrite = false)
{
if (is_dir($source)) {
if (!is_dir($dest)) {
mkdir($dest);
}
$files = scandir($source);
foreach ($files as $file) {
if ($file !== "." && $file !== "..") {
copy_dir("$source/$file", "$dest/$file", $overwrite);
}
}
} else if (file_exists($source) && ($overwrite || !file_exists($dest))) {
copy($source, $dest);
}
}
// 刪除目錄
function remove_dir(string $dir)
{
if (is_link($dir) || is_file($dir)) {
return unlink($dir);
}
$files = array_diff(scandir($dir), array('.', '..'));
foreach ($files as $file) {
(is_dir("$dir/$file") && !is_link($dir)) ? remove_dir("$dir/$file") : unlink("$dir/$file");
}
return rmdir($dir);
}
/**
* @param string $lock_file composer.lock文件絕對地址
* @param string $zip_dest_dir zip文件目標(biāo)目錄,沒有會自動生成
* @return void
*/
function run(string $lock_file, string $zip_dest_dir)
{
$src = dirname($lock_file) . "/vendor";
$json_content = json_decode(read_file($lock_file), true);
$packages = $json_content["packages"];
foreach ($packages as $package) {
$name = $package["name"];
$version = $package["version"];
$target_dir = $zip_dest_dir . "/vendor-temp/$name"; // 在目標(biāo)文件夾下創(chuàng)建vendor-temp緩存目錄
mk_dirs($target_dir);
echo "start -- package name is $name,version is $version \n";
$filenames = glob("$src/$name/composer.json");
foreach ($filenames as $filename) {
if (is_file($filename)) {
$lib_src_dir = dirname($filename);
copy_dir($lib_src_dir, $target_dir, true);
$filename = "$target_dir/composer.json";
writeOneVersion($filename, $version);
archiveOne($filename, $zip_dest_dir);
}
}
echo "end -- package name is $name,version is $version \n\n";
// break;
}
chdir($zip_dest_dir); // 必須切換當(dāng)前目錄,否則remove_dir刪除不了
remove_dir($zip_dest_dir . "/vendor-temp"); // 全部結(jié)束后,刪除vendor-temp目錄
}
/**
* 給一個composer.json文件寫入版本信息
* @param string $filename
* @param string $version
* @return void
*/
function writeOneVersion(string $filename, string $version)
{
$obj = json_decode(read_file($filename));
$obj->version = $version;
$str = json_encode($obj, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT);
write_file($filename, $str);
}
/**
* 打包一個包到目標(biāo)目錄
* @param string $filename 包目錄的composer.json
* @param string $dest 目標(biāo)目錄
* @return void
*/
function archiveOne(string $filename, string $dest)
{
$dir = dirname($filename);
chdir($dir);
echo "current dir is " . getcwd() . "\n";
passthru("composer archive --format=zip --dir=$dest");
}
添加composer 到環(huán)境變量中
修改 php 文件中的main方法的參數(shù)地址
其中,$lock_file 是 composer.lock文件絕對地址,用于提取包的版本信息
$zip_dest_dir 是存放離線zip文件的目錄
修改完后運行php
php generate_repositories.php
生成的zip目錄中,vendor-temp
目錄是臨時用于寫入版本信息的,每次生成都會被同一個包的不同版本重新覆蓋,只有zip目錄下的包才是不重復(fù)的,因為生成時會區(qū)分版本信息。
其他項目修改 composer.json文件,需要配置離線倉庫目錄,
composer.json 離線配置參考,重點是 repositories
配置
{
"minimum-stability": "dev",
"require": {
"php": ">=8.0",
"workerman/webman-framework": "^1.5.0",
"monolog/monolog": "^2.0",
"ext-pdo": "*",
"webman/admin": "^0.6.24",
"webman/console": "^1.3"
},
"repositories": [
{
"packagist.org": false
},
{
"type": "artifact",
"url": "D:/3PHP/repos/zips/"
}
]
}
"packagist.org": false 關(guān)閉去互聯(lián)網(wǎng)請求
下面就是配置剛才生成的zip包目錄地址
然后,就可以使用正常的
composer require webman/admin
來安裝依賴了
如果遇到某些包沒有不成功的,就是剛才生成zip時,沒有生成成功,大概是因為那個包的composer.json文件寫的不規(guī)范,導(dǎo)致 composer
archive 不成功
可以查看輸出時報紅的信息,current package name is 這個包后面的就是當(dāng)前錯誤的包,然后手動去這個包目錄下執(zhí)行
composer archive --format=zip
就會提示錯誤信息,對應(yīng)修改 composer.json
文件即可,生成后,手動復(fù)制到 zip目標(biāo)目錄下。
不錯的思路,學(xué)習(xí)一下