Files
hdk-core/src/Service/UtilsService.php

336 lines
9.4 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
declare(strict_types=1);
namespace Singularity\HDK\Core\Service;
use Closure;
use Exception;
use Generator;
use Hyperf\HttpServer\Contract\RequestInterface;
use JetBrains\PhpStorm\NoReturn;
use JetBrains\PhpStorm\Pure;
use Psr\Http\Message\ResponseInterface;
/**
* 工具类
*/
class UtilsService
{
/**
* 生成验证码
*
* @param int $length
*
* @return string
* @throws Exception
*/
#[Pure]
public function generateSecureCode(int $length = 4): string
{
$code = '';
for ($times = 0; $times < $length; $times++) {
$code .= random_int(1, 9);
}
return $code;
}
/**
* 获取真实IP
* @param RequestInterface $request
* @return string
*/
public function getRealIpAddress(RequestInterface $request): string
{
$x_forwarded_for = $request->header('x-forwarded-for');
var_dump($x_forwarded_for);
$x_real_ip = $request->header('x-real-ip');
var_dump(__METHOD__ . 'x-real-ip: ' . $x_real_ip);
$remote_addr = $request->server('remote_addr');
var_dump(__METHOD__ . 'remote_addr: ' . $remote_addr);
return $x_forwarded_for
?? $x_real_ip
?? $remote_addr
?? 'unknown';
}
/**
* 在响应头中添加 Link 字段
*
* @param ResponseInterface $response
* @param string|null $urlReference
* @param string $rel
* @param array<string, string> $params
* @param bool $trimQuote
*
* @return ResponseInterface
*/
public function extendLinkToHeader(
ResponseInterface $response,
?string $urlReference,
string $rel = '',
array $params = [],
bool $trimQuote = true
): ResponseInterface {
if (is_null($urlReference)) {
return $response;
}
$params['rel'] = $rel;
if (!$trimQuote) {
foreach ($params as &$value) {
$value = sprintf("\"%s\"", trim($value, '"'));
}
}
$link_list = [];
if ($response->hasHeader('Link')) {
$link = $response->getHeaderLine('Link');
$link_list = explode(',', $link);
$link_list = array_map('trim', $link_list);
}
$link_list[] = "<$urlReference>; " . http_build_query($params, '', '; ');
return $response->withHeader(
'Link',
join(',', $link_list)
);
}
/**
* 构建 URL
*
* @param string $url
* @param array<string, string> $moreQueries 要附加的 query 参数
* @param bool $anchorQuery 开启后query 参数将被尽量组合到锚点中,以支持 spa 路由
* @return string
*/
#[Pure]
public function buildUrl(string $url, array $moreQueries = [], bool $anchorQuery = false): string
{
if (count($moreQueries) === 0) {
return $url;
}
$url_info = parse_url($url);
$result = '';
// 协议
if (isset($url_info['scheme'])) {
$result .= $url_info['scheme'];
}
// 域名
if (isset($url_info['host'])) {
if (!empty($result)) {
$result .= ':';
}
$result .= '//';
// 用户名
if (isset($url_info['user'])) {
$result .= $url_info['user'];
// 密码
if (isset($url_info['pass'])) {
$result .= ':' . $url_info['pass'];
}
$result .= '@';
}
$result .= $url_info['host'];
}
// 端口
if (isset($url_info['port'])) {
$result .= ':' . $url_info['port'];
}
// 路径
$result .= $url_info['path'];
// 查询参数
if (!empty($url_info['query'])) {
$query_params = $url_info['query'];
parse_str($query_params, $origin_params);
if (!$anchorQuery) {
$origin_params += $moreQueries;
}
$result .= '?' . http_build_query($origin_params);
} else {
if (!$anchorQuery) {
$result .= '?' . http_build_query($moreQueries);
}
}
// 锚点
if (!empty($url_info['fragment'])) {
$fragment = $url_info['fragment'];
if ($anchorQuery) {
$fragment_data = parse_url($url_info['fragment']);
parse_str($fragment_data['query'] ?? '', $hash_queries);
$hash_queries += $moreQueries;
$fragment = $fragment_data['path'] . '?' . http_build_query($hash_queries);
}
$result .= '#' . $fragment;
}
return $result;
}
/**
* 以迭代器的形式响应
*
* @param string $fullName
*
* @return Generator
* @throws Exception
*/
public function csvReaderGenerator(string $fullName): Generator
{
if (!is_file($fullName)) {
throw new Exception('指定的 csv 文件不存在');
}
$file = fopen($fullName, 'r');
while ($data = fgetcsv($file)) { //每次读取CSV里面的一行内容
yield $data;
}
fclose($file);
}
/**
* 以数组形式响应
*
* @param string $fullName
* @param array<string> $headers
* @param int|null $batchNumber
* @return array<int, mixed>
* @throws Exception
*/
public function csvReader(string $fullName, array $headers = [], ?int $batchNumber = null): array
{
if (!is_file($fullName)) {
throw new Exception($fullName . ' 不存在');
}
$file = fopen($fullName, 'r');
$result = [];
$row_number = 0;
while ($data = fgetcsv($file)) { //每次读取CSV里面的一行内容
$row_number++;
$data = empty($headers) ? $data : array_combine($headers, $data);
$data = array_map(function ($row) {
return is_string($row) ? trim($row) : $row;
}, $data);
if (!is_null($batchNumber)) {
$result[intval($row_number / $batchNumber)][] = $data;
} else {
$result[] = $data;
}
}
fclose($file);
return $result;
}
/**
* level倒序快速无极分类时间复杂度 O(n),空间复杂度 O(1)
* 条件数组索引是数据parent_id对应的id只支持level倒序
*
* @param array<int, mixed> $list
* @param int $level
* @param string $parentIdName
* @param string $childrenName
* @param Closure|null $filterCallback
*
* @return void
*/
#[NoReturn]
public function unlimitedSubCategoriesQuicklyWithLevel(
array &$list,
int $level = 1,
string $parentIdName = 'parent_id',
string $childrenName = 'children',
?Closure $filterCallback = null
): void {
foreach ($list as $i => &$item) {
if (!isset($item[$parentIdName])) {
break;
}
unset($list[$i]);
$item = isset($filterCallback) ? $filterCallback($item) : $item;
//判定非顶级的pid是否存在如果存在则再pid所在的数组下面加入一个字段items来将本身存进去
if (isset($list[$item[$parentIdName]])) {
$list[$item[$parentIdName]][$childrenName][] = $item;
continue;
}
//取出顶级
if ($item['level'] === $level) {
$list[$childrenName][] = $item;
}
}
$list = $list[$childrenName];
}
/**
* 将内存/硬盘大小转化为带单位大小
*
* @param float|int|string $size 空间大小单位Byte
* @param bool $unitToUpper 单位转换为大写(默认 false
* @param int $precision 小数点后保留的位数(默认 2
*
* @return array{'size': float, 'unit': string}
*/
#[Pure]
public function convertStorageSize(
float|int|string $size,
bool $unitToUpper = false,
int $precision = 2
): array {
$unit = ['b', 'kb', 'mb', 'gb', 'tb', 'pb'];
if ($size > 0) {
$i = floor(log($size, 1024));
$size = round($size / pow(1024, $i), $precision);
} else {
$i = 0;
$size = 0;
}
return [
'size' => $size,
'unit' => $unitToUpper ? strtoupper($unit[$i]) : $unit[$i],
];
}
/**
* 将时长转换为带单位时间
*
* @param int|float $duration 时长单位ms
* @param bool $unitToUpper
* @param bool $format
* @param integer $precision
*
* @return array{'duration': float, 'unit': string}
*/
#[Pure]
public function convertDuration(
int|float $duration,
bool $unitToUpper = false,
bool $format = true,
int $precision = 3
): array {
$unit = ['ms', 's'];
if ($duration > 1) {
$i = floor(log($duration, 1000));
$duration = round($duration / pow(1000, $i), $precision);
} elseif ($duration < 1) {
$i = 0;
} else {
$i = 0;
$duration = 0;
}
return [
'duration' => $format ? number_format($duration, $precision) : $duration,
'unit' => $unitToUpper ? strtoupper($unit[$i]) : $unit[$i],
];
}
}