PHP 之简单在线更新
一、效果图
二、示例代码
1、客户端
common.php
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<?php /** * Created by PhpStorm. * User: Mr.Yang * Date: 2022/5/11 * Time: 15:24 * QQ: 2575404985 */ define('ROOT_PATH', dirname(__FILE__)); // 执行URL请求,并返回数据 function get_url($url, $fields = array(), $UserAgent = null, $vfSSL = false) { $SSL = substr($url, 0, 8) == "https://" ? true : false; $ch = curl_init(); if ($UserAgent) { // 在HTTP请求中包含一个"User-Agent: "头的字符串。 curl_setopt($ch, CURLOPT_USERAGENT, $UserAgent); } else { curl_setopt($ch, CURLOPT_USERAGENT, $_SERVER["HTTP_USER_AGENT"]); } curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 60); // 在发起连接前等待的时间,如果设置为0,则无限等待 curl_setopt($ch, CURLOPT_TIMEOUT, 90); // 设置cURL允许执行的最长秒数 curl_setopt($ch, CURLOPT_URL, $url); // 设置请求地址 curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); // 设置cURL 参数,要求结果保存到字符串中还是输出到屏幕上。 // SSL验证 if ($SSL) { if ($vfSSL) { curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true); curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0); curl_setopt($ch, CURLOPT_CAINFO, CORE_PATH . '/cacert.pem'); } else { curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); // 信任任何证书 curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0); // 不检查证书中是否设置域名 } } // 数据字段 if ($fields) { curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_POSTFIELDS, $fields); } $output = curl_exec($ch); if (curl_errno($ch)) { error('请求远程地址错误:' . curl_error($ch)); } curl_close($ch); return $output; } // 递归替换 function preg_replace_r($search, $replace, $subject) { while (preg_match($search, $subject)) { $subject = preg_replace($search, $replace, $subject); } return $subject; } // 检测目录是否存在 function check_dir($path, $create = false) { if (is_dir($path)) { return true; } elseif ($create) { return create_dir($path); } } // 创建目录 function create_dir($path) { if (! file_exists($path)) { if (mkdir($path, 0777, true)) { return true; } } return false; } // 检查文件是否存在 function check_file($path, $create = false, $content = null) { if (file_exists($path)) { return true; } elseif ($create) { return create_file($path, $content); } } // 创建文件 function create_file($path, $content = null, $over = false) { if (file_exists($path) && ! $over) { return false; } elseif (file_exists($path)) { @unlink($path); } check_dir(dirname($path), true); $handle = fopen($path, 'w') or die('创建文件失败,请检查目录权限!'); fwrite($handle, $content); return fclose($handle); }
index.php
<?php extract($_REQUEST); $action = $action ? $action : 'index'; include_once 'common.php'; $url = "服务端地址"; $files = json_decode(get_url($url . "/upgrade.php"), true); //var_dump($files); foreach ($files as $key => $value) { // 过滤掉相对路径 $value['path'] = preg_replace_r('{\.\.(\/|\\\\)}', '', $value['path']); $file = ROOT_PATH . $value['path']; if (@md5(@file_get_contents($file)) != $value['md5']) { // 筛选数据库更新脚本 if (preg_match('/([\w]+)-([\w\.]+)-update\.sql/i', $file, $matches)) { if ($matches[1] != 'mysql' || $matches[2] != '1.0.0') { continue; } } if (file_exists($file)) { $files[$key]['type'] = '<span style="color:Red">覆盖</span>'; $files[$key]['ltime'] = date('Y-m-d H:i:s', filemtime($file)); } else { $files[$key]['type'] = '新增'; $files[$key]['ltime'] = '无'; } $files[$key]['ctime'] = date('Y-m-d H:i:s', $files[$key]['ctime']); $upfile[] = $files[$key]; } } if (!$upfile) { die("您的系统无任何文件需要更新!"); } if ($action=='download') { foreach ($upfile as $k => $v){ //获取内容 $res = json_decode(file_get_contents($url."/upgrade.php?action=getFile&path=".$v['path']), true); if ($res['code'] == 0){ $file = ROOT_PATH . $v['path']; //判断是否有目录,没有则创建 check_dir(dirname($file), true); //备份对应文件 $back = ROOT_PATH."/upgrade/".date("YmdHis").$v['path']; check_dir(dirname($back), true); copy($file, $back); $data = base64_decode($res['data']); //判断是否为数据库文件 if (!file_put_contents($file, $data)){ echo "文件 " . basename($v['path']) . " 更新失败!<br>"; }else{ if (stripos($v['path'], '/db/') === 0 && preg_match('/\.sql$/i', $v['path']) > 0) { $sqls = explode(';', $data); var_dump($sqls); } echo "文件 " . basename($v['path']) . " 更新成功!<br>"; } }else{ echo "文件 " . basename($v['path']) . " 更新失败!<br>"; } } } ?> <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>在线更新</title> </head> <style> th { background-color: #f2f2f2; } td, th { padding: 9px 15px; min-height: 20px; line-height: 20px; font-size: 14px; } </style> <body> <?php if ($action == 'index'){?> <table style="width: 100%;text-align: center;background-color: #fff;color: #666;"> <thead> <tr> <th>更新文件</th> <th>更新方式</th> <th>本地时间</th> <th>更新时间</th> </tr> </thead> <tbody> <?php foreach ($upfile as $k => $v){?> <tr> <td><?php echo $v['path']?></td> <td><?php echo $v['type']?></td> <td><?php echo $v['ltime']?></td> <td><?php echo $v['ctime']?></td> </tr> <?php }?> </tbody> </table> <?php }?> <div style="text-align: center;margin-top: 20px;"> <a href="index.php">重新检查</a> <?php if($action != 'download'){?><a href="index.php?action=download">下载更新</a><?php }?> </div> </body> </html>
2、服务端
upgrade.php
<?php /** * Created by PhpStorm. * User: Mr.Yang * Date: 2022/5/11 * Time: 15:04 * QQ: 2575404985 */ extract($_REQUEST); $action = $action ? $action : ''; switch ($action){ case 'getFile': if (file_exists("./release/".$path)){ $content = base64_encode(file_get_contents("./release/".$path)); die(json_encode(array('code' => 0, 'data' => $content))); }else{ die(json_encode(array('code' => -1, 'data' => ''))); } break; default: $file = getSourceFile(); $file_array = array(); foreach ($file as $k => $v){ $content = file_get_contents($v); if ($v == './upgrade.php'){ continue; } $file_array[] = array( 'path' => str_replace('./release', '', $v), 'md5' => md5($content), 'ctime' => filemtime($v) ); } die(json_encode($file_array, JSON_UNESCAPED_UNICODE)); } function getSourceFile($path = './release') { static $arr = array(); if (is_dir($path)) { $array = glob($path . '/*'); foreach ($array as $k => $v) { if (is_dir($v)) { getSourceFile($v); }else{ $arr[] = $v; } } } else { $arr = $path; } return $arr; }