Skip to content

Commit

Permalink
Update the Core function from PECL OAuth extension to native
Browse files Browse the repository at this point in the history
  • Loading branch information
hlu2 committed Jun 15, 2017
1 parent 3a18efc commit 12856b6
Show file tree
Hide file tree
Showing 16 changed files with 811 additions and 122 deletions.
93 changes: 93 additions & 0 deletions src/Core/HttpClients/BaseCurl.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
<?php
namespace QuickBooksOnline\API\Core\HttpClients;

use QuickBooksOnline\API\Exception\SdkException;

class BaseCurl{

private $curl;

public function __construct(){
$this->init();
}

public function init(){
if (!extension_loaded('curl')) {
throw new \Exception('The PHP exention curl must be installed to use this PDK.');
}
$this->curl = curl_init();
}

public function isCurlSet(){
if(!isset($this->curl)) return false;
return true;
}

public function setupOption($k, $v){
if($this->isCurlSet()){
curl_setopt($this->curl, $k, $v);
} else {
throw new SdkException("cURL instance is not set when calling setup Option.");
}
}

public function setupCurlOptArray(array $ary){
if($this->isCurlSet()){
curl_setopt_array($this->curl, $ary);
} else {
throw new SdkException("cURL instance is not set when calling setup curl Option from array.");
}
}

public function execute(){
if($this->isCurlSet()){
return curl_exec($this->curl);
} else {
throw new SdkException("cURL instance is not set when trying to execute it.");
}
}

public function errno(){
if($this->isCurlSet()){
return curl_errno($this->curl);
} else {
throw new SdkException("cURL instance is not set when trying to get errno code from the execution.");
}
}

public function error(){
if($this->isCurlSet()){
return curl_error($this->curl);
} else {
throw new SdkException("cURL instance is not set when trying to get error from the execution.");
}
}

public function version(){
return curl_version();
}

/**
* Get info from a curl reference from the type
* Mainly used after execute()
* use $type=CURLINFO_HEADER_OUT for header, and $type=CURLINFO_HTTP_CODE for http_code
*/
public function getInfo($type)
{
if($this->isCurlSet()){
return curl_getinfo($this->curl, $type);
}else {
throw new SdkException("cURL instance is not set when trying to get info from the type:" . $type);
}
}

/**
* Close the resource connection to curl
*/
public function close()
{
curl_close($this->curl);
}
}

?>
129 changes: 129 additions & 0 deletions src/Core/HttpClients/CurlHttpClient.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
<?php
namespace QuickBooksOnline\API\Core\HttpClients;

use QuickBooksOnline\API\Exception\SdkException;

class CurlHttpClient{

//The basecURL will be re-used once it is created.
private $basecURL;
private $rawResponse;
private $faultHandler;

public function __construct($curl = null)
{
if(isset($curl)){
$this->basecURL = $curl;
}else{
$this->basecURL = new BaseCurl();
}
}

public function makeAPICall($url, $method, array $headers, $body, $timeOut, $verifySSL){
$this->clearAllPreviousData();
$this->prepareRequest($url, $method, $headers, $body, $timeOut, $verifySSL);
$this->executeRequest();
$this->handleErrors();
return $this->getIntuitResponse();
}

private function clearAllPreviousData(){
$this->response = null;
$this->faultHandler = null;
}
/**
* Set the cURL with all required parameters.
* For Query Parameters, for get, please append it to the URI. For POST, please use the $body
*/
private function prepareRequest($url, $method, array $headerArray, $body, $timeOut, $verifySSL){
//Set basic Curl Info
$curl_opt = [
CURLOPT_URL => $url,
CURLOPT_CUSTOMREQUEST => trim($method),
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => $this->getHeaders($headerArray),
//10 seconds is allowed to make the connection to the server
CURLOPT_CONNECTTIMEOUT => 10,
CURLOPT_TIMEOUT => isset($timeOut) ? $timeOut : 100,
CURLOPT_RETURNTRANSFER => true,
//When CURLOPT_HEADER is set to 0 the only effect is that header info from the response is excluded from the output.
//So if you don't need it that's a few less KBs that curl will return to you.
//In our case, header is required
CURLOPT_HEADER => true
];

if ($method !== "GET" && isset($body)) {
$curl_opt[CURLOPT_POSTFIELDS] = $body;
}

//Set SSL. Only Enabled for OAuth 2
$this->setSSL($curl_opt, $verifySSL);

$this->intializeCurl();
$this->basecURL->setupCurlOptArray($curl_opt);
}

private function executeRequest(){
$this->rawResponse = $this->basecURL->execute();
}

private function handleErrors(){
if($this->basecURL->errno()){
$errorMsg = $this->basecURL->error();
$errorNumber = $this->basecURL->errno();
throw new SdkException("cURL error during making API call. cURL Error Number:[" . $errorNumber . "] with error:[" . $errorMsg . "]");
}
}

private function getIntuitResponse(){
$headerSize = $this->basecURL->getInfo(CURLINFO_HEADER_SIZE);
$rawHeaders = mb_substr($this->rawResponse, 0, $headerSize);
$rawBody = mb_substr($this->rawResponse, $headerSize);
$httpStatusCode = $this->basecURL->getInfo(CURLINFO_HTTP_CODE);
$IntuitResponse = new IntuitResponse($rawHeaders, $rawBody, $httpStatusCode);
return $IntuitResponse;
}

private function intializeCurl(){

if($this->basecURL->isCurlSet()){ return; }
else {$this->basecURL->init();}
}

private function getHeaders($headers){
if(!isset($headers) || empty($headers)){
throw new SdkException("Error. The headers set for cURL are either NULL or Empty");
}else{
$convertedHeaders = $this->convertHeaderArrayToHeaders($headers);
return $convertedHeaders;
}
}

private function setSSL($curl_opt, $verifySSL){
if($verifySSL){
$curl_opt[CURLOPT_SSL_VERIFYPEER] = true;
$curl_opt[CURLOPT_SSL_VERIFYHOST] = 2;
$curl_opt[URLOPT_CAINFO] = "";//"Some File Location."
}
}

/**
* Convert an Array to Curl Headers
* @param array $headerArray The request headers
* @return Curl Array Headers
*/
public function convertHeaderArrayToHeaders(array $headerArray){
$headers = array();
foreach($headerArray as $k => $v){
$headers[] = $k . ":" . $v;
}
return $headers;
}

public function closeConnection(){
$this->basecURL->close();
}

}

