mirror of
http://124.126.16.154:8888/singularity/HyperfDevelopmentKitCore.git
synced 2026-01-15 05:15:07 +08:00
336 lines
9.4 KiB
PHP
336 lines
9.4 KiB
PHP
<?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],
|
||
];
|
||
}
|
||
}
|