PluginProbe
Analytify – Google Analytics Dashboard For WordPress (GA4 analytics tracking) / 1.0.0
Analytify – Google Analytics Dashboard For WordPress (GA4 analytics tracking) v1.0.0
9.1.2 9.1.1 9.1.0 9.0.2 9.0.1 trunk 1.0.0 1.0.1 1.0.2 1.0.3 1.0.4 1.0.5 1.0.6 1.0.7 1.1.0 1.1.1 1.2.0 1.2.1 1.2.2 1.2.3 1.2.4 1.2.5 1.3.0 1.3.1 1.3.2 All 153 releases
wp-analytify / lib / Google / IO / Abstract.php

Abstract.php in Analytify – Google Analytics Dashboard For WordPress (GA4 analytics tracking) 1.0.0, at lib/Google/IO/Abstract.php

330 lines 10.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /*
3 * Copyright 2013 Google Inc.
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18 /**
19 * Abstract IO base class
20 */
21
22 require_once ANALYTIFY_LIB_PATH . 'Google/Client.php';
23 require_once ANALYTIFY_LIB_PATH . 'Google/IO/Exception.php';
24 require_once ANALYTIFY_LIB_PATH . 'Google/Http/CacheParser.php';
25 require_once ANALYTIFY_LIB_PATH . 'Google/Http/Request.php';
26
27 abstract class Analytify_Google_IO_Abstract
28 {
29 const UNKNOWN_CODE = 0;
30 const FORM_URLENCODED = 'application/x-www-form-urlencoded';
31 private static $CONNECTION_ESTABLISHED_HEADERS = array(
32 "HTTP/1.0 200 Connection established\r\n\r\n",
33 "HTTP/1.1 200 Connection established\r\n\r\n",
34 );
35 private static $ENTITY_HTTP_METHODS = array("POST" => null, "PUT" => null);
36
37 /** @var Analytify_Google_Client */
38 protected $client;
39
40 public function __construct(Analytify_Google_Client $client)
41 {
42 $this->client = $client;
43 $timeout = $client->getClassConfig('Analytify_Google_IO_Abstract', 'request_timeout_seconds');
44 if ($timeout > 0) {
45 $this->setTimeout($timeout);
46 }
47 }
48
49 /**
50 * Executes a Analytify_Google_Http_Request and returns the resulting populated Analytify_Google_Http_Request
51 * @param Analytify_Google_Http_Request $request
52 * @return Analytify_Google_Http_Request $request
53 */
54 abstract public function executeRequest(Analytify_Google_Http_Request $request);
55
56 /**
57 * Set options that update the transport implementation's behavior.
58 * @param $options
59 */
60 abstract public function setOptions($options);
61
62 /**
63 * Set the maximum request time in seconds.
64 * @param $timeout in seconds
65 */
66 abstract public function setTimeout($timeout);
67
68 /**
69 * Get the maximum request time in seconds.
70 * @return timeout in seconds
71 */
72 abstract public function getTimeout();
73
74 /**
75 * Test for the presence of a cURL header processing bug
76 *
77 * The cURL bug was present in versions prior to 7.30.0 and caused the header
78 * length to be miscalculated when a "Connection established" header added by
79 * some proxies was present.
80 *
81 * @return boolean
82 */
83 abstract protected function needsQuirk();
84
85 /**
86 * @visible for testing.
87 * Cache the response to an HTTP request if it is cacheable.
88 * @param Analytify_Google_Http_Request $request
89 * @return bool Returns true if the insertion was successful.
90 * Otherwise, return false.
91 */
92 public function setCachedRequest(Analytify_Google_Http_Request $request)
93 {
94 // Determine if the request is cacheable.
95 if (Analytify_Google_Http_CacheParser::isResponseCacheable($request)) {
96 $this->client->getCache()->set($request->getCacheKey(), $request);
97 return true;
98 }
99
100 return false;
101 }
102
103 /**
104 * Execute an HTTP Request
105 *
106 * @param Analytify_Google_HttpRequest $request the http request to be executed
107 * @return Analytify_Google_HttpRequest http request with the response http code,
108 * response headers and response body filled in
109 * @throws Analytify_Google_IO_Exception on curl or IO error
110 */
111 public function makeRequest(Analytify_Google_Http_Request $request)
112 {
113 // First, check to see if we have a valid cached version.
114 $cached = $this->getCachedRequest($request);
115 if ($cached !== false && $cached instanceof Analytify_Google_Http_Request) {
116 if (!$this->checkMustRevalidateCachedRequest($cached, $request)) {
117 return $cached;
118 }
119 }
120
121 if (array_key_exists($request->getRequestMethod(), self::$ENTITY_HTTP_METHODS)) {
122 $request = $this->processEntityRequest($request);
123 }
124
125 list($responseData, $responseHeaders, $respHttpCode) = $this->executeRequest($request);
126
127 if ($respHttpCode == 304 && $cached) {
128 // If the server responded NOT_MODIFIED, return the cached request.
129 $this->updateCachedRequest($cached, $responseHeaders);
130 return $cached;
131 }
132
133 if (!isset($responseHeaders['Date']) && !isset($responseHeaders['date'])) {
134 $responseHeaders['Date'] = date("r");
135 }
136
137 $request->setResponseHttpCode($respHttpCode);
138 $request->setResponseHeaders($responseHeaders);
139 $request->setResponseBody($responseData);
140 // Store the request in cache (the function checks to see if the request
141 // can actually be cached)
142 $this->setCachedRequest($request);
143 return $request;
144 }
145
146 /**
147 * @visible for testing.
148 * @param Analytify_Google_Http_Request $request
149 * @return Analytify_Google_Http_Request|bool Returns the cached object or
150 * false if the operation was unsuccessful.
151 */
152 public function getCachedRequest(Analytify_Google_Http_Request $request)
153 {
154 if (false === Analytify_Google_Http_CacheParser::isRequestCacheable($request)) {
155 return false;
156 }
157
158 return $this->client->getCache()->get($request->getCacheKey());
159 }
160
161 /**
162 * @visible for testing
163 * Process an http request that contains an enclosed entity.
164 * @param Analytify_Google_Http_Request $request
165 * @return Analytify_Google_Http_Request Processed request with the enclosed entity.
166 */
167 public function processEntityRequest(Analytify_Google_Http_Request $request)
168 {
169 $postBody = $request->getPostBody();
170 $contentType = $request->getRequestHeader("content-type");
171
172 // Set the default content-type as application/x-www-form-urlencoded.
173 if (false == $contentType) {
174 $contentType = self::FORM_URLENCODED;
175 $request->setRequestHeaders(array('content-type' => $contentType));
176 }
177
178 // Force the payload to match the content-type asserted in the header.
179 if ($contentType == self::FORM_URLENCODED && is_array($postBody)) {
180 $postBody = http_build_query($postBody, '', '&');
181 $request->setPostBody($postBody);
182 }
183
184 // Make sure the content-length header is set.
185 if (!$postBody || is_string($postBody)) {
186 $postsLength = strlen($postBody);
187 $request->setRequestHeaders(array('content-length' => $postsLength));
188 }
189
190 return $request;
191 }
192
193 /**
194 * Check if an already cached request must be revalidated, and if so update
195 * the request with the correct ETag headers.
196 * @param Analytify_Google_Http_Request $cached A previously cached response.
197 * @param Analytify_Google_Http_Request $request The outbound request.
198 * return bool If the cached object needs to be revalidated, false if it is
199 * still current and can be re-used.
200 */
201 protected function checkMustRevalidateCachedRequest($cached, $request)
202 {
203 if (Analytify_Google_Http_CacheParser::mustRevalidate($cached)) {
204 $addHeaders = array();
205 if ($cached->getResponseHeader('etag')) {
206 // [13.3.4] If an entity tag has been provided by the origin server,
207 // we must use that entity tag in any cache-conditional request.
208 $addHeaders['If-None-Match'] = $cached->getResponseHeader('etag');
209 } elseif ($cached->getResponseHeader('date')) {
210 $addHeaders['If-Modified-Since'] = $cached->getResponseHeader('date');
211 }
212
213 $request->setRequestHeaders($addHeaders);
214 return true;
215 } else {
216 return false;
217 }
218 }
219
220 /**
221 * Update a cached request, using the headers from the last response.
222 * @param Analytify_Google_HttpRequest $cached A previously cached response.
223 * @param mixed Associative array of response headers from the last request.
224 */
225 protected function updateCachedRequest($cached, $responseHeaders)
226 {
227 if (isset($responseHeaders['connection'])) {
228 $hopByHop = array_merge(
229 self::$HOP_BY_HOP,
230 explode(
231 ',',
232 $responseHeaders['connection']
233 )
234 );
235
236 $endToEnd = array();
237 foreach ($hopByHop as $key) {
238 if (isset($responseHeaders[$key])) {
239 $endToEnd[$key] = $responseHeaders[$key];
240 }
241 }
242 $cached->setResponseHeaders($endToEnd);
243 }
244 }
245
246 /**
247 * Used by the IO lib and also the batch processing.
248 *
249 * @param $respData
250 * @param $headerSize
251 * @return array
252 */
253 public function parseHttpResponse($respData, $headerSize)
254 {
255 // check proxy header
256 foreach (self::$CONNECTION_ESTABLISHED_HEADERS as $established_header) {
257 if (stripos($respData, $established_header) !== false) {
258 // existed, remove it
259 $respData = str_ireplace($established_header, '', $respData);
260 // Subtract the proxy header size unless the cURL bug prior to 7.30.0
261 // is present which prevented the proxy header size from being taken into
262 // account.
263 if (!$this->needsQuirk()) {
264 $headerSize -= strlen($established_header);
265 }
266 break;
267 }
268 }
269
270 if ($headerSize) {
271 $responseBody = substr($respData, $headerSize);
272 $responseHeaders = substr($respData, 0, $headerSize);
273 } else {
274 list($responseHeaders, $responseBody) = explode("\r\n\r\n", $respData, 2);
275 }
276
277 $responseHeaders = $this->getHttpResponseHeaders($responseHeaders);
278 return array($responseHeaders, $responseBody);
279 }
280
281 /**
282 * Parse out headers from raw headers
283 * @param rawHeaders array or string
284 * @return array
285 */
286 public function getHttpResponseHeaders($rawHeaders)
287 {
288 if (is_array($rawHeaders)) {
289 return $this->parseArrayHeaders($rawHeaders);
290 } else {
291 return $this->parseStringHeaders($rawHeaders);
292 }
293 }
294
295 private function parseStringHeaders($rawHeaders)
296 {
297 $headers = array();
298 $responseHeaderLines = explode("\r\n", $rawHeaders);
299 foreach ($responseHeaderLines as $headerLine) {
300 if ($headerLine && strpos($headerLine, ':') !== false) {
301 list($header, $value) = explode(': ', $headerLine, 2);
302 $header = strtolower($header);
303 if (isset($responseHeaders[$header])) {
304 $headers[$header] .= "\n" . $value;
305 } else {
306 $headers[$header] = $value;
307 }
308 }
309 }
310 return $headers;
311 }
312
313 private function parseArrayHeaders($rawHeaders)
314 {
315 $header_count = count($rawHeaders);
316 $headers = array();
317
318 for ($i = 0; $i < $header_count; $i++) {
319 $header = $rawHeaders[$i];
320 // Times will have colons in - so we just want the first match.
321 $header_parts = explode(': ', $header, 2);
322 if (count($header_parts) == 2) {
323 $headers[$header_parts[0]] = $header_parts[1];
324 }
325 }
326
327 return $headers;
328 }
329 }
330