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

336 lines
9.4 KiB
PHP
Raw Normal View History

2022-12-19 17:28:55 +08:00
<?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;
2022-12-19 17:28:55 +08:00
use JetBrains\PhpStorm\Pure;
use Psr\Http\Message\ResponseInterface;
/**
* 工具类
*/
class UtilsService
{
/**
* 生成验证码
*
* @param int $length
*
* @return string
* @throws Exception
2022-12-19 17:28:55 +08:00
*/
#[Pure]
2022-12-19 17:28:55 +08:00
public function generateSecureCode(int $length = 4): string
{
$code = '';
for ($times = 0; $times < $length; $times++) {
$code .= random_int(1, 9);
2022-12-19 17:28:55 +08:00
}
return $code;
}
/**
* 获取真实IP
* @param RequestInterface $request
* @return string
*/
public function getRealIpAddress(RequestInterface $request): string
2022-12-19 17:28:55 +08:00
{
$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';
2022-12-19 17:28:55 +08:00
}
2022-12-19 17:28:55 +08:00
/**
* 在响应头中添加 Link 字段
*
* @param ResponseInterface $response
* @param string|null $urlReference
* @param string $rel
* @param array<string, string> $params
* @param bool $trimQuote
2022-12-19 17:28:55 +08:00
*
* @return ResponseInterface
2022-12-19 17:28:55 +08:00
*/
public function extendLinkToHeader(
ResponseInterface $response,
?string $urlReference,
string $rel = '',
array $params = [],
bool $trimQuote = true
): ResponseInterface {
if (is_null($urlReference)) {
return $response;
}
2022-12-19 17:28:55 +08:00
$params['rel'] = $rel;
if (!$trimQuote) {
foreach ($params as &$value) {
$value = sprintf("\"%s\"", trim($value, '"'));
2022-12-19 17:28:55 +08:00
}
}
2022-12-19 17:28:55 +08:00
$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, '', '; ');
2022-12-19 17:28:55 +08:00
return $response->withHeader(
'Link',
join(',', $link_list)
);
}
2022-12-19 17:28:55 +08:00
/**
* 构建 URL
*
* @param string $url
* @param array<string, string> $moreQueries 要附加的 query 参数
* @param bool $anchorQuery 开启后query 参数将被尽量组合到锚点中,以支持 spa 路由
2022-12-19 17:28:55 +08:00
* @return string
*/
#[Pure]
public function buildUrl(string $url, array $moreQueries = [], bool $anchorQuery = false): string
2022-12-19 17:28:55 +08:00
{
if (count($moreQueries) === 0) {
return $url;
}
2022-12-19 17:28:55 +08:00
$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;
2022-12-19 17:28:55 +08:00
}
2022-12-19 17:28:55 +08:00
/**
* 以迭代器的形式响应
*
* @param string $fullName
*
* @return Generator
* @throws Exception
2022-12-19 17:28:55 +08:00
*/
public function csvReaderGenerator(string $fullName): Generator
{
if (!is_file($fullName)) {
throw new Exception('指定的 csv 文件不存在');
}
2022-12-19 17:28:55 +08:00
$file = fopen($fullName, 'r');
while ($data = fgetcsv($file)) { //每次读取CSV里面的一行内容
yield $data;
}
2022-12-19 17:28:55 +08:00
fclose($file);
}
2022-12-19 17:28:55 +08:00
/**
* 以数组形式响应
*
* @param string $fullName
* @param array<string> $headers
* @param int|null $batchNumber
* @return array<int, mixed>
* @throws Exception
2022-12-19 17:28:55 +08:00
*/
public function csvReader(string $fullName, array $headers = [], ?int $batchNumber = null): array
{
if (!is_file($fullName)) {
throw new Exception($fullName . ' 不存在');
}
2022-12-19 17:28:55 +08:00
$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;
}
}
2022-12-19 17:28:55 +08:00
fclose($file);
return $result;
}
2022-12-19 17:28:55 +08:00
/**
* 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
2022-12-19 17:28:55 +08:00
*
* @return void
*/
#[NoReturn]
2022-12-19 17:28:55 +08:00
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;
}
2022-12-19 17:28:55 +08:00
unset($list[$i]);
$item = isset($filterCallback) ? $filterCallback($item) : $item;
//判定非顶级的pid是否存在如果存在则再pid所在的数组下面加入一个字段items来将本身存进去
if (isset($list[$item[$parentIdName]])) {
$list[$item[$parentIdName]][$childrenName][] = $item;
continue;
}
2022-12-19 17:28:55 +08:00
//取出顶级
if ($item['level'] === $level) {
$list[$childrenName][] = $item;
}
}
$list = $list[$childrenName];
}
2022-12-19 17:28:55 +08:00
/**
* 将内存/硬盘大小转化为带单位大小
*
* @param float|int|string $size 空间大小单位Byte
* @param bool $unitToUpper 单位转换为大写(默认 false
* @param int $precision 小数点后保留的位数(默认 2
2022-12-19 17:28:55 +08:00
*
* @return array{'size': float, 'unit': string}
2022-12-19 17:28:55 +08:00
*/
#[Pure]
2022-12-19 17:28:55 +08:00
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],
];
}
2022-12-19 17:28:55 +08:00
/**
* 将时长转换为带单位时间
*
* @param int|float $duration 时长单位ms
* @param bool $unitToUpper
* @param bool $format
* @param integer $precision
2022-12-19 17:28:55 +08:00
*
* @return array{'duration': float, 'unit': string}
2022-12-19 17:28:55 +08:00
*/
#[Pure]
2022-12-19 17:28:55 +08:00
public function convertDuration(
int|float $duration,
bool $unitToUpper = false,
bool $format = true,
int $precision = 3
): array {
$unit = ['ms', 's'];
2022-12-19 17:28:55 +08:00
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],
];
}
}