Well after much experimentation and searching I figured out the answer for this problem. The biggest culprit is indeed the content type. If you use the following code:
$data = array(
'uploaded_file' => curl_file_create($local_file['tmp_name'], $local_file['type'], $fileName)
);
/// redacted for space
CURLOPT_POSTFIELDS=> $data,
curl will automatically strip out any content-type you give it and supply its own. you get the following headers:
Content-Type: multipart/form-data; boundary=----------637571310612295910
Content-Length: 12184
------------637571310612295910
Content-Disposition: form-data; name="uploaded_file"; filename="{filename}.docx"
Content-Type: application/vnd.openxmlformats-officedocument.wordprocessingml.document
Sharepoint does not like this at all. So, you need to send binary data, not multipart/form-data. This can be achieved like so:
$uploadFile = file_get_contents($local_file['tmp_name']);
$curl = curl_init();
curl_setopt_array($curl, array(
CURLOPT_URL => $client_upload_url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_NONE ,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS=> $uploadFile, //<-- where the magic happens
CURLOPT_HTTPHEADER => array(
"Accept: application/json;odata=verbose",
"cache-control: no-cache",
"X-RequestDigest: " . $digest_value,
"Content-Type: application/vnd.openxmlformats-officedocument.wordprocessingml.document",//
"Authorization: {values}
),
));
This will net you a result like follows
Accept: application/json; odata=verbose
Cache-Control: no-cache
X-RequestDigest:{redacted}
Authorization: {redacted}
Connection: Keep-Alive
Request-Id: |1bf3eca4-45702011fc30c20b.2.
Content-Type: application/vnd.openxmlformats-officedocument.wordprocessingml.document
Content-Length: 11947
{raw body here, not multipart/formdata}
moral of the story is file_get_contents will get you the binary data as a string. Which you can dump directly in the CURLOPT_POSTFIELDS.
The inspiration for this was astonishingly from a post from 2008 found here