Skip to content

Instantly share code, notes, and snippets.

@rxnlabs
Last active July 18, 2023 20:13
Show Gist options
  • Save rxnlabs/f9086204b035c1f92095dabfc53aa181 to your computer and use it in GitHub Desktop.
Save rxnlabs/f9086204b035c1f92095dabfc53aa181 to your computer and use it in GitHub Desktop.
WordPress - Upload files to remote API using WP_Http / wp_remote_post + cURL + fsockopen + CurlFile
<?php
$url = 'https://insert-domain-here.com/api-endpoint';
// The file is stored on your system/host
$path_to_uploaded_file = '/full-path-to-file/picture.jpg';
$form_fields = [ 'first_name' => 'Foo', 'last_name' => 'Bar' ];
if ( file_exists( $path_to_uploaded_file ) ) {
$form_fields['profile_picture'] = new CurlFile( $path_to_uploaded_file );
}
/*
Use an anonymous function and pass the local variable that we want to post to that function since
the http_api_curl hook does not pass the data that we actually want to POST with the hook
If $form_fields is global variable, no need to pass it to the anonymous function using the 'use' keyword since 'use' is great for local scope variable
*/
$file_upload_request = function( $handle_or_parameters, $request = '', $url = '' ) use ( $form_fields ) {
updateWPHTTPRequest( $handle_or_parameters, $form_fields );
};
// handle cURL requests if we have cURL installed
add_action( 'http_api_curl', $file_upload_request, 10 );
// handle fsockopen requests if we don't have cURL installed
add_action( 'requests-fsockopen.before_send', $file_upload_request, 10, 3 );
$request = wp_remote_post( $url, [
'body' => $form_fields,
'headers' => [
'content-type' => 'multipart/form-data', // set Content-type: multipart/form-data header for file upload
]
] );
$body = wp_remote_retrieve_body( $request );
$response_code = wp_remote_retrieve_response_code( $request );
// Remove actions so we don't modify any other WP_Http requests
remove_action( 'http_api_curl', $file_upload_request );
remove_action( 'requests-fsockopen.before_send', $file_upload_request );
// Let's upload another photo cause a possum had just attacked me in that photo. I want a cooler photo.
$url2 = 'https://insert-domain-here.com/api-endpoint2';
$path_to_uploaded_file2 = '/full-path-to-file/picture2.jpg';
$form_fields2 = [ 'first_name' => 'Bar', 'last_name' => 'Foo' ];
if ( file_exists( $path_to_uploaded_file2 ) ) {
$form_fields['profile_picture'] = new CurlFile( $path_to_uploaded_file2 );
}
$file_upload_request2 = function( $handle_or_parameters, $request = '', $url = '' ) use ( $form_fields2 ) {
updateWPHTTPRequest( $handle_or_parameters, $form_fields2 );
};
$request2 = wp_remote_post( $url2, [
'body' => $form_fields2,
'headers' => [
'content-type' => 'multipart/form-data',
]
] );
$body2 = wp_remote_retrieve_body( $request2 );
$response_code2 = wp_remote_retrieve_response_code( $request2 );
remove_action( 'http_api_curl', $file_upload_request2 );
remove_action( 'requests-fsockopen.before_send', $file_upload_request2 );
/*
.
.:;:.
.:;;;;;:.
;;;;;
;;;;;
;;;;;
;;;;;
;:;;;
You saw what we had to do above right?? We have to use a local variable of $form_fields and $form_fields2 to pass to our hook
so we can modify the wp_remote_post request to use the form fields in the way that we want.
You'll either have to use global variables or localled scoped variables to pass the array or form fields that we want to post
to the remote API
*/
/**
* Update the WP cURL and fsockopen requests to make them work with file uploads.
* @param resource|array $handle_or_parameters cURL handle or fsockopen parameters. This is passed by reference
* @param array $form_body_arguments Form data to POST to the remote service
* @return void
*/
function updateWPHTTPRequest( &$handle_or_parameters, $form_body_arguments ) {
if ( function_exists( 'curl_init' ) && function_exists( 'curl_exec' ) ) {
foreach ( $form_body_arguments as $value ) {
// Only do this if we are using PHP 5.5+ CURLFile file to upload a file
if ( 'object' === gettype( $value ) && $value instanceof CURLFile ) {
/*
Use the request body as an array to force cURL make a requests using 'multipart/form-data'
as the Content-type header instead of WP's default habit of converting the request to
a string using http_build_query function
*/
curl_setopt( $handle_or_parameters, CURLOPT_POSTFIELDS, $form_body_arguments );
break;
}
}
} elseif ( function_exists( 'fsockopen' ) ) {
// UNTESTED SINCE I HAVE cURL INSTALLED AND CANNOT TEST THIS
$form_fields = [];
$form_files = [];
foreach ( $form_body_arguments as $name => $value ) {
if ( file_exists( $value ) ) {
// Not great for large files since it dumps into memory but works well for small files
$form_files[$name] = file_get_contents( $value );
} else {
$form_fields[$name] = $value;
}
}
/**
* Convert form fields arrays to a string that fsockopen requests can understand
*
* @see https://gist.github.com/maxivak/18fcac476a2f4ea02e5f80b303811d5f
*/
function build_data_files( $boundary, $fields, $files ){
$data = '';
$eol = "\r\n";
$delimiter = '-------------' . $boundary;
foreach ( $fields as $name => $content ) {
$data .= "--" . $delimiter . $eol
. 'Content-Disposition: form-data; name="' . $name . "\"".$eol.$eol
. $content . $eol;
}
foreach ( $files as $name => $content ) {
$data .= "--" . $delimiter . $eol
. 'Content-Disposition: form-data; name="' . $name . '"; filename="' . $name . '"' . $eol
//. 'Content-Type: image/png'.$eol
. 'Content-Transfer-Encoding: binary'.$eol
;
$data .= $eol;
$data .= $content . $eol;
}
$data .= "--" . $delimiter . "--".$eol;
return $data;
}
$boundary = uniqid();
$handle_or_parameters = build_data_files( $boundary, $form_fields, $form_files );
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment