PHP File Uploads
An upload arrives through a temporary server file and an error code. Validate the upload itself, enforce size/type rules and allocate your own private destination name.
How it works
This standalone POST lab accepts a small UTF-8 plain-text note, not an executable script or a public image gallery. The original filename is not used as a storage path. finfo inspects the temporary file, while the actual move uses move_uploaded_file. The lab reads and immediately deletes its private stored copy. A production feature also needs authentication, CSRF protection, quotas, scanning where appropriate and a controlled retrieval path.
Run it locally
Save this standalone lab file in a separate local folder. Start a PHP development server on an unused loopback port, for example php -S 127.0.0.1:8090 -t . . Open the shown filename through that server, not a file URL. Exercise GET and POST as described. This is not a POST endpoint on Bhylu, and the built-in development server is not a production server.
php -l uploads.php
php -S 127.0.0.1:8090 -t .Download this source as a local PHP lab file. Keep lab files separate from the Bhylu project and production data.
Example
<?php
declare(strict_types=1);
if (($_SERVER["REQUEST_METHOD"] ?? "GET") !== "POST") { http_response_code(405); echo "Use POST"; exit; }
$file = $_FILES["note"] ?? null;
if (!is_array($file) || ($file["error"] ?? null) !== UPLOAD_ERR_OK || !is_string($file["tmp_name"] ?? null)) { http_response_code(422); echo "Upload failed"; exit; }
$temp = $file["tmp_name"];
if (!is_uploaded_file($temp) || filesize($temp) > 65536) { http_response_code(413); echo "Upload too large"; exit; }
$mime = (new finfo(FILEINFO_MIME_TYPE))->file($temp);
$content = file_get_contents($temp);
if ($mime !== "text/plain" || $content === false || !mb_check_encoding($content,"UTF-8") || str_contains($content,"\0")) { http_response_code(415); echo "Use plain UTF-8 text"; exit; }
$target = sys_get_temp_dir() . "/bhylu-upload-" . bin2hex(random_bytes(16)) . ".txt";
if (!move_uploaded_file($temp,$target)) { http_response_code(500); echo "Storage failed"; exit; }
try { echo "Stored ", filesize($target), " bytes privately"; } finally { unlink($target); }
Expected result
A valid 27-byte text note returns Stored 27 bytes privately.The review submits real multipart uploads, including a traversal-looking original filename. The private random destination ignores that name. The rejection paths test missing and non-text content; no public executable file is left behind. MIME inspection is one layer, not a claim that every accepted text file is safe in every later output context.
Requests to check
POST uploads.php→ status 200; multipart note upload.POST uploads.php→ status 422; form fields: .POST uploads.php→ status 415; multipart note upload.
Use a local browser form or HTTP client. A CSRF token from one session does not authorise a different session. The automated review uses actual HTTP requests, not just command-line output.
Check the official PHP manual for detailed version and API contracts.