PHP 8.1: Curl: File uploads from strings with CURLStringFile

Version8.1
TypeNew Feature

PHP Curl extension supports making HTTP(s) requests with file uploads. Since PHP 5.5, PHP Curl extension provides a CURLFile class that accepts a URI or a path to a file, a mime type, and a file name to upload it as.

With CURLFile class, it accepts a path or a URI, and not the contents itself. For files already stored on disk or any other stream, using CURLFile class is quite straight-forward:

$txt_curlfile = new \CURLFile('test.txt', 'text/plain', 'test.txt');

$ch = curl_init('http://example.com/upload.php');
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, ['file' => $txt_curlfile]);
curl_exec($ch);

Using CURLFile was difficult if the file being uploaded is already stored in the memory, or not written to disk. It was possible to use data:// URIs with Base64 encoding, but it was not straight forward.

$txt = 'test content';
$txt_file = 'data://application/octet-stream;base64,' . base64_encode($txt);
$txt_curlfile = new \CURLFile($txt_file, 'text/plain', 'test.txt');

$ch = curl_init('http://example.com/upload.php');
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, ['file' => $txt_curlfile]);
curl_exec($ch);

From PHP 8.1 and later, Curl extension has a new CURLStringFile class, that works similar to CURLFile class, but accepts the contents of the file instead of a path or a URI.

$txt = 'test content';
$txt_curlfile = new \CURLStringFile($txt, 'text/plain', 'test.txt');

$ch = curl_init('http://example.com/upload.php');
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, ['file' => $txt_curlfile]);
curl_exec($ch);

The new CURLStringFile class makes it easy to make a Curl file upload request using data that is already stored in memory. Some example use cases include uploading an image that PHP processes and is stored in a PHP variable, a processed XML file, or a PDF file generated from a PHP library.

CURLStringFile class synopsis

class CURLStringFile {
    public string $data;
    public string $postname;
    public string $mime;

    public function __construct(string $data, string $postname, string $mime = "application/octet-stream") {}
}
  • Unlike CURLFile class, CURLStringFile class objects allow serializing and unserializing.
  • CURLStringFile objects can be clone. This is true for CURLFile instances as well.
  • CURLStringFile class enforces typed properties. It is not allowed to set values other than string values.

CURLStringFile Polyfill

Curl extension treats CURLFile class objects as a file upload, and it treats the same way for all classes that extend CURLFile.

Taking advantage of that, it is possible to create a polyfill for CURLStringFile.

class CURLStringFile extends CURLFile {
    public function __construct(string $data, string $postname, string $mime = "application/octet-stream") {
        $this->name     = 'data://'. $mime .';base64,' . base64_encode($data);
        $this->mime     = $mime;
        $this->postname = $postname;
    }
}

Internally, the polyfilled CURLStringFile class behaves exactly same as a CURLFile class with a data:// URI, but it is not handled inside CURLStringFile::__construct hiding the complexity, and working as a polyfill.

The polyfill above should work in all PHP versions >= 7.0.

Backwards Compatibility Impact

CURLStringFile is a new class from the Curl extension, and it will not be possible to use it in PHP versions older than PHP 8.1. However, it is possible to polyfill this class to older PHP versions.


Implementation