1 <?php
2 namespace OpenDNS\MES;
3 use Guzzle\Http\Client;
4 use Guzzle\Http\QueryString;
5
6 /**
7 * Base abstract for all MeS APIs
8 */
9 abstract class Request
10 {
11 const ENV_PROD = 'api';
12 const ENV_CERT = 'cert';
13 const ENV_TEST = 'test';
14
15 /** @var string HTTP Method to use for the request */
16 protected $httpMethod = 'POST';
17
18 /** @var string Base domain name for the API as a whole */
19 protected $apiBaseDomain = 'merchante-solutions.com';
20
21 /** @var string Environment subdomain for the api */
22 protected $apiEnvironment;
23
24 /** @var string The path to an API's endpoints on the server */
25 protected $apiBasePath;
26
27 /** @var string The name of a specific endpoint within the api base path */
28 protected $apiEndpointPath;
29
30 /** @var string[] key/value pairs to be set by default on new requests */
31 protected $defaultFieldValues = array();
32
33 /** @var Guzzle\Http\Client Guzzle HTTP client instance */
34 private $httpClient;
35
36 /** @var Guzzle\Http\Message\Request Guzzle request object */
37 private $request;
38
39 /** @var Guzzle\Http\Message\Response Guzzle response object */
40 private $response;
41
42 private $requestFields = array();
43
44 /**
45 * Constructor, for constructing new instances
46 *
47 * @param string $environment One of the ENV consts for API environment
48 * @return void
49 */
50 public function __construct($environment)
51 {
52 $this->apiEnvironment = $environment;
53 $this->setDefaults();
54 }
55
56 /**
57 * Factory method for convenient chaining
58 *
59 * @param string $environment One of the ENV consts for API environment
60 * @return self A new instance
61 */
62 public static function factory($environment)
63 {
64 return new static($environment);
65 }
66
67 /**
68 * Run the request and return the response body
69 *
70 * @return Guzzle\Http\Stream\StreamInterface A Guzzle stream object representing the response body
71 */
72 public final function execute()
73 {
74 $this->response = null;
75 $httpMethod = strtolower($this->httpMethod);
76
77 $dataParam = $this->httpMethod === 'GET' ? 'query' : 'body';
78
79 $request = $this->getClient()
80 ->$httpMethod($this->getApiUrl());
81
82 if ($this->httpMethod === 'GET' || $this->httpMethod === 'HEAD') {
83 $request->getQuery()
84 ->replace($this->requestFields);
85 } else {
86 $request = $request->setBody($this->requestFields);
87 }
88
89 $this->response = $request->send();
90
91 return $this->processResponse($this->response->getBody());
92 }
93
94 /**
95 * Attach a request subscriber for unit testing.
96 *
97 * @internal For unit testing only
98 * @ignore
99 */
100 public function addClientSubscriber($subscriber)
101 {
102 return $this->getClient()->addSubscriber($subscriber);
103 }
104
105 /**
106 * Assemble a full API url out of class properties
107 *
108 * @return string the combined API url
109 */
110 protected function getApiUrl()
111 {
112 $url = 'https://' . $this->apiEnvironment . '.' . $this->apiBaseDomain;
113
114 if ($this->apiBasePath !== null) {
115 $url .= $this->apiBasePath;
116 }
117
118 if ($this->apiEndpointPath) {
119 $url .= '/' . $this->apiEndpointPath;
120 }
121
122 return $url;
123 }
124
125 /**
126 * Return a singleton Guzzle HTTP client
127 *
128 * @return Guzzle\Http\Client
129 */
130 protected function getClient()
131 {
132 if ($this->httpClient === null) {
133 $this->httpClient = new Client();
134 }
135
136 return $this->httpClient;
137 }
138
139 /**
140 * Remove a set field from the current request
141 *
142 * @param string $field Field name to unset from the request
143 * @return self The current class instance for chaining
144 */
145 protected function removeField($field)
146 {
147 unset($this->requestFields[$field]);
148 return $this;
149 }
150
151 /**
152 * Set a new field on the current request
153 *
154 * @param string $field The field name to set
155 * @param int|string The value of the field (will be cast to string)
156 * @return self The current class instance for chaining
157 */
158 protected function setField($field, $value)
159 {
160 $this->requestFields[$field] = $value;
161 return $this;
162 }
163
164 /**
165 * Set any defined default fields on the request
166 * @return self The current class instance for chaining
167 */
168 protected function setDefaults()
169 {
170 foreach ($this->defaultFieldValues as $field => $value) {
171 $this->setField($field, $value);
172 }
173
174 return $this;
175 }
176
177 /**
178 * Allows child classes to post-process the results before returning them
179 *
180 * @param Guzzle\Http\Stream\StreamInterface $response The Guzzle stream object returned by execute()
181 * @return Response An object representing the MeS response
182 */
183 protected function processResponse($response)
184 {
185 return new Response($response);
186 }
187
188 }
189