部署钉钉验证库
parent
fc5300cc64
commit
1c3e19f1af
@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* 会话管理接口
|
||||
*/
|
||||
class Chat
|
||||
{
|
||||
public static function createChat($accessToken, $chatOpt)
|
||||
{
|
||||
$response = Http::post("/chat/create",
|
||||
array("access_token" => $accessToken),
|
||||
json_encode($chatOpt));
|
||||
return $response;
|
||||
}
|
||||
|
||||
public static function bindChat($accessToken, $chatid,$agentid)
|
||||
{
|
||||
$response = Http::get("/chat/bind",
|
||||
array("access_token" => $accessToken,"chatid"=>$chatid,"agentid"=>$agentid));
|
||||
return $response;
|
||||
}
|
||||
|
||||
public static function sendmsg($accessToken, $opt)
|
||||
{
|
||||
$response = Http::post("/chat/send",
|
||||
array("access_token" => $accessToken),
|
||||
json_encode($opt));
|
||||
return $response;
|
||||
}
|
||||
|
||||
public static function callback($accessToken, $opt)
|
||||
{
|
||||
$response = Http::post("/call_back/register_call_back",
|
||||
array("access_token" => $accessToken),
|
||||
json_encode($opt));
|
||||
return $response;
|
||||
}
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
class Department
|
||||
{
|
||||
public static function createDept($accessToken, $dept)
|
||||
{
|
||||
$response = Http::post("/department/create",
|
||||
array("access_token" => $accessToken),
|
||||
json_encode($dept));
|
||||
return $response->id;
|
||||
}
|
||||
|
||||
|
||||
public static function listDept($accessToken)
|
||||
{
|
||||
$response = Http::get("/department/list",
|
||||
array("access_token" => $accessToken));
|
||||
return $response->department;
|
||||
}
|
||||
|
||||
|
||||
public static function deleteDept($accessToken, $id)
|
||||
{
|
||||
$response = Http::get("/department/delete",
|
||||
array("access_token" => $accessToken, "id" => $id));
|
||||
return $response->errcode == 0;
|
||||
}
|
||||
}
|
@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
require_once(__DIR__ . "/../config.php");
|
||||
require_once(__DIR__ . "/../util/Http.php");
|
||||
require_once(__DIR__ . "/../util/Cache.php");
|
||||
require_once(__DIR__ . "/ISVService.php");
|
||||
|
||||
class ISVClass{
|
||||
|
||||
public static function getSuiteAccessToken(){
|
||||
$suiteTicket = Cache::getSuiteTicket();
|
||||
if(!$suiteTicket){
|
||||
Log::e("ERROR: suiteTicket not cached,please check the callback url");
|
||||
return false;
|
||||
}
|
||||
$suiteAccessToken = ISVService::getSuiteAccessToken($suiteTicket);
|
||||
return $suiteAccessToken;
|
||||
}
|
||||
|
||||
public static function getIsvCorpAccessToken($suiteAccessToken, $corpId, $permanetCode){
|
||||
$key = "dingdingActive_".$corpId;
|
||||
$corpAccessToken = ISVService::getIsvCorpAccessToken($suiteAccessToken, $corpId, $permanetCode);
|
||||
$status = Cache::getActiveStatus($key);
|
||||
if($status<=0&&$corpAccessToken!=""){
|
||||
ISVService::activeSuite($suiteAccessToken, $corpId, $permanetCode);
|
||||
}
|
||||
|
||||
ISVService::getAuthInfo($suiteAccessToken, $corpId, $permanetCode);
|
||||
return $corpAccessToken;
|
||||
}
|
||||
|
||||
public static function getCorpInfo($corpId){
|
||||
$suiteAccessToken = ISVClass::getSuiteAccessToken();
|
||||
$corpInfo = ISVService::getCorpInfoByCorId($corpId);
|
||||
$corpAccessToken = ISVClass::getIsvCorpAccessToken($suiteAccessToken,$corpInfo['corp_id'],$corpInfo['permanent_code']);
|
||||
$corpInfo['corpAccessToken'] = $corpAccessToken;
|
||||
|
||||
return $corpInfo;
|
||||
}
|
||||
}
|
||||
|
||||
function check($res)
|
||||
{
|
||||
if ($res->errcode != 0)
|
||||
{
|
||||
exit("Failed: " . json_encode($res));
|
||||
}
|
||||
}
|
@ -0,0 +1,216 @@
|
||||
<?php
|
||||
|
||||
require_once(__DIR__ . "/../util/Log.php");
|
||||
require_once(__DIR__ . "/../util/Http.php");
|
||||
require_once(__DIR__ . "/../util/Cache.php");
|
||||
|
||||
/**
|
||||
* ISV授权方法类
|
||||
*/
|
||||
class ISVService
|
||||
{
|
||||
public static function getSuiteAccessToken($suiteTicket)
|
||||
{
|
||||
$suiteAccessToken = Cache::getSuiteAccessToken();
|
||||
if (!$suiteAccessToken)
|
||||
{
|
||||
$response = Http::post("/service/get_suite_token",
|
||||
null,
|
||||
json_encode(array(
|
||||
"suite_key" => SUITE_KEY,
|
||||
"suite_secret" => SUITE_SECRET,
|
||||
"suite_ticket" => $suiteTicket
|
||||
)));
|
||||
self::check($response);
|
||||
$suiteAccessToken = $response->suite_access_token;
|
||||
Cache::setSuiteAccessToken($suiteAccessToken);
|
||||
}
|
||||
return $suiteAccessToken;
|
||||
}
|
||||
|
||||
public static function getCorpInfoByCorId($corpId){
|
||||
$corpList = json_decode(Cache::getCorpInfo(),true);
|
||||
if(!is_array($corpList)){
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach($corpList as $corp){
|
||||
if($corp['corp_id']==$corpId){
|
||||
return $corp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static function getCorpInfoByTmpCode($code){
|
||||
$corpList = json_decode(Cache::getCorpInfo(),true);
|
||||
if(!is_array($corpList)){
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach($corpList as $corp){
|
||||
if($corp['tmp_auth_code']==$code){
|
||||
return $corp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static function getPermanentCodeInfo($suiteAccessToken,$tmpAuthCode)
|
||||
{
|
||||
$permanentCodeInfo = json_decode(ISVService::getCorpInfoByTmpCode($tmpAuthCode));
|
||||
|
||||
if (!$permanentCodeInfo)
|
||||
{
|
||||
$permanentCodeResult = Http::post("/service/get_permanent_code",
|
||||
array(
|
||||
"suite_access_token" => $suiteAccessToken
|
||||
),
|
||||
json_encode(array(
|
||||
"tmp_auth_code" => $tmpAuthCode
|
||||
)));
|
||||
self::check($permanentCodeResult);
|
||||
$permanentCodeInfo = self::savePermanentCodeInfo($permanentCodeResult,$tmpAuthCode);
|
||||
}
|
||||
return $permanentCodeInfo;
|
||||
}
|
||||
|
||||
public static function savePermanentCodeInfo($permanentCodeInfo,$tmpAuthCode){
|
||||
$arr = array();
|
||||
$arr['corp_name'] = $permanentCodeInfo->auth_corp_info->corp_name;
|
||||
$arr['corp_id'] = $permanentCodeInfo->auth_corp_info->corpid;
|
||||
$arr['permanent_code'] = $permanentCodeInfo->permanent_code;
|
||||
$arr['tmp_auth_code'] = $tmpAuthCode;
|
||||
$corpInfo = json_decode(Cache::getCorpInfo());
|
||||
if(!$corpInfo){
|
||||
$corpInfo = array();
|
||||
}
|
||||
|
||||
$corpExist = false;
|
||||
foreach($corpInfo as $cp){
|
||||
if($cp->corp_id == $arr['corp_id']){
|
||||
$corpExist = true;
|
||||
}
|
||||
}
|
||||
|
||||
if(!$corpExist){
|
||||
$corpInfo[] = $arr;
|
||||
Cache::setCorpInfo(json_encode($corpInfo));
|
||||
}
|
||||
return $arr;
|
||||
}
|
||||
|
||||
public static function getCurAgentId($corpId,$appId){
|
||||
$authInfo = json_decode(Cache::getAuthInfo("corpAuthInfo_".$corpId));
|
||||
$agents = $authInfo->agent;
|
||||
$agentId = 0;
|
||||
foreach($agents as $agent){
|
||||
if($agent->appid==$appId){
|
||||
$agentId = $agent->agentid;
|
||||
break;
|
||||
}
|
||||
}
|
||||
Log::i("[AGENTID]".$corpId."-----".$appId."-----".$agentId);
|
||||
return $agentId;
|
||||
}
|
||||
|
||||
public static function getIsvCorpAccessToken($suiteAccessToken, $authCorpId, $permanentCode)
|
||||
{
|
||||
$key = "IsvCorpAccessToken_".$authCorpId;
|
||||
$corpAccessToken = Cache::getIsvCorpAccessToken($key);
|
||||
if (!$corpAccessToken)
|
||||
{
|
||||
$response = Http::post("/service/get_corp_token",
|
||||
array(
|
||||
"suite_access_token" => $suiteAccessToken
|
||||
),
|
||||
json_encode(array(
|
||||
"auth_corpid" => $authCorpId,
|
||||
"permanent_code" => $permanentCode
|
||||
)));
|
||||
self::check($response);
|
||||
$corpAccessToken = $response->access_token;
|
||||
Cache::setIsvCorpAccessToken($key,$corpAccessToken);
|
||||
}
|
||||
return $corpAccessToken;
|
||||
}
|
||||
|
||||
public static function getAuthInfo($suiteAccessToken, $authCorpId, $permanentCode)
|
||||
{
|
||||
$authInfo = json_decode(Cache::getAuthInfo("corpAuthInfo_".$authCorpId));
|
||||
if (!$authInfo)
|
||||
{
|
||||
$authInfo = Http::post("/service/get_auth_info",
|
||||
array(
|
||||
"suite_access_token" => $suiteAccessToken
|
||||
),
|
||||
json_encode(array(
|
||||
"suite_key" => SUITE_KEY,
|
||||
"auth_corpid" => $authCorpId,
|
||||
"permanent_code" => $permanentCode
|
||||
)));
|
||||
self::check($authInfo);
|
||||
Cache::setAuthInfo("corpAuthInfo_".$authCorpId,json_encode($authInfo->auth_info));
|
||||
}
|
||||
|
||||
return $authInfo;
|
||||
}
|
||||
|
||||
|
||||
public static function getAgent($suiteAccessToken, $authCorpId, $permanentCode, $agentId)
|
||||
{
|
||||
$response = Http::post("/service/get_agent",
|
||||
array(
|
||||
"suite_access_token" => $suiteAccessToken
|
||||
),
|
||||
json_encode(array(
|
||||
"suite_key" => SUITE_KEY,
|
||||
"auth_corpid" => $authCorpId,
|
||||
"permanent_code" => $permanentCode,
|
||||
"agentid" => $agentId
|
||||
)));
|
||||
self::check($response);
|
||||
return $response;
|
||||
}
|
||||
|
||||
|
||||
public static function activeSuite($suiteAccessToken, $authCorpId, $permanentCode)
|
||||
{
|
||||
$key = "dingdingActive_".$authCorpId;
|
||||
$response = Http::post("/service/activate_suite",
|
||||
array(
|
||||
"suite_access_token" => $suiteAccessToken
|
||||
),
|
||||
json_encode(array(
|
||||
"suite_key" => SUITE_KEY,
|
||||
"auth_corpid" => $authCorpId,
|
||||
"permanent_code" => $permanentCode
|
||||
)));
|
||||
|
||||
if($response->errcode==0){
|
||||
Cache::setActiveStatus($key);
|
||||
}
|
||||
self::check($response);
|
||||
return $response;
|
||||
}
|
||||
|
||||
public static function removeCorpInfo($authCorpId){
|
||||
$arr = array();
|
||||
$key1 = "dingdingActive_".$authCorpId;
|
||||
$key2 = "corpAuthInfo_".$authCorpId;
|
||||
$key3 = "IsvCorpAccessToken_".$authCorpId;
|
||||
$key4 = "js_ticket_".$authCorpId;
|
||||
$arr[] = $key1;
|
||||
$arr[] = $key2;
|
||||
$arr[] = $key3;
|
||||
$arr[] = $key4;
|
||||
Cache::removeByKeyArr($arr);
|
||||
}
|
||||
|
||||
static function check($res)
|
||||
{
|
||||
if ($res->errcode != 0)
|
||||
{
|
||||
Log::e("[FAIL]: " . json_encode($res));
|
||||
exit("Failed: " . json_encode($res));
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
<?php
|
||||
class Message
|
||||
{
|
||||
public static function sendToConversation($accessToken, $opt)
|
||||
{
|
||||
$response = Http::post("/message/send_to_conversation",
|
||||
array("access_token" => $accessToken),
|
||||
json_encode($opt));
|
||||
return $response;
|
||||
}
|
||||
|
||||
public static function send($accessToken, $opt)
|
||||
{
|
||||
$response = Http::post("/message/send",
|
||||
array("access_token" => $accessToken),json_encode($opt));
|
||||
return $response;
|
||||
}
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
<?php
|
||||
class User
|
||||
{
|
||||
public static function getUserInfo($accessToken, $code)
|
||||
{
|
||||
$response = Http::get("/user/getuserinfo",
|
||||
array("access_token" => $accessToken, "code" => $code));
|
||||
return json_encode($response);
|
||||
}
|
||||
|
||||
|
||||
public static function simplelist($accessToken,$deptId){
|
||||
$response = Http::get("/user/simplelist",
|
||||
array("access_token" => $accessToken,"department_id"=>$deptId));
|
||||
return $response->userlist;
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
{
|
||||
"require": {
|
||||
"nategood/httpful": "*"
|
||||
}
|
||||
}
|
@ -0,0 +1,68 @@
|
||||
{
|
||||
"_readme": [
|
||||
"This file locks the dependencies of your project to a known state",
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"hash": "e23d30d76ba6b3d3bcdab145c9f7f9f3",
|
||||
"packages": [
|
||||
{
|
||||
"name": "nategood/httpful",
|
||||
"version": "0.2.19",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/nategood/httpful.git",
|
||||
"reference": "bd73f89d34d8f879c54ac46eb94b0f7be1d00820"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/nategood/httpful/zipball/bd73f89d34d8f879c54ac46eb94b0f7be1d00820",
|
||||
"reference": "bd73f89d34d8f879c54ac46eb94b0f7be1d00820",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-curl": "*",
|
||||
"php": ">=5.3"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "*"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-0": {
|
||||
"Httpful": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Nate Good",
|
||||
"email": "me@nategood.com",
|
||||
"homepage": "http://nategood.com"
|
||||
}
|
||||
],
|
||||
"description": "A Readable, Chainable, REST friendly, PHP HTTP Client",
|
||||
"homepage": "http://github.com/nategood/httpful",
|
||||
"keywords": [
|
||||
"api",
|
||||
"curl",
|
||||
"http",
|
||||
"requests",
|
||||
"rest",
|
||||
"restful"
|
||||
],
|
||||
"time": "2015-03-08 15:22:23"
|
||||
}
|
||||
],
|
||||
"packages-dev": [],
|
||||
"aliases": [],
|
||||
"minimum-stability": "stable",
|
||||
"stability-flags": [],
|
||||
"prefer-stable": false,
|
||||
"prefer-lowest": false,
|
||||
"platform": [],
|
||||
"platform-dev": []
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
<?php
|
||||
define('DIR_ROOT', dirname(__FILE__).'/');
|
||||
define("OAPI_HOST", "https://oapi.dingtalk.com");
|
||||
//Suite
|
||||
define("CREATE_SUITE_KEY", "");
|
||||
define("SUITE_KEY", "ding48bce8fd3957c96b");
|
||||
define("SUITE_SECRET", "
|
||||
C4-8rUDK1u5Twpsw7U3yo42s_bbYxFIqzLMp2j7uI80Sa8D-OPbtSoCMgZxHxo2d");
|
||||
define("TOKEN", "123456");
|
||||
define("APPID", "");
|
||||
define("ENCODING_AES_KEY", "abcdefgABCDEFG0123456789hHiIjJKkLlMmnNOpQrs");
|
@ -0,0 +1,90 @@
|
||||
<?php
|
||||
include_once "sha1.php";
|
||||
include_once "pkcs7Encoder.php";
|
||||
include_once "errorCode.php";
|
||||
|
||||
|
||||
class DingtalkCrypt
|
||||
{
|
||||
private $m_token;
|
||||
private $m_encodingAesKey;
|
||||
private $m_suiteKey;
|
||||
|
||||
|
||||
public function DingtalkCrypt($token, $encodingAesKey, $suiteKey)
|
||||
{
|
||||
$this->m_token = $token;
|
||||
$this->m_encodingAesKey = $encodingAesKey;
|
||||
$this->m_suiteKey = $suiteKey;
|
||||
}
|
||||
|
||||
|
||||
public function EncryptMsg($plain, $timeStamp, $nonce, &$encryptMsg)
|
||||
{
|
||||
$pc = new Prpcrypt($this->m_encodingAesKey);
|
||||
|
||||
$array = $pc->encrypt($plain, $this->m_suiteKey);
|
||||
$ret = $array[0];
|
||||
if ($ret != 0) {
|
||||
return $ret;
|
||||
}
|
||||
|
||||
if ($timeStamp == null) {
|
||||
$timeStamp = time();
|
||||
}
|
||||
$encrypt = $array[1];
|
||||
|
||||
$sha1 = new SHA1;
|
||||
$array = $sha1->getSHA1($this->m_token, $timeStamp, $nonce, $encrypt);
|
||||
$ret = $array[0];
|
||||
if ($ret != 0) {
|
||||
return $ret;
|
||||
}
|
||||
$signature = $array[1];
|
||||
|
||||
$encryptMsg = json_encode(array(
|
||||
"msg_signature" => $signature,
|
||||
"encrypt" => $encrypt,
|
||||
"timeStamp" => $timeStamp,
|
||||
"nonce" => $nonce
|
||||
));
|
||||
return ErrorCode::$OK;
|
||||
}
|
||||
|
||||
|
||||
public function DecryptMsg($signature, $timeStamp = null, $nonce, $encrypt, &$decryptMsg)
|
||||
{
|
||||
if (strlen($this->m_encodingAesKey) != 43) {
|
||||
return ErrorCode::$IllegalAesKey;
|
||||
}
|
||||
|
||||
$pc = new Prpcrypt($this->m_encodingAesKey);
|
||||
|
||||
if ($timeStamp == null) {
|
||||
$timeStamp = time();
|
||||
}
|
||||
|
||||
$sha1 = new SHA1;
|
||||
$array = $sha1->getSHA1($this->m_token, $timeStamp, $nonce, $encrypt);
|
||||
$ret = $array[0];
|
||||
|
||||
if ($ret != 0) {
|
||||
return $ret;
|
||||
}
|
||||
|
||||
$verifySignature = $array[1];
|
||||
if ($verifySignature != $signature) {
|
||||
return ErrorCode::$ValidateSignatureError;
|
||||
}
|
||||
|
||||
$result = $pc->decrypt($encrypt, $this->m_suiteKey);
|
||||
if ($result[0] != 0) {
|
||||
return $result[0];
|
||||
}
|
||||
$decryptMsg = $result[1];
|
||||
|
||||
return ErrorCode::$OK;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* error code 说明.
|
||||
* <ul>
|
||||
* <li>-900004: encodingAesKey 非法</li>
|
||||
* <li>-900005: 签名验证错误</li>
|
||||
* <li>-900006: sha加密生成签名失败</li>
|
||||
* <li>-900007: aes 加密失败</li>
|
||||
* <li>-900008: aes 解密失败</li>
|
||||
* <li>-900010: suiteKey 校验错误</li>
|
||||
* </ul>
|
||||
*/
|
||||
class ErrorCode
|
||||
{
|
||||
public static $OK = 0;
|
||||
|
||||
public static $IllegalAesKey = 900004;
|
||||
public static $ValidateSignatureError = 900005;
|
||||
public static $ComputeSignatureError = 900006;
|
||||
public static $EncryptAESError = 900007;
|
||||
public static $DecryptAESError = 900008;
|
||||
public static $ValidateSuiteKeyError = 900010;
|
||||
}
|
||||
|
||||
?>
|
@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
include_once "errorCode.php";
|
||||
|
||||
class SHA1
|
||||
{
|
||||
public function getSHA1($token, $timestamp, $nonce, $encrypt_msg)
|
||||
{
|
||||
try {
|
||||
$array = array($encrypt_msg, $token, $timestamp, $nonce);
|
||||
sort($array, SORT_STRING);
|
||||
$str = implode($array);
|
||||
return array(ErrorCode::$OK, sha1($str));
|
||||
} catch (Exception $e) {
|
||||
print $e . "\n";
|
||||
return array(ErrorCode::$ComputeSignatureError, null);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
?>
|
@ -0,0 +1 @@
|
||||
<?php exit();?>
|
@ -0,0 +1,16 @@
|
||||
<?php
|
||||
require_once(__DIR__ . "/config.php");
|
||||
require_once(__DIR__ . "/util/Log.php");
|
||||
require_once(__DIR__ . "/util/Cache.php");
|
||||
require_once(__DIR__ . "/api/Auth.php");
|
||||
require_once(__DIR__ . "/api/User.php");
|
||||
require_once(__DIR__ . "/api/Message.php");
|
||||
require_once(__DIR__ . "/api/ISVClass.php");
|
||||
|
||||
$code = $_GET['code'];
|
||||
$corpId = $_GET['corpid'];
|
||||
$corpInfo = ISVClass::getCorpInfo($corpId);
|
||||
$accessToken = $corpInfo['corpAccessToken'];
|
||||
$res = Auth::getPerson($accessToken,$code);
|
||||
echo $res;
|
||||
exit;
|
@ -0,0 +1,60 @@
|
||||
E/ 2020-01-08 09:57:27 ERROR: suiteTicket not cached,please check the callback url
|
||||
E/ 2020-01-08 09:57:32 [FAIL]: {"errcode":40014,"errmsg":"\u4e0d\u5408\u6cd5\u7684access_token"}
|
||||
E/ 2020-01-08 09:57:57 [] ERR:900004
|
||||
E/ 2020-01-08 09:57:57 []CREATE SUITE ERR:900004
|
||||
E/ 2020-01-08 09:59:01 [] ERR:900004
|
||||
E/ 2020-01-08 09:59:01 []CREATE SUITE ERR:900004
|
||||
E/ 2020-01-08 09:59:06 [] ERR:900004
|
||||
E/ 2020-01-08 09:59:06 []CREATE SUITE ERR:900004
|
||||
E/ 2020-01-08 10:00:15 [] ERR:900004
|
||||
E/ 2020-01-08 10:00:15 []CREATE SUITE ERR:900004
|
||||
E/ 2020-01-08 10:46:10 [] ERR:900004
|
||||
E/ 2020-01-08 10:46:10 []CREATE SUITE ERR:900004
|
||||
E/ 2020-01-08 10:46:33 [] ERR:900004
|
||||
E/ 2020-01-08 10:46:33 []CREATE SUITE ERR:900004
|
||||
E/ 2020-01-08 10:46:35 [] ERR:900004
|
||||
E/ 2020-01-08 10:46:35 []CREATE SUITE ERR:900004
|
||||
E/ 2020-01-08 10:47:08 [] ERR:900004
|
||||
E/ 2020-01-08 10:47:08 []CREATE SUITE ERR:900004
|
||||
E/ 2020-01-08 10:47:09 [] ERR:900004
|
||||
E/ 2020-01-08 10:47:09 []CREATE SUITE ERR:900004
|
||||
E/ 2020-01-08 10:47:55 [] ERR:900004
|
||||
E/ 2020-01-08 10:47:55 []CREATE SUITE ERR:900004
|
||||
E/ 2020-01-08 10:47:57 [] ERR:900004
|
||||
E/ 2020-01-08 10:47:57 []CREATE SUITE ERR:900004
|
||||
E/ 2020-01-08 10:48:29 [] ERR:900004
|
||||
E/ 2020-01-08 10:48:29 []CREATE SUITE ERR:900004
|
||||
E/ 2020-01-08 10:48:30 [] ERR:900004
|
||||
E/ 2020-01-08 10:48:30 []CREATE SUITE ERR:900004
|
||||
E/ 2020-01-08 10:48:30 [] ERR:900004
|
||||
E/ 2020-01-08 10:48:30 []CREATE SUITE ERR:900004
|
||||
E/ 2020-01-08 10:48:31 [] ERR:900004
|
||||
E/ 2020-01-08 10:48:31 []CREATE SUITE ERR:900004
|
||||
E/ 2020-01-08 10:48:31 [] ERR:900004
|
||||
E/ 2020-01-08 10:48:31 []CREATE SUITE ERR:900004
|
||||
E/ 2020-01-08 02:44:14 [] ERR:900004
|
||||
E/ 2020-01-08 02:44:14 []CREATE SUITE ERR:900004
|
||||
E/ 2020-01-08 02:48:15 [] ERR:900005
|
||||
E/ 2020-01-08 02:48:15 []CREATE SUITE ERR:900005
|
||||
E/ 2020-01-08 02:48:38 [] ERR:900005
|
||||
E/ 2020-01-08 02:48:38 []CREATE SUITE ERR:900005
|
||||
E/ 2020-01-08 02:49:13 [] ERR:900005
|
||||
E/ 2020-01-08 02:49:13 []CREATE SUITE ERR:900005
|
||||
E/ 2020-01-08 02:49:34 [] ERR:900005
|
||||
E/ 2020-01-08 02:49:34 []CREATE SUITE ERR:900005
|
||||
E/ 2020-01-08 02:50:35 [] ERR:900005
|
||||
E/ 2020-01-08 02:50:35 []CREATE SUITE ERR:900005
|
||||
E/ 2020-01-08 02:50:36 [] ERR:900005
|
||||
E/ 2020-01-08 02:50:36 []CREATE SUITE ERR:900005
|
||||
E/ 2020-01-08 02:50:36 [] ERR:900005
|
||||
E/ 2020-01-08 02:50:36 []CREATE SUITE ERR:900005
|
||||
E/ 2020-01-08 02:50:36 [] ERR:900005
|
||||
E/ 2020-01-08 02:50:36 []CREATE SUITE ERR:900005
|
||||
I/ 2020-01-08 04:17:37 DECRYPT MSG SUCCESS [] {"EventType":"check_url"}
|
||||
I/ 2020-01-08 04:17:37 RESPONSE: {"msg_signature":"dddd562596877498f925cfde6be89dcdb2b0b260","encrypt":"bfWI8rpT2xLmwK78PjZOy7yv4UptXvYMPPNaZdeUtPrJBV15bBCJB\/X8RNzU9nybvfgt2fP5WJ6wKE\/5cjNeew==","timeStamp":"1578453834041","nonce":"bpmnoUjO"}
|
||||
E/ 2020-01-08 04:45:11 ERROR: suiteTicket not cached,please check the callback url
|
||||
E/ 2020-01-08 04:45:12 [FAIL]: {"errcode":40014,"errmsg":"\u4e0d\u5408\u6cd5\u7684access_token"}
|
||||
I/ 2020-01-08 04:45:24 DECRYPT MSG SUCCESS [] {"EventType":"check_url"}
|
||||
I/ 2020-01-08 04:45:24 RESPONSE: {"msg_signature":"9b012263e14a86181c1b1440cf3b9d24d10fb94f","encrypt":"WCP4qSubCQ4zqzuLvc5rWvOEsoISdZf44MfTeJudHwS\/OdHbgthABNmWHOmWHXm8jvIz+9MHLX+jJzB3osGCbw==","timeStamp":"1578453834041","nonce":"bpmnoUjO"}
|
||||
E/ 2020-01-08 04:46:48 [] ERR:900005
|
||||
E/ 2020-01-08 04:46:48 []CREATE SUITE ERR:900005
|
@ -0,0 +1,24 @@
|
||||
/**
|
||||
* Created by liqiao on 8/14/15.
|
||||
*/
|
||||
|
||||
var log = document.createElement('div');
|
||||
log.setAttribute('id', 'log');
|
||||
document.body.appendChild(log);
|
||||
|
||||
var logger = {
|
||||
i: function(info) {
|
||||
add(info, 'i');
|
||||
},
|
||||
e: function(err) {
|
||||
add(err, 'e');
|
||||
}
|
||||
};
|
||||
|
||||
function add(msg, level) {
|
||||
var row = document.createElement('div');
|
||||
row.setAttribute('class', 'log-row log-' + level);
|
||||
row.innerHTML = msg;
|
||||
|
||||
document.querySelector('#log').appendChild(row);
|
||||
}
|
File diff suppressed because one or more lines are too long
@ -0,0 +1,41 @@
|
||||
body {
|
||||
padding: 50px;
|
||||
font: 14px "Lucida Grande", Helvetica, Arial, sans-serif;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #00B7FF;
|
||||
}
|
||||
|
||||
.log-i {
|
||||
color: green;
|
||||
}
|
||||
|
||||
.log-e {
|
||||
color: red;
|
||||
}
|
||||
|
||||
.tag {
|
||||
display: inline-block;
|
||||
width: 170px;
|
||||
}
|
||||
|
||||
.api {
|
||||
display: inline-block;
|
||||
width: 300px;
|
||||
}
|
||||
|
||||
.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.row button {
|
||||
width: 100%;
|
||||
height: 100px;
|
||||
}
|
||||
|
||||
.log-row {
|
||||
margin: 20px 10px 20px 10px;
|
||||
font-size: 30px;
|
||||
word-break: break-all;word-wrap: break-word;
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
<?php
|
||||
require_once(__DIR__ . "/config.php");
|
||||
require_once(__DIR__ . "/util/Log.php");
|
||||
require_once(__DIR__ . "/util/Cache.php");
|
||||
require_once(__DIR__ . "/api/Auth.php");
|
||||
require_once(__DIR__ . "/api/User.php");
|
||||
require_once(__DIR__ . "/api/Message.php");
|
||||
require_once(__DIR__ . "/api/ISVClass.php");
|
||||
|
||||
$event = $_POST["event"];
|
||||
switch($event){
|
||||
case '':
|
||||
echo json_encode(array("error_code"=>"4000"));
|
||||
break;
|
||||
case 'send_to_conversation':
|
||||
$sender = $_POST['sender'];
|
||||
$cid = $_POST['cid'];
|
||||
$content = $_POST['content'];
|
||||
$corpId = $_POST['corpId'];
|
||||
$corpInfo = ISVClass::getCorpInfo($corpId);
|
||||
$accessToken = $corpInfo['corpAccessToken'];
|
||||
$option = array(
|
||||
"sender"=>$sender,
|
||||
"cid"=>$cid,
|
||||
"msgtype"=>"text",
|
||||
"text"=>array("content"=>$content)
|
||||
);
|
||||
$response = Message::sendToConversation($accessToken,$option);
|
||||
echo json_encode($response);
|
||||
break;
|
||||
|
||||
case 'get_userinfo':
|
||||
$corpId = $_POST['corpId'];
|
||||
$corpInfo = ISVClass::getCorpInfo($corpId);
|
||||
$accessToken = $corpInfo['corpAccessToken'];
|
||||
$code = $_POST["code"];
|
||||
$userInfo = User::getUserInfo($accessToken, $code);
|
||||
echo json_encode($userInfo);
|
||||
break;
|
||||
}
|
@ -0,0 +1,61 @@
|
||||
<?php
|
||||
|
||||
require_once(__DIR__ . "/../config.php");
|
||||
require_once(__DIR__ . "/../vendor/nategood/httpful/bootstrap.php");
|
||||
|
||||
Class Http
|
||||
{
|
||||
public static function get($path, $params)
|
||||
{
|
||||
$url = self::joinParams($path, $params);
|
||||
$response = \Httpful\Request::get($url)->send();
|
||||
if ($response->hasErrors())
|
||||
{
|
||||
var_dump($response);
|
||||
}
|
||||
if ($response->body->errcode != 0)
|
||||
{
|
||||
var_dump($response->body);
|
||||
}
|
||||
return $response->body;
|
||||
}
|
||||
|
||||
|
||||
public static function post($path, $params, $data)
|
||||
{
|
||||
$url = self::joinParams($path, $params);
|
||||
$response = \Httpful\Request::post($url)
|
||||
->body($data)
|
||||
->sendsJson()
|
||||
->send();
|
||||
if ($response->hasErrors())
|
||||
{
|
||||
var_dump($response);
|
||||
}
|
||||
if ($response->body->errcode != 0)
|
||||
{
|
||||
var_dump($response->body);
|
||||
}
|
||||
return $response->body;
|
||||
}
|
||||
|
||||
|
||||
private static function joinParams($path, $params)
|
||||
{
|
||||
$url = OAPI_HOST . $path;
|
||||
if (count($params) > 0)
|
||||
{
|
||||
$url = $url . "?";
|
||||
foreach ($params as $key => $value)
|
||||
{
|
||||
$url = $url . $key . "=" . $value . "&";
|
||||
}
|
||||
$length = count($url);
|
||||
if ($url[$length - 1] == '&')
|
||||
{
|
||||
$url = substr($url, 0, $length - 1);
|
||||
}
|
||||
}
|
||||
return $url;
|
||||
}
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
class Log
|
||||
{
|
||||
public static function i($msg)
|
||||
{
|
||||
self::write('I', $msg);
|
||||
}
|
||||
|
||||
public static function e($msg)
|
||||
{
|
||||
self::write('E', $msg);
|
||||
}
|
||||
|
||||
private static function write($level, $msg)
|
||||
{
|
||||
$filename = DIR_ROOT . "isv.log";
|
||||
$logFile = fopen($filename, "aw");
|
||||
fwrite($logFile, $level . "/" . date(" Y-m-d h:i:s") . " " . $msg . "\n");
|
||||
fclose($logFile);
|
||||
}
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
<?php
|
||||
|
||||
// autoload.php @generated by Composer
|
||||
|
||||
require_once __DIR__ . '/composer' . '/autoload_real.php';
|
||||
|
||||
return ComposerAutoloaderInit9c336c4d71b270c36d1e071cf0b19270::getLoader();
|
@ -0,0 +1,413 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Composer.
|
||||
*
|
||||
* (c) Nils Adermann <naderman@naderman.de>
|
||||
* Jordi Boggiano <j.boggiano@seld.be>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Composer\Autoload;
|
||||
|
||||
/**
|
||||
* ClassLoader implements a PSR-0 class loader
|
||||
*
|
||||
* See https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-0.md
|
||||
*
|
||||
* $loader = new \Composer\Autoload\ClassLoader();
|
||||
*
|
||||
* // register classes with namespaces
|
||||
* $loader->add('Symfony\Component', __DIR__.'/component');
|
||||
* $loader->add('Symfony', __DIR__.'/framework');
|
||||
*
|
||||
* // activate the autoloader
|
||||
* $loader->register();
|
||||
*
|
||||
* // to enable searching the include path (eg. for PEAR packages)
|
||||
* $loader->setUseIncludePath(true);
|
||||
*
|
||||
* In this example, if you try to use a class in the Symfony\Component
|
||||
* namespace or one of its children (Symfony\Component\Console for instance),
|
||||
* the autoloader will first look for the class under the component/
|
||||
* directory, and it will then fallback to the framework/ directory if not
|
||||
* found before giving up.
|
||||
*
|
||||
* This class is loosely based on the Symfony UniversalClassLoader.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
* @author Jordi Boggiano <j.boggiano@seld.be>
|
||||
*/
|
||||
class ClassLoader
|
||||
{
|
||||
// PSR-4
|
||||
private $prefixLengthsPsr4 = array();
|
||||
private $prefixDirsPsr4 = array();
|
||||
private $fallbackDirsPsr4 = array();
|
||||
|
||||
// PSR-0
|
||||
private $prefixesPsr0 = array();
|
||||
private $fallbackDirsPsr0 = array();
|
||||
|
||||
private $useIncludePath = false;
|
||||
private $classMap = array();
|
||||
|
||||
private $classMapAuthoritative = false;
|
||||
|
||||
public function getPrefixes()
|
||||
{
|
||||
if (!empty($this->prefixesPsr0)) {
|
||||
return call_user_func_array('array_merge', $this->prefixesPsr0);
|
||||
}
|
||||
|
||||
return array();
|
||||
}
|
||||
|
||||
public function getPrefixesPsr4()
|
||||
{
|
||||
return $this->prefixDirsPsr4;
|
||||
}
|
||||
|
||||
public function getFallbackDirs()
|
||||
{
|
||||
return $this->fallbackDirsPsr0;
|
||||
}
|
||||
|
||||
public function getFallbackDirsPsr4()
|
||||
{
|
||||
return $this->fallbackDirsPsr4;
|
||||
}
|
||||
|
||||
public function getClassMap()
|
||||
{
|
||||
return $this->classMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $classMap Class to filename map
|
||||
*/
|
||||
public function addClassMap(array $classMap)
|
||||
{
|
||||
if ($this->classMap) {
|
||||
$this->classMap = array_merge($this->classMap, $classMap);
|
||||
} else {
|
||||
$this->classMap = $classMap;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a set of PSR-0 directories for a given prefix, either
|
||||
* appending or prepending to the ones previously set for this prefix.
|
||||
*
|
||||
* @param string $prefix The prefix
|
||||
* @param array|string $paths The PSR-0 root directories
|
||||
* @param bool $prepend Whether to prepend the directories
|
||||
*/
|
||||
public function add($prefix, $paths, $prepend = false)
|
||||
{
|
||||
if (!$prefix) {
|
||||
if ($prepend) {
|
||||
$this->fallbackDirsPsr0 = array_merge(
|
||||
(array) $paths,
|
||||
$this->fallbackDirsPsr0
|
||||
);
|
||||
} else {
|
||||
$this->fallbackDirsPsr0 = array_merge(
|
||||
$this->fallbackDirsPsr0,
|
||||
(array) $paths
|
||||
);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$first = $prefix[0];
|
||||
if (!isset($this->prefixesPsr0[$first][$prefix])) {
|
||||
$this->prefixesPsr0[$first][$prefix] = (array) $paths;
|
||||
|
||||
return;
|
||||
}
|
||||
if ($prepend) {
|
||||
$this->prefixesPsr0[$first][$prefix] = array_merge(
|
||||
(array) $paths,
|
||||
$this->prefixesPsr0[$first][$prefix]
|
||||
);
|
||||
} else {
|
||||
$this->prefixesPsr0[$first][$prefix] = array_merge(
|
||||
$this->prefixesPsr0[$first][$prefix],
|
||||
(array) $paths
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a set of PSR-4 directories for a given namespace, either
|
||||
* appending or prepending to the ones previously set for this namespace.
|
||||
*
|
||||
* @param string $prefix The prefix/namespace, with trailing '\\'
|
||||
* @param array|string $paths The PSR-0 base directories
|
||||
* @param bool $prepend Whether to prepend the directories
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function addPsr4($prefix, $paths, $prepend = false)
|
||||
{
|
||||
if (!$prefix) {
|
||||
// Register directories for the root namespace.
|
||||
if ($prepend) {
|
||||
$this->fallbackDirsPsr4 = array_merge(
|
||||
(array) $paths,
|
||||
$this->fallbackDirsPsr4
|
||||
);
|
||||
} else {
|
||||
$this->fallbackDirsPsr4 = array_merge(
|
||||
$this->fallbackDirsPsr4,
|
||||
(array) $paths
|
||||
);
|
||||
}
|
||||
} elseif (!isset($this->prefixDirsPsr4[$prefix])) {
|
||||
// Register directories for a new namespace.
|
||||
$length = strlen($prefix);
|
||||
if ('\\' !== $prefix[$length - 1]) {
|
||||
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
|
||||
}
|
||||
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
|
||||
$this->prefixDirsPsr4[$prefix] = (array) $paths;
|
||||
} elseif ($prepend) {
|
||||
// Prepend directories for an already registered namespace.
|
||||
$this->prefixDirsPsr4[$prefix] = array_merge(
|
||||
(array) $paths,
|
||||
$this->prefixDirsPsr4[$prefix]
|
||||
);
|
||||
} else {
|
||||
// Append directories for an already registered namespace.
|
||||
$this->prefixDirsPsr4[$prefix] = array_merge(
|
||||
$this->prefixDirsPsr4[$prefix],
|
||||
(array) $paths
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a set of PSR-0 directories for a given prefix,
|
||||
* replacing any others previously set for this prefix.
|
||||
*
|
||||
* @param string $prefix The prefix
|
||||
* @param array|string $paths The PSR-0 base directories
|
||||
*/
|
||||
public function set($prefix, $paths)
|
||||
{
|
||||
if (!$prefix) {
|
||||
$this->fallbackDirsPsr0 = (array) $paths;
|
||||
} else {
|
||||
$this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a set of PSR-4 directories for a given namespace,
|
||||
* replacing any others previously set for this namespace.
|
||||
*
|
||||
* @param string $prefix The prefix/namespace, with trailing '\\'
|
||||
* @param array|string $paths The PSR-4 base directories
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function setPsr4($prefix, $paths)
|
||||
{
|
||||
if (!$prefix) {
|
||||
$this->fallbackDirsPsr4 = (array) $paths;
|
||||
} else {
|
||||
$length = strlen($prefix);
|
||||
if ('\\' !== $prefix[$length - 1]) {
|
||||
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
|
||||
}
|
||||
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
|
||||
$this->prefixDirsPsr4[$prefix] = (array) $paths;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Turns on searching the include path for class files.
|
||||
*
|
||||
* @param bool $useIncludePath
|
||||
*/
|
||||
public function setUseIncludePath($useIncludePath)
|
||||
{
|
||||
$this->useIncludePath = $useIncludePath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Can be used to check if the autoloader uses the include path to check
|
||||
* for classes.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function getUseIncludePath()
|
||||
{
|
||||
return $this->useIncludePath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Turns off searching the prefix and fallback directories for classes
|
||||
* that have not been registered with the class map.
|
||||
*
|
||||
* @param bool $classMapAuthoritative
|
||||
*/
|
||||
public function setClassMapAuthoritative($classMapAuthoritative)
|
||||
{
|
||||
$this->classMapAuthoritative = $classMapAuthoritative;
|
||||
}
|
||||
|
||||
/**
|
||||
* Should class lookup fail if not found in the current class map?
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isClassMapAuthoritative()
|
||||
{
|
||||
return $this->classMapAuthoritative;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers this instance as an autoloader.
|
||||
*
|
||||
* @param bool $prepend Whether to prepend the autoloader or not
|
||||
*/
|
||||
public function register($prepend = false)
|
||||
{
|
||||
spl_autoload_register(array($this, 'loadClass'), true, $prepend);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregisters this instance as an autoloader.
|
||||
*/
|
||||
public function unregister()
|
||||
{
|
||||
spl_autoload_unregister(array($this, 'loadClass'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the given class or interface.
|
||||
*
|
||||
* @param string $class The name of the class
|
||||
* @return bool|null True if loaded, null otherwise
|
||||
*/
|
||||
public function loadClass($class)
|
||||
{
|
||||
if ($file = $this->findFile($class)) {
|
||||
includeFile($file);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the path to the file where the class is defined.
|
||||
*
|
||||
* @param string $class The name of the class
|
||||
*
|
||||
* @return string|false The path if found, false otherwise
|
||||
*/
|
||||
public function findFile($class)
|
||||
{
|
||||
// work around for PHP 5.3.0 - 5.3.2 https://bugs.php.net/50731
|
||||
if ('\\' == $class[0]) {
|
||||
$class = substr($class, 1);
|
||||
}
|
||||
|
||||
// class map lookup
|
||||
if (isset($this->classMap[$class])) {
|
||||
return $this->classMap[$class];
|
||||
}
|
||||
if ($this->classMapAuthoritative) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$file = $this->findFileWithExtension($class, '.php');
|
||||
|
||||
// Search for Hack files if we are running on HHVM
|
||||
if ($file === null && defined('HHVM_VERSION')) {
|
||||
$file = $this->findFileWithExtension($class, '.hh');
|
||||
}
|
||||
|
||||
if ($file === null) {
|
||||
// Remember that this class does not exist.
|
||||
return $this->classMap[$class] = false;
|
||||
}
|
||||
|
||||
return $file;
|
||||
}
|
||||
|
||||
private function findFileWithExtension($class, $ext)
|
||||
{
|
||||
// PSR-4 lookup
|
||||
$logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;
|
||||
|
||||
$first = $class[0];
|
||||
if (isset($this->prefixLengthsPsr4[$first])) {
|
||||
foreach ($this->prefixLengthsPsr4[$first] as $prefix => $length) {
|
||||
if (0 === strpos($class, $prefix)) {
|
||||
foreach ($this->prefixDirsPsr4[$prefix] as $dir) {
|
||||
if (is_file($file = $dir . DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $length))) {
|
||||
return $file;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// PSR-4 fallback dirs
|
||||
foreach ($this->fallbackDirsPsr4 as $dir) {
|
||||
if (is_file($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
|
||||
return $file;
|
||||
}
|
||||
}
|
||||
|
||||
// PSR-0 lookup
|
||||
if (false !== $pos = strrpos($class, '\\')) {
|
||||
// namespaced class name
|
||||
$logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
|
||||
. strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
|
||||
} else {
|
||||
// PEAR-like class name
|
||||
$logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;
|
||||
}
|
||||
|
||||
if (isset($this->prefixesPsr0[$first])) {
|
||||
foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
|
||||
if (0 === strpos($class, $prefix)) {
|
||||
foreach ($dirs as $dir) {
|
||||
if (is_file($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
|
||||
return $file;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// PSR-0 fallback dirs
|
||||
foreach ($this->fallbackDirsPsr0 as $dir) {
|
||||
if (is_file($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
|
||||
return $file;
|
||||
}
|
||||
}
|
||||
|
||||
// PSR-0 include paths.
|
||||
if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
|
||||
return $file;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Scope isolated include.
|
||||
*
|
||||
* Prevents access to $this/self from included files.
|
||||
*/
|
||||
function includeFile($file)
|
||||
{
|
||||
include $file;
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
// autoload_classmap.php @generated by Composer
|
||||
|
||||
$vendorDir = dirname(dirname(__FILE__));
|
||||
$baseDir = dirname($vendorDir);
|
||||
|
||||
return array(
|
||||
);
|
@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
// autoload_namespaces.php @generated by Composer
|
||||
|
||||
$vendorDir = dirname(dirname(__FILE__));
|
||||
$baseDir = dirname($vendorDir);
|
||||
|
||||
return array(
|
||||
'Httpful' => array($vendorDir . '/nategood/httpful/src'),
|
||||
);
|
@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
// autoload_psr4.php @generated by Composer
|
||||
|
||||
$vendorDir = dirname(dirname(__FILE__));
|
||||
$baseDir = dirname($vendorDir);
|
||||
|
||||
return array(
|
||||
);
|
@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
// autoload_real.php @generated by Composer
|
||||
|
||||
class ComposerAutoloaderInit9c336c4d71b270c36d1e071cf0b19270
|
||||
{
|
||||
private static $loader;
|
||||
|
||||
public static function loadClassLoader($class)
|
||||
{
|
||||
if ('Composer\Autoload\ClassLoader' === $class) {
|
||||
require __DIR__ . '/ClassLoader.php';
|
||||
}
|
||||
}
|
||||
|
||||
public static function getLoader()
|
||||
{
|
||||
if (null !== self::$loader) {
|
||||
return self::$loader;
|
||||
}
|
||||
|
||||
spl_autoload_register(array('ComposerAutoloaderInit9c336c4d71b270c36d1e071cf0b19270', 'loadClassLoader'), true, true);
|
||||
self::$loader = $loader = new \Composer\Autoload\ClassLoader();
|
||||
spl_autoload_unregister(array('ComposerAutoloaderInit9c336c4d71b270c36d1e071cf0b19270', 'loadClassLoader'));
|
||||
|
||||
$map = require __DIR__ . '/autoload_namespaces.php';
|
||||
foreach ($map as $namespace => $path) {
|
||||
$loader->set($namespace, $path);
|
||||
}
|
||||
|
||||
$map = require __DIR__ . '/autoload_psr4.php';
|
||||
foreach ($map as $namespace => $path) {
|
||||
$loader->setPsr4($namespace, $path);
|
||||
}
|
||||
|
||||
$classMap = require __DIR__ . '/autoload_classmap.php';
|
||||
if ($classMap) {
|
||||
$loader->addClassMap($classMap);
|
||||
}
|
||||
|
||||
$loader->register(true);
|
||||
|
||||
return $loader;
|
||||
}
|
||||
}
|
||||
|
||||
function composerRequire9c336c4d71b270c36d1e071cf0b19270($file)
|
||||
{
|
||||
require $file;
|
||||
}
|
@ -0,0 +1,54 @@
|
||||
[
|
||||
{
|
||||
"name": "nategood/httpful",
|
||||
"version": "0.2.19",
|
||||
"version_normalized": "0.2.19.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/nategood/httpful.git",
|
||||
"reference": "bd73f89d34d8f879c54ac46eb94b0f7be1d00820"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/nategood/httpful/zipball/bd73f89d34d8f879c54ac46eb94b0f7be1d00820",
|
||||
"reference": "bd73f89d34d8f879c54ac46eb94b0f7be1d00820",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-curl": "*",
|
||||
"php": ">=5.3"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "*"
|
||||
},
|
||||
"time": "2015-03-08 15:22:23",
|
||||
"type": "library",
|
||||
"installation-source": "dist",
|
||||
"autoload": {
|
||||
"psr-0": {
|
||||
"Httpful": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Nate Good",
|
||||
"email": "me@nategood.com",
|
||||
"homepage": "http://nategood.com"
|
||||
}
|
||||
],
|
||||
"description": "A Readable, Chainable, REST friendly, PHP HTTP Client",
|
||||
"homepage": "http://github.com/nategood/httpful",
|
||||
"keywords": [
|
||||
"api",
|
||||
"curl",
|
||||
"http",
|
||||
"requests",
|
||||
"rest",
|
||||
"restful"
|
||||
]
|
||||
}
|
||||
]
|
@ -0,0 +1,5 @@
|
||||
.DS_Store
|
||||
composer.lock
|
||||
vendor
|
||||
downloads
|
||||
.idea/*
|
@ -0,0 +1,7 @@
|
||||
language: php
|
||||
before_script: cd tests
|
||||
php:
|
||||
- 5.3
|
||||
- 5.4
|
||||
- 5.5
|
||||
- hhvm
|
@ -0,0 +1,7 @@
|
||||
Copyright (c) 2012 Nate Good <me@nategood.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
@ -0,0 +1,4 @@
|
||||
<?php
|
||||
|
||||
require(__DIR__ . '/src/Httpful/Bootstrap.php');
|
||||
\Httpful\Bootstrap::init();
|
@ -0,0 +1,51 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Build the whole library into a single file
|
||||
* as an easy drop in solution as opposed to
|
||||
* relying on autoloader. Sometimes we just
|
||||
* want to hack with an API as a one off thing.
|
||||
* Httpful should make this easy.
|
||||
*/
|
||||
|
||||
function exit_unless($condition, $msg = null) {
|
||||
if ($condition)
|
||||
return;
|
||||
echo "[FAIL]\n$msg\n";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Create the Httpful Phar
|
||||
echo "Building Phar... ";
|
||||
$base_dir = dirname(__FILE__);
|
||||
$source_dir = $base_dir . '/src/Httpful/';
|
||||
$phar_path = $base_dir . '/downloads/httpful.phar';
|
||||
$phar = new Phar($phar_path, 0, 'httpful.phar');
|
||||
$stub = <<<HEREDOC
|
||||
<?php
|
||||
// Phar Stub File
|
||||
Phar::mapPhar('httpful.phar');
|
||||
include('phar://httpful.phar/Httpful/Bootstrap.php');
|
||||
\Httpful\Bootstrap::pharInit();
|
||||
|
||||
__HALT_COMPILER();
|
||||
HEREDOC;
|
||||
try {
|
||||
$phar->setStub($stub);
|
||||
} catch(Exception $e) {
|
||||
$phar = false;
|
||||
}
|
||||
exit_unless($phar, "Unable to create a phar. Make certain you have phar.readonly=0 set in your ini file.");
|
||||
$phar->buildFromDirectory(dirname($source_dir));
|
||||
echo "[ OK ]\n";
|
||||
|
||||
|
||||
|
||||
// Add it to git!
|
||||
//echo "Adding httpful.phar to the repo... ";
|
||||
//$return_code = 0;
|
||||
//passthru("git add $phar_path", $return_code);
|
||||
//exit_unless($return_code === 0, "Unable to add download files to git.");
|
||||
//echo "[ OK ]\n";
|
||||
echo "\nBuild completed successfully.\n\n";
|
@ -0,0 +1,27 @@
|
||||
{
|
||||
"name": "nategood/httpful",
|
||||
"description": "A Readable, Chainable, REST friendly, PHP HTTP Client",
|
||||
"homepage": "http://github.com/nategood/httpful",
|
||||
"license": "MIT",
|
||||
"keywords": ["http", "curl", "rest", "restful", "api", "requests"],
|
||||
"version": "0.2.19",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Nate Good",
|
||||
"email": "me@nategood.com",
|
||||
"homepage": "http://nategood.com"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": ">=5.3",
|
||||
"ext-curl": "*"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-0": {
|
||||
"Httpful": "src/"
|
||||
}
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "*"
|
||||
}
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
<?php
|
||||
/**
|
||||
* Grab some The Dead Weather albums from Freebase
|
||||
*/
|
||||
require(__DIR__ . '/../bootstrap.php');
|
||||
|
||||
$uri = "https://www.googleapis.com/freebase/v1/mqlread?query=%7B%22type%22:%22/music/artist%22%2C%22name%22:%22The%20Dead%20Weather%22%2C%22album%22:%5B%5D%7D";
|
||||
$response = \Httpful\Request::get($uri)
|
||||
->expectsJson()
|
||||
->sendIt();
|
||||
|
||||
echo 'The Dead Weather has ' . count($response->body->result->album) . " albums.\n";
|
@ -0,0 +1,9 @@
|
||||
<?php
|
||||
// XML Example from GitHub
|
||||
require(__DIR__ . '/../bootstrap.php');
|
||||
use \Httpful\Request;
|
||||
|
||||
$uri = 'https://github.com/api/v2/xml/user/show/nategood';
|
||||
$request = Request::get($uri)->send();
|
||||
|
||||
echo "{$request->body->name} joined GitHub on " . date('M jS', strtotime($request->body->{'created-at'})) ."\n";
|
@ -0,0 +1,44 @@
|
||||
<?php
|
||||
require(__DIR__ . '/../bootstrap.php');
|
||||
|
||||
// We can override the default parser configuration options be registering
|
||||
// a parser with different configuration options for a particular mime type
|
||||
|
||||
// Example setting a namespace for the XMLHandler parser
|
||||
$conf = array('namespace' => 'http://example.com');
|
||||
\Httpful\Httpful::register(\Httpful\Mime::XML, new \Httpful\Handlers\XmlHandler($conf));
|
||||
|
||||
// We can also add the parsers with our own...
|
||||
class SimpleCsvHandler extends \Httpful\Handlers\MimeHandlerAdapter
|
||||
{
|
||||
/**
|
||||
* Takes a response body, and turns it into
|
||||
* a two dimensional array.
|
||||
*
|
||||
* @param string $body
|
||||
* @return mixed
|
||||
*/
|
||||
public function parse($body)
|
||||
{
|
||||
return str_getcsv($body);
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes a two dimensional array and turns it
|
||||
* into a serialized string to include as the
|
||||
* body of a request
|
||||
*
|
||||
* @param mixed $payload
|
||||
* @return string
|
||||
*/
|
||||
public function serialize($payload)
|
||||
{
|
||||
$serialized = '';
|
||||
foreach ($payload as $line) {
|
||||
$serialized .= '"' . implode('","', $line) . '"' . "\n";
|
||||
}
|
||||
return $serialized;
|
||||
}
|
||||
}
|
||||
|
||||
\Httpful\Httpful::register('text/csv', new SimpleCsvHandler());
|
@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
require(__DIR__ . '/../bootstrap.php');
|
||||
|
||||
use \Httpful\Request;
|
||||
|
||||
// Get event details for a public event
|
||||
$uri = "http://api.showclix.com/Event/8175";
|
||||
$response = Request::get($uri)
|
||||
->expectsType('json')
|
||||
->send();
|
||||
|
||||
// Print out the event details
|
||||
echo "The event {$response->body->event} will take place on {$response->body->event_start}\n";
|
||||
|
||||
// Example overriding the default JSON handler with one that encodes the response as an array
|
||||
\Httpful\Httpful::register(\Httpful\Mime::JSON, new \Httpful\Handlers\JsonHandler(array('decode_as_array' => true)));
|
||||
|
||||
$response = Request::get($uri)
|
||||
->expectsType('json')
|
||||
->send();
|
||||
|
||||
// Print out the event details
|
||||
echo "The event {$response->body['event']} will take place on {$response->body['event_start']}\n";
|
@ -0,0 +1,97 @@
|
||||
<?php
|
||||
|
||||
namespace Httpful;
|
||||
|
||||
/**
|
||||
* Bootstrap class that facilitates autoloading. A naive
|
||||
* PSR-0 autoloader.
|
||||
*
|
||||
* @author Nate Good <me@nategood.com>
|
||||
*/
|
||||
class Bootstrap
|
||||
{
|
||||
|
||||
const DIR_GLUE = DIRECTORY_SEPARATOR;
|
||||
const NS_GLUE = '\\';
|
||||
|
||||
public static $registered = false;
|
||||
|
||||
/**
|
||||
* Register the autoloader and any other setup needed
|
||||
*/
|
||||
public static function init()
|
||||
{
|
||||
spl_autoload_register(array('\Httpful\Bootstrap', 'autoload'));
|
||||
self::registerHandlers();
|
||||
}
|
||||
|
||||
/**
|
||||
* The autoload magic (PSR-0 style)
|
||||
*
|
||||
* @param string $classname
|
||||
*/
|
||||
public static function autoload($classname)
|
||||
{
|
||||
self::_autoload(dirname(dirname(__FILE__)), $classname);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the autoloader and any other setup needed
|
||||
*/
|
||||
public static function pharInit()
|
||||
{
|
||||
spl_autoload_register(array('\Httpful\Bootstrap', 'pharAutoload'));
|
||||
self::registerHandlers();
|
||||
}
|
||||
|
||||
/**
|
||||
* Phar specific autoloader
|
||||
*
|
||||
* @param string $classname
|
||||
*/
|
||||
public static function pharAutoload($classname)
|
||||
{
|
||||
self::_autoload('phar://httpful.phar', $classname);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string base
|
||||
* @param string classname
|
||||
*/
|
||||
private static function _autoload($base, $classname)
|
||||
{
|
||||
$parts = explode(self::NS_GLUE, $classname);
|
||||
$path = $base . self::DIR_GLUE . implode(self::DIR_GLUE, $parts) . '.php';
|
||||
|
||||
if (file_exists($path)) {
|
||||
require_once($path);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Register default mime handlers. Is idempotent.
|
||||
*/
|
||||
public static function registerHandlers()
|
||||
{
|
||||
if (self::$registered === true) {
|
||||
return;
|
||||
}
|
||||
|
||||
// @todo check a conf file to load from that instead of
|
||||
// hardcoding into the library?
|
||||
$handlers = array(
|
||||
\Httpful\Mime::JSON => new \Httpful\Handlers\JsonHandler(),
|
||||
\Httpful\Mime::XML => new \Httpful\Handlers\XmlHandler(),
|
||||
\Httpful\Mime::FORM => new \Httpful\Handlers\FormHandler(),
|
||||
\Httpful\Mime::CSV => new \Httpful\Handlers\CsvHandler(),
|
||||
);
|
||||
|
||||
foreach ($handlers as $mime => $handler) {
|
||||
// Don't overwrite if the handler has already been registered
|
||||
if (Httpful::hasParserRegistered($mime))
|
||||
continue;
|
||||
Httpful::register($mime, $handler);
|
||||
}
|
||||
|
||||
self::$registered = true;
|
||||
}
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
<?php
|
||||
|
||||
namespace Httpful\Exception;
|
||||
|
||||
class ConnectionErrorException extends \Exception
|
||||
{
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
<?php
|
||||
/**
|
||||
* Mime Type: text/csv
|
||||
* @author Raja Kapur <rajak@twistedthrottle.com>
|
||||
*/
|
||||
|
||||
namespace Httpful\Handlers;
|
||||
|
||||
class CsvHandler extends MimeHandlerAdapter
|
||||
{
|
||||
/**
|
||||
* @param string $body
|
||||
* @return mixed
|
||||
*/
|
||||
public function parse($body)
|
||||
{
|
||||
if (empty($body))
|
||||
return null;
|
||||
|
||||
$parsed = array();
|
||||
$fp = fopen('data://text/plain;base64,' . base64_encode($body), 'r');
|
||||
while (($r = fgetcsv($fp)) !== FALSE) {
|
||||
$parsed[] = $r;
|
||||
}
|
||||
|
||||
if (empty($parsed))
|
||||
throw new \Exception("Unable to parse response as CSV");
|
||||
return $parsed;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $payload
|
||||
* @return string
|
||||
*/
|
||||
public function serialize($payload)
|
||||
{
|
||||
$fp = fopen('php://temp/maxmemory:'. (6*1024*1024), 'r+');
|
||||
$i = 0;
|
||||
foreach ($payload as $fields) {
|
||||
if($i++ == 0) {
|
||||
fputcsv($fp, array_keys($fields));
|
||||
}
|
||||
fputcsv($fp, $fields);
|
||||
}
|
||||
rewind($fp);
|
||||
$data = stream_get_contents($fp);
|
||||
fclose($fp);
|
||||
return $data;
|
||||
}
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
<?php
|
||||
/**
|
||||
* Mime Type: application/x-www-urlencoded
|
||||
* @author Nathan Good <me@nategood.com>
|
||||
*/
|
||||
|
||||
namespace Httpful\Handlers;
|
||||
|
||||
class FormHandler extends MimeHandlerAdapter
|
||||
{
|
||||
/**
|
||||
* @param string $body
|
||||
* @return mixed
|
||||
*/
|
||||
public function parse($body)
|
||||
{
|
||||
$parsed = array();
|
||||
parse_str($body, $parsed);
|
||||
return $parsed;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $payload
|
||||
* @return string
|
||||
*/
|
||||
public function serialize($payload)
|
||||
{
|
||||
return http_build_query($payload, null, '&');
|
||||
}
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
<?php
|
||||
/**
|
||||
* Mime Type: application/json
|
||||
* @author Nathan Good <me@nategood.com>
|
||||
*/
|
||||
|
||||
namespace Httpful\Handlers;
|
||||
|
||||
class JsonHandler extends MimeHandlerAdapter
|
||||
{
|
||||
private $decode_as_array = false;
|
||||
|
||||
public function init(array $args)
|
||||
{
|
||||
$this->decode_as_array = !!(array_key_exists('decode_as_array', $args) ? $args['decode_as_array'] : false);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $body
|
||||
* @return mixed
|
||||
*/
|
||||
public function parse($body)
|
||||
{
|
||||
$body = $this->stripBom($body);
|
||||
if (empty($body))
|
||||
return null;
|
||||
$parsed = json_decode($body, $this->decode_as_array);
|
||||
if (is_null($parsed) && 'null' !== strtolower($body))
|
||||
throw new \Exception("Unable to parse response as JSON");
|
||||
return $parsed;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $payload
|
||||
* @return string
|
||||
*/
|
||||
public function serialize($payload)
|
||||
{
|
||||
return json_encode($payload);
|
||||
}
|
||||
}
|
@ -0,0 +1,54 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Handlers are used to parse and serialize payloads for specific
|
||||
* mime types. You can register a custom handler via the register
|
||||
* method. You can also override a default parser in this way.
|
||||
*/
|
||||
|
||||
namespace Httpful\Handlers;
|
||||
|
||||
class MimeHandlerAdapter
|
||||
{
|
||||
public function __construct(array $args = array())
|
||||
{
|
||||
$this->init($args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initial setup of
|
||||
* @param array $args
|
||||
*/
|
||||
public function init(array $args)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $body
|
||||
* @return mixed
|
||||
*/
|
||||
public function parse($body)
|
||||
{
|
||||
return $body;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $payload
|
||||
* @return string
|
||||
*/
|
||||
function serialize($payload)
|
||||
{
|
||||
return (string) $payload;
|
||||
}
|
||||
|
||||
protected function stripBom($body)
|
||||
{
|
||||
if ( substr($body,0,3) === "\xef\xbb\xbf" ) // UTF-8
|
||||
$body = substr($body,3);
|
||||
else if ( substr($body,0,4) === "\xff\xfe\x00\x00" || substr($body,0,4) === "\x00\x00\xfe\xff" ) // UTF-32
|
||||
$body = substr($body,4);
|
||||
else if ( substr($body,0,2) === "\xff\xfe" || substr($body,0,2) === "\xfe\xff" ) // UTF-16
|
||||
$body = substr($body,2);
|
||||
return $body;
|
||||
}
|
||||
}
|
@ -0,0 +1,44 @@
|
||||
# Handlers
|
||||
|
||||
Handlers are simple classes that are used to parse response bodies and serialize request payloads. All Handlers must extend the `MimeHandlerAdapter` class and implement two methods: `serialize($payload)` and `parse($response)`. Let's build a very basic Handler to register for the `text/csv` mime type.
|
||||
|
||||
<?php
|
||||
|
||||
class SimpleCsvHandler extends \Httpful\Handlers\MimeHandlerAdapter
|
||||
{
|
||||
/**
|
||||
* Takes a response body, and turns it into
|
||||
* a two dimensional array.
|
||||
*
|
||||
* @param string $body
|
||||
* @return mixed
|
||||
*/
|
||||
public function parse($body)
|
||||
{
|
||||
return str_getcsv($body);
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes a two dimensional array and turns it
|
||||
* into a serialized string to include as the
|
||||
* body of a request
|
||||
*
|
||||
* @param mixed $payload
|
||||
* @return string
|
||||
*/
|
||||
public function serialize($payload)
|
||||
{
|
||||
$serialized = '';
|
||||
foreach ($payload as $line) {
|
||||
$serialized .= '"' . implode('","', $line) . '"' . "\n";
|
||||
}
|
||||
return $serialized;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Finally, you must register this handler for a particular mime type.
|
||||
|
||||
Httpful::register('text/csv', new SimpleCsvHandler());
|
||||
|
||||
After this registering the handler in your source code, by default, any responses with a mime type of text/csv should be parsed by this handler.
|
@ -0,0 +1,15 @@
|
||||
<?php
|
||||
/**
|
||||
* Mime Type: text/html
|
||||
* Mime Type: application/html+xml
|
||||
*
|
||||
* @author Nathan Good <me@nategood.com>
|
||||
*/
|
||||
|
||||
namespace Httpful\Handlers;
|
||||
|
||||
class XHtmlHandler extends MimeHandlerAdapter
|
||||
{
|
||||
// @todo add html specific parsing
|
||||
// see DomDocument::load http://docs.php.net/manual/en/domdocument.loadhtml.php
|
||||
}
|
@ -0,0 +1,152 @@
|
||||
<?php
|
||||
/**
|
||||
* Mime Type: application/xml
|
||||
*
|
||||
* @author Zack Douglas <zack@zackerydouglas.info>
|
||||
* @author Nathan Good <me@nategood.com>
|
||||
*/
|
||||
|
||||
namespace Httpful\Handlers;
|
||||
|
||||
class XmlHandler extends MimeHandlerAdapter
|
||||
{
|
||||
/**
|
||||
* @var string $namespace xml namespace to use with simple_load_string
|
||||
*/
|
||||
private $namespace;
|
||||
|
||||
/**
|
||||
* @var int $libxml_opts see http://www.php.net/manual/en/libxml.constants.php
|
||||
*/
|
||||
private $libxml_opts;
|
||||
|
||||
/**
|
||||
* @param array $conf sets configuration options
|
||||
*/
|
||||
public function __construct(array $conf = array())
|
||||
{
|
||||
$this->namespace = isset($conf['namespace']) ? $conf['namespace'] : '';
|
||||
$this->libxml_opts = isset($conf['libxml_opts']) ? $conf['libxml_opts'] : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $body
|
||||
* @return mixed
|
||||
* @throws Exception if unable to parse
|
||||
*/
|
||||
public function parse($body)
|
||||
{
|
||||
$body = $this->stripBom($body);
|
||||
if (empty($body))
|
||||
return null;
|
||||
$parsed = simplexml_load_string($body, null, $this->libxml_opts, $this->namespace);
|
||||
if ($parsed === false)
|
||||
throw new \Exception("Unable to parse response as XML");
|
||||
return $parsed;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $payload
|
||||
* @return string
|
||||
* @throws Exception if unable to serialize
|
||||
*/
|
||||
public function serialize($payload)
|
||||
{
|
||||
list($_, $dom) = $this->_future_serializeAsXml($payload);
|
||||
return $dom->saveXml();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $payload
|
||||
* @return string
|
||||
* @author Ted Zellers
|
||||
*/
|
||||
public function serialize_clean($payload)
|
||||
{
|
||||
$xml = new \XMLWriter;
|
||||
$xml->openMemory();
|
||||
$xml->startDocument('1.0','ISO-8859-1');
|
||||
$this->serialize_node($xml, $payload);
|
||||
return $xml->outputMemory(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param XMLWriter $xmlw
|
||||
* @param mixed $node to serialize
|
||||
* @author Ted Zellers
|
||||
*/
|
||||
public function serialize_node(&$xmlw, $node){
|
||||
if (!is_array($node)){
|
||||
$xmlw->text($node);
|
||||
} else {
|
||||
foreach ($node as $k => $v){
|
||||
$xmlw->startElement($k);
|
||||
$this->serialize_node($xmlw, $v);
|
||||
$xmlw->endElement();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @author Zack Douglas <zack@zackerydouglas.info>
|
||||
*/
|
||||
private function _future_serializeAsXml($value, $node = null, $dom = null)
|
||||
{
|
||||
if (!$dom) {
|
||||
$dom = new \DOMDocument;
|
||||
}
|
||||
if (!$node) {
|
||||
if (!is_object($value)) {
|
||||
$node = $dom->createElement('response');
|
||||
$dom->appendChild($node);
|
||||
} else {
|
||||
$node = $dom;
|
||||
}
|
||||
}
|
||||
if (is_object($value)) {
|
||||
$objNode = $dom->createElement(get_class($value));
|
||||
$node->appendChild($objNode);
|
||||
$this->_future_serializeObjectAsXml($value, $objNode, $dom);
|
||||
} else if (is_array($value)) {
|
||||
$arrNode = $dom->createElement('array');
|
||||
$node->appendChild($arrNode);
|
||||
$this->_future_serializeArrayAsXml($value, $arrNode, $dom);
|
||||
} else if (is_bool($value)) {
|
||||
$node->appendChild($dom->createTextNode($value?'TRUE':'FALSE'));
|
||||
} else {
|
||||
$node->appendChild($dom->createTextNode($value));
|
||||
}
|
||||
return array($node, $dom);
|
||||
}
|
||||
/**
|
||||
* @author Zack Douglas <zack@zackerydouglas.info>
|
||||
*/
|
||||
private function _future_serializeArrayAsXml($value, &$parent, &$dom)
|
||||
{
|
||||
foreach ($value as $k => &$v) {
|
||||
$n = $k;
|
||||
if (is_numeric($k)) {
|
||||
$n = "child-{$n}";
|
||||
}
|
||||
$el = $dom->createElement($n);
|
||||
$parent->appendChild($el);
|
||||
$this->_future_serializeAsXml($v, $el, $dom);
|
||||
}
|
||||
return array($parent, $dom);
|
||||
}
|
||||
/**
|
||||
* @author Zack Douglas <zack@zackerydouglas.info>
|
||||
*/
|
||||
private function _future_serializeObjectAsXml($value, &$parent, &$dom)
|
||||
{
|
||||
$refl = new \ReflectionObject($value);
|
||||
foreach ($refl->getProperties() as $pr) {
|
||||
if (!$pr->isPrivate()) {
|
||||
$el = $dom->createElement($pr->getName());
|
||||
$parent->appendChild($el);
|
||||
$this->_future_serializeAsXml($pr->getValue($value), $el, $dom);
|
||||
}
|
||||
}
|
||||
return array($parent, $dom);
|
||||
}
|
||||
}
|
@ -0,0 +1,86 @@
|
||||
<?php
|
||||
|
||||
namespace Httpful;
|
||||
|
||||
/**
|
||||
* @author Nate Good <me@nategood.com>
|
||||
*/
|
||||
class Http
|
||||
{
|
||||
const HEAD = 'HEAD';
|
||||
const GET = 'GET';
|
||||
const POST = 'POST';
|
||||
const PUT = 'PUT';
|
||||
const DELETE = 'DELETE';
|
||||
const PATCH = 'PATCH';
|
||||
const OPTIONS = 'OPTIONS';
|
||||
const TRACE = 'TRACE';
|
||||
|
||||
/**
|
||||
* @return array of HTTP method strings
|
||||
*/
|
||||
public static function safeMethods()
|
||||
{
|
||||
return array(self::HEAD, self::GET, self::OPTIONS, self::TRACE);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
* @param string HTTP method
|
||||
*/
|
||||
public static function isSafeMethod($method)
|
||||
{
|
||||
return in_array($method, self::safeMethods());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
* @param string HTTP method
|
||||
*/
|
||||
public static function isUnsafeMethod($method)
|
||||
{
|
||||
return !in_array($method, self::safeMethods());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array list of (always) idempotent HTTP methods
|
||||
*/
|
||||
public static function idempotentMethods()
|
||||
{
|
||||
// Though it is possible to be idempotent, POST
|
||||
// is not guarunteed to be, and more often than
|
||||
// not, it is not.
|
||||
return array(self::HEAD, self::GET, self::PUT, self::DELETE, self::OPTIONS, self::TRACE, self::PATCH);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
* @param string HTTP method
|
||||
*/
|
||||
public static function isIdempotent($method)
|
||||
{
|
||||
return in_array($method, self::safeidempotentMethodsMethods());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
* @param string HTTP method
|
||||
*/
|
||||
public static function isNotIdempotent($method)
|
||||
{
|
||||
return !in_array($method, self::idempotentMethods());
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Technically anything *can* have a body,
|
||||
* they just don't have semantic meaning. So say's Roy
|
||||
* http://tech.groups.yahoo.com/group/rest-discuss/message/9962
|
||||
*
|
||||
* @return array of HTTP method strings
|
||||
*/
|
||||
public static function canHaveBody()
|
||||
{
|
||||
return array(self::POST, self::PUT, self::PATCH, self::OPTIONS);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
namespace Httpful;
|
||||
|
||||
class Httpful {
|
||||
const VERSION = '0.2.19';
|
||||
|
||||
private static $mimeRegistrar = array();
|
||||
private static $default = null;
|
||||
|
||||
/**
|
||||
* @param string $mime_type
|
||||
* @param MimeHandlerAdapter $handler
|
||||
*/
|
||||
public static function register($mimeType, \Httpful\Handlers\MimeHandlerAdapter $handler)
|
||||
{
|
||||
self::$mimeRegistrar[$mimeType] = $handler;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $mime_type defaults to MimeHandlerAdapter
|
||||
* @return MimeHandlerAdapter
|
||||
*/
|
||||
public static function get($mimeType = null)
|
||||
{
|
||||
if (isset(self::$mimeRegistrar[$mimeType])) {
|
||||
return self::$mimeRegistrar[$mimeType];
|
||||
}
|
||||
|
||||
if (empty(self::$default)) {
|
||||
self::$default = new \Httpful\Handlers\MimeHandlerAdapter();
|
||||
}
|
||||
|
||||
return self::$default;
|
||||
}
|
||||
|
||||
/**
|
||||
* Does this particular Mime Type have a parser registered
|
||||
* for it?
|
||||
* @return bool
|
||||
*/
|
||||
public static function hasParserRegistered($mimeType)
|
||||
{
|
||||
return isset(self::$mimeRegistrar[$mimeType]);
|
||||
}
|
||||
}
|
@ -0,0 +1,60 @@
|
||||
<?php
|
||||
|
||||
namespace Httpful;
|
||||
|
||||
/**
|
||||
* Class to organize the Mime stuff a bit more
|
||||
* @author Nate Good <me@nategood.com>
|
||||
*/
|
||||
class Mime
|
||||
{
|
||||
const JSON = 'application/json';
|
||||
const XML = 'application/xml';
|
||||
const XHTML = 'application/html+xml';
|
||||
const FORM = 'application/x-www-form-urlencoded';
|
||||
const UPLOAD = 'multipart/form-data';
|
||||
const PLAIN = 'text/plain';
|
||||
const JS = 'text/javascript';
|
||||
const HTML = 'text/html';
|
||||
const YAML = 'application/x-yaml';
|
||||
const CSV = 'text/csv';
|
||||
|
||||
/**
|
||||
* Map short name for a mime type
|
||||
* to a full proper mime type
|
||||
*/
|
||||
public static $mimes = array(
|
||||
'json' => self::JSON,
|
||||
'xml' => self::XML,
|
||||
'form' => self::FORM,
|
||||
'plain' => self::PLAIN,
|
||||
'text' => self::PLAIN,
|
||||
'upload' => self::UPLOAD,
|
||||
'html' => self::HTML,
|
||||
'xhtml' => self::XHTML,
|
||||
'js' => self::JS,
|
||||
'javascript'=> self::JS,
|
||||
'yaml' => self::YAML,
|
||||
'csv' => self::CSV,
|
||||
);
|
||||
|
||||
/**
|
||||
* Get the full Mime Type name from a "short name".
|
||||
* Returns the short if no mapping was found.
|
||||
* @return string full mime type (e.g. application/json)
|
||||
* @param string common name for mime type (e.g. json)
|
||||
*/
|
||||
public static function getFullMime($short_name)
|
||||
{
|
||||
return array_key_exists($short_name, self::$mimes) ? self::$mimes[$short_name] : $short_name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
* @param string $short_name
|
||||
*/
|
||||
public static function supportsMimeType($short_name)
|
||||
{
|
||||
return array_key_exists($short_name, self::$mimes);
|
||||
}
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
<?php
|
||||
namespace Httpful;
|
||||
|
||||
if (!defined('CURLPROXY_SOCKS4')) {
|
||||
define('CURLPROXY_SOCKS4', 4);
|
||||
}
|
||||
|
||||
/**
|
||||
* Class to organize the Proxy stuff a bit more
|
||||
*/
|
||||
class Proxy
|
||||
{
|
||||
const HTTP = CURLPROXY_HTTP;
|
||||
const SOCKS4 = CURLPROXY_SOCKS4;
|
||||
const SOCKS5 = CURLPROXY_SOCKS5;
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,194 @@
|
||||
<?php
|
||||
|
||||
namespace Httpful;
|
||||
|
||||
/**
|
||||
* Models an HTTP response
|
||||
*
|
||||
* @author Nate Good <me@nategood.com>
|
||||
*/
|
||||
class Response
|
||||
{
|
||||
|
||||
public $body,
|
||||
$raw_body,
|
||||
$headers,
|
||||
$raw_headers,
|
||||
$request,
|
||||
$code = 0,
|
||||
$content_type,
|
||||
$parent_type,
|
||||
$charset,
|
||||
$meta_data,
|
||||
$is_mime_vendor_specific = false,
|
||||
$is_mime_personal = false;
|
||||
|
||||
private $parsers;
|
||||
/**
|
||||
* @param string $body
|
||||
* @param string $headers
|
||||
* @param Request $request
|
||||
* @param array $meta_data
|
||||
*/
|
||||
public function __construct($body, $headers, Request $request, array $meta_data = array())
|
||||
{
|
||||
$this->request = $request;
|
||||
$this->raw_headers = $headers;
|
||||
$this->raw_body = $body;
|
||||
$this->meta_data = $meta_data;
|
||||
|
||||
$this->code = $this->_parseCode($headers);
|
||||
$this->headers = Response\Headers::fromString($headers);
|
||||
|
||||
$this->_interpretHeaders();
|
||||
|
||||
$this->body = $this->_parse($body);
|
||||
}
|
||||
|
||||
/**
|
||||
* Status Code Definitions
|
||||
*
|
||||
* Informational 1xx
|
||||
* Successful 2xx
|
||||
* Redirection 3xx
|
||||
* Client Error 4xx
|
||||
* Server Error 5xx
|
||||
*
|
||||
* http://pretty-rfc.herokuapp.com/RFC2616#status.codes
|
||||
*
|
||||
* @return bool Did we receive a 4xx or 5xx?
|
||||
*/
|
||||
public function hasErrors()
|
||||
{
|
||||
return $this->code >= 400;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return return bool
|
||||
*/
|
||||
public function hasBody()
|
||||
{
|
||||
return !empty($this->body);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the response into a clean data structure
|
||||
* (most often an associative array) based on the expected
|
||||
* Mime type.
|
||||
* @return array|string|object the response parse accordingly
|
||||
* @param string Http response body
|
||||
*/
|
||||
public function _parse($body)
|
||||
{
|
||||
// If the user decided to forgo the automatic
|
||||
// smart parsing, short circuit.
|
||||
if (!$this->request->auto_parse) {
|
||||
return $body;
|
||||
}
|
||||
|
||||
// If provided, use custom parsing callback
|
||||
if (isset($this->request->parse_callback)) {
|
||||
return call_user_func($this->request->parse_callback, $body);
|
||||
}
|
||||
|
||||
// Decide how to parse the body of the response in the following order
|
||||
// 1. If provided, use the mime type specifically set as part of the `Request`
|
||||
// 2. If a MimeHandler is registered for the content type, use it
|
||||
// 3. If provided, use the "parent type" of the mime type from the response
|
||||
// 4. Default to the content-type provided in the response
|
||||
$parse_with = $this->request->expected_type;
|
||||
if (empty($this->request->expected_type)) {
|
||||
$parse_with = Httpful::hasParserRegistered($this->content_type)
|
||||
? $this->content_type
|
||||
: $this->parent_type;
|
||||
}
|
||||
|
||||
return Httpful::get($parse_with)->parse($body);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse text headers from response into
|
||||
* array of key value pairs
|
||||
* @return array parse headers
|
||||
* @param string $headers raw headers
|
||||
*/
|
||||
public function _parseHeaders($headers)
|
||||
{
|
||||
$headers = preg_split("/(\r|\n)+/", $headers, -1, \PREG_SPLIT_NO_EMPTY);
|
||||
$parse_headers = array();
|
||||
for ($i = 1; $i < count($headers); $i++) {
|
||||
list($key, $raw_value) = explode(':', $headers[$i], 2);
|
||||
$key = trim($key);
|
||||
$value = trim($raw_value);
|
||||
if (array_key_exists($key, $parse_headers)) {
|
||||
// See HTTP RFC Sec 4.2 Paragraph 5
|
||||
// http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2
|
||||
// If a header appears more than once, it must also be able to
|
||||
// be represented as a single header with a comma-separated
|
||||
// list of values. We transform accordingly.
|
||||
$parse_headers[$key] .= ',' . $value;
|
||||
} else {
|
||||
$parse_headers[$key] = $value;
|
||||
}
|
||||
}
|
||||
return $parse_headers;
|
||||
}
|
||||
|
||||
public function _parseCode($headers)
|
||||
{
|
||||
$end = strpos($headers, "\r\n");
|
||||
if ($end === false) $end = strlen($headers);
|
||||
$parts = explode(' ', substr($headers, 0, $end));
|
||||
if (count($parts) < 2 || !is_numeric($parts[1])) {
|
||||
throw new \Exception("Unable to parse response code from HTTP response due to malformed response");
|
||||
}
|
||||
return intval($parts[1]);
|
||||
}
|
||||
|
||||
/**
|
||||
* After we've parse the headers, let's clean things
|
||||
* up a bit and treat some headers specially
|
||||
*/
|
||||
public function _interpretHeaders()
|
||||
{
|
||||
// Parse the Content-Type and charset
|
||||
$content_type = isset($this->headers['Content-Type']) ? $this->headers['Content-Type'] : '';
|
||||
$content_type = explode(';', $content_type);
|
||||
|
||||
$this->content_type = $content_type[0];
|
||||
if (count($content_type) == 2 && strpos($content_type[1], '=') !== false) {
|
||||
list($nill, $this->charset) = explode('=', $content_type[1]);
|
||||
}
|
||||
|
||||
// RFC 2616 states "text/*" Content-Types should have a default
|
||||
// charset of ISO-8859-1. "application/*" and other Content-Types
|
||||
// are assumed to have UTF-8 unless otherwise specified.
|
||||
// http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.7.1
|
||||
// http://www.w3.org/International/O-HTTP-charset.en.php
|
||||
if (!isset($this->charset)) {
|
||||
$this->charset = substr($this->content_type, 5) === 'text/' ? 'iso-8859-1' : 'utf-8';
|
||||
}
|
||||
|
||||
// Is vendor type? Is personal type?
|
||||
if (strpos($this->content_type, '/') !== false) {
|
||||
list($type, $sub_type) = explode('/', $this->content_type);
|
||||
$this->is_mime_vendor_specific = substr($sub_type, 0, 4) === 'vnd.';
|
||||
$this->is_mime_personal = substr($sub_type, 0, 4) === 'prs.';
|
||||
}
|
||||
|
||||
// Parent type (e.g. xml for application/vnd.github.message+xml)
|
||||
$this->parent_type = $this->content_type;
|
||||
if (strpos($this->content_type, '+') !== false) {
|
||||
list($vendor, $this->parent_type) = explode('+', $this->content_type, 2);
|
||||
$this->parent_type = Mime::getFullMime($this->parent_type);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
return $this->raw_body;
|
||||
}
|
||||
}
|
@ -0,0 +1,58 @@
|
||||
<?php
|
||||
|
||||
namespace Httpful\Response;
|
||||
|
||||
final class Headers implements \ArrayAccess, \Countable {
|
||||
|
||||
private $headers;
|
||||
|
||||
private function __construct($headers)
|
||||
{
|
||||
$this->headers = $headers;
|
||||
}
|
||||
|
||||
public static function fromString($string)
|
||||
{
|
||||
$lines = preg_split("/(\r|\n)+/", $string, -1, PREG_SPLIT_NO_EMPTY);
|
||||
array_shift($lines); // HTTP HEADER
|
||||
$headers = array();
|
||||
foreach ($lines as $line) {
|
||||
list($name, $value) = explode(':', $line, 2);
|
||||
$headers[strtolower(trim($name))] = trim($value);
|
||||
}
|
||||
return new self($headers);
|
||||
}
|
||||
|
||||
public function offsetExists($offset)
|
||||
{
|
||||
return isset($this->headers[strtolower($offset)]);
|
||||
}
|
||||
|
||||
public function offsetGet($offset)
|
||||
{
|
||||
if (isset($this->headers[$name = strtolower($offset)])) {
|
||||
return $this->headers[$name];
|
||||
}
|
||||
}
|
||||
|
||||
public function offsetSet($offset, $value)
|
||||
{
|
||||
throw new \Exception("Headers are read-only.");
|
||||
}
|
||||
|
||||
public function offsetUnset($offset)
|
||||
{
|
||||
throw new \Exception("Headers are read-only.");
|
||||
}
|
||||
|
||||
public function count()
|
||||
{
|
||||
return count($this->headers);
|
||||
}
|
||||
|
||||
public function toArray()
|
||||
{
|
||||
return $this->headers;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,604 @@
|
||||
<?php
|
||||
/**
|
||||
* Port over the original tests into a more traditional PHPUnit
|
||||
* format. Still need to hook into a lightweight HTTP server to
|
||||
* better test some things (e.g. obscure cURL settings). I've moved
|
||||
* the old tests and node.js server to the tests/.legacy directory.
|
||||
*
|
||||
* @author Nate Good <me@nategood.com>
|
||||
*/
|
||||
namespace Httpful\Test;
|
||||
|
||||
require(dirname(dirname(dirname(__FILE__))) . '/bootstrap.php');
|
||||
\Httpful\Bootstrap::init();
|
||||
|
||||
use Httpful\Httpful;
|
||||
use Httpful\Request;
|
||||
use Httpful\Mime;
|
||||
use Httpful\Http;
|
||||
use Httpful\Response;
|
||||
use Httpful\Handlers\JsonHandler;
|
||||
|
||||
define('TEST_SERVER', WEB_SERVER_HOST . ':' . WEB_SERVER_PORT);
|
||||
|
||||
class HttpfulTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
const TEST_SERVER = TEST_SERVER;
|
||||
const TEST_URL = 'http://127.0.0.1:8008';
|
||||
const TEST_URL_400 = 'http://127.0.0.1:8008/400';
|
||||
|
||||
const SAMPLE_JSON_HEADER =
|
||||
"HTTP/1.1 200 OK
|
||||
Content-Type: application/json
|
||||
Connection: keep-alive
|
||||
Transfer-Encoding: chunked\r\n";
|
||||
const SAMPLE_JSON_RESPONSE = '{"key":"value","object":{"key":"value"},"array":[1,2,3,4]}';
|
||||
const SAMPLE_CSV_HEADER =
|
||||
"HTTP/1.1 200 OK
|
||||
Content-Type: text/csv
|
||||
Connection: keep-alive
|
||||
Transfer-Encoding: chunked\r\n";
|
||||
const SAMPLE_CSV_RESPONSE =
|
||||
"Key1,Key2
|
||||
Value1,Value2
|
||||
\"40.0\",\"Forty\"";
|
||||
const SAMPLE_XML_RESPONSE = '<stdClass><arrayProp><array><k1><myClass><intProp>2</intProp></myClass></k1></array></arrayProp><stringProp>a string</stringProp><boolProp>TRUE</boolProp></stdClass>';
|
||||
const SAMPLE_XML_HEADER =
|
||||
"HTTP/1.1 200 OK
|
||||
Content-Type: application/xml
|
||||
Connection: keep-alive
|
||||
Transfer-Encoding: chunked\r\n";
|
||||
const SAMPLE_VENDOR_HEADER =
|
||||
"HTTP/1.1 200 OK
|
||||
Content-Type: application/vnd.nategood.message+xml
|
||||
Connection: keep-alive
|
||||
Transfer-Encoding: chunked\r\n";
|
||||
const SAMPLE_VENDOR_TYPE = "application/vnd.nategood.message+xml";
|
||||
const SAMPLE_MULTI_HEADER =
|
||||
"HTTP/1.1 200 OK
|
||||
Content-Type: application/json
|
||||
Connection: keep-alive
|
||||
Transfer-Encoding: chunked
|
||||
X-My-Header:Value1
|
||||
X-My-Header:Value2\r\n";
|
||||
|
||||
function testInit()
|
||||
{
|
||||
$r = Request::init();
|
||||
// Did we get a 'Request' object?
|
||||
$this->assertEquals('Httpful\Request', get_class($r));
|
||||
}
|
||||
|
||||
function testDetermineLength()
|
||||
{
|
||||
$r = Request::init();
|
||||
$this->assertEquals(1, $r->_determineLength('A'));
|
||||
$this->assertEquals(2, $r->_determineLength('À'));
|
||||
$this->assertEquals(2, $r->_determineLength('Ab'));
|
||||
$this->assertEquals(3, $r->_determineLength('Àb'));
|
||||
$this->assertEquals(6, $r->_determineLength('世界'));
|
||||
}
|
||||
|
||||
function testMethods()
|
||||
{
|
||||
$valid_methods = array('get', 'post', 'delete', 'put', 'options', 'head');
|
||||
$url = 'http://example.com/';
|
||||
foreach ($valid_methods as $method) {
|
||||
$r = call_user_func(array('Httpful\Request', $method), $url);
|
||||
$this->assertEquals('Httpful\Request', get_class($r));
|
||||
$this->assertEquals(strtoupper($method), $r->method);
|
||||
}
|
||||
}
|
||||
|
||||
function testDefaults()
|
||||
{
|
||||
// Our current defaults are as follows
|
||||
$r = Request::init();
|
||||
$this->assertEquals(Http::GET, $r->method);
|
||||
$this->assertFalse($r->strict_ssl);
|
||||
}
|
||||
|
||||
function testShortMime()
|
||||
{
|
||||
// Valid short ones
|
||||
$this->assertEquals(Mime::JSON, Mime::getFullMime('json'));
|
||||
$this->assertEquals(Mime::XML, Mime::getFullMime('xml'));
|
||||
$this->assertEquals(Mime::HTML, Mime::getFullMime('html'));
|
||||
$this->assertEquals(Mime::CSV, Mime::getFullMime('csv'));
|
||||
$this->assertEquals(Mime::UPLOAD, Mime::getFullMime('upload'));
|
||||
|
||||
// Valid long ones
|
||||
$this->assertEquals(Mime::JSON, Mime::getFullMime(Mime::JSON));
|
||||
$this->assertEquals(Mime::XML, Mime::getFullMime(Mime::XML));
|
||||
$this->assertEquals(Mime::HTML, Mime::getFullMime(Mime::HTML));
|
||||
$this->assertEquals(Mime::CSV, Mime::getFullMime(Mime::CSV));
|
||||
$this->assertEquals(Mime::UPLOAD, Mime::getFullMime(Mime::UPLOAD));
|
||||
|
||||
// No false positives
|
||||
$this->assertNotEquals(Mime::XML, Mime::getFullMime(Mime::HTML));
|
||||
$this->assertNotEquals(Mime::JSON, Mime::getFullMime(Mime::XML));
|
||||
$this->assertNotEquals(Mime::HTML, Mime::getFullMime(Mime::JSON));
|
||||
$this->assertNotEquals(Mime::XML, Mime::getFullMime(Mime::CSV));
|
||||
}
|
||||
|
||||
function testSettingStrictSsl()
|
||||
{
|
||||
$r = Request::init()
|
||||
->withStrictSsl();
|
||||
|
||||
$this->assertTrue($r->strict_ssl);
|
||||
|
||||
$r = Request::init()
|
||||
->withoutStrictSsl();
|
||||
|
||||
$this->assertFalse($r->strict_ssl);
|
||||
}
|
||||
|
||||
function testSendsAndExpectsType()
|
||||
{
|
||||
$r = Request::init()
|
||||
->sendsAndExpectsType(Mime::JSON);
|
||||
$this->assertEquals(Mime::JSON, $r->expected_type);
|
||||
$this->assertEquals(Mime::JSON, $r->content_type);
|
||||
|
||||
$r = Request::init()
|
||||
->sendsAndExpectsType('html');
|
||||
$this->assertEquals(Mime::HTML, $r->expected_type);
|
||||
$this->assertEquals(Mime::HTML, $r->content_type);
|
||||
|
||||
$r = Request::init()
|
||||
->sendsAndExpectsType('form');
|
||||
$this->assertEquals(Mime::FORM, $r->expected_type);
|
||||
$this->assertEquals(Mime::FORM, $r->content_type);
|
||||
|
||||
$r = Request::init()
|
||||
->sendsAndExpectsType('application/x-www-form-urlencoded');
|
||||
$this->assertEquals(Mime::FORM, $r->expected_type);
|
||||
$this->assertEquals(Mime::FORM, $r->content_type);
|
||||
|
||||
$r = Request::init()
|
||||
->sendsAndExpectsType(Mime::CSV);
|
||||
$this->assertEquals(Mime::CSV, $r->expected_type);
|
||||
$this->assertEquals(Mime::CSV, $r->content_type);
|
||||
}
|
||||
|
||||
function testIni()
|
||||
{
|
||||
// Test setting defaults/templates
|
||||
|
||||
// Create the template
|
||||
$template = Request::init()
|
||||
->method(Http::POST)
|
||||
->withStrictSsl()
|
||||
->expectsType(Mime::HTML)
|
||||
->sendsType(Mime::FORM);
|
||||
|
||||
Request::ini($template);
|
||||
|
||||
$r = Request::init();
|
||||
|
||||
$this->assertTrue($r->strict_ssl);
|
||||
$this->assertEquals(Http::POST, $r->method);
|
||||
$this->assertEquals(Mime::HTML, $r->expected_type);
|
||||
$this->assertEquals(Mime::FORM, $r->content_type);
|
||||
|
||||
// Test the default accessor as well
|
||||
$this->assertTrue(Request::d('strict_ssl'));
|
||||
$this->assertEquals(Http::POST, Request::d('method'));
|
||||
$this->assertEquals(Mime::HTML, Request::d('expected_type'));
|
||||
$this->assertEquals(Mime::FORM, Request::d('content_type'));
|
||||
|
||||
Request::resetIni();
|
||||
}
|
||||
|
||||
function testAccept()
|
||||
{
|
||||
$r = Request::get('http://example.com/')
|
||||
->expectsType(Mime::JSON);
|
||||
|
||||
$this->assertEquals(Mime::JSON, $r->expected_type);
|
||||
$r->_curlPrep();
|
||||
$this->assertContains('application/json', $r->raw_headers);
|
||||
}
|
||||
|
||||
function testCustomAccept()
|
||||
{
|
||||
$accept = 'application/api-1.0+json';
|
||||
$r = Request::get('http://example.com/')
|
||||
->addHeader('Accept', $accept);
|
||||
|
||||
$r->_curlPrep();
|
||||
$this->assertContains($accept, $r->raw_headers);
|
||||
$this->assertEquals($accept, $r->headers['Accept']);
|
||||
}
|
||||
|
||||
function testUserAgent()
|
||||
{
|
||||
$r = Request::get('http://example.com/')
|
||||
->withUserAgent('ACME/1.2.3');
|
||||
|
||||
$this->assertArrayHasKey('User-Agent', $r->headers);
|
||||
$r->_curlPrep();
|
||||
$this->assertContains('User-Agent: ACME/1.2.3', $r->raw_headers);
|
||||
$this->assertNotContains('User-Agent: HttpFul/1.0', $r->raw_headers);
|
||||
|
||||
$r = Request::get('http://example.com/')
|
||||
->withUserAgent('');
|
||||
|
||||
$this->assertArrayHasKey('User-Agent', $r->headers);
|
||||
$r->_curlPrep();
|
||||
$this->assertContains('User-Agent:', $r->raw_headers);
|
||||
$this->assertNotContains('User-Agent: HttpFul/1.0', $r->raw_headers);
|
||||
}
|
||||
|
||||
function testAuthSetup()
|
||||
{
|
||||
$username = 'nathan';
|
||||
$password = 'opensesame';
|
||||
|
||||
$r = Request::get('http://example.com/')
|
||||
->authenticateWith($username, $password);
|
||||
|
||||
$this->assertEquals($username, $r->username);
|
||||
$this->assertEquals($password, $r->password);
|
||||
$this->assertTrue($r->hasBasicAuth());
|
||||
}
|
||||
|
||||
function testDigestAuthSetup()
|
||||
{
|
||||
$username = 'nathan';
|
||||
$password = 'opensesame';
|
||||
|
||||
$r = Request::get('http://example.com/')
|
||||
->authenticateWithDigest($username, $password);
|
||||
|
||||
$this->assertEquals($username, $r->username);
|
||||
$this->assertEquals($password, $r->password);
|
||||
$this->assertTrue($r->hasDigestAuth());
|
||||
}
|
||||
|
||||
function testJsonResponseParse()
|
||||
{
|
||||
$req = Request::init()->sendsAndExpects(Mime::JSON);
|
||||
$response = new Response(self::SAMPLE_JSON_RESPONSE, self::SAMPLE_JSON_HEADER, $req);
|
||||
|
||||
$this->assertEquals("value", $response->body->key);
|
||||
$this->assertEquals("value", $response->body->object->key);
|
||||
$this->assertInternalType('array', $response->body->array);
|
||||
$this->assertEquals(1, $response->body->array[0]);
|
||||
}
|
||||
|
||||
function testXMLResponseParse()
|
||||
{
|
||||
$req = Request::init()->sendsAndExpects(Mime::XML);
|
||||
$response = new Response(self::SAMPLE_XML_RESPONSE, self::SAMPLE_XML_HEADER, $req);
|
||||
$sxe = $response->body;
|
||||
$this->assertEquals("object", gettype($sxe));
|
||||
$this->assertEquals("SimpleXMLElement", get_class($sxe));
|
||||
$bools = $sxe->xpath('/stdClass/boolProp');
|
||||
list( , $bool ) = each($bools);
|
||||
$this->assertEquals("TRUE", (string) $bool);
|
||||
$ints = $sxe->xpath('/stdClass/arrayProp/array/k1/myClass/intProp');
|
||||
list( , $int ) = each($ints);
|
||||
$this->assertEquals("2", (string) $int);
|
||||
$strings = $sxe->xpath('/stdClass/stringProp');
|
||||
list( , $string ) = each($strings);
|
||||
$this->assertEquals("a string", (string) $string);
|
||||
}
|
||||
|
||||
function testCsvResponseParse()
|
||||
{
|
||||
$req = Request::init()->sendsAndExpects(Mime::CSV);
|
||||
$response = new Response(self::SAMPLE_CSV_RESPONSE, self::SAMPLE_CSV_HEADER, $req);
|
||||
|
||||
$this->assertEquals("Key1", $response->body[0][0]);
|
||||
$this->assertEquals("Value1", $response->body[1][0]);
|
||||
$this->assertInternalType('string', $response->body[2][0]);
|
||||
$this->assertEquals("40.0", $response->body[2][0]);
|
||||
}
|
||||
|
||||
function testParsingContentTypeCharset()
|
||||
{
|
||||
$req = Request::init()->sendsAndExpects(Mime::JSON);
|
||||
// $response = new Response(SAMPLE_JSON_RESPONSE, "", $req);
|
||||
// // Check default content type of iso-8859-1
|
||||
$response = new Response(self::SAMPLE_JSON_RESPONSE, "HTTP/1.1 200 OK
|
||||
Content-Type: text/plain; charset=utf-8\r\n", $req);
|
||||
$this->assertInstanceOf('Httpful\Response\Headers', $response->headers);
|
||||
$this->assertEquals($response->headers['Content-Type'], 'text/plain; charset=utf-8');
|
||||
$this->assertEquals($response->content_type, 'text/plain');
|
||||
$this->assertEquals($response->charset, 'utf-8');
|
||||
}
|
||||
|
||||
function testParsingContentTypeUpload()
|
||||
{
|
||||
$req = Request::init();
|
||||
|
||||
$req->sendsType(Mime::UPLOAD);
|
||||
// $response = new Response(SAMPLE_JSON_RESPONSE, "", $req);
|
||||
// // Check default content type of iso-8859-1
|
||||
$this->assertEquals($req->content_type, 'multipart/form-data');
|
||||
}
|
||||
|
||||
function testAttach() {
|
||||
$req = Request::init();
|
||||
$testsPath = realpath(dirname(__FILE__) . DIRECTORY_SEPARATOR . '..');
|
||||
$filename = $testsPath . DIRECTORY_SEPARATOR . 'test_image.jpg';
|
||||
$req->attach(array('index' => $filename));
|
||||
$payload = $req->payload['index'];
|
||||
// PHP 5.5 + will take advantage of CURLFile while previous
|
||||
// versions just use the string syntax
|
||||
if (is_string($payload)) {
|
||||
$this->assertEquals($payload, '@' . $filename . ';type=image/jpeg');
|
||||
} else {
|
||||
$this->assertInstanceOf('CURLFile', $payload);
|
||||
}
|
||||
|
||||
$this->assertEquals($req->content_type, Mime::UPLOAD);
|
||||
$this->assertEquals($req->serialize_payload_method, Request::SERIALIZE_PAYLOAD_NEVER);
|
||||
}
|
||||
|
||||
function testIsUpload() {
|
||||
$req = Request::init();
|
||||
|
||||
$req->sendsType(Mime::UPLOAD);
|
||||
|
||||
$this->assertTrue($req->isUpload());
|
||||
}
|
||||
|
||||
function testEmptyResponseParse()
|
||||
{
|
||||
$req = Request::init()->sendsAndExpects(Mime::JSON);
|
||||
$response = new Response("", self::SAMPLE_JSON_HEADER, $req);
|
||||
$this->assertEquals(null, $response->body);
|
||||
|
||||
$reqXml = Request::init()->sendsAndExpects(Mime::XML);
|
||||
$responseXml = new Response("", self::SAMPLE_XML_HEADER, $reqXml);
|
||||
$this->assertEquals(null, $responseXml->body);
|
||||
}
|
||||
|
||||
function testNoAutoParse()
|
||||
{
|
||||
$req = Request::init()->sendsAndExpects(Mime::JSON)->withoutAutoParsing();
|
||||
$response = new Response(self::SAMPLE_JSON_RESPONSE, self::SAMPLE_JSON_HEADER, $req);
|
||||
$this->assertInternalType('string', $response->body);
|
||||
$req = Request::init()->sendsAndExpects(Mime::JSON)->withAutoParsing();
|
||||
$response = new Response(self::SAMPLE_JSON_RESPONSE, self::SAMPLE_JSON_HEADER, $req);
|
||||
$this->assertInternalType('object', $response->body);
|
||||
}
|
||||
|
||||
function testParseHeaders()
|
||||
{
|
||||
$req = Request::init()->sendsAndExpects(Mime::JSON);
|
||||
$response = new Response(self::SAMPLE_JSON_RESPONSE, self::SAMPLE_JSON_HEADER, $req);
|
||||
$this->assertEquals('application/json', $response->headers['Content-Type']);
|
||||
}
|
||||
|
||||
function testRawHeaders()
|
||||
{
|
||||
$req = Request::init()->sendsAndExpects(Mime::JSON);
|
||||
$response = new Response(self::SAMPLE_JSON_RESPONSE, self::SAMPLE_JSON_HEADER, $req);
|
||||
$this->assertContains('Content-Type: application/json', $response->raw_headers);
|
||||
}
|
||||
|
||||
function testHasErrors()
|
||||
{
|
||||
$req = Request::init()->sendsAndExpects(Mime::JSON);
|
||||
$response = new Response('', "HTTP/1.1 100 Continue\r\n", $req);
|
||||
$this->assertFalse($response->hasErrors());
|
||||
$response = new Response('', "HTTP/1.1 200 OK\r\n", $req);
|
||||
$this->assertFalse($response->hasErrors());
|
||||
$response = new Response('', "HTTP/1.1 300 Multiple Choices\r\n", $req);
|
||||
$this->assertFalse($response->hasErrors());
|
||||
$response = new Response('', "HTTP/1.1 400 Bad Request\r\n", $req);
|
||||
$this->assertTrue($response->hasErrors());
|
||||
$response = new Response('', "HTTP/1.1 500 Internal Server Error\r\n", $req);
|
||||
$this->assertTrue($response->hasErrors());
|
||||
}
|
||||
|
||||
function testWhenError() {
|
||||
$caught = false;
|
||||
|
||||
try {
|
||||
Request::get('malformed:url')
|
||||
->whenError(function($error) use(&$caught) {
|
||||
$caught = true;
|
||||
})
|
||||
->timeoutIn(0.1)
|
||||
->send();
|
||||
} catch (\Httpful\Exception\ConnectionErrorException $e) {}
|
||||
|
||||
$this->assertTrue($caught);
|
||||
}
|
||||
|
||||
function testBeforeSend() {
|
||||
$invoked = false;
|
||||
$changed = false;
|
||||
$self = $this;
|
||||
|
||||
try {
|
||||
Request::get('malformed://url')
|
||||
->beforeSend(function($request) use(&$invoked,$self) {
|
||||
$self->assertEquals('malformed://url', $request->uri);
|
||||
$self->assertEquals('A payload', $request->serialized_payload);
|
||||
$request->uri('malformed2://url');
|
||||
$invoked = true;
|
||||
})
|
||||
->whenError(function($error) { /* Be silent */ })
|
||||
->body('A payload')
|
||||
->send();
|
||||
} catch (\Httpful\Exception\ConnectionErrorException $e) {
|
||||
$this->assertTrue(strpos($e->getMessage(), 'malformed2') !== false);
|
||||
$changed = true;
|
||||
}
|
||||
|
||||
$this->assertTrue($invoked);
|
||||
$this->assertTrue($changed);
|
||||
}
|
||||
|
||||
function test_parseCode()
|
||||
{
|
||||
$req = Request::init()->sendsAndExpects(Mime::JSON);
|
||||
$response = new Response(self::SAMPLE_JSON_RESPONSE, self::SAMPLE_JSON_HEADER, $req);
|
||||
$code = $response->_parseCode("HTTP/1.1 406 Not Acceptable\r\n");
|
||||
$this->assertEquals(406, $code);
|
||||
}
|
||||
|
||||
function testToString()
|
||||
{
|
||||
$req = Request::init()->sendsAndExpects(Mime::JSON);
|
||||
$response = new Response(self::SAMPLE_JSON_RESPONSE, self::SAMPLE_JSON_HEADER, $req);
|
||||
$this->assertEquals(self::SAMPLE_JSON_RESPONSE, (string)$response);
|
||||
}
|
||||
|
||||
function test_parseHeaders()
|
||||
{
|
||||
$parse_headers = Response\Headers::fromString(self::SAMPLE_JSON_HEADER);
|
||||
$this->assertCount(3, $parse_headers);
|
||||
$this->assertEquals('application/json', $parse_headers['Content-Type']);
|
||||
$this->assertTrue(isset($parse_headers['Connection']));
|
||||
}
|
||||
|
||||
function testMultiHeaders()
|
||||
{
|
||||
$req = Request::init();
|
||||
$response = new Response(self::SAMPLE_JSON_RESPONSE, self::SAMPLE_MULTI_HEADER, $req);
|
||||
$parse_headers = $response->_parseHeaders(self::SAMPLE_MULTI_HEADER);
|
||||
$this->assertEquals('Value1,Value2', $parse_headers['X-My-Header']);
|
||||
}
|
||||
|
||||
function testDetectContentType()
|
||||
{
|
||||
$req = Request::init();
|
||||
$response = new Response(self::SAMPLE_JSON_RESPONSE, self::SAMPLE_JSON_HEADER, $req);
|
||||
$this->assertEquals('application/json', $response->headers['Content-Type']);
|
||||
}
|
||||
|
||||
function testMissingBodyContentType()
|
||||
{
|
||||
$body = 'A string';
|
||||
$request = Request::post(HttpfulTest::TEST_URL, $body)->_curlPrep();
|
||||
$this->assertEquals($body, $request->serialized_payload);
|
||||
}
|
||||
|
||||
function testParentType()
|
||||
{
|
||||
// Parent type
|
||||
$request = Request::init()->sendsAndExpects(Mime::XML);
|
||||
$response = new Response('<xml><name>Nathan</name></xml>', self::SAMPLE_VENDOR_HEADER, $request);
|
||||
|
||||
$this->assertEquals("application/xml", $response->parent_type);
|
||||
$this->assertEquals(self::SAMPLE_VENDOR_TYPE, $response->content_type);
|
||||
$this->assertTrue($response->is_mime_vendor_specific);
|
||||
|
||||
// Make sure we still parsed as if it were plain old XML
|
||||
$this->assertEquals("Nathan", $response->body->name->__toString());
|
||||
}
|
||||
|
||||
function testMissingContentType()
|
||||
{
|
||||
// Parent type
|
||||
$request = Request::init()->sendsAndExpects(Mime::XML);
|
||||
$response = new Response('<xml><name>Nathan</name></xml>',
|
||||
"HTTP/1.1 200 OK
|
||||
Connection: keep-alive
|
||||
Transfer-Encoding: chunked\r\n", $request);
|
||||
|
||||
$this->assertEquals("", $response->content_type);
|
||||
}
|
||||
|
||||
function testCustomMimeRegistering()
|
||||
{
|
||||
// Register new mime type handler for "application/vnd.nategood.message+xml"
|
||||
Httpful::register(self::SAMPLE_VENDOR_TYPE, new DemoMimeHandler());
|
||||
|
||||
$this->assertTrue(Httpful::hasParserRegistered(self::SAMPLE_VENDOR_TYPE));
|
||||
|
||||
$request = Request::init();
|
||||
$response = new Response('<xml><name>Nathan</name></xml>', self::SAMPLE_VENDOR_HEADER, $request);
|
||||
|
||||
$this->assertEquals(self::SAMPLE_VENDOR_TYPE, $response->content_type);
|
||||
$this->assertEquals('custom parse', $response->body);
|
||||
}
|
||||
|
||||
public function testShorthandMimeDefinition()
|
||||
{
|
||||
$r = Request::init()->expects('json');
|
||||
$this->assertEquals(Mime::JSON, $r->expected_type);
|
||||
|
||||
$r = Request::init()->expectsJson();
|
||||
$this->assertEquals(Mime::JSON, $r->expected_type);
|
||||
}
|
||||
|
||||
public function testOverrideXmlHandler()
|
||||
{
|
||||
// Lazy test...
|
||||
$prev = \Httpful\Httpful::get(\Httpful\Mime::XML);
|
||||
$this->assertEquals($prev, new \Httpful\Handlers\XmlHandler());
|
||||
$conf = array('namespace' => 'http://example.com');
|
||||
\Httpful\Httpful::register(\Httpful\Mime::XML, new \Httpful\Handlers\XmlHandler($conf));
|
||||
$new = \Httpful\Httpful::get(\Httpful\Mime::XML);
|
||||
$this->assertNotEquals($prev, $new);
|
||||
}
|
||||
|
||||
public function testHasProxyWithoutProxy()
|
||||
{
|
||||
$r = Request::get('someUrl');
|
||||
$this->assertFalse($r->hasProxy());
|
||||
}
|
||||
|
||||
public function testHasProxyWithProxy()
|
||||
{
|
||||
$r = Request::get('some_other_url');
|
||||
$r->useProxy('proxy.com');
|
||||
$this->assertTrue($r->hasProxy());
|
||||
}
|
||||
|
||||
public function testParseJSON()
|
||||
{
|
||||
$handler = new JsonHandler();
|
||||
|
||||
$bodies = array(
|
||||
'foo',
|
||||
array(),
|
||||
array('foo', 'bar'),
|
||||
null
|
||||
);
|
||||
foreach ($bodies as $body) {
|
||||
$this->assertEquals($body, $handler->parse(json_encode($body)));
|
||||
}
|
||||
|
||||
try {
|
||||
$result = $handler->parse('invalid{json');
|
||||
} catch(\Exception $e) {
|
||||
$this->assertEquals('Unable to parse response as JSON', $e->getMessage());
|
||||
return;
|
||||
}
|
||||
$this->fail('Expected an exception to be thrown due to invalid json');
|
||||
}
|
||||
|
||||
// /**
|
||||
// * Skeleton for testing against the 5.4 baked in server
|
||||
// */
|
||||
// public function testLocalServer()
|
||||
// {
|
||||
// if (!defined('WITHOUT_SERVER') || (defined('WITHOUT_SERVER') && !WITHOUT_SERVER)) {
|
||||
// // PHP test server seems to always set content type to application/octet-stream
|
||||
// // so force parsing as JSON here
|
||||
// Httpful::register('application/octet-stream', new \Httpful\Handlers\JsonHandler());
|
||||
// $response = Request::get(TEST_SERVER . '/test.json')
|
||||
// ->sendsAndExpects(MIME::JSON);
|
||||
// $response->send();
|
||||
// $this->assertTrue(...);
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
class DemoMimeHandler extends \Httpful\Handlers\MimeHandlerAdapter
|
||||
{
|
||||
public function parse($body)
|
||||
{
|
||||
return 'custom parse';
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,21 @@
|
||||
<?php
|
||||
/**
|
||||
* @author nick fox <quixand gmail com>
|
||||
*/
|
||||
namespace Httpful\Test;
|
||||
|
||||
class requestTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
|
||||
/**
|
||||
* @author Nick Fox
|
||||
* @expectedException Httpful\Exception\ConnectionErrorException
|
||||
* @expectedExceptionMessage Unable to connect
|
||||
*/
|
||||
public function testGet_InvalidURL()
|
||||
{
|
||||
// Silence the default logger via whenError override
|
||||
\Httpful\Request::get('unavailable.url')->whenError(function($error) {})->send();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
$php_version = phpversion();
|
||||
$php_major = floatval(substr($php_version, 0, 3));
|
||||
|
||||
// Define SIGKILL if pcntl is not found
|
||||
if (!function_exists('pcntl_signal')) {
|
||||
define('SIGKILL', 9);
|
||||
}
|
||||
|
||||
if ($php_major < 5.4) {
|
||||
define('WITHOUT_SERVER', true);
|
||||
} else {
|
||||
// Command that starts the built-in web server
|
||||
$command = sprintf('php -S %s:%d -t %s >./server.log 2>&1 & echo $!', WEB_SERVER_HOST, WEB_SERVER_PORT, WEB_SERVER_DOCROOT);
|
||||
|
||||
// Execute the command and store the process ID
|
||||
$output = array();
|
||||
exec($command, $output, $exit_code);
|
||||
|
||||
// sleep for a second to let server come up
|
||||
sleep(1);
|
||||
$pid = (int) $output[0];
|
||||
|
||||
// check server.log to see if it failed to start
|
||||
$server_logs = file_get_contents("./server.log");
|
||||
if (strpos($server_logs, "Fail") !== false) {
|
||||
// server failed to start for some reason
|
||||
print "Failed to start server! Logs:" . PHP_EOL . PHP_EOL;
|
||||
print_r($server_logs);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
echo sprintf('%s - Web server started on %s:%d with PID %d', date('r'), WEB_SERVER_HOST, WEB_SERVER_PORT, $pid) . PHP_EOL;
|
||||
|
||||
register_shutdown_function(function() {
|
||||
// cleanup after ourselves -- remove log file, shut down server
|
||||
global $pid;
|
||||
unlink("./server.log");
|
||||
posix_kill($pid, SIGKILL);
|
||||
});
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
<phpunit bootstrap="./bootstrap-server.php">
|
||||
<testsuites>
|
||||
<testsuite name="Httpful">
|
||||
<directory>.</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
<php>
|
||||
<const name="WEB_SERVER_HOST" value="localhost" />
|
||||
<const name="WEB_SERVER_PORT" value="1349" />
|
||||
<const name="WEB_SERVER_DOCROOT" value="./static" />
|
||||
</php>
|
||||
<logging>
|
||||
<log type="coverage-text" target="php://stdout" showUncoveredFiles="false"/>
|
||||
</logging>
|
||||
</phpunit>
|
||||
|
@ -0,0 +1 @@
|
||||
{"foo": "bar", "baz": false}
|
Binary file not shown.
After Width: | Height: | Size: 51 KiB |
Loading…
Reference in New Issue