A QCodo powered CMS
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

411 lines
13 KiB

  1. <?php
  2. if(!defined('QUINTACMS') ) die('No Quinta.');
  3. if (!defined("WEBSERVICEREQUEST.CLASS.PHP")){
  4. define("WEBSERVICEREQUEST.CLASS.PHP",1);
  5. /**
  6. * Class WebRequestType - enumerator class for types of shipping requests
  7. *@package Quinta
  8. * @subpackage Classes
  9. */
  10. class WebRequestType
  11. {
  12. ///@var const - a POST request
  13. const POST = 1;
  14. ///@var const - a GET request
  15. const GET = 2;
  16. ///@var const - a SOAP request
  17. const SOAP = 3;
  18. }
  19. /**
  20. * Class WebServiceRequest - base class for classes that perform request actions with a web service
  21. *
  22. * This class provides the basic request actions and properties for all of the Shipping and Payment Action classes.
  23. * This includes making the connection to the payment service provider, sending the request in either GET or
  24. * POST (usually in XML ..) and accepting the response from the server.
  25. * The request to send is stored in strRequest and the response is stored in strResponse regardless
  26. * of format or request type. Subclasses are responsible for initilizing these priory to calling createRequest(),
  27. * formatting the request and for handling the response. The required properties (eg. RemoteDomainName,
  28. * RemotePassword, etc ..) must be initilized by users of this or subclasses.
  29. *
  30. * Subclasses must implement these methods:
  31. * - createPostRequest: initializes strRequest with a formatted POST query (may return null if unused)
  32. * - createGetRequest: initializes strRequest with a formatted GET query (may return null if unused)
  33. * - handleResponse: parse strResponse for relevant data ..
  34. *
  35. * See the documentation for the Shipping and Payment Action subclasses
  36. * for more details.
  37. *
  38. *@todo
  39. * - Support SOAP connections
  40. * - Support using CURL
  41. *
  42. *@author Erik Winn <sidewalksoftware@gmail.com>
  43. *
  44. *@version 0.3
  45. *
  46. *@package Quinta
  47. * @subpackage Classes
  48. */
  49. abstract class WebServiceRequest{
  50. //////////// HTTP members:
  51. /**
  52. *@var string Username, login or account id for the service
  53. */
  54. protected $strRemoteAccountId;
  55. /**
  56. *@var string password for the service
  57. */
  58. protected $strRemotePassword;
  59. /**
  60. *@var string the FQD for the payment service provider API server
  61. */
  62. protected $strRemoteDomainName;
  63. /**
  64. * Note: This must contain the separater character that follows the script name, eg. '?' or '&'
  65. * if the request is of type GET
  66. *@var string the URL portion after the domain name leading to API script
  67. */
  68. protected $strRemoteCgiUrl;
  69. /**
  70. * This is appended to the CGI URL: www.foo.com/$CgiUrl . ApiName .. etc.
  71. *@var string the name of the API call to use
  72. */
  73. protected $strApiName;
  74. /**
  75. *@var string storage for response from service
  76. */
  77. protected $strResponse;
  78. /**
  79. *@var string storage for the request to service
  80. */
  81. protected $strRequest;
  82. /**
  83. *@var string The type of request to be made (GET | POST | SOAP )
  84. */
  85. protected $intRequestType;
  86. /**
  87. *@var integer Port number to use for the connection (80, 443)
  88. */
  89. protected $intPort;
  90. /**
  91. *@var integer Connection time out in seconds
  92. */
  93. protected $intTimeOut = 90;
  94. //////////// SOAP members:
  95. /**
  96. * This is an array handed to the SOAP client - the values should match those
  97. * found in the WSDL
  98. *@var array storage for the SOAP request to service
  99. */
  100. protected $arySoapRequest;
  101. /**
  102. * This is an array handed to the SOAP client - the values should match those
  103. * found in the WSDL
  104. *@var mixed storage for the SOAP response
  105. */
  106. protected $mixSoapResponse;
  107. /**
  108. *@var string name of the SOAP function to call
  109. */
  110. protected $strSoapFunction;
  111. /**
  112. *@var string location of the WSDL to use
  113. */
  114. protected $strWsdlUri;
  115. //////////// Common members:
  116. /**
  117. *@var string Errors
  118. */
  119. protected $strErrors;
  120. /**
  121. *@var boolean True if there were errors or if the transaction/connection failed for any reason
  122. */
  123. protected $blnHasErrors;
  124. /**
  125. *@var boolean True if we should use SSL to connect to the provider
  126. */
  127. protected $blnUseSsl = true;
  128. /**
  129. * NOTE: You must explicitly set this to disable testing mode ..
  130. *@var boolean True for testing (and by default)
  131. */
  132. protected $blnTestMode = true;
  133. /**
  134. * Parses the response from the web services provider
  135. */
  136. abstract protected function handleResponse();
  137. /**
  138. * Creates GET query string for the transaction appropriate to the provider API, storing
  139. * the result in strRequest.
  140. */
  141. abstract protected function createGETRequest();
  142. /**
  143. * Creates POST query string for the transaction appropriate to the provider API, storing
  144. * the result in strRequest.
  145. */
  146. abstract protected function createPOSTRequest();
  147. /**
  148. * Connects to web SOAP service and submits the request.
  149. * This function merely passes the arySoapRequest to the SOAP client
  150. * and stores the result in mixSoapResponse.
  151. * Note: strWsdlUri, strSoapFunction, and arySoapRequest_must_ _all_
  152. * be set before calling this.
  153. *@return boolean true on success
  154. */
  155. protected function submitSoapRequest(){
  156. //Fedex Example code does this - not sure why or if it is really needed ..
  157. ini_set("soap.wsdl_cache_enabled", "0");
  158. $objClient = new SoapClient($this->strWsdlUri, array('trace' => 1));
  159. //typical php - this does not work, __soapCall breaks the parameter array:
  160. // $this->mixSoapResponse = $objClient->__soapCall($this->strSoapFunction, $this->arySoapRequest);
  161. // so we have to do this:
  162. $strFunctionName = $this->strSoapFunction;
  163. $this->mixSoapResponse = $objClient->$strFunctionName($this->arySoapRequest);
  164. }
  165. /**
  166. * Connects to web service and submits the request. Note that
  167. * this function merely constructs a request URL from internal variables
  168. * that are set in createRequest, it may therefor contain a GET query
  169. * string or a POST depending on the subclass requirements.
  170. *@return boolean true on success
  171. */
  172. protected function submitRequest(){
  173. if( WebRequestType::SOAP === $this->intRequestType)
  174. return $this->submitSoapRequest();
  175. $strProtocol = '';
  176. if($this->UseSsl){
  177. $strProtocol = 'ssl://';
  178. $this->intPort = 443;
  179. }else{
  180. // $strProtocol = 'http://';
  181. $this->intPort = 80;
  182. }
  183. $strTarget = $strProtocol . $this->strRemoteDomainName;
  184. //attempt to connect ..
  185. @$fp = fsockopen($strTarget,
  186. $this->intPort,
  187. $intError,
  188. $strError,
  189. $this->intTimeOut
  190. );
  191. //did we connect?
  192. if (!$fp){
  193. if($this->TestMode)
  194. throw new Exception("Web Service request failed: $strError ($intError) ");
  195. else
  196. return false;
  197. }else{
  198. // optionally add an API extension:
  199. if($this->strApiName)
  200. $strUrl = $this->strRemoteCgiUrl . $this->strApiName;
  201. else
  202. $strUrl = $this->strRemoteCgiUrl;
  203. //construct the request ..
  204. switch( $this->intRequestType ){
  205. case WebRequestType::GET:
  206. $out = "GET " . $strUrl . $this->strRequest . " HTTP/1.1\r\n";
  207. $out .= "Host:" . $this->strRemoteDomainName . "\r\n";
  208. $out .= "User-Agent: QuintaCMS " . QUINTA_VERSION . "\r\n";
  209. $out .= "Content-Type: application/x-www-form-urlencoded\r\n";
  210. $out .= "Connection: Close\r\n\r\n";
  211. break;
  212. case WebRequestType::POST:
  213. $out = "POST " . $strUrl . " HTTP/1.1\r\n";
  214. $out .= "Host:" . $this->strRemoteDomainName . "\r\n";
  215. $out .= "User-Agent: QuintaCMS " . QUINTA_VERSION . "\r\n";
  216. // $out .= "MIME-Version: 1.0\r\n";
  217. $out .= "Content-Type: application/x-www-form-urlencoded\r\n";
  218. // $out .= "Accept: text/xml\r\n";
  219. $out .= "Content-length: " . strlen($this->strRequest) . "\r\n";
  220. $out .= "Cache-Control: no-cache\r\n";
  221. $out .= "Connection: Close\r\n\r\n";
  222. $out .= $this->strRequest . "\r\n\r\n";
  223. break;
  224. default:
  225. throw new Exception('WebService RequestType unsupported: ' . $this->intRequestType);
  226. }
  227. //send the request
  228. fwrite($fp, $out );
  229. $this->strResponse = '';
  230. //store the response
  231. while ( !feof($fp) )
  232. $this->strResponse .= fgets($fp, 128);
  233. $this->strResponse .= $out;
  234. fclose($fp);
  235. return true;
  236. }
  237. }
  238. /**
  239. * This function directs the call to the appropriate creation function and returns
  240. * a string containing either a query string for a GET or a content string for a POST.
  241. *
  242. * A WebRequestType may be provided to override a default as an alternative to setting
  243. * it explicitly. Note that this will set the RequestType for the object.
  244. *
  245. *@param string intRequestType - you may provide the RequestType
  246. */
  247. protected function createRequest($intWebRequestType=null){
  248. if(null !== $intWebRequestType)
  249. $this->intRequestType = $intWebRequestType;
  250. switch($this->intRequestType){
  251. case WebRequestType::GET:
  252. $this->createGETRequest();
  253. return $this->strRequest;
  254. break;
  255. case WebRequestType::POST:
  256. $this->createPOSTRequest();
  257. return $this->strRequest;
  258. break;
  259. case WebRequestType::SOAP:
  260. default:
  261. throw new Exception('WebService RequestType unsupported: ' . $this->RequestType);
  262. }
  263. }
  264. /**
  265. * Utility function to extract a root XML node string by tag
  266. *
  267. *@param string text of the node name
  268. *@return null | DOMDocument - a DOMDocument containing the node or null on failure
  269. */
  270. protected function getDomDocument($strTag){
  271. $strStartTag = '<' . $strTag . '>';
  272. $strEndTag = '</' . $strTag . '>';
  273. $intStartPos = strpos( $this->strResponse, $strStartTag );
  274. //no start? try leaving the start tag open ended to allow definitions ..
  275. if(false === $intStartPos){
  276. $strStartTag = '<' . $strTag . ' ';
  277. $intStartPos = strpos( $this->strResponse, $strStartTag );
  278. }
  279. $intEndPos = strpos($this->strResponse, $strEndTag);
  280. $intLength = ($intEndPos + strlen($strEndTag)) - $intStartPos;
  281. if(false !== $intStartPos && false !== $intEndPos){
  282. $objDomDoc = new DOMDocument();
  283. //don't let Domdoc complain about incorrect Endicia XML ..
  284. if( @$objDomDoc->loadXML(substr( $this->strResponse, $intStartPos, $intLength)) );
  285. return $objDomDoc;
  286. }
  287. return null;
  288. }
  289. public function __get($strName){
  290. switch ($strName){
  291. case 'RemotePassword':
  292. return $this->strRemotePassword ;
  293. case 'RemoteAccountId':
  294. return $this->strRemoteAccountId ;
  295. case 'RemoteCgiUrl':
  296. return $this->strRemoteCgiUrl ;
  297. case 'RemoteDomainName':
  298. return $this->strRemoteDomainName ;
  299. case 'Errors':
  300. return $this->strErrors ;
  301. case 'HasErrors':
  302. return $this->blnHasErrors ;
  303. case 'UseSsl':
  304. return $this->blnUseSsl ;
  305. case 'TestMode':
  306. return $this->blnTestMode ;
  307. case 'TimeOut':
  308. return $this->intTimeOut ;
  309. case 'Port':
  310. return $this->intPort ;
  311. default:
  312. throw new Exception('WebService Request - Unknown __get property: ' . $strName);
  313. }
  314. }
  315. public function __set($strName, $mixValue){
  316. switch ($strName){
  317. case 'RemoteDomainName':
  318. try {
  319. return ($this->strRemoteDomainName = QType::Cast($mixValue, QType::String ));
  320. } catch (QInvalidCastException $objExc) {
  321. $objExc->IncrementOffset();
  322. throw $objExc;
  323. }
  324. case 'RemoteCgiUrl':
  325. try {
  326. return ($this->strRemoteCgiUrl = QType::Cast($mixValue, QType::String ));
  327. } catch (QInvalidCastException $objExc) {
  328. $objExc->IncrementOffset();
  329. throw $objExc;
  330. }
  331. case 'RemoteAccountId':
  332. try {
  333. return ($this->strRemoteAccountId = QType::Cast($mixValue, QType::String ));
  334. } catch (QInvalidCastException $objExc) {
  335. $objExc->IncrementOffset();
  336. throw $objExc;
  337. }
  338. case 'RemotePassword':
  339. try {
  340. return ($this->strRemotePassword = QType::Cast($mixValue, QType::String ));
  341. } catch (QInvalidCastException $objExc) {
  342. $objExc->IncrementOffset();
  343. throw $objExc;
  344. }
  345. case 'Errors':
  346. try {
  347. return ($this->strErrors = QType::Cast($mixValue, QType::String ));
  348. } catch (QInvalidCastException $objExc) {
  349. $objExc->IncrementOffset();
  350. throw $objExc;
  351. }
  352. case 'HasErrors':
  353. try {
  354. return ($this->blnHasErrors = QType::Cast($mixValue, QType::Boolean ));
  355. } catch (QInvalidCastException $objExc) {
  356. $objExc->IncrementOffset();
  357. throw $objExc;
  358. }
  359. case 'UseSsl':
  360. try {
  361. return ($this->blnUseSsl = QType::Cast($mixValue, QType::Boolean ));
  362. } catch (QInvalidCastException $objExc) {
  363. $objExc->IncrementOffset();
  364. throw $objExc;
  365. }
  366. case 'TestMode':
  367. try {
  368. return ($this->blnTestMode = QType::Cast($mixValue, QType::Boolean ));
  369. } catch (QInvalidCastException $objExc) {
  370. $objExc->IncrementOffset();
  371. throw $objExc;
  372. }
  373. case 'Port':
  374. try {
  375. return ($this->intPort = QType::Cast($mixValue, QType::Integer ));
  376. } catch (QInvalidCastException $objExc) {
  377. $objExc->IncrementOffset();
  378. throw $objExc;
  379. }
  380. case 'TimeOut':
  381. try {
  382. return ($this->intTimeOut = QType::Cast($mixValue, QType::Integer ));
  383. } catch (QInvalidCastException $objExc) {
  384. $objExc->IncrementOffset();
  385. throw $objExc;
  386. }
  387. default:
  388. throw new Exception('WebService Request - Unknown __set property: ' . $strName);
  389. }
  390. }
  391. }//end class
  392. }//end define
  393. ?>