mirror of
https://github.com/lkeme/BiliHelper-personal.git
synced 2025-12-19 09:30:10 +08:00
338 lines
11 KiB
PHP
338 lines
11 KiB
PHP
<?php
|
|
|
|
/**
|
|
* Website: https://mudew.com/
|
|
* Author: Lkeme
|
|
* License: The MIT License
|
|
* Email: Useri@live.cn
|
|
* Updated: 2021 ~ 2022
|
|
*/
|
|
|
|
namespace BiliHelper\Core;
|
|
|
|
use BiliHelper\Tool\Generator;
|
|
|
|
class Curl
|
|
{
|
|
private static $client;
|
|
private static $async_opt;
|
|
private static $results = [];
|
|
private static $result = [];
|
|
private static $buvid = '';
|
|
|
|
/**
|
|
* @use POST请求
|
|
* @param $os
|
|
* @param $url
|
|
* @param array $params
|
|
* @param array $headers
|
|
* @param int $timeout
|
|
* @return mixed
|
|
*/
|
|
public static function post($os, $url, $params = [], $headers = [], $timeout = 30)
|
|
{
|
|
Log::debug("POST: {$url}");
|
|
$headers = self::getHeaders($os, $headers);
|
|
$payload['form_params'] = count($params) ? $params : [];
|
|
$options = self::getClientOpt($payload, $headers);
|
|
$request = self::clientHandle($url, 'post', $options);
|
|
$body = $request->getBody();
|
|
Log::debug($body);
|
|
return $body;
|
|
}
|
|
|
|
/**
|
|
* @use GET请求
|
|
* @param $os
|
|
* @param $url
|
|
* @param array $params
|
|
* @param array $headers
|
|
* @param int $timeout
|
|
* @return mixed
|
|
*/
|
|
public static function get($os, $url, $params = [], $headers = [], $timeout = 30)
|
|
{
|
|
Log::debug("GET: {$url}");
|
|
$headers = self::getHeaders($os, $headers);
|
|
$payload['query'] = count($params) ? $params : [];
|
|
$options = self::getClientOpt($payload, $headers);
|
|
$request = self::clientHandle($url, 'get', $options);
|
|
$body = $request->getBody();
|
|
Log::debug($body);
|
|
return $body;
|
|
}
|
|
|
|
/**
|
|
* @use PUT请求
|
|
* @param $os
|
|
* @param $url
|
|
* @param array $params
|
|
* @param array $headers
|
|
* @param int $timeout
|
|
* @return mixed
|
|
*/
|
|
public static function put($os, $url, $params = [], $headers = [], $timeout = 30)
|
|
{
|
|
Log::debug("PUT: {$url}");
|
|
$headers = self::getHeaders($os, $headers);
|
|
$payload['json'] = count($params) ? $params : [];
|
|
$options = self::getClientOpt($payload, $headers);
|
|
$request = self::clientHandle($url, 'post', $options);
|
|
$body = $request->getBody();
|
|
Log::debug($body);
|
|
return $body;
|
|
}
|
|
|
|
/**
|
|
* @use 并发POST请求
|
|
* @param $os
|
|
* @param $url
|
|
* @param array $tasks
|
|
* @param array $headers
|
|
* @param int $timeout
|
|
* @return array
|
|
*/
|
|
public static function async($os, $url, $tasks = [], $headers = [], $timeout = 30): array
|
|
{
|
|
self::$async_opt = [
|
|
'tasks' => $tasks,
|
|
'counter' => 1,
|
|
'count' => count($tasks),
|
|
'concurrency' => count($tasks) < 10 ? count($tasks) : 10
|
|
];
|
|
Log::debug("ASYNC: {$url}");
|
|
$headers = self::getHeaders($os, $headers);
|
|
$requests = function ($total) use ($url, $headers, $tasks) {
|
|
foreach ($tasks as $task) {
|
|
yield function () use ($url, $headers, $task) {
|
|
$payload['form_params'] = $task['payload'];
|
|
$options = self::getClientOpt($payload, $headers);
|
|
return self::clientHandle($url, 'postAsync', $options);
|
|
};
|
|
}
|
|
};
|
|
$pool = new \GuzzleHttp\Pool(self::$client, $requests(self::$async_opt['count']), [
|
|
'concurrency' => self::$async_opt['concurrency'],
|
|
'fulfilled' => function ($response, $index) {
|
|
$res = $response->getBody();
|
|
// Log::notice("启动多线程 {$index}");
|
|
array_push(self::$results, [
|
|
'content' => $res,
|
|
'source' => self::$async_opt['tasks'][$index]['source']
|
|
]);
|
|
self::countedAndCheckEnded();
|
|
},
|
|
'rejected' => function ($reason, $index) {
|
|
Log::error("多线程第{$index}个请求失败, ERROR: {$reason}");
|
|
self::countedAndCheckEnded();
|
|
},
|
|
]);
|
|
// 开始发送请求
|
|
$promise = $pool->promise();
|
|
$promise->wait();
|
|
return self::getResults();
|
|
}
|
|
|
|
/**
|
|
* @use 单次请求
|
|
* @param $method
|
|
* @param $url
|
|
* @param array $payload
|
|
* @param array $headers
|
|
* @param int $timeout
|
|
* @return false|string|null
|
|
*/
|
|
public static function request($method, $url, $payload = [], $headers = [], $timeout = 10)
|
|
{
|
|
Log::debug("REQUEST: {$url}");
|
|
$options = array(
|
|
'http' => array(
|
|
'method' => strtoupper($method),
|
|
'header' => self::arr2str($headers),
|
|
'content' => http_build_query($payload),
|
|
'timeout' => $timeout,
|
|
),
|
|
);
|
|
$result = $url ? @file_get_contents($url, false, stream_context_create($options)) : null;
|
|
Log::debug($result);
|
|
return $result ?: null;
|
|
}
|
|
|
|
/**
|
|
* @use 计数搭配并发使用
|
|
*/
|
|
private static function countedAndCheckEnded()
|
|
{
|
|
if (self::$async_opt['counter'] < self::$async_opt['count']) {
|
|
self::$async_opt['counter']++;
|
|
return;
|
|
}
|
|
// 请求结束
|
|
self::$async_opt = [];
|
|
}
|
|
|
|
/**
|
|
* @use 请求中心异常处理
|
|
* @param string $url
|
|
* @param string $method
|
|
* @param array $options
|
|
* @return mixed
|
|
*/
|
|
private static function clientHandle(string $url, string $method, array $options)
|
|
{
|
|
$max_retry = range(1, 40);
|
|
foreach ($max_retry as $retry) {
|
|
try {
|
|
$response = call_user_func_array([self::$client, $method], [$url, $options]);
|
|
if (is_null($response) or empty($response)) throw new \Exception("Value IsEmpty");
|
|
return $response;
|
|
} catch (\GuzzleHttp\Exception\RequestException $e) {
|
|
// var_dump($e->getRequest());
|
|
if ($e->hasResponse()) var_dump($e->getResponse());
|
|
} catch (\Exception $e) {
|
|
// $e->getHandlerContext()
|
|
// var_dump($e);
|
|
}
|
|
Log::warning("Target -> URL: {$url} METHOD: {$method}");
|
|
Log::warning("CURl -> RETRY: {$retry} ERROR: {$e->getMessage()} ERRNO: {$e->getCode()} STATUS: Waiting for recovery!");
|
|
sleep(15);
|
|
}
|
|
exit('网络异常,超出最大尝试次数,退出程序~');
|
|
}
|
|
|
|
/**
|
|
* @use 获取请求配置
|
|
* @param array $add_options
|
|
* @param array $headers
|
|
* @param float $timeout
|
|
* @return array
|
|
*/
|
|
private static function getClientOpt(array $add_options, array $headers = [], float $timeout = 30.0): array
|
|
{
|
|
self::$client = new \GuzzleHttp\Client();
|
|
$default_options = [
|
|
'headers' => $headers,
|
|
'timeout' => $timeout,
|
|
'http_errors' => false,
|
|
'verify' => getConf('verify_ssl', 'network.ssl'),
|
|
];
|
|
if (getConf('enable', 'network.proxy')) {
|
|
$default_options['proxy'] = getConf('proxy', 'network.proxy');
|
|
}
|
|
return array_merge($default_options, $add_options);
|
|
}
|
|
|
|
/**
|
|
* @use 获取Headers
|
|
* @param string $os
|
|
* @param array $headers
|
|
* @return array
|
|
*/
|
|
private static function getHeaders(string $os = 'app', array $headers = []): array
|
|
{
|
|
if (!self::$buvid) {
|
|
self::$buvid = Generator::buvid();
|
|
}
|
|
$app_headers = [
|
|
'env' => 'prod',
|
|
'APP-KEY' => 'android',
|
|
'Buvid' => self::$buvid,
|
|
'Accept' => '*/*',
|
|
'Accept-Encoding' => 'gzip',
|
|
'Accept-Language' => 'zh-cn',
|
|
'Connection' => 'keep-alive',
|
|
// 'Content-Type' => 'application/x-www-form-urlencoded',
|
|
// 'User-Agent' => 'Mozilla/5.0 BiliDroid/5.51.1 (bbcallen@gmail.com)',
|
|
'User-Agent' => 'Mozilla/5.0 BiliDroid/6.28.0 (bbcallen@gmail.com) os/android model/MuMu mobi_app/android build/6280300 channel/bili innerVer/6280300 osVer/6.0.1 network/2',
|
|
// 'Referer' => 'https://live.bilibili.com/',
|
|
];
|
|
$pc_headers = [
|
|
'Accept' => "application/json, text/plain, */*",
|
|
'Accept-Encoding' => 'gzip, deflate',
|
|
'Accept-Language' => "zh-CN,zh;q=0.9",
|
|
// 'Content-Type' => 'application/x-www-form-urlencoded',
|
|
'User-Agent' => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.30 Safari/537.36 Edg/90.0.818.8',
|
|
// 'Referer' => 'https://live.bilibili.com/',
|
|
];
|
|
$other_headers = [
|
|
'User-Agent' => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4450.0 Safari/537.36',
|
|
];
|
|
$default_headers = ${$os . "_headers"} ?? $other_headers;
|
|
if (in_array($os, ['app', 'pc']) && getCookie() != "") {
|
|
$default_headers['Cookie'] = getCookie();
|
|
}
|
|
// return self::formatHeaders(array_merge($default_headers, $headers));
|
|
return array_merge($default_headers, $headers);
|
|
}
|
|
|
|
/**
|
|
* @use 数组
|
|
* @return array
|
|
*/
|
|
private static function getResults(): array
|
|
{
|
|
$results = self::$results;
|
|
self::$results = [];
|
|
return $results;
|
|
}
|
|
|
|
/**
|
|
* @use 关联数组转字符串
|
|
* @param array $array
|
|
* @param string $separator
|
|
* @return string
|
|
*/
|
|
private static function arr2str(array $array, string $separator = "\r\n"): string
|
|
{
|
|
$tmp = '';
|
|
foreach ($array as $key => $value) {
|
|
$tmp .= "{$key}:{$value}{$separator}";
|
|
}
|
|
return $tmp;
|
|
}
|
|
|
|
/**
|
|
* @use GET请求
|
|
* @param $os
|
|
* @param $url
|
|
* @param array $params
|
|
* @param array $headers
|
|
* @param int $timeout
|
|
* @return mixed
|
|
*/
|
|
public static function headers($os, $url, $params = [], $headers = [], $timeout = 30)
|
|
{
|
|
Log::debug('HEADERS: ' . $url);
|
|
$headers = self::getHeaders($os, $headers);
|
|
$payload['query'] = count($params) ? $params : [];
|
|
$payload['allow_redirects'] = false;
|
|
$options = self::getClientOpt($payload, $headers);
|
|
$request = self::clientHandle($url, 'get', $options);
|
|
Log::debug("获取Headers");
|
|
return $request->getHeaders();
|
|
}
|
|
|
|
/**
|
|
* @use 格式化Headers
|
|
* @param array $headers
|
|
* @return array
|
|
*/
|
|
private static function formatHeaders(array $headers): array
|
|
{
|
|
return array_map(function ($k, $v) {
|
|
return $k . ': ' . $v;
|
|
}, array_keys($headers), $headers);
|
|
}
|
|
|
|
/**
|
|
* @use 字符串or其他
|
|
* @return array
|
|
*/
|
|
private static function getResult(): array
|
|
{
|
|
$result = self::$result;
|
|
self::$result = [];
|
|
return array_shift($result);
|
|
}
|
|
}
|