PHP 工具类
<?php
/**
* Created by : PhpStorm
* User: Chen
* Date: 2022/5/11
* Time: 15:58
* */
namespace app\common\util;
use DateTime;
use Exception;
use PHPMailer\PHPMailer\PHPMailer;
use PhpOffice\PhpSpreadsheet\Cell\DataType;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Writer\Xlsx;
use RecursiveDirectoryIterator;
use RecursiveIteratorIterator;
use SplFileInfo;
use think\facade\Env;
use ZipArchive;
/**
* 工具类
*/
class ChenUtils
{
/**
* 将天数换算成年月日
*/
public static function convert($sum)
{
$years = floor($sum / 365);
$months = floor(($sum - ($years * 365)) / 30);
$days = ($sum - ($years * 365) - ($months * 30));
$str = '';
if ($years != 0) {
$str .= $years . '年';
}
if ($months != 0) {
$str .= $months . '月';
}
if ($days != 0) {
$str .= $days . '天';
}
return $str;
}
public static function isEmail($email)
{
$pattern = '/^[\w-]+(\.[\w-]+)*@[\w-]+(\.[\w-]+)+$/';
if (preg_match($pattern, $email)) {
return true;
} else {
return false;
}
}
// 传时间戳返回周几
function getWeek($time)
{
$week = date('w', $time);
switch ($week) {
case 1:
$week = '周一';
break;
case 2:
$week = '周二';
break;
case 3:
$week = '周三';
break;
case 4:
$week = '周四';
break;
case 5:
$week = '周五';
break;
case 6:
$week = '周六';
break;
case 0:
$week = '周日';
break;
}
return $week;
}
/**
* 传html过来返回第一张图片src
*/ public static function getFirstImg($html)
{
$img = '';
$pattern = '/<img.*?src="(.*?)".*?>/';
preg_match($pattern, $html, $match);
if (isset($match[1])) {
$img = $match[1] ?? '';
}
return $img;
}
/**
* 传html返回所有图片src
*/ public static function getAllImg($html)
{
$img = [];
$pattern = '/<img.*?src="(.*?)".*?>/';
preg_match_all($pattern, $html, $match);
if (isset($match[1])) {
$img = $match[1] ?? [];
}
return $img;
}
/**
* 传富文本过来返回第一段文字
*/
public static function getFirstText($html)
{
$text = '';
/* $pattern = '/<p.*?><span.*?>(.*?)<\/span><\/p>/';*/
$pattern = '/<p.*?>(.*?)<\/p>/';
preg_match($pattern, $html, $match);
if (isset($match[1])) {
$text = $match[1];
}
// 判断是否有文字, 没有则尝试截取p标签内的中文
if ($text == '') {
$pattern = '/<p.*?>[\u4e00-\u9fa5]<\/p>/';
preg_match($pattern, $html, $match);
if (isset($match[1])) {
$text = $match[1];
}
}
return $text;
}
/**
* 时间字段处理
*
* @param $time
* @return string
*/ public static function timeTran($time)
{
$nowTime = time();
$showTime = strtotime($time);
$difference = $nowTime - $showTime;
if ($difference < 0) {
return $time;
}
if ($difference < 60) {
return $difference . '秒前';
}
if ($difference < 3600) {
return floor($difference / 60) . '分钟前';
}
if ($difference < 86400) {
return floor($difference / 3600) . '小时前';
}
if ($difference < 2592000) {
return floor($difference / 86400) . '天前'; //30天内
}
if ($difference < 31104000) {
return floor($difference / 2592000) . '个月前'; //12个月内
}
return floor($difference / 31536000) . '年前';
}
/**
* 检查身份证号码是否正确
* @param $id_card
* @return boolean
*/ public static function idCardCheck($id_card)
{
if (empty($id_card)) {
return false;
}
$id_card = strtoupper($id_card);
$regx = "/(^\d{15}$)|(^\d{17}([0-9]|X)$)/";
$arr_split = array();
if (!preg_match($regx, $id_card)) {
return false;
}
//检查15位
if (15 == strlen($id_card)) {
$regx = "/^(\d{6})+(\d{2})+(\d{2})+(\d{2})+(\d{3})$/";
@preg_match($regx, $id_card, $arr_split);
//检查生日日期是否正确
$dtm_birth = "19" . $arr_split[2] . '/' . $arr_split[3] . '/' . $arr_split[4];
if (!strtotime($dtm_birth)) {
return false;
} else {
return true;
}
} else {
//检查18位
$regx = "/^(\d{6})+(\d{4})+(\d{2})+(\d{2})+(\d{3})([0-9]|X)$/";
@preg_match($regx, $id_card, $arr_split);
$dtm_birth = $arr_split[2] . '/' . $arr_split[3] . '/' . $arr_split[4];
if (!strtotime($dtm_birth)) {
return false;
} else {
//检验18位身份证的校验码是否正确。
//校验位按照ISO 7064:1983.MOD 11-
//数字代码:012345678910X98765432
$arr_int = array(7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2);
$arr_ch = array('1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2');
$sign = 0;
for ($i = 0; $i < 17; $i++) {
$b = (int)$id_card[$i];
$w = $arr_int[$i];
$sign += $b * $w;
}
$n = $sign % 11;
$val_num = $arr_ch[$n];
if ($val_num != substr($id_card, 17, 1)) {
return false;
} else {
return true;
}
}
}
}
/**
* 检查营业执照(社会统一信用代码)是否正确
* @param $id_card
* @return bool
*/ public static function businessLicenseCheck($business_license)
{
if (empty($business_license)) {
return false;
}
$business_license = strtoupper($business_license);
$regx = "/^[0-9A-Z]{15}$|^[0-9A-Z]{18}$|^[0-9A-Z]{20}$/";
if (!preg_match($regx, $business_license)) {
return false;
}
return true;
}
/**
* 生成唯一订单号
* @form 表名
* @order_no 订单号字段名
*/
public static function getOrderSn($form, $order_no)
{
$order_sn = ChenUtils . phpdate('Ymd') . substr(implode(NULL, array_map('ord', str_split(substr(uniqid(), 7, 13), 1))), 0, 8);
$form = db($form)->where($order_no, $order_sn)->find();
if ($form) {
return self::getOrderSn($form, $order_no);
} else {
return $order_sn;
}
}
/**
* [生成随机字符串]
* @param integer $length [生成的长度]
* @param integer $type [生成的类型]
* @php 随机码类型:0,数字+大写字母;1,数字;2,小写字母;3,大写字母;4,特殊字符;-1,数字+大小写字母+特殊字符
*/
public static function randCode($length = 5, $type = 0)
{
$arr = array(1 => "0123456789", 2 => "abcdefghijklmnopqrstuvwxyz", 3 => "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 4 => "~@#$%^&*(){}[]|");
if ($type == 0) {
array_pop($arr);
$string = implode("", $arr);
} else if ($type == "-1") {
$string = implode("", $arr);
} else {
$string = $arr[$type];
}
$count = strlen($string) - 1;
for ($i = 0; $i < $length; $i++) {
$str[$i] = $string[rand(0, $count)];
$code .= $str[$i];
}
return $code;
}
/**
* 生成唯一字符串
* @param string $prefix [前缀]
* @param int $type [字符串的类型]
* @param int $length [字符串的长度]
* @param int $time [是否带时间1-带,0-不带]
* @return string
*/ public static function randSole(string $prefix, int $type = 0, int $length = 18, int $time = 0, $dao = null): string
{
$str = $time == 0 ? '' : date('YmdHis', time());
$charSets = [
'0123456789', // 数字
'abcdefghijklmnopqrstuvwxyz', // 小写字母
'ABCDEFGHIJKLMNOPQRSTUVWXYZ', // 大写字母
'0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ', // 数字 + 小写 + 大写
'!@#$%^&*()_+=-~`', // 特殊字符
'0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!@#$%^&*()_+=-~`' // 数字 + 小写 + 大写 + 特殊字符
];
if (isset($charSets[$type])) {
$rand = $charSets[$type];
$randLength = strlen($rand);
for ($i = mb_strlen($str); $i < $length; $i++) {
$str .= $rand[mt_rand(0, $randLength - 1)];
}
}
$cacheKey = $prefix . $str;
if (cache($cacheKey)) {
return self::randSole($prefix, $type, $length, $time);
} else {
cache($cacheKey, $str, 3600);
return $str;
}
}
/**
* 加密解密方法
* @param string $string 需要加密的字段
* @param string $code 约定的密钥
* @param bool $operation 默认false表示加密,传入true表示解密
* @return false|string
*/ public static function secret(string $string, string $code, bool $operation = false)
{
$code = md5($code);
$iv = substr($code, 0, 16);
$key = substr($code, 16);
if ($operation) {
return openssl_decrypt(base64_decode($string), "AES-128-CBC", $key, OPENSSL_RAW_DATA, $iv);
}
return base64_encode(openssl_encrypt($string, "AES-128-CBC", $key, OPENSSL_RAW_DATA, $iv));
}
/**
* 压缩指定文件夹并解压到指定文件夹
* @param string $path 压缩文件夹路径
* @param string $outputPath 解压文件夹路径
* @param string $filename 压缩文件名
* @return void
*/ public function zip(string $path, string $outputPath, string $filename)
{
$rootPath = $path;
$zip = new ZipArchive();
$zip->open($outputPath . $filename . '.zip', ZipArchive::CREATE | ZipArchive::OVERWRITE);
/** @var SplFileInfo[] $files */
$files = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($rootPath),
RecursiveIteratorIterator::LEAVES_ONLY
);
foreach ($files as $name => $file) {
if (!$file->isDir()) {
$filePath = $file->getRealPath();
// $relativePath = substr($filePath, strlen($rootPath) + 1);
$relativePath = substr($filePath, strlen($rootPath));
$zip->addFile($filePath, $relativePath);
}
}
$zip = new ZipArchive();
$zip->open($outputPath . $filename . '.zip');
$zip->extractTo($outputPath . $filename . '/');
$zip->close();
}
/**
* 删除文件夹or文件
* @param string $path 删除文件夹路径
* @return void
*/public static function delDirAndFile(string $path)
{
if (is_dir($path)) {
$dh = opendir($path);
while ($file = readdir($dh)) {
if ($file != "." && $file != "..") {
$fullpath = $path . "/" . $file;
if (!is_dir($fullpath)) {
unlink($fullpath);
} else {
// 递归
self::delDirAndFile($fullpath);
}
}
}
closedir($dh);
rmdir($path);
} else {
if (file_exists($path)) {
unlink($path);
}
}
}
/**
* 分组排序
* @param array $data 需要分组的数据
* @param string $field 分组字段
* @return array
*/ public static function group(array $data, string $field)
{
$result = array();
foreach ($data as $key => $value) {
$result[$value[$field]][] = $value;
usort($result[$value[$field]], function ($a, $b) {
if (isset($a['one_ear']) && isset($b['one_ear'])) {
return $a['one_ear'] > $b['one_ear'];
} else {
return isset($a['one_ear']) ? -1 : 1;
}
});
}
return $result;
}
/**
* 计算指定日期的一周开始及结束日期
* @param DateTime $date 日期
* @param Int $start 周几作为一周的开始 1-6为周一~周六,0为周日,默认0
* @retrun Array
*/ public static function getWeekRange($date, $start = 0)
{
// 将日期转时间戳
$dt = new DateTime($date);
$timestamp = $dt->format('U');
// 获取日期是周几
$day = (new DateTime('@' . $timestamp))->format('w');
// 计算开始日期
if ($day >= $start) {
$startdate_timestamp = mktime(0, 0, 0, date('m', $timestamp), date('d', $timestamp) - ($day - $start), date('Y', $timestamp));
} elseif ($day < $start) {
$startdate_timestamp = mktime(0, 0, 0, date('m', $timestamp), date('d', $timestamp) - 7 + $start - $day, date('Y', $timestamp));
}
// 结束日期=开始日期+6
$enddate_timestamp = mktime(0, 0, 0, date('m', $startdate_timestamp), date('d', $startdate_timestamp) + 6, date('Y', $startdate_timestamp));
$startdate = (new DateTime('@' . $startdate_timestamp))->format('Y-m-d');
$enddate = (new DateTime('@' . $enddate_timestamp))->format('Y-m-d');
return array($startdate, $enddate);
}
/**
* 生成图片
* @param array $config 参数,包括图片和文字
* @param string $filename 生成海报文件名,不传此参数则不生成文件,直接输出图片
* @return false|string|void [type] [description]
*/ public static function createPoster(array $config = array(), string $filename = "")
{
//要看报什么错注释掉这个
// if(empty($filename)) header("content-type: image/png");
$imageDefault = array(
'left' => 0,
'top' => 0,
'right' => 0,
'bottom' => 0,
'width' => 100,
'height' => 100,
'opacity' => 100
);
$textDefault = array(
'text' => '',
'left' => 0,
'top' => 0,
'fontSize' => 32, //字号
'fontColor' => '255,255,255', //字体颜色
'angle' => 0,
);
$background = $config['background'];//海报最底层得背景
//背景方法
$backgroundInfo = getimagesize($background);
$backgroundFun = 'imagecreatefrom' . image_type_to_extension($backgroundInfo[2], false);
$background = $backgroundFun($background);
$backgroundWidth = imagesx($background); //背景宽度
$backgroundHeight = imagesy($background); //背景高度
$imageRes = imageCreatetruecolor($backgroundWidth, $backgroundHeight);
$color = imagecolorallocate($imageRes, 0, 0, 0);
imagefill($imageRes, 0, 0, $color);
// imageColorTransparent($imageRes, $color); //颜色透明
imagecopyresampled($imageRes, $background, 0, 0, 0, 0, imagesx($background), imagesy($background), imagesx($background), imagesy($background));
//处理了图片
if (!empty($config['image'])) {
foreach ($config['image'] as $key => $val) {
$val = array_merge($imageDefault, $val);
$info = getimagesize($val['url']);
$function = 'imagecreatefrom' . image_type_to_extension($info[2], false);
if ($val['stream']) { //如果传的是字符串图像流
$info = getimagesizefromstring($val['url']);
$function = 'imagecreatefromstring';
}
$res = $function($val['url']);
$resWidth = $info[0];
$resHeight = $info[1];
//建立画板 ,缩放图片至指定尺寸
$canvas = imagecreatetruecolor($val['width'], $val['height']);
imagesavealpha($canvas, true);//不要丢了$imageRes图像的透明色;
imagealphablending($canvas, false);//不合并颜色,直接用$canvas图像颜色替换,包括透明色;
imagesavealpha($imageRes, true);//不要丢了$imageRes图像的透明色;
imagefill($canvas, 0, 0, $color);
//关键函数,参数(目标资源,源,目标资源的开始坐标x,y, 源资源的开始坐标x,y,目标资源的宽高w,h,源资源的宽高w,h)
imagecopyresampled($canvas, $res, 0, 0, 0, 0, $val['width'], $val['height'], $resWidth, $resHeight);
$val['left'] = $val['left'] < 0 ? $backgroundWidth - abs($val['left']) - $val['width'] : $val['left'];
$val['top'] = $val['top'] < 0 ? $backgroundHeight - abs($val['top']) - $val['height'] : $val['top'];
//放置图像
// imagecopymerge($imageRes, $canvas, $val['left'], $val['top'], $val['right'], $val['bottom'], $val['width'], $val['height'], $val['opacity']);//左,上,右,下,宽度,高度,透明度
imagecopy($imageRes, $canvas, $val['left'], $val['top'], $val['right'], $val['bottom'], $val['width'], $val['height']);
}
}
//处理文字
if (!empty($config['text'])) {
foreach ($config['text'] as $key => $val) {
$val = array_merge($textDefault, $val);
list($R, $G, $B) = explode(',', $val['fontColor']);
$fontColor = imagecolorallocate($imageRes, $R, $G, $B);
$val['left'] = $val['left'] < 0 ? $backgroundWidth - abs($val['left']) : $val['left'];
$val['top'] = $val['top'] < 0 ? $backgroundHeight - abs($val['top']) : $val['top'];
// 是否添加阴影
if (isset($val['shadow']) && $val['shadow']) {
$fontColorHui = imagecolorallocate($imageRes, 120, 120, 120);
//为文字添加阴影
imagettftext($imageRes, $val['fontSize'], $val['angle'], (int)$val['left'] + 10, (int)$val['top'] + 10, $fontColorHui, $val['fontPath'], $val['text']);
}
//添加文本
imagettftext($imageRes, $val['fontSize'], $val['angle'], $val['left'], $val['top'], $fontColor, $val['fontPath'], $val['text']);
}
}
//生成图片
if (!empty($filename)) {
// $res = imagejpeg($imageRes, $filename, 90); //保存到本地
$res = imagepng($imageRes, $filename); //保存到本地
imagedestroy($imageRes);
if (!$res) return false;
return $filename;
} else {
ob_end_clean();
header("Content-type:image/png");
// imagejpeg($imageRes); //在浏览器上显示
imagepng($imageRes); //在浏览器上显示
imagedestroy($imageRes);
}
}
// 按字符串长度分割字符串为数组
public static function mbStrSplit($string, $len = 1)
{
$start = 0;
$strlen = mb_strlen($string);
$array = [];
while ($strlen) {
$array[] = mb_substr($string, $start, $len, "utf8");
$string = mb_substr($string, $len, $strlen, "utf8");
$strlen = mb_strlen($string);
}
return $array;
}
/**
* 海报调用实例
*/
public function getPoster()
{
$file = Env::get('root_path') . 'public' . DIRECTORY_SEPARATOR . 'uploads/';
$config = array(
'text' => array(
array(
'text' => '我是小红',
'left' => 40,
'top' => 40,
'fontPath' => $file . 'ueditor/font/zkklt.ttf',
'fontSize' => 18,
'fontColor' => '0,221,34',
'angle' => 0,
),
array(
'text' => '好吃的小红',
'left' => 20,
'top' => 280,
'fontPath' => $file . 'ueditor/font/zkklt.ttf',
'fontSize' => 18,
'fontColor' => '0,0,238',
'angle' => 0,
)
),
'image' => array(
array(
'url' => $file . 'ueditor/chishi/xiaohoshu.png',
'left' => 130,
'top' => 10,
'stream' => 0,
'right' => 0,
'bottom' => 0,
'width' => 40,
'height' => 40,
'opacity' => 100
),
array(
'url' => $file . 'ueditor/chishi/xiaohoshu.png',
'left' => 135,
'top' => 250,
'stream' => 0,
'right' => 0,
'bottom' => 0,
'width' => 40,
'height' => 40,
'opacity' => 100
),
array(
'url' => $file . 'ueditor/chishi/xiaohoshu.png',
'left' => 30,
'top' => 100,
'right' => 0,
'stream' => 0,
'bottom' => 0,
'width' => 150,
'height' => 150,
'opacity' => 100
),
),
'background' => $file . 'ueditor/chishi/chishibeijing.jpg',
);
$filename = $file . 'images/' . time() . '.jpg';
// return utils::createPoster($config, $filename);
return ChenUtils::createPoster($config);
}
/**
* 生成二维码
* @param string $code 二维码内容
* @param string $path 二维码保存路径
* @param string|boolean $QrCode 二维码保存图片路径 false则直接输出二维码
* @param string|boolean $logoQrCode 二维码带logo保存图片路径 false则直接输出带logo二维码
* @param string $logo logo图片路径
* @param string $errorCorrectionLevel 容错级别
* @param int $matrixPointSize 生成图片大小
* @return string
*/ public static function qrCode(string $code, string $path, $QrCode, $logoQrCode = '', string $logo = '', string $errorCorrectionLevel = 'M', int $matrixPointSize = 10)
{
// 导入二维码类
$QrCode_temp = $QrCode;
include_once(dirname($_SERVER["DOCUMENT_ROOT"]) . '\extend\org\phpqrcode.php');
// 直接输出二维码
if (!$QrCode) {
ob_end_clean();
header("Content-type:image/png");
return QRcode::png($code, $QrCode, $errorCorrectionLevel, $matrixPointSize, 2);
}
// 生成二维码图片返回路径
QRcode::png($code, $QrCode, $errorCorrectionLevel, $matrixPointSize, 2);
//生成中间带logo的二维码
if ($logo != '') {
$QrCode = imagecreatefromstring(file_get_contents($QrCode));//获取已经生成的原始二维码
$logo = imagecreatefromstring(file_get_contents($logo));//获取二维码中间的logo图片
$QR_width = imagesx($QrCode); //二维码图片宽度
$QR_height = imagesy($QrCode); //二维码图片高度
$logo_width = imagesx($logo); //logo图片宽度
$logo_height = imagesy($logo); //logo图片高度
// $logo_qr_width = $QR_width / 5;
$logo_qr_width = $QR_width / 4;
$scale = $logo_width / $logo_qr_width;
$logo_qr_height = $logo_height / $scale;
// $from_width = ($QR_width - $logo_qr_width) / 2;
$from_width = ($QR_width - $logo_qr_width) / 2;
//重新组合图片并调整大小
imagecopyresampled($QrCode, $logo, $from_width, $from_width, 0, 0, $logo_qr_width, $logo_qr_height, $logo_width, $logo_height);
// 不保存直接输出
if (!$logoQrCode) {
//输出图片
ob_end_clean();
header("Content-type:image/png");
imagepng($QrCode);
imagedestroy($QrCode);
unlink($QrCode_temp);
return;
} else {
//保存图片
imagepng($QrCode, $logoQrCode);
// 删除不带logo的二维码
unlink($QrCode_temp);
}
}
return $logoQrCode;
}
/**
* 图片转base64
* @param string $file 图片路径
* @return string
*/
public static function base64EncodeImage(string $file)
{
if (!file_exists($file)) {
return false;
}
$mimeType = mime_content_type($file);
return 'data:' . $mimeType . ';base64,' . base64_encode(file_get_contents($file));
}
/**
* 携带参数发送get/post请求
* @param string $url 请求地址
* @param array $data 请求参数
* @param string $method 请求方式
* @param array $header 请求头
* @return bool|string
*/ public static function curlRequest(string $url, $data = [], string $method = 'get', array $header = [], $timeout = 30)
{
$ch = curl_init();
if (substr($url, 0, 5) == 'https') {
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
}
if ($method == 'post') {
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
} else {
if (!empty($data)) {
$url .= '?' . http_build_query($data);
}
}
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
if (!empty($header)) {
curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
}
$output = curl_exec($ch);
curl_close($ch);
return $output;
}
/**
* 携带参数发送get/post请求
* @param string $url 请求地址
* @param array|string $data 请求参数
* @param string $method 请求方式
* @param array $header 请求头
* @return string
*/
public static function clientRequest(string $url, $data, string $method = 'GET', array $header = [], $timeout = 90): string
{
try {
$client = new Client([
'timeout' => $timeout, // 默认超时时间
'verify' => false,
'proxy' => false,
RequestOptions::HEADERS => $header,
]);
$params = [];
if ($method === 'GET') {
if (!empty($data)) {
$params = [
RequestOptions::QUERY => $data,
];
}
} else {
if (isset($header['Content-Type']) && $header['Content-Type'] === 'application/json') {
$params = [
RequestOptions::JSON => $data,
];
} else {
$params = [
RequestOptions::FORM_PARAMS => $data,
];
}
}
$response = $client->request($method, $url, $params);
return $response->getBody();
} catch (GuzzleException|RequestException $e) {
throw new \Exception("HTTP Request Failed: " . $e->getMessage());
}
}
/**
* 每次生成文件时检测是否达到上限,是则删除
*/
public static function checkFile($file_path = '')
{
$file = Env::get('root_path') . 'public' . DIRECTORY_SEPARATOR . 'chanyu/chanyu.json';
// 检测存储文件是否存在,不存在则创建
if (!file_exists($file)) {
$img = array('file' => []);
file_put_contents($file, json_encode($img));
}
// 读取文件内容
$chanyu = json_decode(file_get_contents($file), true);
// 检测文件数量是否达到某个数量,达到则删除
if (count($chanyu['file']) >= config('code.chanyu_max')) {
foreach ($chanyu['file'] as $k => $v) {
if (file_exists($v)) {
unlink($v);
}
unset($chanyu['file'][$k]);
}
}
$chanyu['file'][] = $file_path;
file_put_contents($file, json_encode($chanyu));
}
/**
* 判断图片路径是否为网络图片,是则下载到本地
*/
public static function downloadImage($url, $save_dir = '', $filename = '', $type = 0)
{
if (!preg_match('/^(http|https):\/\/.*/', $url)) {
return false;
}
if (trim($save_dir) == '') {
$save_dir = './';
}
if (0 !== strrpos($save_dir, '/')) {
$save_dir .= '/';
}
//创建保存目录
if (!file_exists($save_dir) && !mkdir($save_dir, 0777, true)) {
return false;
}
//获取远程文件所采用的方法
if ($type) {
$ch = curl_init();
$timeout = 5;
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $timeout);
$img = curl_exec($ch);
curl_close($ch);
} else {
ob_start();
readfile($url);
$img = ob_get_contents();
ob_end_clean();
}
//$size=strlen($img);
// 获取文件后缀
$ext = strrchr($url, '.');
//文件大小
$fp2 = @fopen($save_dir . $filename . $ext, 'a');
fwrite($fp2, $img);
fclose($fp2);
unset($img, $url);
return $save_dir . $filename . $ext;
}
/**
* 發送郵件
* @param array $config 配置
* [
* 'CharSet' => 'UTF-8', //设定邮件编码
* 'Host' => '', // SMTP服务器
* 'Username' => '', // 邮箱账号
* 'Password' => '', // 邮箱密码
* 'SMTPSecure' => 'ssl', // 允许 TLS 或者ssl协议
* 'Port' => 465, // 服务器端口 25 或者465 具体要看邮箱服务器支持
* ];
* @param array $from 發件人
* [
* 'name' => 'ccc',
* 'email' => '123456789@qq.com',
* ] * @param array $content 邮件内容
* [
* 'title' => '標題', // 邮件标题
* 'isHtml' => true, // 是否是html
* 'body' => '<h1>这里是邮件内容</h1>', // 邮件内容
* 'altBody' => '测试邮件内容', // 如果邮件客户端不支持html则显示此内容
* ]
* @param string $cc 抄送
* @param string $bcc 密送
* @param array $attachment 附件
* [
* [ * 'path' => '..\1.jpg', * 'newName' => '2.jpg', // 带newName就重命名
* ],
* [ * 'path' => '..\1.jpg', * ] * ] * @return array
*/ public static function PHPMailer($config = [], $from = [], $address = [], $content = [], $cc = '', $bcc = '', $attachment = [])
{
// composer require phpmailer/phpmailer
$mail = new PHPMailer(true);
try {
//服务器配置
$mail->CharSet = $config['CharSet']; //设定邮件编码
$mail->SMTPDebug = 0; // 调试模式输出
$mail->isSMTP(); // 使用SMTP
$mail->Host = $config['Host']; // SMTP服务器
$mail->SMTPAuth = true; // 允许 SMTP 认证
$mail->Username = $config['Username']; // SMTP 用户名 即邮箱的用户名
$mail->Password = $config['Password']; // SMTP 密码 部分邮箱是授权码(例如163邮箱)
$mail->SMTPSecure = $config['SMTPSecure']; // 允许 TLS 或者ssl协议
$mail->Port = $config['Port']; // 服务器端口 25 或者465 具体要看邮箱服务器支持
$mail->setFrom($from['email'], $from['name']); //发件人
foreach ($address as $key => $val) {
$mail->addAddress($val['email'], $val['name']); // 收件人
}
$mail->addReplyTo($from['email'], $from['email']); //回复的时候回复给哪个邮箱 建议和发件人一致
if (!empty($cc)) {
$mail->addCC($cc); // 抄送
}
if (!empty($bcc)) {
$mail->addBCC($bcc); // 密送
}
//发送附件
foreach ($attachment as $key => $val) {
if (empty($val['newName'])) {
$mail->addAttachment($val['path']); // 添加附件
} else {
$mail->addAttachment($val['path'], $val['newName']); // 发送附件并且重命名
}
}
//Content
$mail->isHTML($content['isHtml']); // 是否以HTML文档格式发送 发送后客户端可直接显示对应HTML内容
$mail->Subject = $content['title'];
$mail->Body = $content['body'];
$mail->AltBody = $content['altBody'];
$mail->send();
return ['code' => 1, 'msg' => '发送成功'];
} catch (Exception $e) {
return ['code' => 0, 'msg' => "发送失败: {$mail->ErrorInfo}", 'data' => $e->getMessage()];
}
}
/**
* 读取本地视频文件,获取视频第一帧作为封面返回
*/
public static function getVideoCover($filePath)
{
if (!file_exists($filePath)) {
return json([
'success' => false,
'msg' => '视频文件不存在'
]);
}
// TODO 安装php-fmpeg:composer require php-ffmpeg/php-ffmpeg
// 判断当前是linux还是window环境
if (DIRECTORY_SEPARATOR == '/') {
// linux环境
// TODO linux安装ffmpeg
// 地址:https://cloud.tencent.com/developer/article/1985211
$ffmpeg = FFMpeg::create([
'ffmpeg.binaries' => '/usr/bin/ffmpeg',
'ffprobe.binaries' => '/usr/bin/ffprobe',
'timeout' => 3600, // The timeout for the underlying process
'ffmpeg.threads' => 12, // The number of threads that FFMpeg should use
]);
} else {
// window环境
$ffmpeg = FFMpeg::create([
'ffmpeg.binaries' => Env::get('root_path') . 'public' . DIRECTORY_SEPARATOR . 'ffmpeg' . DIRECTORY_SEPARATOR . 'bin' . DIRECTORY_SEPARATOR . 'ffmpeg.exe',
'ffprobe.binaries' => Env::get('root_path') . 'public' . DIRECTORY_SEPARATOR . 'ffmpeg' . DIRECTORY_SEPARATOR . 'bin' . DIRECTORY_SEPARATOR . 'ffprobe.exe',
'timeout' => 3600, // The timeout for the underlying process
'ffmpeg.threads' => 12, // The number of threads that FFMpeg should use
]);
}
$video = $ffmpeg->open($filePath);
$frame = $video->frame(TimeCode::fromSeconds(1));
$path = Env::get('root_path') . 'public' . DIRECTORY_SEPARATOR . 'uploads' . DIRECTORY_SEPARATOR . 'video' . DIRECTORY_SEPARATOR . 'cover';
// 判断文件夹是否存在
if (!file_exists($path)) {
//检查是否有该文件夹,如果没有就创建,并给予最高权限
mkdir($path, 0700, true);
}
$frame->save($path . DIRECTORY_SEPARATOR . md5($filePath) . '.jpg');
return json([
'success' => true,
'data' => '/uploads/video/cover/' . md5($filePath) . '.jpg'
]);
}
/**
* 导出excel(多sheet版)
* composer require phpoffice/phpspreadsheet
* @param string $path 路径
* @param array $data 数据 [ 'Sheet1' => [ ... ] ]
* @param string $filename 文件名
* @param array $cellname 列定义 [ [字段名, 显示标题, 宽度, 对齐方式], ... ]
* @param callable $fun 回调函数用于自定义处理数据
* @return array
*/
public static function exportExcel(string $path, array $data, string $filename, array $cellname, callable $fun): array
{
try {
$spreadsheet = new Spreadsheet();
$spreadsheet->removeSheetByIndex(0);
$i = 0;
foreach ($data as $sheetName => $rows) {
$sheet = $spreadsheet->createSheet($i);
$sheet->setTitle((string)$sheetName);
$j = 1;
foreach ($cellname as $col) {
$column_name = $sheet->getColumnDimensionByColumn($j)->getColumnIndex();
$sheet->setCellValue($column_name . '1', $col[1]); // 标题
$sheet->getColumnDimensionByColumn($j)->setWidth($col[2]); // 宽度
$alignment = $col[3] ?? 'CENTER';
$sheet->getStyle($column_name . '1')
->getAlignment()
->setHorizontal($alignment);
$j++;
}
foreach ($rows as $k => $row) {
$l = 1;
foreach ($cellname as $col) {
$field = $col[0];
$column_name = $sheet->getColumnDimensionByColumn($l)->getColumnIndex();
$item = '';
$value = self::getNestedValue($row, $field);
if (is_array($value)) {
foreach ($value as $rk => $rv) {
$item .= $rk . ':' . $rv . "\n";
}
} else {
$item = $value ?? '';
}
$sheet->setCellValueExplicit($column_name . ($k + 2), $item, DataType::TYPE_STRING);
$l++;
}
}
$fun($sheet, $rows);
$i++;
}
$writer = new Xlsx($spreadsheet);
$writer->save($path . $filename);
return [
'code' => 1,
'msg' => '导出成功',
'data' => $filename,
'filename' => $filename,
];
} catch (Exception $e) {
return [
'code' => 0,
'msg' => '导出失败',
'data' => $e->getMessage(),
'line' => $e->getLine(),
'file' => $e->getFile(),
];
}
}
/**
* 通过点语法获取嵌套数组的值
* @param array $array 原始数组
* @param string $key 点分隔的键路径
* @return mixed
*/
private static function getNestedValue(array $array, string $key)
{
if (strpos($key, '.') === false) {
return $array[$key] ?? null;
}
$keys = explode('.', $key);
$value = $array;
foreach ($keys as $nestedKey) {
if (!is_array($value) || !isset($value[$nestedKey])) {
return null;
}
$value = $value[$nestedKey];
}
return $value;
}
public static function readExcel($filePath)
{
// 判断文件是否存在
if (!file_exists($filePath)) {
return [
'code' => 100,
'msg' => '文件不存在'
];
}
try {
// 使用指定的读取器
$reader = new \PhpOffice\PhpSpreadsheet\Reader\Xlsx();
if ($reader->canRead($filePath)) {
$reader = new \PhpOffice\PhpSpreadsheet\Reader\Xlsx();
} else {
$reader = new \PhpOffice\PhpSpreadsheet\Reader\Xls();
}
// 关键:需要读取样式与格式,才能识别日期
$reader->setReadDataOnly(false);
// 读取 Excel 文件
$spreadsheet = $reader->load($filePath);
// 获取第一个工作表
$sheet = $spreadsheet->getActiveSheet();
// 获取最大行和列
$maxRow = $sheet->getHighestDataRow();
$maxCol = $sheet->getHighestDataColumn();
// 将字母列号转换为数字列号
$maxColIndex = \PhpOffice\PhpSpreadsheet\Cell\Coordinate::columnIndexFromString($maxCol);
// 读取数据
$data = [];
for ($row = 1; $row <= $maxRow; $row++) {
$rowData = [];
for ($col = 1; $col <= $maxColIndex; $col++) {
$column_name = $sheet->getColumnDimensionByColumn($col)->getColumnIndex();
$cell = $sheet->getCell($column_name . $row);
// 若是日期,且为数值序列,统一转成标准格式;否则使用格式化后的显示值
if (\PhpOffice\PhpSpreadsheet\Shared\Date::isDateTime($cell)) {
$raw = $cell->getValue();
if (is_numeric($raw)) {
$cellValue = \PhpOffice\PhpSpreadsheet\Shared\Date::excelToDateTimeObject($raw)->format('Y-m-d H:i:s');
} else {
$cellValue = $cell->getFormattedValue();
}
} else {
$cellValue = $cell->getFormattedValue();
}
$cellValue = ChenUtils::removeBlank($cellValue);
$rowData[] = $cellValue;
}
$data[] = $rowData;
}
foreach ($data as $k => $v) {
foreach ($v as $kk => $vv) {
if (is_string($vv)) {
$data[$k][$kk] = trim($vv);
}
}
}
return [
'code' => 200,
'data' => $data
];
} catch (\PhpOffice\PhpSpreadsheet\Exception $e) {
return [
'code' => 100,
'msg' => $e->getMessage()
];
}
}
/**
* 指定年月,返回开始跟结束时间
* @param $year
* @param $month
* @return array
*/ function getMonthRange($year, $month)
{
$start = strtotime($year . '-' . $month); // 月份开始时间
$end = strtotime(date('Y-m-t', strtotime($year . '-' . $month))); // 月份结束时间
return array(
'start' => date('Y-m-d H:i:s', $start),
'end' => date('Y-m-d H:i:s', $end)
);
}
/**
* 指定年月,返回月每个周的开始跟结束时间
* @param $year
* @param $month
* @return array
*/ function getWeeksInMonth($year, $month)
{
$firstDayOfMonth = date("N", strtotime("$year-$month-01"));
$lastDayOfMonth = date("t", strtotime("$year-$month-01"));
$daysInFirstWeek = 7 - ($firstDayOfMonth - 1) % 7;
$weeksInMonth = ceil(($lastDayOfMonth - $daysInFirstWeek) / 7) + 1;
$weeks = array();
//处理第一周
$firstWeekStart = date("Y-m-d", strtotime("$year-$month-01 -" . ($firstDayOfMonth - 1) . " days"));
$firstWeekEnd = date("Y-m-d", strtotime("$year-$month-01 +" . ($daysInFirstWeek - 1) . " days"));
if ($daysInFirstWeek < 7 && $month > 1) {
$lastMonthDays = date("t", strtotime("$year-" . ($month - 1) . "-01"));
$daysInLastMonth = $firstDayOfMonth - 1;
$firstWeekStart = date("Y-m-d", strtotime("$year-" . ($month - 1) . "-01 +" . ($lastMonthDays - $daysInLastMonth) . " days"));
}
$weeks[] = array("start" => $firstWeekStart, "end" => $firstWeekEnd);
//处理其它周
for ($i = 2; $i <= $weeksInMonth; $i++) {
$weekStart = date("Y-m-d", strtotime("$year-$month-01 +" . (($i - 1) * 7 - $daysInFirstWeek) . " days"));
$weekEnd = date("Y-m-d", strtotime("$weekStart +6 days"));
$weeks[] = array("start" => $weekStart, "end" => $weekEnd);
}
return $weeks;
}
/**
* 将图片的白色部分设为透明
* @param $sourceImage string 源图像
* @param $outputImage string 输出图像(后缀png)
* @return void
*/ public
static function transparentWhite($sourceImage, $outputImage)
{
// 创建源图像资源
$source = imagecreatefrompng($sourceImage);
// 获取源图像的宽度和高度
$width = imagesx($source);
$height = imagesy($source);
// 创建目标图像资源,使用真彩色图像
$target = imagecreatetruecolor($width, $height);
// 定义白色的RGB值
$white = imagecolorallocate($target, 255, 255, 255);
// 设置白色为透明
imagecolortransparent($target, $white);
// 在目标图像上绘制源图像
imagecopy($target, $source, 0, 0, 0, 0, $width, $height);
// 保存目标图像
imagepng($target, $outputImage);
// 释放资源
imagedestroy($source);
imagedestroy($target);
}
/**
* tree数据排序
* @param array $data 线性结构数组
* @param string $symbol 名称前面加符号
* @param string $name 名称
* @param string $id_name 数组id名
* @param string $parent_id_name 数组祖先id名
* @param int $level 此值请勿给参数
* @param int $parent_id 此值请勿给参数
* @return array
*/ public static function linear_to_tree($data, $sub_key_name = 'sub', $id_name = 'id', $parent_id_name = 'pid', $parent_id = 0): array
{
$tree = [];
foreach ($data as $row) {
if ($row[$parent_id_name] == $parent_id) {
$temp = $row;
$child = self::linear_to_tree($data, $sub_key_name, $id_name, $parent_id_name, $row[$id_name]);
if ($child) {
$temp[$sub_key_name] = $child;
}
$tree[] = $temp;
}
}
return $tree;
}
/**
* csv字符串,转数组
*/
public
static function csvToArray($csv)
{
$csv = explode("\n", $csv);
$csv = array_filter($csv);
$csv = array_map(function ($v) {
return explode(',', $v);
}, $csv);
foreach ($csv as $k => $v) {
foreach ($v as $kk => $vv) {
$csv[$k][$kk] = str_replace("\r", '', $vv);
}
}
return $csv;
}
/**
* 读取csv文件
*/
public static function readCsvFile($filePath, $code = 'auto'): array
{
// 判断文件是否存在
if (!file_exists($filePath)) {
return [
'code' => 100,
'msg' => '文件不存在'
];
}
try {
$data = [];
if (($handle = fopen($filePath, "r")) !== false) {
// 设置CSV文件编码
$originalEncoding = mb_internal_encoding();
mb_internal_encoding('UTF-8');
while (($row = fgetcsv($handle, 0, ",")) !== false) {
// 清除每一行数据的乱码字符
foreach ($row as &$cell) {
$cell = mb_convert_encoding($cell, 'UTF-8', $code);
}
$data[] = $row;
}
// 恢复原始编码设置
mb_internal_encoding($originalEncoding);
fclose($handle);
}
foreach ($data as $k => $v) {
foreach ($v as $kk => $vv) {
if (is_string($vv)) {
$data[$k][$kk] = trim($vv);
}
}
}
return [
'code' => 200,
'data' => $data
];
} catch (\Exception $e) {
return [
'code' => 100,
'msg' => $e->getMessage(),
'line'=> $e->getLine(),
'file' => $e->getFile()
];
}
}
/**
* 移除空白字符
*/
public static function removeBlank($str): array|string|null
{
// 移除控制字符
$str = preg_replace('/[[:cntrl:]]/', '', $str);
// 移除高位字符
$str = preg_replace('/[^\PC\s]/u', '', $str);
// 移除BOM头
$str = preg_replace('/\x{FEFF}/u', '', $str);
return $str;
}
/**
* 计算文件夹大小
*/
public static function getDirSize(string $string)
{
$size = 0;
foreach (new RecursiveIteratorIterator(new RecursiveDirectoryIterator($string)) as $file) {
$size += $file->getSize();
}
return $size;
}
/**
* 流(接收所有后返回)
* @param string $url 请求的 URL
* @return array 解析后的 JSON 数据,失败返回 null
* @throws Exception
*/
public static function fetchStreamJson(string $url, array $header = [], string $method = 'GET', array $postData = []): array
{
$client = new Client();
try {
$options = [
'stream' => true,
'verify' => false,
'proxy' => false,
RequestOptions::HEADERS => $header,
];
if ($method === 'POST') {
$options['json'] = $postData;
}
$response = $client->request($method, $url, $options);
$body = $response->getBody();
$buffer = '';
$results = [];
while (!$body->eof()) {
$chunk = $body->read(1024);
$buffer .= $chunk;
$lines = explode("\n", $buffer);
$buffer = array_pop($lines);
foreach ($lines as $line) {
if (str_starts_with(trim($line), 'data:')) {
$jsonData = trim(substr($line, 5));
if ($jsonData === '') continue;
$decoded = json_decode($jsonData, true);
if (json_last_error() === JSON_ERROR_NONE) {
$results[] = $decoded;
} else {
error_log("JSON decode error in line: " . $jsonData);
}
}
}
}
if (!empty($buffer)) {
$jsonData = trim($buffer);
if (str_starts_with($jsonData, '{') || str_starts_with($jsonData, '[')) {
$decoded = json_decode($jsonData, true);
if (json_last_error() === JSON_ERROR_NONE) {
$results[] = $decoded;
}
}
}
return $results;
} catch (GuzzleException $e) {
throw new Exception("Guzzle request error: " . $e->getMessage());
}
}
/**
* 流(控制器使用)
* @throws GuzzleException
*
* 示例:
* header('Content-Type: text/event-stream');
* header('Cache-Control: no-cache');
* header('X-Accel-Buffering: no'); // 防止 Nginx 缓存
* Utils::streamJsonResponse(
* function($output) {
* echo $output;
* },
* 'http://xxj.lygzh.com/v1/workflows/run',
* $header,
* 'POST',
* $postData
* );
*/
public static function streamJsonResponse(callable $outputCallback, string $url, array $header = [], string $method = 'GET', array $postData = [])
{
$client = new \GuzzleHttp\Client();
try {
$options = [
'stream' => true,
'verify' => false,
'proxy' => false,
'headers' => $header,
];
if ($method === 'POST') {
$options['json'] = $postData;
}
$response = $client->request($method, $url, $options);
$body = $response->getBody();
$buffer = '';
while (!$body->eof()) {
$chunk = $body->read(1024);
$buffer .= $chunk;
$lines = explode("\n", $buffer);
$buffer = array_pop($lines); // 保留不完整的一行
foreach ($lines as $line) {
$line = trim($line);
if (str_starts_with($line, 'data:')) {
$jsonData = trim(substr($line, 5));
if (!empty($jsonData)) {
$outputCallback("data: $jsonData\n\n");
}
}
}
}
// 处理剩余 buffer
if (!empty($buffer)) {
$line = trim($buffer);
if (str_starts_with($line, 'data:')) {
$jsonData = trim(substr($line, 5));
if (!empty($jsonData)) {
$outputCallback("data: $jsonData\n\n");
}
}
}
// 发送完成信号
$outputCallback("data: [DONE]\n\n");
} catch (\Exception $e) {
$outputCallback("data: [ERROR] " . $e->getMessage() . "\n\n");
}
}
}
本作品采用 知识共享署名-相同方式共享 4.0 国际许可协议 进行许可。
评论已关闭