Skip to content

Instantly share code, notes, and snippets.

@afair
Last active December 24, 2022 06:18
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save afair/a7c7adc52b7b49bf362935e665a87633 to your computer and use it in GitHub Desktop.
Save afair/a7c7adc52b7b49bf362935e665a87633 to your computer and use it in GitHub Desktop.
PHP Function to make a remote HTTP request (POST, Authentication, API, Files, etc.) and returns the response
<?php
/**
* Makes a remote HTTP request and returns the response.
*
* @author Allen Fair <allen.fair@gmail.com>
* @copyright 2018 Allen Fair
* @license https://opensource.org/licenses/MIT MIT
*
* @param string $url The full URL endpoint: "https://example.com/endpoint"
* @param array $opt An Associative Array of extended request options
*
* string 'method' one of: GET, POST, PUT, PATCH, DELETE, HEAD
* string 'url' URL override
* string 'endpoint' Optional endpoint appended to URL
* string 'user' user name to send for HTTP Basic Authorization
* string 'token' creates an "Authorization: Bearer" header
* string 'accept' creates an Accept: header for the mime/type
* string 'password' password to send for HTTP Basic Authorization
* array 'headers' array of "Header-Name: value", ...
* array 'query' name=>value pairs for the Query String
* array 'post' name=>value pairs for the POST Data body
* array 'files' name=>['path'=>"/path/file", 'filename'=>"name.ext",
* 'type'=>"mime1/mime2"] pairs of files to send
* array 'cookies' name=value pairs for cookies to send
* array 'http_options' name=value pairs for "HTTP context options"
* callable 'notifications' a stream_notification_callback for events on the stream.
*
* @return array [$http_status_code, $headers_hash, $body]
*
* Note: sending very large files/requests must fit into memory. Chunking is not supported.
*
* @example
* list($status, $headers, $content) = http_request('https://www.example.com/endpoint', array(
* 'method' => 'POST',
* 'headers' => array('From'=>'pat@mycompany.com'),
* 'accept' => 'application/json',
* 'user' => $apikey, 'password'=>'', 'token'=>$token, // Authentication
* 'query' => array('q' => 'PHP HTTP request'),
* 'post' => array('email' => 'pat@mycompany.com'),
* 'files' => array('path'=> './avatar.png', 'filename'=>'avatar.png', 'type'=>'image/png'),
* 'cookies' => array('session' => $jwt),
* 'http_options' => array('timeout'=> 5.0, 'user_agent'=>"php_request/1.0"),
* ));
*/
function http_request($url, $opt=array()) {
$http_options = array_key_exists('http_options', $opt) ? $opt['http_options'] : array();
$headers = array_key_exists('headers', $opt) ? $opt['headers'] : array();
$url = array_key_exists('url', $opt) ? $opt['url'] : $url;
$endpoint = array_key_exists('endpoint', $opt) ? $opt['endpoint'] : null;
$method = array_key_exists('method', $opt) ? strtoupper($opt['method']) : 'GET';
$query = array_key_exists('query', $opt) ? $opt['query'] : null;
$post = array_key_exists('post', $opt) ? $opt['post'] : null;
$files = array_key_exists('files', $opt) ? $opt['files'] : null;
$notifications= array_key_exists('notifications', $opt) ? $opt['notifications'] : null;
// Headers
if (array_key_exists('accept', $opt)) {
$headers[] = "Accept: " . $opt['accept'];
}
if (array_key_exists('user', $opt)) {
$headers[] = "Authorization: Basic ".base64_encode($opt['user'].':'.$opt['password']);
}
elseif (array_key_exists('token', $opt)) {
$headers[] = "Authorization: Bearer ".$opt['token'];
}
if (array_key_exists('cookies', $opt)) {
foreach ($opt['cookies'] as $n=>$v) $headers[] = "Cookie: $n=$v";
}
// URL, appended Endpoint, and Query Parameters
if ($endpoint) $url .= $endpoint;
if (!empty($query)) {
$url .= (strpos("?",$url)===false ? "?" : "&") . http_build_query($query);
}
// File + Post Parameters in multipart format
if (!empty($files)) {
$boundary = '--------------------------'.microtime(true);
$content = array();
foreach ($files as $field=>$f) {
$content[] ="--$boundary";
$content[] = "Content-Disposition: form-data; name=\"$field\"; filename=\"".basename($filename)."\"";
$content[] = "Content-Type: {$f['type']}";
$content[] = "Content-Length: " . filesize($f['path']);
$content[] = "";
$content[] = file_get_contents($f['path']);
$content[] = "";
}
foreach ($post as $field=>$v) {
$content[] ="--$boundary";
$content[] = "Content-Disposition: form-data; name=\"$field\"";
$content[] = "";
$content[] = $v;
$content[] = "";
}
$content[] = "--$boundary--";
$headers[] = "Content-Type: multipart/form-data; boundary=$boundary";
$http_options['content'] = implode("\r\n", $content);
unset($content);
}
// Post Parameters in urlencoded format
elseif (!empty($post)) {
$headers[] = "Content-Type: application/x-www-form-urlencoded";
$http_options['content'] = http_build_query($post);
}
// Make Request
if (!$http_options['user_agent']) $http_options['user_agent'] = "http_request.php/1.0";
$http_options['method'] = $method;
if ($http_options['content']) $headers[] = "Content-Length: ".strlen($http_options['content']);
if (!empty($headers)) {
$http_options['header'] = implode("\r\n", $headers);
}
$context = stream_context_create(array('http'=>$http_options));
if ($notifications) {
stream_context_set_params($context, array("notification" => $notifications));
}
if ($method == 'HEAD') {
$headlist = @get_headers($url, 0, $context);
$response = null;
} else {
$response = @file_get_contents($url, false, $context);
$headlist = $http_response_header;
}
// Format Response
$status = preg_match("/(\d\d\d) (.+)/", $headlist[0], $m) ? $m[1] : 500;
$headers = array("Status" => $headlist[0]);
foreach ($headlist as $h) {
if (preg_match("/^([\w\-]+):\s*(.+)/", $h, $m)) $headers[$m[1]] = $m[2];
}
return array($status, $headers, $response);
}
?>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment