You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PHP 8中循环生成Zip后无法关闭第二个Zip的问题

问题分析与修复方案

核心错误原因

  1. mfzip类的cz()方法缺少空值校验:当$this->zip未被正确初始化(比如newzip()失败)时,直接调用$this->zip->close()会触发Invalid or uninitialized Zip object致命错误。
  2. 主脚本创建新Zip后未设置密码且未处理失败场景:创建第二个Zip后,没有调用passzip()设置密码,且未检查newzip()的结果是否成功,后续循环仍会尝试向无效的Zip对象添加文件,最终关闭时出错。
  3. Zip大小计算逻辑不准确:你通过累加文件在Zip中的大小判断是否拆分,但实际Zip的磁盘大小是经过压缩的,这个逻辑可能导致拆分时机不符合预期(非致命,但需要注意)。

修复后的代码

1. 修正mfzip类

主要给所有方法添加Zip对象有效性校验,避免无效调用:

/**
 * marcfunk zipping class
**/
class mfzip {
    
    ## vars
    public $fstats;
    public $result;
    public $zip;
    
    
    /**
     * create zip archive
     * @param: string, filename of zipfile
    **/
    public function newzip($filename = "") {
        $this->result = 900;
        $this->zip = null;
        
        if($filename !== "") {
            $newzip = new ZipArchive();
            if($newzip->open($filename, ZipArchive::CREATE) === TRUE) {
                $this->result = 200;
                $this->zip = $newzip;
            }
        }
    }
    
    
    /**
     * set zip password
     * @param: string, password for zip
    **/
    public function passzip($hash) {
        $this->result = 900;
        if($this->zip instanceof ZipArchive) {
            if($this->zip->setPassword($hash)) {
                $this->result = 200;
            }
        }
    }
    
    
    /**
     * add file to archive
     * @param: string, path to local file
     * @param: string, path in zip file
    **/
    public function add($local = "", $remote = "") {
        $this->result = 900;
        if($this->zip instanceof ZipArchive && $local !== "" && file_exists($local) && $remote !== "") {
            if($this->zip->addFile($local, $remote)) {
                $this->result = 200;
            }
        }
    }
    
    
    /**
     * crypt file
     * @param: string, path in zip file
    **/
    public function cf($remote = "") {
        $this->result = 900;
        if($this->zip instanceof ZipArchive && $remote !== "") {
            if($this->zip->setEncryptionName($remote, ZipArchive::EM_AES_256)) {
                $this->result = 200;
            }
        }
    }
    
    
    /**
     * stats of file
     * @param: string, path in zip file
    **/
    public function stats($remote = "") {
        $this->result = 900;
        $this->fstats = null;
        if($this->zip instanceof ZipArchive && $remote !== "") {
            if($stats = $this->zip->statName($remote)) {
                $this->fstats = $stats;
                $this->result = 200;
            }
        }
    }
    
    
    /**
     * close zip
    **/
    public function cz() {
        $this->result = 900;
        if($this->zip instanceof ZipArchive) {
            if($this->zip->close() === TRUE) {
                $this->result = 200;
                // 关闭后清空zip对象,避免重复调用
                $this->zip = null;
            }
        }
    }
}

2. 修正主脚本

主要修复创建新Zip后的密码设置和失败处理逻辑:

/**
 * check and process file list
**/
## log
$log->logging($lf, "Check backuppathes list.");

## check file list
if(!empty($fullfilelist)) {
    
    ## log
    $log->logging($lf, "Setting zip parameter.");
    
    ## zip counter
    $zc = 0;
    
    ## zip size
    $zs = 0;
    
    ## zip path
    $zp = __DIR__ . DIRECTORY_SEPARATOR . "zip" . DIRECTORY_SEPARATOR;
    // 确保目录存在
    if(!is_dir($zp)) {
        mkdir($zp, 0755, true);
        $log->logging($lf, "Created zip directory: " . $zp);
    }
    
    ## zipfile
    $zipfile = $zp . date("Y-m-d") . "-" . str_pad($zc, 3, "0", STR_PAD_LEFT) . ".zip";
    
    ## log
    $log->logging($lf, "Create zip file: " . $zipfile);
    
    ## start zip file
    $myzip[$zc] = new mfzip;
    $myzip[$zc]->newzip($zipfile);
    
    ## check new zip file
    if($myzip[$zc]->result == 200) {
        
        ## log
        $log->logging($lf, "Zip " . $zipfile . " starts! Set password...");
        
        ## set zip password
        $myzip[$zc]->passzip($CFG['zippass']);
        
        ## check setting
        if($myzip[$zc]->result == 200) {
            
            ## log
            $log->logging($lf, "Password set! Process pathes...");
            
            ## process file list
            foreach($fullfilelist as $ffl) {
                
                ## local file
                $l = $ffl;
                
                ## log
                $log->logging($lf, "Add local file: " . $l);
                
                ## file in zip
                $r = substr($ffl, 3); // 去掉多余的长度参数,默认到末尾即可
                
                ## checking file reading
                if(is_readable($l)) {
                
                    ## add file
                    $myzip[$zc]->add($l, $r);
                    
                    ## check adding
                    if($myzip[$zc]->result == 200) {
                        
                        ## log
                        $log->logging($lf, "Adding successful! Crypt file...");
                        
                        ## crypt file
                        $myzip[$zc]->cf($r);
                        
                        ## check crypt
                        if($myzip[$zc]->result == 200) {
                            
                            ## log
                            $log->logging($lf, "Crypting successful! Get stats of file...");
                            
                            ## get file stats
                            $myzip[$zc]->stats($r);
                        
                            ## check stats
                            if($myzip[$zc]->result == 200 && is_array($myzip[$zc]->fstats)) {
                                
                                ## log
                                $log->logging($lf, "Get stats successful! Checking zip size...");
                                
                                ## filesize
                                $rfs = $myzip[$zc]->fstats['size'];
                                
                                ## new zip size
                                $zs = $zs + $rfs;
                                
                                ## log
                                $log->logging($lf, "File in zip: " . $rfs . " bytes | current zip size: " . $zs . " bytes");
                                
                                ## check zip size with configured size
                                if($zs > $CFG['zipsize']) {
                                    
                                    ## log
                                    $log->logging($lf, "Close zip and start new file (" . $zc . ")!");
                                    
                                    ## close zip
                                    $myzip[$zc]->cz();
                                    
                                    ## check closing
                                    if($myzip[$zc]->result == 200) {
                                        
                                        ## log
                                        $log->logging($lf, "Closing successfully: " . $zipfile);
                                        
                                        ## counting up
                                        $zc++;
                                        
                                        ## zipfile
                                        $zipfile = $zp . date("Y-m-d") . "-" . str_pad($zc, 3, "0", STR_PAD_LEFT) . ".zip";
                                        
                                        ## start new zip file
                                        $myzip[$zc] = new mfzip;
                                        $myzip[$zc]->newzip($zipfile);
                                        
                                        ## check new file
                                        if($myzip[$zc]->result == 200) {
                                            
                                            ## log
                                            $log->logging($lf, "Create new zip file: " . $zipfile);
                                            
                                            ## set password for new zip
                                            $myzip[$zc]->passzip($CFG['zippass']);
                                            if($myzip[$zc]->result != 200) {
                                                $log->logging($lf, "ERROR: Can't set password for new zip file: " . $zipfile);
                                                break;
                                            }
                                            
                                        } else {
                                            $log->logging($lf, "ERROR: Creating new zip file: " . $zipfile);
                                            break;
                                        }
                                    
                                        ## set zip size to 0
                                        $zs = 0;
                                        
                                    } else {
                                        
                                        ## log
                                        $log->logging($lf, "ERROR: Closing error: " . $zipfile);
                                        break;
                                        
                                    }
                                    
                                }
                                
                            } else {
                                
                                ## log
                                $log->logging($lf, "ERROR: Can't get stats of file " . $r);
                                
                            }
                            
                        } else {
                            
                            ## log
                            $log->logging($lf, "ERROR: Can't crypt file: " . $r);
                            
                        }
                        
                    } else {
                            
                        ## log
                        $log->logging($lf, "ERROR: Can't adding file: " . $r);
                        
                    }
                    
                } else {
                    
                    ## log
                    $log->logging($lf, "ERROR: Can't read file: " . $l);
                    
                }
                
            }
            
        } else {
            
            ## log
            $log->logging($lf, "ERROR: Can't set password for zip file: " . $zipfile);
            
        }
        
        ## log
        $log->logging($lf, "Last closing file: " . $zipfile);
        
        ## 检查当前Zip对象是否有效再关闭
        if(isset($myzip[$zc]) && $myzip[$zc]->zip instanceof ZipArchive) {
            $myzip[$zc]->cz();
            
            ## check closing
            if($myzip[$zc]->result == 200) {
                
                ## log
                $log->logging($lf, "Closing successfully!");
                
            } else {
                
                ## log
                $log->logging($lf, "ERROR: Closing error: " . $zipfile);
                
            }
        } else {
            $log->logging($lf, "ERROR: No valid zip object to close for file: " . $zipfile);
        }
        
    } else {
        
        ## log
        $log->logging($lf, "ERROR: Creating zip file: " . $zipfile);
        
    }
    
} else {
    
    ## log
    $log->logging($lf, "ERROR: No backup pathes!");
    
}

额外优化点

  1. 使用DIRECTORY_SEPARATOR替代硬编码的\\,让代码跨平台兼容
  2. 创建Zip目录前先检查是否存在,避免目录不存在导致的Zip创建失败
  3. 去掉substr()中多余的长度参数,默认截取到字符串末尾即可
  4. 关闭Zip后清空$this->zip对象,避免重复调用close()导致错误

内容的提问来源于stack exchange,提问作者marcfunk

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.01 18:40:39