Menu

Reference / Http

UploadedFile

finalclass
final class UploadedFile

One file received in a multipart/form-data upload: php's $_FILES row as an object.

Get them from Request::files() or Request::allFiles(). The bytes were written to a temporary file during parsing. Unless you claim it with moveTo(), the temporary file is deleted when the request ends, whether the handler returned or threw. tmpName is empty unless error is ERR_OK.

Example

// Receiving a multipart/form-data upload.

use Http\{Request, Response, Server};
use function Async\{async, spawn};

// Bind a loopback listener on a free port (no fixed port in the example).
$listener = stream_socket_server('tcp://127.0.0.1:0');
stream_set_blocking($listener, false);
$name = stream_socket_get_name($listener, false);
$port = (int) substr($name, strrpos($name, ':') + 1);

/** One request on a fresh connection; returns the raw response. */
function fetch(int $port, string $request, string $body = ''): string
{
    $c = fsockopen('127.0.0.1', $port);
    fwrite($c, $request . "Connection: close\r\n\r\n" . $body);
    $raw = '';
    while (($chunk = fread($c, 8192)) !== '' && $chunk !== false) {
        $raw .= $chunk;
    }
    fclose($c);
    return $raw;
}

$server = Server::onListener($listener)->acceptWait(0.02);
$dest = sys_get_temp_dir() . '/mc_doc_upload_' . getmypid() . '.txt';

async(function () use ($server, $port, $dest) {
    spawn(fn () => $server->serve(function (Request $req) use ($dest): Response {
        $lines = ['title: ' . ($req->postArray()['title'] ?? '-')];
        foreach ($req->files() as $field => $f) {
            $lines[] = "$field: name={$f->name} type={$f->type} size={$f->size} error={$f->error} valid="
                . var_export($f->isValid(), true);
            $lines[] = 'contents: ' . $f->contents();
            $lines[] = 'moved: ' . var_export($f->moveTo($dest), true);
            $lines[] = 'valid after move: ' . var_export($f->isValid(), true);
        }
        return (new Response())->text(implode("\n", $lines) . "\n");
    }));

    $b = 'XBOUNDARYX';
    $body = "--$b\r\nContent-Disposition: form-data; name=\"title\"\r\n\r\nQuarterly report\r\n"
        . "--$b\r\nContent-Disposition: form-data; name=\"doc\"; filename=\"q3.txt\"\r\nContent-Type: text/plain\r\n\r\nrevenue up\r\n"
        . "--$b--\r\n";
    $raw = fetch(
        $port,
        "POST /upload HTTP/1.1\r\nHost: localhost\r\nContent-Type: multipart/form-data; boundary=$b\r\nContent-Length: " . strlen($body) . "\r\n",
        $body,
    );
    echo explode("\r\n\r\n", $raw, 2)[1];
    echo 'on disk: ', file_get_contents($dest), "\n";
    unlink($dest);
    $server->stop();
});

Output

title: Quarterly report
doc: name=q3.txt type=text/plain size=10 error=0 valid=true
contents: revenue up
moved: true
valid after move: false
on disk: revenue up

See also: Request

Constants

ERR_OK0The upload succeeded (php UPLOAD_ERR_OK).
ERR_INI_SIZE1The file exceeded Server::uploadMaxFilesize() (php UPLOAD_ERR_INI_SIZE).
ERR_FORM_SIZE2The file exceeded the form's MAX_FILE_SIZE field (php UPLOAD_ERR_FORM_SIZE).
ERR_PARTIAL3The body ended in the middle of the file (php UPLOAD_ERR_PARTIAL).
ERR_NO_FILE4The part carried no file, for example filename="" (php UPLOAD_ERR_NO_FILE).
ERR_NO_TMP_DIR6There was no temporary directory to write to (php UPLOAD_ERR_NO_TMP_DIR).
ERR_CANT_WRITE7The temporary file could not be written (php UPLOAD_ERR_CANT_WRITE).

Methods

__construct()Describes one uploaded file.
isValid()Whether the file arrived intact and has not been moved: error is ERR_OK, tmpName is set, and moveTo() has not succeeded yet.
moveTo()Moves the temporary file to its final place, so it survives the end of the request.
contents()Reads the whole file into memory.

__construct()#source

function __construct(
    public string $field,
    public /** basename of what the client sent */string $name,
    public /** the client's filename as sent, php 8.1's `full_path` */string $fullPath,
    public string $type,
    public int $size,
    public int $error,
    public string $tmpName
)

Describes one uploaded file. The server builds these while parsing; construct one yourself only in tests.

Parameters

$field string
The form field name (name= of the part).
$name /** basename of what the client sent */string
Base name of the file as the client sent it.
$fullPath /** the client's filename as sent, php 8.1's `full_path` */string
The client's file name exactly as sent (php 8.1's full_path).
$type string
The part's Content-Type, as the client claimed it. Empty when error is not ERR_OK.
$size int
Size in bytes; 0 when error is not ERR_OK.
$error int
One of the ERR_* constants.
$tmpName string
Path of the temporary file; empty when error is not ERR_OK.

isValid()#source

function isValid(): bool

Whether the file arrived intact and has not been moved: error is ERR_OK, tmpName is set, and moveTo() has not succeeded yet.

Returns

bool — true if moveTo() and contents() will work.

moveTo()#source

function moveTo(string $dest): bool

Moves the temporary file to its final place, so it survives the end of the request.

Uses rename() and falls back to copy-and-delete across devices. After a successful move isValid() is false. Does nothing and answers false for a file that is not valid.

Parameters

$dest string
Destination path. An existing file is overwritten.

Returns

bool — true on success.

contents()#source

function contents(): string

Reads the whole file into memory.

Returns

string — The bytes, or an empty string if the file is not valid.

View source