实现自定义二进制打包命令

sdffghy

最近尝试webman的二进制打包,确实很方便,但是也出现了一些问题。

比如:使用phinx数据库迁移,多语言,上传文件至public目录等,目前我遇到的都是资源读取问题。

这些问题解决非常简单,常常只需要将配置项的base_path()变成base_path(false)然后将目录拷贝至webman.bin同目录就能解决,其他错误情况也类似,不过对部署上线就非常麻烦了。

通过编写一个自定义命令行可以轻松解决这个问题。

安装命令行工具

composer require webman/console ^1.2.24

创建自定义打包命令

php webman make:command custom:build:bin

实现代码

目前只简单实现目录copy,将打包结果压缩成zip,方便分发和部署,其他需求可自行修改扩展。

<?php

namespace app\command;

use RecursiveIteratorIterator;
use RecursiveDirectoryIterator;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Command\Command;
use Webman\Console\Commands\BuildBinCommand;
use ZipArchive;

class CustomBuildBinCommand extends Command
{
    protected static $defaultName = 'custom:build:bin';
    protected static $defaultDescription = 'Custom build bin';

    /**
     * @return void
     */
    protected function configure(): void
    {
        $this->addArgument('version', InputArgument::OPTIONAL, '打包的PHP版本', '8.1');

        $this->addArgument('zip', InputArgument::OPTIONAL, '是否将打包输出目录压缩', 0);
    }

    /**
     * @param InputInterface $input
     * @param OutputInterface $output
     * @return int
     */
    protected function execute(InputInterface $input, OutputInterface $output): int
    {

        // 是否将打包输出目录压缩
        $zip = $input->getArgument('zip');

        // 打包输出目录
        $buildDir = config('plugin.webman.console.app.build_dir', base_path() . '/build');

        $output->writeln('<comment>正在打包...</comment>');

        // 调用打包bin命令
        $command = new BuildBinCommand();
        $command->execute($input, $output);

        $output->writeln('<comment>复制目录...</comment>');

        // 复制db目录 phinx数据库迁移
        $this->copyDirectory(base_path() . '/db', $buildDir . '/db');

        // 复制public目录(如果需要读取里面的资源)
        $this->copyDirectory(base_path() . '/public', $buildDir . '/public');

        // 复制resource目录(使用多语言)
        $this->copyDirectory(base_path() . '/resource', $buildDir . '/resource');

        $output->writeln('<info>复制完毕</info>');

        if ($zip) {

            $output->writeln('<comment>压缩中...</comment>');

            $zipFilename = $buildDir . '/build.zip';

            // 删除之前的压缩包
            if (file_exists($zipFilename)) {
                unlink($zipFilename);
            }

            // 压缩目录并跳过非必要的文件
            $this->zipDirectory($buildDir, $zipFilename, [
                '*.zip',
                '*.sfx',
                '*.phar',
                '.env'
            ]);

            $output->writeln('<info>压缩完成! ' . $zipFilename . '</info>');
        }

        $output->writeln('<info>打包完成!</info>');

        return self::SUCCESS;

    }

    /**
     * 拷贝目录 包含子目录及文件
     * @param $src
     * @param $dst
     * @return false|void
     */
    protected function copyDirectory($src, $dst)
    {
        $dir = opendir($src);
        if (!$dir) {
            return false;
        }

        if (!is_dir($dst)) {
            mkdir($dst, 0755, true);
        }

        while (false !== ($file = readdir($dir))) {
            if (($file != '.') && ($file != '..')) {
                $srcFile = $src . '/' . $file;
                $dstFile = $dst . '/' . $file;
                if (is_dir($srcFile)) {
                    // 递归调用自身,拷贝子目录
                    $this->copyDirectory($srcFile, $dstFile);
                } else {
                    // 拷贝文件
                    copy($srcFile, $dstFile);
                }
            }
        }
        closedir($dir);
    }

    /**
     * zip压缩目录
     * @param $source
     * @param $destination
     * @param array $exclude
     * @return bool
     */
    protected function zipDirectory($source, $destination, array $exclude = []): bool
    {
        $zip = new ZipArchive();
        if (!$zip->open($destination, ZipArchive::CREATE)) {
            return false;
        }

        $source = str_replace('\\', '/', realpath($source));

        if (is_dir($source) === true) {
            $files = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($source), RecursiveIteratorIterator::SELF_FIRST);

            foreach ($files as $file) {
                $file = str_replace('\\', '/', $file);
                $fileName = basename($file);

                if ($fileName == '.' || $fileName == '..') {
                    continue;
                }

                echo "Source: $source, File: $file\n";

                // Skip excluded files and dirs
                $skip = false;
                foreach ($exclude as $pattern) {
                    if (fnmatch($pattern, $file)) {
                        $skip = true;
                        break;
                    }
                }

                if ($skip) {

                    echo "Skipping: $file\n";

                    continue;
                }

                $file = realpath($file);
                if (is_dir($file) === true) {
                    $zip->addEmptyDir(str_replace($source . '/', '', $file . '/'));
                } else if (is_file($file) === true) {
                    $zip->addFromString(str_replace($source . '/', '', $file), file_get_contents($file));
                }
            }
        } else if (is_file($source) === true) {
            $zip->addFromString(basename($source), file_get_contents($source));
        }

        return $zip->close();
    }

}

使用方法:

打包php8.1版本,并将打包结果压缩zip:

php -d phar.readonly=0 webman custom:build:bin 8.1 true

打包完成后在输出目录会看到一个build.zip压缩包。

553 0 4
0个评论

sdffghy

330
积分
0
获赞数
0
粉丝数
2023-08-25 加入
🔝