?>
26 changes: 21 additions & 5 deletions src/Core/HttpClients/FaultHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,21 +19,22 @@ class FaultHandler
private $httpStatusCode;

/**
* The response body from Intuit
*/
private $responseBody;
* The response body from Intuit
*/
private $responseBody;

/**
*The call is made through OAuth, if any code other than 2xx is returned, OAuth will have an error message generated as well.
* The OAuth error message will store here.
*/
private $oAuthHelperError;
private $oAuthHelperError;
/**
* Initializes a new instance of the FaultHandler class.
* @param ServiceContext context
*/
public function __construct($context, $OAuthException)
public function __construct($context = null, $OAuthException = null)
{
if($context == null && $OAuthException == null) return;
$this->context = $context;
$this->generateErrorFromOAuthMsg($OAuthException);
}
Expand All @@ -52,6 +53,21 @@ private function generateErrorFromOAuthMsg($OAuthException)
}
}

public function setHttpStatusCode($statusCode)
{
$this->httpStatusCode = $statusCode;
}

public function setOAuthHelperError($error)
{
$this->oAuthHelperError = $error;
}

public function setResponseBody($_responseBody)
{
$this->responseBody = $_responseBody;
}

public function getHttpStatusCode()
{
return $this->httpStatusCode;
Expand Down
73 changes: 73 additions & 0 deletions src/Core/HttpClients/IntuitResponse.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
<?php
namespace QuickBooksOnline\API\Core\HttpClients;

use QuickBooksOnline\API\Exception\SdkException;

class IntuitResponse{

private $headers;

private $body;

private $httpResponseCode;

private $faultHandler;

public function __construct($passedHeaders, $passedBody, $passedHttpResponseCode){
if(isset($passedHeaders)){
$this->setHeaders($passedHeaders);
}else{
throw new SdkException("Headers are null.");
}

if(isset($passedBody)){
$this->body = $passedBody;
}else{
throw new SdkException("Response Body are null.");
}

if(isset($passedHttpResponseCode)){
$this->httpResponseCode = (int)$passedHttpResponseCode;
if($this->httpResponseCode < 200 || $this->httpResponseCode >= 300){
$this->faultHandler = new FaultHandler();
$this->faultHandler->setHttpStatusCode($this->httpResponseCode);
$this->faultHandler->setResponseBody($this->body);
//Manually set the error message
$this->faultHandler->setOAuthHelperError("Invalid auth/bad request (got a 401, expected HTTP/1.1 20X or a redirect)");
}
}else{
throw new SdkException("Passed Http status code is null.");
}
}

public function setHeaders($rawHeaders){
$rawHeaders = str_replace("\r\n", "\n", $rawHeaders);
$response_headers_rows = explode("\n", trim($rawHeaders));
foreach ($response_headers_rows as $line) {
if(strpos($line, ': ') == false){
continue;
}else {
list($key, $value) = explode(': ', $line);
$this->headers[$key] = $value;
}
}

}

public function getHeaders(){
return $this->headers;
}

public function getBody(){
return $this->body;
}

public function getStatusCode(){
return $this->httpResponseCode;
}

public function getFaultHandler(){
return $this->faultHandler;
}

}
Loading

0 comments on commit 12856b6

Please sign in to comment.