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;
|
2023-01-13 18:00:33 +08:00
|
|
|
|
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
|
2023-01-13 18:00:33 +08:00
|
|
|
|
* @throws Exception
|
2022-12-19 17:28:55 +08:00
|
|
|
|
*/
|
2023-01-13 18:00:33 +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++) {
|
2023-01-13 18:00:33 +08:00
|
|
|
|
$code .= random_int(1, 9);
|
2022-12-19 17:28:55 +08:00
|
|
|
|
}
|
|
|
|
|
|
return $code;
|
|
|
|
|
|
}
|
2023-01-09 18:22:10 +08:00
|
|
|
|
|
2023-01-13 18:00:33 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 获取真实IP
|
|
|
|
|
|
* @param RequestInterface $request
|
|
|
|
|
|
* @return string
|
|
|
|
|
|
*/
|
|
|
|
|
|
public function getRealIpAddress(RequestInterface $request): string
|
2022-12-19 17:28:55 +08:00
|
|
|
|
{
|
2023-01-13 18:45:13 +08:00
|
|
|
|
$x_forwarded_for = $request->header('x-forwarded-for');
|
|
|
|
|
|
var_dump($x_forwarded_for);
|
2023-01-13 18:00:33 +08:00
|
|
|
|
$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);
|
2023-01-13 18:45:13 +08:00
|
|
|
|
return $x_forwarded_for
|
|
|
|
|
|
?? $x_real_ip
|
2023-01-13 18:00:33 +08:00
|
|
|
|
?? $remote_addr
|
|
|
|
|
|
?? 'unknown';
|
2022-12-19 17:28:55 +08:00
|
|
|
|
}
|
2023-01-09 18:22:10 +08:00
|
|
|
|
|
2022-12-19 17:28:55 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 在响应头中添加 Link 字段
|
|
|
|
|
|
*
|
2023-01-09 18:22:10 +08:00
|
|
|
|
* @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
|
|
|
|
*
|
2023-01-09 18:22:10 +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;
|
|
|
|
|
|
}
|
2023-01-09 18:22:10 +08:00
|
|
|
|
|
2022-12-19 17:28:55 +08:00
|
|
|
|
$params['rel'] = $rel;
|
|
|
|
|
|
if (!$trimQuote) {
|
|
|
|
|
|
foreach ($params as &$value) {
|
2023-01-09 18:22:10 +08:00
|
|
|
|
$value = sprintf("\"%s\"", trim($value, '"'));
|
2022-12-19 17:28:55 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2023-01-09 18:22:10 +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, '', '; ');
|
2023-01-09 18:22:10 +08:00
|
|
|
|
|
2022-12-19 17:28:55 +08:00
|
|
|
|
return $response->withHeader(
|
|
|
|
|
|
'Link',
|
|
|
|
|
|
join(',', $link_list)
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
2023-01-09 18:22:10 +08:00
|
|
|
|
|
2022-12-19 17:28:55 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 构建 URL
|
|
|
|
|
|
*
|
|
|
|
|
|
* @param string $url
|
2023-01-13 18:00:33 +08:00
|
|
|
|
* @param array<string, string> $moreQueries 要附加的 query 参数
|
|
|
|
|
|
* @param bool $anchorQuery 开启后,query 参数将被尽量组合到锚点中,以支持 spa 路由
|
2022-12-19 17:28:55 +08:00
|
|
|
|
* @return string
|
|
|
|
|
|
*/
|
2023-01-13 18:00:33 +08:00
|
|
|
|
#[Pure]
|
|
|
|
|
|
public function buildUrl(string $url, array $moreQueries = [], bool $anchorQuery = false): string
|
2022-12-19 17:28:55 +08:00
|
|
|
|
{
|
2023-01-13 18:00:33 +08:00
|
|
|
|
if (count($moreQueries) === 0) {
|
|
|
|
|
|
return $url;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2022-12-19 17:28:55 +08:00
|
|
|
|
$url_info = parse_url($url);
|
2023-01-09 18:22:10 +08:00
|
|
|
|
|
2023-01-13 18:00:33 +08:00
|
|
|
|
$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
|
|
|
|
}
|
2023-01-09 18:22:10 +08:00
|
|
|
|
|
2022-12-19 17:28:55 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 以迭代器的形式响应
|
|
|
|
|
|
*
|
|
|
|
|
|
* @param string $fullName
|
|
|
|
|
|
*
|
2023-01-09 18:22:10 +08:00
|
|
|
|
* @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 文件不存在');
|
|
|
|
|
|
}
|
2023-01-09 18:22:10 +08:00
|
|
|
|
|
2022-12-19 17:28:55 +08:00
|
|
|
|
$file = fopen($fullName, 'r');
|
|
|
|
|
|
while ($data = fgetcsv($file)) { //每次读取CSV里面的一行内容
|
|
|
|
|
|
yield $data;
|
|
|
|
|
|
}
|
2023-01-09 18:22:10 +08:00
|
|
|
|
|
2022-12-19 17:28:55 +08:00
|
|
|
|
fclose($file);
|
|
|
|
|
|
}
|
2023-01-09 18:22:10 +08:00
|
|
|
|
|
2022-12-19 17:28:55 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 以数组形式响应
|
|
|
|
|
|
*
|
2023-01-09 18:22:10 +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 . ' 不存在');
|
|
|
|
|
|
}
|
2023-01-09 18:22:10 +08:00
|
|
|
|
|
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;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2023-01-09 18:22:10 +08:00
|
|
|
|
|
2022-12-19 17:28:55 +08:00
|
|
|
|
fclose($file);
|
|
|
|
|
|
return $result;
|
|
|
|
|
|
}
|
2023-01-09 18:22:10 +08:00
|
|
|
|
|
2022-12-19 17:28:55 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* (level倒序)快速无极分类(时间复杂度 O(n),空间复杂度 O(1))
|
|
|
|
|
|
* 条件:数组索引是数据parent_id对应的id,只支持level倒序
|
|
|
|
|
|
*
|
2023-01-09 18:22:10 +08:00
|
|
|
|
* @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
|
|
|
|
|
|
*/
|
2023-01-13 18:00:33 +08:00
|
|
|
|
#[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;
|
|
|
|
|
|
}
|
2023-01-09 18:22:10 +08:00
|
|
|
|
|
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;
|
|
|
|
|
|
}
|
2023-01-09 18:22:10 +08:00
|
|
|
|
|
2022-12-19 17:28:55 +08:00
|
|
|
|
//取出顶级
|
|
|
|
|
|
if ($item['level'] === $level) {
|
|
|
|
|
|
$list[$childrenName][] = $item;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
$list = $list[$childrenName];
|
|
|
|
|
|
}
|
2023-01-09 18:22:10 +08:00
|
|
|
|
|
|
|
|
|
|
|
2022-12-19 17:28:55 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 将内存/硬盘大小转化为带单位大小
|
|
|
|
|
|
*
|
2023-01-09 18:22:10 +08:00
|
|
|
|
* @param float|int|string $size 空间大小(单位:Byte)
|
|
|
|
|
|
* @param bool $unitToUpper 单位转换为大写(默认 false)
|
|
|
|
|
|
* @param int $precision 小数点后保留的位数(默认 2)
|
2022-12-19 17:28:55 +08:00
|
|
|
|
*
|
2023-01-09 18:22:10 +08:00
|
|
|
|
* @return array{'size': float, 'unit': string}
|
2022-12-19 17:28:55 +08:00
|
|
|
|
*/
|
2023-01-09 18:22:10 +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],
|
|
|
|
|
|
];
|
|
|
|
|
|
}
|
2023-01-09 18:22:10 +08:00
|
|
|
|
|
2022-12-19 17:28:55 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 将时长转换为带单位时间
|
|
|
|
|
|
*
|
|
|
|
|
|
* @param int|float $duration 时长(单位:ms)
|
2023-01-09 18:22:10 +08:00
|
|
|
|
* @param bool $unitToUpper
|
|
|
|
|
|
* @param bool $format
|
|
|
|
|
|
* @param integer $precision
|
2022-12-19 17:28:55 +08:00
|
|
|
|
*
|
2023-01-09 18:22:10 +08:00
|
|
|
|
* @return array{'duration': float, 'unit': string}
|
2022-12-19 17:28:55 +08:00
|
|
|
|
*/
|
2023-01-09 18:22:10 +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'];
|
2023-01-09 18:22:10 +08:00
|
|
|
|
|
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],
|
|
|
|
|
|
];
|
|
|
|
|
|
}
|
2023-01-09 18:22:10 +08:00
|
|
|
|
}
|