| Code Coverage | ||||||||||
| Classes and Traits | Functions and Methods | Lines | ||||||||
| Total |  | 0.00% | 0 / 1 |  | 0.00% | 0 / 8 | CRAP |  | 25.97% | 40 / 154 | 
| Util |  | 0.00% | 0 / 1 |  | 0.00% | 0 / 8 | 1148.88 |  | 25.97% | 40 / 154 | 
| urlEncode ($parms) |  | 0.00% | 0 / 1 | 5.02 |  | 60.00% | 6 / 10 | |||
| mergeCurlOptions ($base, $additional) |  | 0.00% | 0 / 1 | 132 |  | 0.00% | 0 / 21 | |||
| ensureCurlErrorConstants () |  | 0.00% | 0 / 1 | 12 |  | 0.00% | 0 / 25 | |||
| addQueryData ($url, $parms) |  | 0.00% | 0 / 1 | 5.07 |  | 85.71% | 12 / 14 | |||
| assembleUrl ($parts) |  | 0.00% | 0 / 1 | 9.80 |  | 78.57% | 22 / 28 | |||
| parseCookieHeader ($hdr) |  | 0.00% | 0 / 1 | 90 |  | 0.00% | 0 / 23 | |||
| parseCookieElement ($elm) |  | 0.00% | 0 / 1 | 110 |  | 0.00% | 0 / 32 | |||
| __construct () |  | 0.00% | 0 / 1 | 2 |  | 0.00% | 0 / 1 | |||
| <?php | |
| /** | |
| * @package Moar\Net\Http | |
| */ | |
| namespace Moar\Net\Http; | |
| /** | |
| * HTTP utilities. | |
| * | |
| * @package Moar\Net\Http | |
| */ | |
| class Util { | |
| const COOKIE_NAME = 'cookie-name'; | |
| const COOKIE_VALUE = 'cookie-value'; | |
| /** | |
| * Make a URL-encoded string from a key=>value array | |
| * @param array $parms Parameter array | |
| * @return string URL-encoded message body | |
| */ | |
| public static function urlEncode ($parms) { | |
| $payload = array(); | |
| foreach ($parms as $key => $value) { | |
| if (is_array($value)) { | |
| foreach ($value as $item) { | |
| $payload[] = urlencode($key) . '=' . urlencode($item); | |
| } | |
| } else { | |
| $payload[] = urlencode($key) . '=' . urlencode($value); | |
| } | |
| } | |
| return implode('&', $payload); | |
| } //end urlEncode | |
| /** | |
| * Merge two arrays of curl options together into a new array. | |
| * | |
| * Values in the additional array will override values in the base array | |
| * except in the case of CURLOPT_HTTPHEADER where values from the additional | |
| * array will be merged with values from the base array if any exist. | |
| * | |
| * The additional array can be keyed with either ints which will be assumed | |
| * to be CURLOPT_* values or strings such as 'CURLOPT_USERPWD' which will be | |
| * turned into ints via constant(). This makes setting up options in a DI or | |
| * other non-php scripted senario easier to implement. | |
| * | |
| * @param array $base Base options | |
| * @param array $additional Additional options | |
| * @return array New array of base options with additional options overlayed | |
| * @throws \InvalidArgumentException If invalid string key is used | |
| */ | |
| public static function mergeCurlOptions ($base, $additional) { | |
| if (null === $additional || empty($additional)) { | |
| return $base; | |
| } | |
| foreach ($additional as $key => $val) { | |
| if (!is_int($key)) { | |
| // treat non-numeric keys as CURLOPT_* constants to be resolved | |
| try { | |
| $curlkey = constant($key); | |
| } catch (Exception $ignored) { | |
| // no-op | |
| } | |
| if (null === $curlkey) { | |
| throw new \InvalidArgumentException("Invalid curl option [{$key}]."); | |
| } | |
| $key = $curlkey; | |
| } | |
| if (CURLOPT_HTTPHEADER == $key && | |
| array_key_exists(CURLOPT_HTTPHEADER, $base)) { | |
| // additional headers are cumlative | |
| if (is_array($val)) { | |
| // concatinate header collection | |
| foreach ($val as $header) { | |
| $base[CURLOPT_HTTPHEADER][] = $header; | |
| } | |
| } else { | |
| // add single header | |
| $base[CURLOPT_HTTPHEADER][] = $val; | |
| } | |
| } else { | |
| $base[$key] = $val; | |
| } | |
| } //end foreach | |
| return $base; | |
| } //end mergeCurlOptions | |
| /** | |
| * Ensure that constants are available for useful cURL error codes. | |
| * | |
| * @return void | |
| * @see http://curl.haxx.se/libcurl/c/libcurl-errors.html | |
| */ | |
| public static function ensureCurlErrorConstants () { | |
| $defs = array( | |
| 'CURLE_COULDNT_CONNECT' => 7, | |
| 'CURLE_COULDNT_RESOLVE_HOST' => 6, | |
| 'CURLE_HTTP_RETURNED_ERROR' => 22, | |
| 'CURLE_OPERATION_TIMEDOUT' => 28, | |
| 'CURLE_PEER_FAILED_VERIFICATION' => 51, | |
| 'CURLE_SSL_CACERT' => 60, | |
| 'CURLE_SSL_CACERT_BADFILE' => 77, | |
| 'CURLE_SSL_CERTPROBLEM' => 58, | |
| 'CURLE_SSL_CIPHER' => 59, | |
| 'CURLE_SSL_CONNECT_ERROR' => 35, | |
| 'CURLE_SSL_CRL_BADFILE' => 82, | |
| 'CURLE_SSL_ENGINE_INITFAILED' => 66, | |
| 'CURLE_SSL_ENGINE_NOTFOUND' => 53, | |
| 'CURLE_SSL_ENGINE_SETFAILED' => 54, | |
| 'CURLE_SSL_ISSUER_ERROR' => 83, | |
| 'CURLE_SSL_SHUTDOWN_FAILED' => 80, | |
| 'CURLE_UNSUPPORTED_PROTOCOL' => 1, | |
| 'CURLE_URL_MALFORMAT' => 3, | |
| 'CURLE_USE_SSL_FAILED' => 64, | |
| ); | |
| foreach ($defs as $constName => $errCode) { | |
| if (!defined($constName)) { | |
| define($constName, $errCode); | |
| } | |
| } | |
| } //end ensureCurlConstants | |
| /** | |
| * Append a query string to the given URL. | |
| * | |
| * @param string $url URL to append to | |
| * @param string|array $parms Parameters to add as query string to url | |
| * @return string Composed URL | |
| */ | |
| public static function addQueryData ($url, $parms) { | |
| if (is_array($parms)) { | |
| // construct GET data | |
| $payload = self::urlEncode($parms); | |
| } else if (null !== $parms) { | |
| $payload = (string) $parms; | |
| } | |
| if (!empty($payload)) { | |
| $parts = parse_url($url); | |
| if (isset($parts['query'])) { | |
| $parts['query'] .= "&{$payload}"; | |
| } else { | |
| $parts['query'] = $payload; | |
| } | |
| $url = self::assembleUrl($parts); | |
| } | |
| return $url; | |
| } //end addQueryData | |
| /** | |
| * Assemble a URL from a array of components as would be returned by | |
| * parse_url(). | |
| * | |
| * @param array $parts URL parts | |
| * @return string URL | |
| */ | |
| public static function assembleUrl ($parts) { | |
| $url = ''; | |
| if (isset($parts['scheme'])) { | |
| $url .= "{$parts['scheme']}:"; | |
| } | |
| $url .= '//'; | |
| if (isset($parts['user'])) { | |
| $url .= $parts['user']; | |
| if (isset($parts['password'])) { | |
| $url .= ":{$parts['password']}"; | |
| } | |
| $url .= '@'; | |
| } | |
| if (isset($parts['host'])) { | |
| $url .= $parts['host']; | |
| } | |
| if (isset($parts['port'])) { | |
| $url .= ":{$parts['port']}"; | |
| } | |
| if (isset($parts['path'])) { | |
| $url .= $parts['path']; | |
| } | |
| if (isset($parts['query'])) { | |
| $url .= "?{$parts['query']}"; | |
| } | |
| if (isset($parts['fragment'])) { | |
| $url .= "#{$parts['fragment']}"; | |
| } | |
| return $url; | |
| } //end assembleUrl | |
| /** | |
| * Parse a "Set-Cookie" header to get the component cookie data. | |
| * | |
| * @param string $hdr Header data | |
| * @return array Collection of cookies specified by the header | |
| */ | |
| public static function parseCookieHeader ($hdr) { | |
| $cookies = array(); | |
| $i = 0; | |
| $from = 0; | |
| $len = mb_strlen($hdr, 'latin1'); | |
| $quoted = false; | |
| while ($i < $len) { | |
| if ('"' === $hdr[$i] && '\\' !== $elm[$i - 1]) { | |
| $quoted = !$quoted; | |
| } | |
| $elm = null; | |
| if (!$quoted && ',' === $hdr[$i]) { | |
| $chunk = mb_substr($hdr, $from, $i - $from, 'latin1'); | |
| $elm = self::parseCookieElement($chunk); | |
| $from = $i + 1; | |
| } else if ($i === $len - 1) { | |
| $chunk = mb_substr($hdr, $from, $len - $from, 'latin1'); | |
| $elm = self::parseCookieElement($chunk); | |
| } | |
| if (null !== $elm && isset($elm[self::COOKIE_NAME])) { | |
| $cookies[] = $elm; | |
| } | |
| $i += 1; | |
| } //end while | |
| return $cookies; | |
| } //end parseCookieHeader | |
| /** | |
| * Parse a single cookie setting. | |
| * @param string $elm Cookie element | |
| * @return array Associative array of cookie data | |
| */ | |
| protected static function parseCookieElement ($elm) { | |
| $cookie = array(); | |
| // eat whitespace outside of quotes | |
| // each part ends with a semi-colon outside of quotes | |
| $i = 0; | |
| $from = 0; | |
| $len = mb_strlen($elm, 'latin1'); | |
| $quoted = false; | |
| while ($i < $len) { | |
| if ('"' === $elm[$i] && '\\' !== $elm[$i - 1]) { | |
| $quoted = !$quoted; | |
| } | |
| $chunk = null; | |
| if (!$quoted && ';' === $elm[$i]) { | |
| $chunk = mb_substr($elm, $from, $i - $from, 'latin1'); | |
| $from = $i + 1; | |
| } else if ($i === $len - 1) { | |
| $chunk = mb_substr($elm, $from, $len - $from, 'latin1'); | |
| } | |
| if (null !== $chunk) { | |
| $parts = explode('=', $chunk, 2); | |
| if (count($parts) == 1) { | |
| // we found a flag of some sort | |
| $name = trim($parts[0]); | |
| $cookie[$name] = true; | |
| } else { | |
| $name = trim($parts[0]); | |
| $val = trim($parts[1]); | |
| // remove quotes | |
| $val = trim($val, '"'); | |
| if (empty($cookie)) { | |
| // first key=value pair is the cookie name and value | |
| $cookie[self::COOKIE_NAME] = $name; | |
| $cookie[self::COOKIE_VALUE] = $val; | |
| } else { | |
| $cookie[$name] = $val; | |
| } | |
| } //end if/else | |
| } //end if | |
| $i += 1; | |
| } //end while | |
| return $cookie; | |
| } //end parseCookieElement | |
| /** | |
| * Construction disallowed. | |
| */ | |
| private function __construct () { | |
| // no-op | |
| } | |
| } //end Util |