PHP公共函数合集

<?php

declare(strict_types=1);

/**
 * 判断数组索引是否全为数字
 * @param array $arr
 * @return bool
 */
function is_index_array(array $arr)
{
    $keys = array_keys($arr);
    $stringKeys = array_filter($keys, function ($key) {
        return is_string($key);
    });
    return empty($stringKeys);
}

/**
 * 发送 HTTP 请求并处理结果
 *
 * @param string $url 请求地址
 * @param string $method 请求类型(GET、POST)
 * @param mixed $data 请求参数
 * @param bool|array $headers 请求头数组
 * @return array|mixed 请求结果数组或原始结果
 */
function send_http_request(string $url, string $method = 'get', mixed $data = [], mixed $header = false, $timeout = 15)
{
    $curl = curl_init($url);
    $method = strtoupper($method);
    //请求方式
    curl_setopt($curl, CURLOPT_CUSTOMREQUEST, $method);
    //携带参数
    if ($method == 'POST') {
        //当POST情况下的body数据为纯的数组(索引为自然数)时,需要将数组转成json字符串
        if (is_array($data) && is_index_array($data)) {
            $data = json_encode($data);
        }
        curl_setopt($curl, CURLOPT_POSTFIELDS, $data);
    } elseif ($method == 'GET' && count($data)) {
        $url .= '?' . http_build_query($data);
        curl_setopt($curl, CURLOPT_URL, $url);
    }
    //超时时间
    curl_setopt($curl, CURLOPT_TIMEOUT, $timeout);
    //设置header头
    if ($header !== false) {
        curl_setopt($curl, CURLOPT_HTTPHEADER, $header);
    }

    curl_setopt($curl, CURLOPT_FAILONERROR, false);
    //返回抓取数据
    curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
    //输出header头信息
    curl_setopt($curl, CURLOPT_HEADER, true);
    //TRUE 时追踪句柄的请求字符串,从 PHP 5.1.3 开始可用。这个很关键,就是允许你查看请求header
    curl_setopt($curl, CURLINFO_HEADER_OUT, true);
    //https请求
    if (1 == strpos("$" . $url, "https://")) {
        curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, false);
    }

    list($content, $status) = [curl_exec($curl), curl_getinfo($curl), curl_close($curl)];
    $content = trim(substr($content, $status['header_size']));

    if (intval($status["http_code"]) === 200) {
        // 尝试将结果转码为数组
        $decodedResponse = json_decode($content, true);
        return $decodedResponse !== null ? $decodedResponse : $content;
    } else {
        // $res = compact('url', 'method', 'data', 'header', 'timeout');
        return false;
    }
}

/**
 * 将JSON字符串转换为数组
 */
function json_to_array(string $jsonString): array
{
    $result = json_decode($jsonString, true);
    if (json_last_error() === JSON_ERROR_NONE) {
        return is_array($result) ? $result : [];
    }
    return [];
}

/**
 * 生成指定长度的随机字符串
 * @param int $length 长度,默认10
 * @param bool $onlyNum 是否只包含数字,默认false
 * @return string
 */
function rand_str(int $length = 10, bool $onlyNum = false)
{
    // 定义字符集
    $charset = $onlyNum ? '0123456789' : 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_';

    // 确保字符串长度至少为1
    if ($length < 1) {
        $length = 1;
    }

    // 使用 random_bytes 生成随机字节
    $bytes = random_bytes($length);

    // 将字节转换为$length位的随机字符串
    $key = '';
    for ($i = 0; $i < $length; $i++) {
        // 取得一个介于0到字符集长度-1之间的随机索引
        $index = ord($bytes[$i]) % strlen($charset);
        $key .= $charset[$index];
    }

    return $key;
}

/**
 * 将给定的数据数组转换为树形结构
 * 
 * @param array $data 包含节点信息的数组,每个节点至少包含$idKey和$pidKey字段
 * @param string $pidKey 父节点ID的键名,默认为'pid'
 * @param string $idKey 节点ID的键名,默认为'id'
 * @return array 返回树形结构的数组,根节点的$pidKey值应为0
 */
function tree($data, $pidKey = 'pid', $idKey = 'id')
{
    // 初始化映射数组,用于快速查找每个节点的子节点
    $map = [];
    // 遍历所有节点,建立父节点ID到子节点的映射
    foreach ($data as &$item) {
        $map[$item[$pidKey]][] = &$item;
    }
    // 再次遍历所有节点,为每个节点添加其子节点数组
    foreach ($data as &$item) {
        if (isset($map[$item[$idKey]])) {
            $item['child'] = $map[$item[$idKey]];
        }
    }
    // 返回根节点数组,即父节点ID为0的节点及其所有子节点构成的树
    return $map[0] ?? [];
}

/**
 * 对二维数组根据指定键名去重
 *
 * @param array $data 二维数组
 * @param string $key 指定的键名
 * @return array 去重后的二维数组
 */
function array_multi_unique(array $data, string $key): array
{
    $uniqueData = [];
    $seenValues = [];

    foreach ($data as $item) {
        // 不能使用isset($item[$key]),当$item[$key]的值为null时,使用isset判断为false
        if (array_key_exists($key, $item) && !in_array($item[$key], $seenValues, true)) {
            $seenValues[] = $item[$key];
            $uniqueData[] = $item;
        }
    }

    return $uniqueData;
}

/**
 * 对二维数组根据指定键名排序(支持升序/降序)
 * 如果元素缺少指定键,则视为最小值(升序)或最大值(降序)
 * @param array $data 二维数组
 * @param string $key 指定的键名
 * @param string $order 排序方式,'asc' 表示升序,'desc' 表示降序,默认为 'asc'
 * @return array 排序后的二维数组
 */
function array_multi_sort(array $data, string $key, string $order = 'asc'): array
{
    if (!in_array(strtolower($order), ['asc', 'desc'])) {
        throw new InvalidArgumentException("排序方式必须是 'asc' 或 'desc'");
    }

    usort($data, function ($a, $b) use ($key, $order) {
        // 如果元素缺少指定键,则视为最小值(升序)或最大值(降序)
        $valueA = array_key_exists($key, $a) ? $a[$key] : (strtolower($order) === 'asc' ? null : INF);
        $valueB = array_key_exists($key, $b) ? $b[$key] : (strtolower($order) === 'asc' ? null : INF);

        // 使用空值安全的比较逻辑
        return strtolower($order) === 'asc' ? ($valueA <=> $valueB) : ($valueB <=> $valueA);
    });

    return $data;
}

/**
 * 将文件大小转换为人类可读的格式。
 *
 * 该函数接受一个以字节为单位的文件大小,并将其转换为更易读的格式,
 * 例如 KB、MB 或 GB。转换后的值保留两位小数,并附带相应的单位。
 *
 * @param int $size 文件大小,以字节为单位。
 * @return string 转换后的文件大小字符串,包含数值和单位(如 "1.23 MB")。
 */
function file_format_size($size)
{
    if (!is_numeric($size)) {
        throw new InvalidArgumentException("文件大小必须为数字");
    }
    if ($size  < 0) {
        throw new InvalidArgumentException("文件大小必须大于等于0");
    }
    // 定义文件大小单位数组,从字节到千兆字节。
    $units = ['B', 'KB', 'MB', 'GB'];
    $unitIndex = 0;

    // 循环计算文件大小,直到找到合适的单位
    while ($size >= 1024 && $unitIndex < count($units) - 1) {
        $size /= 1024;
        $unitIndex++;
    }

    // 返回格式化后的文件大小,保留两位小数
    return round($size, 2) . ' ' . $units[$unitIndex];
}

/**
 * 动态调整日期
 *
 * 该函数支持按分钟、小时、天、周、月、季度、年增减日期。
 * 避免直接操作时间戳的复杂性,使用 DateTime 和 DateInterval 进行日期计算。
 *
 * @param string $date 输入日期,格式为 'Y-m-d H:i:s' 或其他可被 DateTime 解析的格式
 * @param int $amount 调整的数量,正数表示增加,负数表示减少
 * @param string $unit 调整单位,支持 'minute', 'hour', 'day', 'week', 'month', 'quarter', 'year'
 * @return string 返回调整后的日期,格式为 'Y-m-d H:i:s'
 * @throws InvalidArgumentException 如果 $unit 不在支持范围内
 */
function date_adjust(string $date, int $amount, string $unit): string
{
    // 支持的单位映射
    $unitMap = [
        'minute' => 'PT%dM',
        'hour'   => 'PT%dH',
        'day'    => 'P%dD',
        'week'   => 'P%dW',
        'month'  => 'P%dM',
        'quarter' => 'P%dM', // 季度按3个月处理
        'year'   => 'P%dY',
    ];

    if (!isset($unitMap[$unit])) {
        throw new InvalidArgumentException("不支持的调整单位: {$unit}");
    }

    // 创建 DateTime 对象
    $dateTime = new DateTime($date);

    // 特殊处理季度
    if ($unit === 'quarter') {
        $amount *= 3; // 季度转换为月
    }

    // 构造间隔字符串
    $intervalSpec = sprintf($unitMap[$unit], abs($amount));
    $interval = new DateInterval($intervalSpec);

    // 根据正负调整日期
    if ($amount >= 0) {
        $dateTime->add($interval);
    } else {
        $dateTime->sub($interval);
    }

    // 返回调整后的日期
    return $dateTime->format('Y-m-d H:i:s');
}

/**
 * 计算两个日期之间的差异值
 *
 * 该函数支持按秒、分钟、小时、天、周、月、季度、年计算两个日期之间的差异。
 * 使用 DateTime 和 DateInterval 进行日期计算。
 *
 * @param string $date1 第一个日期,格式为 'Y-m-d H:i:s' 或其他可被 DateTime 解析的格式
 * @param string $date2 第二个日期,格式为 'Y-m-d H:i:s' 或其他可被 DateTime 解析的格式
 * @param string $unit 计算差异的单位,支持 'second', 'minute', 'hour', 'day', 'week', 'month', 'quarter', 'year'
 * @return int|float 返回两个日期之间的差异值
 * @throws InvalidArgumentException 如果 $unit 不在支持范围内
 */
function date_subtract(string $date1, string $date2, string $unit): int|float
{
    // 支持的单位映射
    $unitMap = [
        'second' => 's',
        'minute' => 'i',
        'hour'   => 'h',
        'day'    => 'days',
        'week'   => 'days',
        'month'  => 'm',
        'quarter' => 'm',
        'year'   => 'y',
    ];

    if (!isset($unitMap[$unit])) {
        throw new InvalidArgumentException("不支持的差异单位: {$unit}");
    }

    // 创建 DateTime 对象
    $dateTime1 = new DateTime($date1);
    $dateTime2 = new DateTime($date2);

    // 计算日期差异
    $interval = $dateTime1->diff($dateTime2);

    // 特殊处理季度
    if ($unit === 'quarter') {
        return $interval->y * 4 + $interval->m / 3;
    }

    // 特殊处理周
    if ($unit === 'week') {
        return $interval->days / 7;
    }

    // 返回差异值
    return $interval->{$unitMap[$unit]};
}

/**
 * thinkphp
 * 判断Redis列表中是否存在某个元素
 * @param string $listKey 列表键名
 * @param mixed $value 要查找的值
 * @return bool
 */
function redis_list_in($listKey, $value)
{
    // 使用LREM命令判断(性能优)
    $count = Cache::store('redis')->lRem($listKey, $value, 0);
    return $count > 0;
}

 

posted @ 2025-03-28 09:21  tros  阅读(53)  评论(0)    收藏  举报