Phalcon搭建多模組框架二十八:封裝curl工具類並模擬api介面請求與響應
阿新 • • 發佈:2019-01-30
在開發中經常會需要寫介面給別人呼叫或者呼叫別人的介面,通常使用http post傳送json格式資料,響應也往往是json格式資料。除了必要的簽名,有時候為了資料安全還需要對資料進行對稱或非對稱加密。這裡我們只是模擬post請求傳送和響應json格式資料。
1、在library/tools下建立HttpCurl.php工具類
<?php
/**
* @desc curl工具類
* @author zhaoyang
* @date 2018年6月13日 下午8:08:28
*/
namespace Library\Tools;
class HttpCurl {
// 控制代碼
private $_ch;
// 請求地址
private $_url;
// 是否做https驗證
private $_httpsVerify;
// 連線過期時間
private $_timeout;
// 啟用時是否將標頭檔案的資訊作為資料流輸出
private $_header;
private $_option = [ ];
private $_error = null;
/**
* @desc 初始化
* @param string $url 地址
* @param bool $httpsVerify 是否進行https驗證
* @param int $timeout 超時時間
* @param bool $header 啟用時是否將標頭檔案的資訊作為資料流輸出
* @author zhaoyang
* @date 2018年6月13日 下午9:00:53
*/
public function __construct(string $url, bool $httpsVerify = false, int $timeout = 30, bool $header = false) {
$this ->_url = $url;
$this->_httpsVerify = $httpsVerify;
$this->_timeout = $timeout;
$this->_header = $header;
$this->init();
}
/**
* @desc 基本初始化
* @author zhaoyang
* @date 2018年6月13日 下午8:13:37
*/
private function init() {
$this->_ch = curl_init();
// 讓 cURL 自己判斷使用哪個版本
$this->_option[CURLOPT_HTTP_VERSION] = CURL_HTTP_VERSION_NONE;
// 在HTTP請求中包含一個"User-Agent: "頭的字串
$this->_option[CURLOPT_USERAGENT] = $_SERVER['HTTP_USER_AGENT'];
// 需要獲取的 URL 地址
$this->_option[CURLOPT_URL] = $this->_url;
// 在嘗試連線時等待的秒數。設定為0,則無限等待
$this->_option[CURLOPT_CONNECTTIMEOUT] = $this->_timeout;
// 允許 cURL 函式執行的最長秒數
$this->_option[CURLOPT_TIMEOUT] = $this->_timeout;
// 只獲取頁面內容,但不輸出
$this->_option[CURLOPT_RETURNTRANSFER] = true;
// 傳送所支援的編碼型別,false傳送所有
$this->_option[CURLOPT_ENCODING] = false;
// 啟用時會將標頭檔案的資訊作為資料流輸出
$this->_option[CURLOPT_HEADER] = $this->_header;
// cURL 驗證對等證書
$this->_option[CURLOPT_SSL_VERIFYPEER] = $this->_httpsVerify;
// 伺服器認證ssl
$this->_option[CURLOPT_SSL_VERIFYHOST] = $this->_httpsVerify;
// 追蹤控制代碼的請求字串
$this->_option[CURLINFO_HEADER_OUT] = true;
}
/**
* @desc 設定option
* @param string|array $option 選項
* @param mixed $value 值
* @return object $this
* @author zhaoyang
* @date 2018年6月13日 下午8:17:37
*/
public function setOption($option, $value = null) {
if (is_array($option)) {
$this->_option = $option + $this->_option;
} else {
$this->_option[$option] = $value;
}
return $this;
}
/**
* @desc 傳送get請求
* @author zhaoyang
* @date 2018年6月13日 下午9:15:16
*/
public function execGet() {
curl_setopt_array($this->_ch, $this->_option);
$result = curl_exec($this->_ch);
$this->_error = [
'errno' => curl_errno($this->_ch),
'error' => curl_error($this->_ch)
];
curl_close($this->_ch);
return $result;
}
/**
* @desc 傳送post請求
* @param array|string $requestData 要傳送的資料
* @param bool $json 是否傳送json格式資料
* @param int $jsonOptions json配置
* @author zhaoyang
* @date 2018年6月13日 下午9:15:33
*/
public function execPost($requestData = null, bool $json = true, int $jsonOptions = null) {
if ($requestData !== null) {
if ($json) {
$isString = is_string($requestData);
(!$isString || ($isString && is_null(json_decode($requestData)))) && $requestData = json_encode($requestData, $jsonOptions);
if (!isset($this->_option[CURLOPT_HTTPHEADER])) {
$this->_option[CURLOPT_HTTPHEADER] = [
'Content-Type: application/json; charset=utf-8',
'Accept: application/json',
'Content-Length:' . strlen($requestData)
];
}
}
$this->_option[CURLOPT_POSTFIELDS] = $requestData;
}
$this->_option[CURLOPT_POST] = true;
curl_setopt_array($this->_ch, $this->_option);
$result = curl_exec($this->_ch);
$this->_error = [
'errno' => curl_errno($this->_ch),
'error' => curl_error($this->_ch)
];
curl_close($this->_ch);
return $result;
}
/**
* @desc 獲取錯誤
* @return array
* @author zhaoyang
* @date 2018年6月13日 下午9:30:23
*/
public function getError() {
return $this->_error;
}
}
2、開啟common/BaseController.php,新增
final protected function sendJson($responseData, int $status = 10000, int $jsonOptions = null, int $depth = 512){
$responseData = [
'status' => $status,
'message' => $status == 10000 ? 'ok' : $responseData,
'data' => $status == 10000 ? $responseData : ''
];
return $this->response->setJsonContent($responseData, $jsonOptions, $depth)->send();
}
完成的common/BaseController.php
<?php
/**
* @desc 控制器基類
* @author zhaoyang
* @date 2018年5月8日 下午10:37:37
*/
namespace Common;
use Phalcon\Mvc\Controller;
use Phalcon\Exception;
class BaseController extends Controller {
/**
* @desc 獲取get引數
* @param string $name 引數名
* @param string|array $filter 過濾型別,支援string、trim、absint、int、email、float、int!、float!、alphanum、striptags、lower、upper、url、special_chars
* 當為false時,不使用預設過濾,當為字串例如'string,trim'時採用引數過濾 ,當為陣列例如['string','trim']時採用引數+預設過濾,當為null等其他值時時採用預設過濾
* @param mixed $defaultValue 預設值
* @param bool $noRecursive 不遞迴過濾
* @return mixed
* @author zhaoyang
* @date 2018年5月8日 下午10:38:50
*/
final protected function get(string $name = null, $filter = null, $defaultValue = null, bool $noRecursive = false) {
$data = array_merge($this->request->getQuery(), $this->dispatcher->getParams());
unset($data['_url']);
return $this->sanitize($data, $name, $filter, $defaultValue, $noRecursive);
}
/**
* @desc 獲取post引數
* @param string $name 引數名
* @param string|array $filter 過濾型別,支援string、trim、absint、int、email、float、int!、float!、alphanum、striptags、lower、upper、url、special_chars
* 當為false時,不使用預設過濾,當為字串'string,trim'時採用引數過濾 ,當為陣列['string','trim']時採用引數+預設過濾,當為null等其他值時時採用預設過濾
* @param mixed $defaultValue 預設值
* @param bool $noRecursive 不遞迴過濾
* @param bool $notAllowEmpty 不允許為空
* @return mixed
* @author zhaoyang
* @date 2018年5月9日 下午8:40:27
*/
final protected function post(string $name = null, $filter = null, $defaultValue = null, bool $noRecursive = false, bool $notAllowEmpty = false) {
$data = $this->request->getPost();
return $this->sanitize($data, $name, $filter, $defaultValue, $noRecursive);
}
/**
* @desc 獲取post或者get引數
* @param string $name 引數名
* @param string|array $filter 過濾型別,支援string、trim、absint、int、email、float、int!、float!、alphanum、striptags、lower、upper、url、special_chars
* 當為false時,不使用預設過濾,當為字串例如'string,trim'時採用引數過濾 ,當為陣列例如['string','trim']時採用引數+預設過濾,當為null等其他值時時採用預設過濾
* @param mixed $defaultValue 預設值
* @param bool $noRecursive 不遞迴過濾
* @return mixed
* @author zhaoyang
* @date 2018年5月9日 下午9:41:49
*/
final protected function request(string $name = null, $filter = null, $defaultValue = null, bool $noRecursive = false){
if (isset($name) && $name !== '') {
return $this->post($name, $filter, $defaultValue, $noRecursive) ?? $this->get($name, $filter, $defaultValue, $noRecursive);
}
return array_merge($this->post(null, $filter, $defaultValue, $noRecursive), $this->get(null, $filter, $defaultValue, $noRecursive));
}
/**
* @param string $name 引數名
* @param string|array $filter 過濾型別,支援string、trim、absint、int、email、float、int!、float!、alphanum、striptags、lower、upper、url、special_chars
* 當為false時,不使用預設過濾,當為字串例如'string,trim'時採用引數過濾 ,當為陣列例如['string','trim']時採用引數+預設過濾,當為null等其他值時時採用預設過濾
* @param mixed $defaultValue 預設值
* @param bool $noRecursive 不遞迴過濾
* @return mixed
* @author zhaoyang
* @date 2018年5月9日 下午10:43:11
*/
final protected function json(string $name = null, $filter = null, $defaultValue = null, bool $noRecursive = false){
$data = $this->request->getJsonRawBody(true);
if (!is_array($data)) {
return [ ];
}
return $this->sanitize($data, $name, $filter, $defaultValue, $noRecursive);
}
/**
* @param array $data 資料來源
* @param string $name 引數名
* @param string|array $filter 過濾型別,支援string、trim、absint、int、email、float、int!、float!、alphanum、striptags、lower、upper、url、special_chars
* 當為false時,不使用預設過濾,當為字串例如'string,trim'時採用引數過濾 ,當為陣列例如['string','trim']時採用引數+預設過濾,當為null等其他值時時採用預設過濾
* @param mixed $defaultValue 預設值
* @param bool $noRecursive 不遞迴過濾
* @return mixed
* @author zhaoyang
* @date 2018年5月9日 下午8:20:15
*/
final protected function sanitize(array $data, string $name = null, $filter = null, $defaultValue = null, bool $noRecursive = false){
$nowFilter = null;
if (is_string($filter) && !empty($filter)) {
$nowFilter = explode(',', $filter);
} else if ($filter !== false) {
$defaultFilter = $this->config->services->filter->default_filter;
$defaultFilter = isset($defaultFilter) ? explode(',', $defaultFilter) : [ ];
if (is_array($filter)) {
$defaultFilter = array_unique(array_merge($filter, $defaultFilter));
}
if (!empty($defaultFilter)) {
$nowFilter = $defaultFilter;
}
}
if (isset($name) && $name !== '') {
if (isset($data[$name]) && $data[$name] !== '') {
$data = $data[$name];
} else {
$data = $defaultValue;
}
}
if (isset($nowFilter)) {
$data = $this->filter->sanitize($data, $nowFilter, $noRecursive);
}
return $data;
}
/**
* @desc 轉發到其他動作
* @param array|string $url 'App\Home\Controllers\forward/index/a/aaa?b=bbb' or 'forward/index/a/aaa?b=bbb' or 'index?b=bbb'
* @param array|string $vars 引數 ['a'=>'aaa','b'=>'bbb'] or 'a=aaa&b=bbb'
* @param sring $namespace 名稱空間
* @return void
* @author zhaoyang
* @date 2018年5月24日 下午5:11:26
*/
final protected function forward($url, $vars = null, $namespace = null) {
if (is_array($url)) {
$forward = $url;
} else if (is_string($url)) {
$forward = [ ];
$lastbBackslash = strrpos($url, '\\');
if ($lastbBackslash) {
$namespace = substr($url, 0, $lastbBackslash);
}
if (!empty($namespace)) {
$forward['namespace'] = $namespace;
}
$start = $lastbBackslash === false ? 0 : $lastbBackslash + 1;
$rest = substr($url, $start);
$restStrposRes = strpos($rest, '?');
if($rest == '' || $restStrposRes === 0){
throw new Exception('方法不能為空');
}
if($restStrposRes === false){
$capname = $rest;
$paramsString = null;
}else {
list ($capname, $paramsString) = explode('?', $rest, 2);
$capname = trim($capname, '/');
if (empty($capname)) {
throw new Exception('控制器或方法不能為空');
}
}
$capnameArr = explode('/', $capname);
$capnameArrCount = count($capnameArr);
if ($capnameArrCount == 1) {
$forward['action'] = $capnameArr[0];
} else {
$forward['controller'] = $capnameArr[0];
$forward['action'] = $capnameArr[1];
for ($i = 2; $i < $capnameArrCount; $i += 2) {
$forward['params'][$capnameArr[$i]] = $capnameArr[$i + 1] ?? null;
}
}
if ($paramsString !== null) {
parse_str($paramsString, $paramsArr);
$forward['params'] = array_merge($forward['params'] ?? [ ], $paramsArr);
}
} else {
throw new Exception('url只能為字串或者陣列');
}
if (is_string($vars)) {
$vars = trim($vars, '?');
parse_str($vars, $vars);
}
if (is_array($vars)) {
$forward['params'] = array_merge($forward['params'] ?? [ ], $vars);
}
$this->dispatcher->forward($forward);
}
/**
* @desc 成功跳轉
* @param string $message 提示資訊
* @param string $jumpUrl 跳轉地址
* @param bool $redirect 是否使用response->redirect
* @param bool $externalRedirect 是否跳轉到外部地址
* @author zhaoyang
* @date 2018年6月9日 下午11:10:10
*/
final protected function success(string $message, string $jumpUrl = null, bool $redirect = false, bool $externalRedirect = false) {
if (is_null($jumpUrl)) {
$this->flashSession->success($message);
echo '<script>history.go(-1);</script>';
return false;
} else if ($redirect || strpos($jumpUrl, '://') !== false) {
$this->flashSession->success($message);
return $this->response->redirect($jumpUrl, $externalRedirect);
} else {
$this->flash->success($message);
return $this->forward($jumpUrl);
}
}
/**
* @desc 失敗跳轉
* @param string $message 提示資訊
* @param string $jumpUrl 跳轉地址
* @param bool $redirect 是否使用response->redirect
* @param bool $externalRedirect 是否跳轉到外部地址
* @author zhaoyang
* @date 2018年6月10日 上午12:10:16
*/
final protected function error(string $message, string $jumpUrl = null, bool $redirect = false, bool $externalRedirect = false) {
if (is_null($jumpUrl)) {
$this->flashSession->error($message);
echo '<script>history.go(-1);</script>';
return false;
} else if ($redirect || strpos($jumpUrl, '://') !== false) {
$this->flashSession->error($message);
return $this->response->redirect($jumpUrl, $externalRedirect);
} else {
$this->flash->error($message);
return $this->forward($jumpUrl);
}
}
/**
* @desc 響應json資料
* @param string|array $responseData 資料或錯誤提示
* @param int $status 返回狀態
* @param int $jsonOptions json選項
* @param int $depth 處理陣列深度
* @return object
* @author zhaoyang
* @date 2018年6月13日 下午10:05:54
*/
final protected function sendJson($responseData, int $status = 10000, int $jsonOptions = null, int $depth = 512){
$responseData = [
'status' => $status,
'message' => $status == 10000 ? 'ok' : $responseData,
'data' => $status == 10000 ? $responseData : ''
];
return $this->response->setJsonContent($responseData, $jsonOptions, $depth)->send();
}
}
3、在home模組下建立HttpCurlController.php
<?php
namespace App\Home\Controllers;
use Common\BaseController;
use Library\Tools\HttpCurl;
class HttpCurlController extends BaseController {
public function indexAction() {
// 簡化請求流程,不做簽名和加密
/* $requestData = [
'a' => '10a',
'b' => 15
]; */
$requestData = [
'a' => 10,
'b' => 15
];
$url = 'http://phalcon.com/admin/index/test';
$httpCurl = new HttpCurl($url);
$res = $httpCurl->execPost($requestData);
if($res === false){
var_dump($httpCurl->getError());
exit;
}
var_dump($res, json_decode($res, true));
exit;
}
}
4、開啟admin模組下IndexController.php控制器
<?php
namespace App\Admin\Controllers;
use Common\BaseController;
use Common\Validate;
class IndexController extends BaseController {
public function indexAction() {
echo __METHOD__, '<br>';
var_dump($this->get());
exit();
}
public function testAction() {
// 忽略校驗簽名等
$rules = [
['a', 'regex', 'a只能為小於9999的正整數', '/^[1-9]\d{0,3}$/', 1],
['b', 'regex', 'b只能為小於9999的正整數', '/^[1-9]\d{0,3}$/', 1]
];
$data = $this->json();
$validate = new Validate();
$messages = $validate->addRules($rules)->validate($data);
if($messages->count()){
return $this->sendJson($messages->current()->getMessage(), 10001);
}
$sum = $data['a'] + $data['b'];
$responseData = [
'sum' => $sum
];
return $this->sendJson($responseData);
}
}
5、訪問/httpcurl/index
這裡只是簡單的模擬怎麼使用,實際開發中還需要做簽名甚至資料加密,由於非對稱加密耗效能,一般接第三方介面使用的比較多,公司多端通訊可以使用對稱加密。