<?php
|
|
if(!defined('QUINTACMS') ) die('No Quinta.');
|
|
|
|
if (!defined("WEBSERVICEREQUEST.CLASS.PHP")){
|
|
define("WEBSERVICEREQUEST.CLASS.PHP",1);
|
|
|
|
/**
|
|
* Class WebRequestType - enumerator class for types of shipping requests
|
|
*@package Quinta
|
|
* @subpackage Classes
|
|
*/
|
|
class WebRequestType
|
|
{
|
|
///@var const - a POST request
|
|
const POST = 1;
|
|
///@var const - a GET request
|
|
const GET = 2;
|
|
///@var const - a SOAP request
|
|
const SOAP = 3;
|
|
}
|
|
|
|
/**
|
|
* Class WebServiceRequest - base class for classes that perform request actions with a web service
|
|
*
|
|
* This class provides the basic request actions and properties for all of the Shipping and Payment Action classes.
|
|
* This includes making the connection to the payment service provider, sending the request in either GET or
|
|
* POST (usually in XML ..) and accepting the response from the server.
|
|
* The request to send is stored in strRequest and the response is stored in strResponse regardless
|
|
* of format or request type. Subclasses are responsible for initilizing these priory to calling createRequest(),
|
|
* formatting the request and for handling the response. The required properties (eg. RemoteDomainName,
|
|
* RemotePassword, etc ..) must be initilized by users of this or subclasses.
|
|
*
|
|
* Subclasses must implement these methods:
|
|
* - createPostRequest: initializes strRequest with a formatted POST query (may return null if unused)
|
|
* - createGetRequest: initializes strRequest with a formatted GET query (may return null if unused)
|
|
* - handleResponse: parse strResponse for relevant data ..
|
|
*
|
|
* See the documentation for the Shipping and Payment Action subclasses
|
|
* for more details.
|
|
*
|
|
*@todo
|
|
* - Support SOAP connections
|
|
* - Support using CURL
|
|
*
|
|
*@author Erik Winn <sidewalksoftware@gmail.com>
|
|
*
|
|
*@version 0.3
|
|
*
|
|
*@package Quinta
|
|
* @subpackage Classes
|
|
*/
|
|
|
|
abstract class WebServiceRequest{
|
|
//////////// HTTP members:
|
|
/**
|
|
*@var string Username, login or account id for the service
|
|
*/
|
|
protected $strRemoteAccountId;
|
|
/**
|
|
*@var string password for the service
|
|
*/
|
|
protected $strRemotePassword;
|
|
/**
|
|
*@var string the FQD for the payment service provider API server
|
|
*/
|
|
protected $strRemoteDomainName;
|
|
/**
|
|
* Note: This must contain the separater character that follows the script name, eg. '?' or '&'
|
|
* if the request is of type GET
|
|
*@var string the URL portion after the domain name leading to API script
|
|
*/
|
|
protected $strRemoteCgiUrl;
|
|
/**
|
|
* This is appended to the CGI URL: www.foo.com/$CgiUrl . ApiName .. etc.
|
|
*@var string the name of the API call to use
|
|
*/
|
|
protected $strApiName;
|
|
/**
|
|
*@var string storage for response from service
|
|
*/
|
|
protected $strResponse;
|
|
/**
|
|
*@var string storage for the request to service
|
|
*/
|
|
protected $strRequest;
|
|
/**
|
|
*@var string The type of request to be made (GET | POST | SOAP )
|
|
*/
|
|
protected $intRequestType;
|
|
/**
|
|
*@var integer Port number to use for the connection (80, 443)
|
|
*/
|
|
protected $intPort;
|
|
/**
|
|
*@var integer Connection time out in seconds
|
|
*/
|
|
protected $intTimeOut = 90;
|
|
|
|
//////////// SOAP members:
|
|
/**
|
|
* This is an array handed to the SOAP client - the values should match those
|
|
* found in the WSDL
|
|
*@var array storage for the SOAP request to service
|
|
*/
|
|
protected $arySoapRequest;
|
|
/**
|
|
* This is an array handed to the SOAP client - the values should match those
|
|
* found in the WSDL
|
|
*@var mixed storage for the SOAP response
|
|
*/
|
|
protected $mixSoapResponse;
|
|
/**
|
|
*@var string name of the SOAP function to call
|
|
*/
|
|
protected $strSoapFunction;
|
|
/**
|
|
*@var string location of the WSDL to use
|
|
*/
|
|
protected $strWsdlUri;
|
|
|
|
//////////// Common members:
|
|
/**
|
|
*@var string Errors
|
|
*/
|
|
protected $strErrors;
|
|
/**
|
|
*@var boolean True if there were errors or if the transaction/connection failed for any reason
|
|
*/
|
|
protected $blnHasErrors;
|
|
/**
|
|
*@var boolean True if we should use SSL to connect to the provider
|
|
*/
|
|
protected $blnUseSsl = true;
|
|
|
|
/**
|
|
* NOTE: You must explicitly set this to disable testing mode ..
|
|
*@var boolean True for testing (and by default)
|
|
*/
|
|
protected $blnTestMode = true;
|
|
|
|
/**
|
|
* Parses the response from the web services provider
|
|
*/
|
|
abstract protected function handleResponse();
|
|
/**
|
|
* Creates GET query string for the transaction appropriate to the provider API, storing
|
|
* the result in strRequest.
|
|
*/
|
|
abstract protected function createGETRequest();
|
|
/**
|
|
* Creates POST query string for the transaction appropriate to the provider API, storing
|
|
* the result in strRequest.
|
|
*/
|
|
abstract protected function createPOSTRequest();
|
|
|
|
/**
|
|
* Connects to web SOAP service and submits the request.
|
|
* This function merely passes the arySoapRequest to the SOAP client
|
|
* and stores the result in mixSoapResponse.
|
|
* Note: strWsdlUri, strSoapFunction, and arySoapRequest_must_ _all_
|
|
* be set before calling this.
|
|
*@return boolean true on success
|
|
*/
|
|
protected function submitSoapRequest(){
|
|
//Fedex Example code does this - not sure why or if it is really needed ..
|
|
ini_set("soap.wsdl_cache_enabled", "0");
|
|
|
|
$objClient = new SoapClient($this->strWsdlUri, array('trace' => 1));
|
|
//typical php - this does not work, __soapCall breaks the parameter array:
|
|
// $this->mixSoapResponse = $objClient->__soapCall($this->strSoapFunction, $this->arySoapRequest);
|
|
// so we have to do this:
|
|
$strFunctionName = $this->strSoapFunction;
|
|
$this->mixSoapResponse = $objClient->$strFunctionName($this->arySoapRequest);
|
|
}
|
|
/**
|
|
* Connects to web service and submits the request. Note that
|
|
* this function merely constructs a request URL from internal variables
|
|
* that are set in createRequest, it may therefor contain a GET query
|
|
* string or a POST depending on the subclass requirements.
|
|
*@return boolean true on success
|
|
*/
|
|
protected function submitRequest(){
|
|
if( WebRequestType::SOAP === $this->intRequestType)
|
|
return $this->submitSoapRequest();
|
|
|
|
$strProtocol = '';
|
|
if($this->UseSsl){
|
|
$strProtocol = 'ssl://';
|
|
$this->intPort = 443;
|
|
}else{
|
|
// $strProtocol = 'http://';
|
|
$this->intPort = 80;
|
|
}
|
|
$strTarget = $strProtocol . $this->strRemoteDomainName;
|
|
//attempt to connect ..
|
|
@$fp = fsockopen($strTarget,
|
|
$this->intPort,
|
|
$intError,
|
|
$strError,
|
|
$this->intTimeOut
|
|
);
|
|
|
|
//did we connect?
|
|
if (!$fp){
|
|
if($this->TestMode)
|
|
throw new Exception("Web Service request failed: $strError ($intError) ");
|
|
else
|
|
return false;
|
|
}else{
|
|
// optionally add an API extension:
|
|
if($this->strApiName)
|
|
$strUrl = $this->strRemoteCgiUrl . $this->strApiName;
|
|
else
|
|
$strUrl = $this->strRemoteCgiUrl;
|
|
//construct the request ..
|
|
switch( $this->intRequestType ){
|
|
case WebRequestType::GET:
|
|
$out = "GET " . $strUrl . $this->strRequest . " HTTP/1.1\r\n";
|
|
$out .= "Host:" . $this->strRemoteDomainName . "\r\n";
|
|
$out .= "User-Agent: QuintaCMS " . QUINTA_VERSION . "\r\n";
|
|
$out .= "Content-Type: application/x-www-form-urlencoded\r\n";
|
|
$out .= "Connection: Close\r\n\r\n";
|
|
break;
|
|
case WebRequestType::POST:
|
|
$out = "POST " . $strUrl . " HTTP/1.1\r\n";
|
|
$out .= "Host:" . $this->strRemoteDomainName . "\r\n";
|
|
$out .= "User-Agent: QuintaCMS " . QUINTA_VERSION . "\r\n";
|
|
// $out .= "MIME-Version: 1.0\r\n";
|
|
$out .= "Content-Type: application/x-www-form-urlencoded\r\n";
|
|
// $out .= "Accept: text/xml\r\n";
|
|
$out .= "Content-length: " . strlen($this->strRequest) . "\r\n";
|
|
$out .= "Cache-Control: no-cache\r\n";
|
|
$out .= "Connection: Close\r\n\r\n";
|
|
$out .= $this->strRequest . "\r\n\r\n";
|
|
break;
|
|
default:
|
|
throw new Exception('WebService RequestType unsupported: ' . $this->intRequestType);
|
|
}
|
|
//send the request
|
|
fwrite($fp, $out );
|
|
|
|
$this->strResponse = '';
|
|
//store the response
|
|
while ( !feof($fp) )
|
|
$this->strResponse .= fgets($fp, 128);
|
|
$this->strResponse .= $out;
|
|
fclose($fp);
|
|
return true;
|
|
}
|
|
|
|
}
|
|
/**
|
|
* This function directs the call to the appropriate creation function and returns
|
|
* a string containing either a query string for a GET or a content string for a POST.
|
|
*
|
|
* A WebRequestType may be provided to override a default as an alternative to setting
|
|
* it explicitly. Note that this will set the RequestType for the object.
|
|
*
|
|
*@param string intRequestType - you may provide the RequestType
|
|
*/
|
|
protected function createRequest($intWebRequestType=null){
|
|
if(null !== $intWebRequestType)
|
|
$this->intRequestType = $intWebRequestType;
|
|
|
|
switch($this->intRequestType){
|
|
case WebRequestType::GET:
|
|
$this->createGETRequest();
|
|
return $this->strRequest;
|
|
break;
|
|
case WebRequestType::POST:
|
|
$this->createPOSTRequest();
|
|
return $this->strRequest;
|
|
break;
|
|
case WebRequestType::SOAP:
|
|
default:
|
|
throw new Exception('WebService RequestType unsupported: ' . $this->RequestType);
|
|
}
|
|
}
|
|
/**
|
|
* Utility function to extract a root XML node string by tag
|
|
*
|
|
*@param string text of the node name
|
|
*@return null | DOMDocument - a DOMDocument containing the node or null on failure
|
|
*/
|
|
protected function getDomDocument($strTag){
|
|
$strStartTag = '<' . $strTag . '>';
|
|
$strEndTag = '</' . $strTag . '>';
|
|
$intStartPos = strpos( $this->strResponse, $strStartTag );
|
|
//no start? try leaving the start tag open ended to allow definitions ..
|
|
if(false === $intStartPos){
|
|
$strStartTag = '<' . $strTag . ' ';
|
|
$intStartPos = strpos( $this->strResponse, $strStartTag );
|
|
}
|
|
$intEndPos = strpos($this->strResponse, $strEndTag);
|
|
$intLength = ($intEndPos + strlen($strEndTag)) - $intStartPos;
|
|
if(false !== $intStartPos && false !== $intEndPos){
|
|
$objDomDoc = new DOMDocument();
|
|
//don't let Domdoc complain about incorrect Endicia XML ..
|
|
if( @$objDomDoc->loadXML(substr( $this->strResponse, $intStartPos, $intLength)) );
|
|
return $objDomDoc;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
public function __get($strName){
|
|
switch ($strName){
|
|
case 'RemotePassword':
|
|
return $this->strRemotePassword ;
|
|
case 'RemoteAccountId':
|
|
return $this->strRemoteAccountId ;
|
|
case 'RemoteCgiUrl':
|
|
return $this->strRemoteCgiUrl ;
|
|
case 'RemoteDomainName':
|
|
return $this->strRemoteDomainName ;
|
|
case 'Errors':
|
|
return $this->strErrors ;
|
|
case 'HasErrors':
|
|
return $this->blnHasErrors ;
|
|
case 'UseSsl':
|
|
return $this->blnUseSsl ;
|
|
case 'TestMode':
|
|
return $this->blnTestMode ;
|
|
case 'TimeOut':
|
|
return $this->intTimeOut ;
|
|
case 'Port':
|
|
return $this->intPort ;
|
|
default:
|
|
throw new Exception('WebService Request - Unknown __get property: ' . $strName);
|
|
}
|
|
}
|
|
|
|
public function __set($strName, $mixValue){
|
|
switch ($strName){
|
|
case 'RemoteDomainName':
|
|
try {
|
|
return ($this->strRemoteDomainName = QType::Cast($mixValue, QType::String ));
|
|
} catch (QInvalidCastException $objExc) {
|
|
$objExc->IncrementOffset();
|
|
throw $objExc;
|
|
}
|
|
case 'RemoteCgiUrl':
|
|
try {
|
|
return ($this->strRemoteCgiUrl = QType::Cast($mixValue, QType::String ));
|
|
} catch (QInvalidCastException $objExc) {
|
|
$objExc->IncrementOffset();
|
|
throw $objExc;
|
|
}
|
|
case 'RemoteAccountId':
|
|
try {
|
|
return ($this->strRemoteAccountId = QType::Cast($mixValue, QType::String ));
|
|
} catch (QInvalidCastException $objExc) {
|
|
$objExc->IncrementOffset();
|
|
throw $objExc;
|
|
}
|
|
case 'RemotePassword':
|
|
try {
|
|
return ($this->strRemotePassword = QType::Cast($mixValue, QType::String ));
|
|
} catch (QInvalidCastException $objExc) {
|
|
$objExc->IncrementOffset();
|
|
throw $objExc;
|
|
}
|
|
case 'Errors':
|
|
try {
|
|
return ($this->strErrors = QType::Cast($mixValue, QType::String ));
|
|
} catch (QInvalidCastException $objExc) {
|
|
$objExc->IncrementOffset();
|
|
throw $objExc;
|
|
}
|
|
case 'HasErrors':
|
|
try {
|
|
return ($this->blnHasErrors = QType::Cast($mixValue, QType::Boolean ));
|
|
} catch (QInvalidCastException $objExc) {
|
|
$objExc->IncrementOffset();
|
|
throw $objExc;
|
|
}
|
|
case 'UseSsl':
|
|
try {
|
|
return ($this->blnUseSsl = QType::Cast($mixValue, QType::Boolean ));
|
|
} catch (QInvalidCastException $objExc) {
|
|
$objExc->IncrementOffset();
|
|
throw $objExc;
|
|
}
|
|
case 'TestMode':
|
|
try {
|
|
return ($this->blnTestMode = QType::Cast($mixValue, QType::Boolean ));
|
|
} catch (QInvalidCastException $objExc) {
|
|
$objExc->IncrementOffset();
|
|
throw $objExc;
|
|
}
|
|
case 'Port':
|
|
try {
|
|
return ($this->intPort = QType::Cast($mixValue, QType::Integer ));
|
|
} catch (QInvalidCastException $objExc) {
|
|
$objExc->IncrementOffset();
|
|
throw $objExc;
|
|
}
|
|
case 'TimeOut':
|
|
try {
|
|
return ($this->intTimeOut = QType::Cast($mixValue, QType::Integer ));
|
|
} catch (QInvalidCastException $objExc) {
|
|
$objExc->IncrementOffset();
|
|
throw $objExc;
|
|
}
|
|
default:
|
|
throw new Exception('WebService Request - Unknown __set property: ' . $strName);
|
|
}
|
|
}
|
|
|
|
}//end class
|
|
}//end define
|
|
|
|
?>
|