A Qcodo based CMS/ecommerce framework
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.

454 lines
18 KiB

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