获取扩展:
composer require jmathai/php-multi-curl:dev-master -v
整体思路:
每个请求绑定一个callback回调,处理完每个请求后,将结果通过绑定的回调参数传回
开箱即用的类:
<?php
require_once './vendor/autoload.php';
class MultiLibrary
{
private $curls;
private $headers;
private $callbacks;
private $objs;
private $mc;
public function __construct()
{
$this->mc = JMathai\PhpMultiCurl\MultiCurl::getInstance();
}
public function Curl($url, $data = array(), $header = array(), $method = 'POST', $timeout = 5, $others = array(), $error = true)
{
$ch = $this->getCurlCh($url, $data, $header, $method, $timeout, $others);
$ret = curl_exec($ch);
$errno = curl_errno($ch);
$res = curl_getinfo($ch);
if (true === $error) {
curl_error_log($ch, $ret, $url);
}
curl_close($ch);
$result = array('ret' => $ret, 'errno' => $errno, 'res' => $res);
return $result;
}
private function getCurlCh(&$url, $data = array(), $header = array(), $method = 'POST', $timeout = 2, $others = array(), $ch = null)
{
if (empty($ch)) {
$ch = curl_init();
}
if (!preg_match('/\?/', $url)) {
$url = $url . '?';
}
if ('GET' === strtoupper($method) && is_array($data)) {
$url = $url . '&' . http_build_query($data);
}
$url = $url . '&log_id=' . LogId::getLogId();
//增加鉴权参数
$app_key = isset($data['app_key']) ? $data['app_key'] : 'uc';
$client_time = isset($data['client_time']) ? $data['client_time'] : intval(microtime(true) * 1000);
$url = $url . '&app_key=' . $app_key . '&client_time=' . $client_time;
$header['X-Access-Token'] = 'uc';
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_ENCODING, '');
/**
* 设置post信息
*/
if ('POST' === strtoupper($method)) {
if (is_array($data)) {
curl_setopt($ch, CURLOPT_POST, count($data));
} else {
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
curl_setopt($ch, CURLOPT_POST, 1);
}
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
}
if ('DELETE' === strtoupper($method)) {
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
}
/**
* 设置超时时间
*/
curl_setopt($ch, CURLOPT_TIMEOUT_MS, intval($timeout * 1000));
/**
* 设置header参数
*/
if (!empty($header) && is_array($header)) {
$tmp = array();
foreach ($header as $key => $value) {
$tmp[] = trim($key) . ': ' . trim($value);
}
curl_setopt($ch, CURLOPT_HTTPHEADER, $tmp);
}
/**
* 设置其它信息
*/
if (is_array($others) && !empty($others)) {
foreach ($others as $key => $value) {
curl_setopt($ch, $key, $value);
}
}
return $ch;
}
public function addRequest($cfg, $headers, $obj, $cb)
{
$this->curls[] = $this->getCfg($cfg);
$this->headers[] = $this->getHeaders($headers);
$this->objs[] = $obj;
$this->callbacks[] = $cb;
}
private function getCfg($cfg)
{
$requestConfig = [];
$url = $cfg['protocol'] . '://' . $cfg['ip'] . ':' . $cfg['port'] . $cfg['url'];
$querystr = http_build_query($cfg['data']);
$requestConfig['url'] = $url.'?'.$querystr;
$requestConfig['timeout'] = $cfg['timeout'];
$requestConfig['connect_timeout'] = $cfg['connect_timeout'];
$url = $requestConfig['url'];
$connect_timeout = $requestConfig['connect_timeout'];
$timeout = $requestConfig['timeout'];
$requestConfig['log_id'] = $cfg['log_id'];
$requestConfig['ch'] = curl_init($url);
$requestConfig['url'] = $url;
curl_setopt($requestConfig['ch'], CURLOPT_RETURNTRANSFER, 1);
curl_setopt($requestConfig['ch'], CURLOPT_NOSIGNAL, 1);
if ($connect_timeout > 0) {
curl_setopt($requestConfig['ch'], CURLOPT_CONNECTTIMEOUT_MS, $connect_timeout);
}
if ($timeout > 0) {
curl_setopt($requestConfig['ch'], CURLOPT_TIMEOUT_MS, $timeout);
}
return $requestConfig;
}
private function getHeaders($headers)
{
$default = [
'Content-Type' => 'application/json',
];
$headers = array_merge($default, $headers);
if (!empty($headers) && is_array($headers)) {
foreach ($headers as $key => $value) {
$headers[] = trim($key) . ': ' . trim($value);
}
}
return $headers;
}
public function execute()
{
$result = array();
//将每个请求添加到curls中
foreach ($this->curls as $i => $request) {
$handle = $request['ch'];
curl_setopt($handle, CURLOPT_HTTPHEADER, $this->headers[$i]);
$result[] = $this->mc->addCurl($handle);
}
foreach ($this->curls as $i => $request) {
//判断请求成功与否,返回code
if ($result[$i]->code == 200) {
$code = 0;
} else {
if ($result[$i]->code != 0) {
$code = $result[$i]->code;
} else {
if ($result[$i]->curl_code == 0) {
$code = 9999; //curl_code和http_code不符合常理
} else {
$code = $result[$i]->curl_code;
}
}
}
$url = $result[$i]->url;
$logFilename = $this->getUrlFilename($url);
if (!empty($logFilename)) {
$data = array();
if (!empty($url)) {
$data['url'] = $url;
}
$data['log_id'] = $this->curls[$i]['log_id'];
$data['connect_time'] = $result[$i]->connect_time;
$data['time'] = $result[$i]->time;
$data['http_code'] = $result[$i]->code;
$data['curl_code'] = $result[$i]->curl_code;
$data['resp_data'] = $result[$i]->response;
$file = '/tmp/curl-' . date('Y-m-d', time()) . '.log';
file_put_contents($file, json_encode($data) . "\n", FILE_APPEND );
}
call_user_func_array(
array($this->objs[$i], $this->callbacks[$i]),
array($code, '', $result[$i]->response)
);
}
}
private function getUrlFilename($url)
{
if (empty($url)) {
return '';
}
$arr = parse_url($url);
if (empty($arr['path'])) {
return '';
}
$pathArr = explode('/', $arr['path']);
if (empty($pathArr)) {
return '';
}
$pathUnderline = implode('_', $pathArr);
if (empty($pathUnderline)) {
return '';
}
$logPath = "s";
$d = date("Y-m-d");
$logFilename = $logPath . $pathUnderline . '-' . $d . ".log";
return $logFilename;
}
}
class LogId
{
static private $logId = '';
public static function getLogId($reset = false)
{
if (!empty(self::$logId) && false === $reset) {
return self::$logId;
}
if (!empty($_SERVER['HTTP_X_YMT_LOGID']) && intval(trim($_SERVER['HTTP_X_YMT_LOGID'])) !== 0) {
$logid = trim($_SERVER['HTTP_X_YMT_LOGID']);
} elseif (isset($_REQUEST['logid']) && intval($_REQUEST['logid']) !== 0) {
$logid = trim($_REQUEST['logid']);
} else {
$ip = intval(self::getHostIp());
$timestamp = explode(' ', microtime());
$pack_0 = sprintf('%04d', $timestamp[1] % 3600);
$pack_1 = sprintf('%04d', intval(($timestamp[0] * 1000000) % 1000));
$pack_2 = sprintf('%04d', mt_rand(0, 987654321) % 1000);
$pack_3 = sprintf('%04d', crc32($ip * (mt_rand(0, 987654321) % 1000)) % 10000);
$logid = ($pack_0 . $pack_1 . $pack_2 . $pack_3 . $pack_1 . $pack_3);
//$logid = ((($arr['sec'] * 100000 + $arr['usec'] % 1000) & 0x7FFFFFFF) | 0x80000000);
}
return $logid;
}
protected static function getHostIp()
{
return isset($_SERVER['SERVER_ADDR']) ? $_SERVER['SERVER_ADDR'] : '';
}
}
Controller:
<?php
require_once 'MultiLibrary.php';
class Index
{
private $multiLibrary;
private $requestId;
private $result = [];
public function __construct()
{
$this->multiLibrary = new MultiLibrary();
$this->requestId = LogId::getLogId();
}
public function index()
{
//TODO 3 times multi curl test
for( $i = 1; $i <= 3; $i++){
$this->getIndex([]);
}
$this->multiLibrary->execute();
print_r($this->result[0]);
}
public function indexCallback($errno, $errmsg, $data)
{
if ($errno == 0){
$this->result[] = $data;
}
}
private function getIndex($data)
{
$requestConfig = [
'data' => $data,
'protocol' => 'http',
'ip' => 'www.baidu.com',
'port' => '80',
'url' => '',
'timeout' => 300,
'connect_timeout' => 300,
'log_id' => $this->requestId,
];
$headers = [];
$this->addRequest($requestConfig, $headers, 'indexCallback');
}
protected function addRequest(array $params, array $headers, $callback)
{
$this->multiLibrary->addRequest($params, $headers, $this, $callback);
}
}
$obj = new Index();
$obj->index();