Skip to content

Instantly share code, notes, and snippets.

@dkrusky
Created October 31, 2018 09:09
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dkrusky/0658e9e4a410a5fc62fdc1842b36d9db to your computer and use it in GitHub Desktop.
Save dkrusky/0658e9e4a410a5fc62fdc1842b36d9db to your computer and use it in GitHub Desktop.
Ultimate curl wrapper
<?php
//abstract class curl {
class curl {
private $error_codes;
private $_headers;
private $_options;
public $headers;
public $options;
private $response_header;
private $default_headers = array();
private $default_options = array(
CURLOPT_RETURNTRANSFER => true,
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_VERBOSE => false,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_IPRESOLVE => CURL_IPRESOLVE_V4
);
function __construct() {
$this->headers = new collection();
$this->_headers = new collection( $this->default_headers );
// sets some defaults for all curl connections
$this->options = new collection( );
$this->_options = new collection( $this->default_options );
// initialize list of all curl error constants, and values
$list = get_defined_constants(TRUE);
$list = preg_grep('/^CURLE_/', array_flip($list['curl']));
foreach ($list as $code=>$constant) {
$list[$code] = array
(
'code' => $code,
'constant' => $constant,
'message' => curl_strerror($code)
);
}
$this->error_codes = $list;
}
private function mergeValues( $arrayA, $arrayB ) {
if(empty($arrayA) && empty($arrayB)) {
return new collection();
} elseif( empty($arrayA) && !empty($arrayB) ) {
return new collection($arrayB);
} elseif( !empty($arrayA) && empty($arrayB) ) {
return new collection($arrayA);
} elseif( !empty($arrayA) && !empty($arrayB) ) {
return new collection(array_merge($arrayA, $arrayB));
}
return new collection();
}
private function getOptions() {
$_options = $this->_options->value(false);
$options = $this->options->value(false);
return $this->mergeValues($options, $options);
}
private function getHeaders() {
$_headers = $this->_headers->value(false);
$headers = $this->headers->value(false);
return $this->mergeValues($_headers, $headers);
}
// reset curl options, and headers to defaults
function reset() {
$this->headers->set(array(), true);
$this->options->set(array(), true);
return true;
}
// DELETE request
function delete($url, $data) {
// remove post/custom request options
$this->options->remove( CURLOPT_POST );
// add curstom request for put
$this->options->add( CURLOPT_CUSTOMREQUEST, "DELETE" );
$this->options->add( CURLOPT_POSTFIELDS, $data );
// process request and return response
return $this->curl_request( $url );
}
// PUT request
function put($url, $data) {
// remove post/custom request options
$this->options->remove( CURLOPT_POST );
// add curstom request for put
$this->options->add( CURLOPT_CUSTOMREQUEST, "PUT" );
$this->options->add( CURLOPT_POSTFIELDS, $data );
// process request and return response
return $this->curl_request( $url );
}
// GET request
function get($url) {
// remove post/custom request options
$this->options->remove( CURLOPT_CUSTOMREQUEST );
$this->options->remove( CURLOPT_POST );
$this->options->remove( CURLOPT_POSTFIELDS );
// process request and return response
return $this->curl_request( $url );
}
// POST request
function post($url, $data) {
// remove custom request if set
$this->options->remove( CURLOPT_CUSTOMREQUEST );
// set basic parameters
$this->options->add( CURLOPT_POST, true );
$this->options->add( CURLOPT_POSTFIELDS, $data );
// process request and return response
return $this->curl_request( $url );
}
// internal handler for processing curl header lines
private function curl_header_response($curl, $header) {
$_header = trim($header);
if(!empty($_header)) {
$this->response_header[] = $_header;
}
return strlen($header);
}
// universal curl handler
private function curl_request($url) {
// clear response_header object
$this->response_header = array();
$options = $this->getOptions();
$headers = $this->getHeaders();
// set url to connect to
$options->add( CURLOPT_URL, $url );
// initialize headers
if(!empty($headers->value())) {
$options->add( CURLOPT_HTTPHEADER, $headers->value() );
}
// set handler to process headers so they can be returned with the result
$options->add( CURLOPT_HEADERFUNCTION, array(&$this, 'curl_header_response') );
// initialize response object and get scheme of url being connected to
$response = new stdClass();
$response->scheme = parse_url($url, PHP_URL_SCHEME);
// if https is set, verify peer, and set ssl version
if($response->scheme == 'https') {
$options->add( CURLOPT_SSL_VERIFYPEER, true );
$options->add( CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_2, false );
$options->add( CURLOPT_CERTINFO, true );
} else {
$options->remove( CURLOPT_SSL_VERIFYPEER, true );
$options->remove( CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_2 );
$options->remove( CURLOPT_CERTINFO, true );
}
$curl = curl_init();
curl_setopt_array(
$curl,
$options->value(false)
);
// get the data
$response->data = new stdClass();
$response->data->raw = curl_exec($curl);
$response->data->type = trim(curl_getinfo($curl, CURLINFO_CONTENT_TYPE));
$response->data->header = $this->response_header;
if(strpos($response->data->type, ';')) {
// parse content-type if contains more than just the mime type
$response->data->type = trim(explode(';', $response->data->type)[0]);
}
// automatically decode based on mime type
switch($response->data->type) {
case 'application/xml';
case 'text/xml';
$response->data->object = simplexml_load_string($response->data->raw);
break;
case 'application/json';
case 'text/json';
case 'application/vnd.api+json';
$response->data->object = json_decode($response->data->raw);
break;
}
// get curl error information
$response->curl = $this->curl_code(curl_errno($curl));
$response->curl->error = curl_error($curl);
// get http status code information
$response->http = new stdClass();
$response->http->code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
$response->http->description = $this->http_code($response->http->code);
$response->http->url = curl_getinfo($curl, CURLINFO_EFFECTIVE_URL);
if($response->scheme == 'https') {
// get ssl information if scheme is https
$response->ssl = new stdClass();
$response->ssl->code = curl_getinfo($curl, CURLINFO_SSL_VERIFYRESULT);
$response->ssl->engines = curl_getinfo($curl, CURLINFO_SSL_ENGINES);
$response->ssl->message = $this->ssl_code($response->ssl->code);
$response->ssl->valid = false;
if($response->ssl->code == 0) {
$response->ssl->valid = true;
$response->ssl->chain = curl_getinfo($curl, CURLINFO_CERTINFO);
}
}
curl_close($curl);
return $response;
}
// used to translate curl error codes into more meaningful information
private function curl_code($int) {
$int = intval($int);
if($int == 0) {
return (object)array(
'code' => 0,
'constant' => 'SUCCESS',
'message' => "Communication was successful"
);
}
if(isset($this->error_codes[$int])) {
return (object)$this->error_codes[$int];
}
return (object)array(
'code' => 13377331,
'constant' => 'L33T_CODE_L33T',
'message' => "No curl error code was found to match the value : {$int}"
);
}
private function ssl_code($int) {
$int = intval($int);
$ssl_codes = array(
'0' => 'X509_V_OK',
'2' => 'X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT',
'3' => 'X509_V_ERR_UNABLE_TO_GET_CRL',
'4' => 'X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE',
'5' => 'X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE',
'6' => 'X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY',
'7' => 'X509_V_ERR_CERT_SIGNATURE_FAILURE',
'8' => 'X509_V_ERR_CRL_SIGNATURE_FAILURE',
'9' => 'X509_V_ERR_CERT_NOT_YET_VALID',
'10' => 'X509_V_ERR_CERT_HAS_EXPIRED',
'11' => 'X509_V_ERR_CRL_NOT_YET_VALID',
'12' => 'X509_V_ERR_CRL_HAS_EXPIRED',
'13' => 'X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD',
'14' => 'X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD',
'15' => 'X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD',
'16' => 'X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD',
'17' => 'X509_V_ERR_OUT_OF_MEM',
'18' => 'X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT',
'19' => 'X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN',
'20' => 'X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY',
'21' => 'X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE',
'22' => 'X509_V_ERR_CERT_CHAIN_TOO_LONG',
'23' => 'X509_V_ERR_CERT_REVOKED',
'24' => 'X509_V_ERR_INVALID_CA',
'25' => 'X509_V_ERR_PATH_LENGTH_EXCEEDED',
'26' => 'X509_V_ERR_INVALID_PURPOSE',
'27' => 'X509_V_ERR_CERT_UNTRUSTED',
'28' => 'X509_V_ERR_CERT_REJECTED',
'29' => 'X509_V_ERR_SUBJECT_ISSUER_MISMATCH',
'30' => 'X509_V_ERR_AKID_SKID_MISMATCH',
'31' => 'X509_V_ERR_AKID_ISSUER_SERIAL_MISMATCH',
'32' => 'X509_V_ERR_KEYUSAGE_NO_CERTSIGN',
'50' => 'X509_V_ERR_APPLICATION_VERIFICATION'
);
if(isset($ssl_codes[$int])) {
return $ssl_codes[$int];
}
return "Undefined Code [{$int}]";
}
// used to translate http status codes into more meaningful information
private function http_code($int) {
$int = intval($int);
$http_codes = array(
100 => 'Continue',
101 => 'Switching Protocols',
102 => 'Processing', // WebDAV; RFC 2518
200 => 'OK',
201 => 'Created',
202 => 'Accepted',
203 => 'Non-Authoritative Information', // since HTTP/1.1
204 => 'No Content',
205 => 'Reset Content',
206 => 'Partial Content',
207 => 'Multi-Status', // WebDAV; RFC 4918
208 => 'Already Reported', // WebDAV; RFC 5842
226 => 'IM Used', // RFC 3229
300 => 'Multiple Choices',
301 => 'Moved Permanently',
302 => 'Found',
303 => 'See Other', // since HTTP/1.1
304 => 'Not Modified',
305 => 'Use Proxy', // since HTTP/1.1
306 => 'Switch Proxy',
307 => 'Temporary Redirect', // since HTTP/1.1
308 => 'Permanent Redirect', // approved as experimental RFC
400 => 'Bad Request',
401 => 'Unauthorized',
402 => 'Payment Required',
403 => 'Forbidden',
404 => 'Not Found',
405 => 'Method Not Allowed',
406 => 'Not Acceptable',
407 => 'Proxy Authentication Required',
408 => 'Request Timeout',
409 => 'Conflict',
410 => 'Gone',
411 => 'Length Required',
412 => 'Precondition Failed',
413 => 'Request Entity Too Large',
414 => 'Request-URI Too Long',
415 => 'Unsupported Media Type',
416 => 'Requested Range Not Satisfiable',
417 => 'Expectation Failed',
418 => 'I\'m a teapot', // RFC 2324
419 => 'Authentication Timeout', // not in RFC 2616
420 => 'Enhance Your Calm', // Twitter
420 => 'Method Failure', // Spring Framework
422 => 'Unprocessable Entity', // WebDAV; RFC 4918
423 => 'Locked', // WebDAV; RFC 4918
424 => 'Failed Dependency', // WebDAV; RFC 4918
424 => 'Method Failure', // WebDAV)
425 => 'Unordered Collection', // Internet draft
426 => 'Upgrade Required', // RFC 2817
428 => 'Precondition Required', // RFC 6585
429 => 'Too Many Requests', // RFC 6585
431 => 'Request Header Fields Too Large', // RFC 6585
444 => 'No Response', // Nginx
449 => 'Retry With', // Microsoft
450 => 'Blocked by Windows Parental Controls', // Microsoft
451 => 'Redirect', // Microsoft
451 => 'Unavailable For Legal Reasons', // Internet draft
494 => 'Request Header Too Large', // Nginx
495 => 'Cert Error', // Nginx
496 => 'No Cert', // Nginx
497 => 'HTTP to HTTPS', // Nginx
499 => 'Client Closed Request', // Nginx
500 => 'Internal Server Error',
501 => 'Not Implemented',
502 => 'Bad Gateway',
503 => 'Service Unavailable',
504 => 'Gateway Timeout',
505 => 'HTTP Version Not Supported',
506 => 'Variant Also Negotiates', // RFC 2295
507 => 'Insufficient Storage', // WebDAV; RFC 4918
508 => 'Loop Detected', // WebDAV; RFC 5842
509 => 'Bandwidth Limit Exceeded', // Apache bw/limited extension
510 => 'Not Extended', // RFC 2774
511 => 'Network Authentication Required', // RFC 6585
598 => 'Network read timeout error', // Unknown
599 => 'Network connect timeout error', // Unknown
);
if(isset($http_codes[$int])) {
return $http_codes[$int];
}
return "Undefined Code";
}
}
// collection object
class collection {
private $collection;
function __construct($array = array()) {
if(!empty($array) && is_array($array)) {
$this->collection = $array;
} else {
$this->collection = array();
}
}
function set($array = array(), $force = false) {
if(!empty($array) && is_array($array)) {
$this->collection = $array;
return true;
}
if($force == true) {
$this->collection = array();
return true;
}
return false;
}
function add($key, $value, $overwrite = true) {
$key = strtolower($key);
if(isset($this->collection[$key])) {
if($overwrite) {
$this->collection[$key] = $value;
return true;
}
return false;
}
$this->collection[$key] = $value;
return true;
}
function remove($key) {
$key = strtolower($key);
if(isset($this->collection[$key])) {
unset($this->collection[$key]);
}
return true;
}
function get($key) {
$key = strtolower($key);
if(isset($this->collection[$key])) {
return $this->collection[$key];
}
return false;
}
function clear() {
$this->collection = array();
return true;
}
function value($compact = true) {
if($compact) {
$result = array();
foreach($this->collection as $key=>$value) {
$result[] = $key . ':' . $value;
}
return $result;
}
return $this->collection;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment