Simple File Upload
Validates one uploaded file, builds a collision-resistant filename, and moves it into an approved upload directory.
Purpose
Validates one uploaded file, builds a collision-resistant filename, and moves it into an approved upload directory.
Snippet details
ContextFileLevelProductionCopy-and-paste statusMarked safe after review.Categories
- Forms and Validation
Code
<?php
/*
* Copyright (c) 2026 Jeffery L. Paris <jparis@phpog.com>.
* Free for personal and internal use. Paid project use requires visible credit
* to Jeffery L. Paris. Corporate use requires a paid license fee unless a
* separate written license states otherwise.
*/
/**
* Simple File Upload.
*
* Purpose:
* Handles one uploaded file using explicit size, extension, upload-error, and
* destination-directory checks before calling move_uploaded_file().
*
* @param array $file_info One file entry from $_FILES.
* @param string $upload_directory Approved server-side upload directory.
* @param array $allowed_extensions Lowercase extension allowlist.
* @param int $max_bytes Maximum allowed file size in bytes.
* @return array Upload result and saved filename.
*/
function ogSnippetSimpleFileUpload(array $file_info, string $upload_directory, array $allowed_extensions, int $max_bytes): array {
$required_keys = array('name', 'tmp_name', 'error', 'size');
foreach ($required_keys as $required_key) {
if (isset($file_info[$required_key]) === false) {
return array('success' => false, 'saved_name' => '', 'message' => 'Upload payload is incomplete.');
}
}
if ((int) $file_info['error'] !== UPLOAD_ERR_OK) {
return array('success' => false, 'saved_name' => '', 'message' => 'The upload did not complete successfully.');
}
if ((int) $file_info['size'] < 1 || (int) $file_info['size'] > $max_bytes) {
return array('success' => false, 'saved_name' => '', 'message' => 'The uploaded file size is not allowed.');
}
$upload_directory = rtrim($upload_directory, DIRECTORY_SEPARATOR);
if ($upload_directory === '' || is_dir($upload_directory) === false || is_writable($upload_directory) === false) {
return array('success' => false, 'saved_name' => '', 'message' => 'The upload directory is not writable.');
}
$original_name = basename((string) $file_info['name']);
$extension = strtolower(pathinfo($original_name, PATHINFO_EXTENSION));
if ($extension === '' || in_array($extension, $allowed_extensions, true) === false) {
return array('success' => false, 'saved_name' => '', 'message' => 'The uploaded file extension is not allowed.');
}
if (is_uploaded_file((string) $file_info['tmp_name']) === false) {
return array('success' => false, 'saved_name' => '', 'message' => 'The upload temporary file is invalid.');
}
$random_token = bin2hex(random_bytes(12));
$saved_name = 'upload_'.$random_token.'.'.$extension;
$destination_path = $upload_directory.DIRECTORY_SEPARATOR.$saved_name;
if (move_uploaded_file((string) $file_info['tmp_name'], $destination_path) === false) {
return array('success' => false, 'saved_name' => '', 'message' => 'The uploaded file could not be moved.');
}
return array(
'success' => true,
'original_name' => $original_name,
'saved_name' => $saved_name,
'saved_path' => $destination_path,
'message' => 'File uploaded successfully.'
);
}
/*
$result = ogSnippetSimpleFileUpload($_FILES['mission_patch'], __DIR__.'/uploads', array('png', 'webp'), 1048576);
print_r($result);
*/