| <?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 | |
|  | |
| ?>
 |