File uploads allow web applications to transfer real files (binary data) efficiently and reliably, while preserving file content and metadata in a standardized way that works across browsers and servers.
The native PHP $_FILES superglobal has several limitations and pitfalls that developers often run into:
$_FILES becomes a deeply nested array that is difficult to iterate over and easy to misuse.$_FILES['error'] can result in processing incomplete or failed uploads.$_FILES may contain entries even when no file was uploaded, typically with the UPLOAD_ERR_NO_FILE error code, which can be misleading if not handled explicitly.To address these issues, a dedicated file upload component was introduced.
To submit a file using an HTML form, ensure the form uses the POST method and the correct encoding type:
<form action="/upload" method="POST" enctype="multipart/form-data">
<label for="document">Choose a document:</label>
<input type="file" id="document" name="document">
<button type="submit">Upload</button>
</form>
Alternatively, files can be sent via JavaScript using FormData:
function upload() {
const fileInput = document.getElementById("document");
const file = fileInput.files[0];
const formData = new FormData();
formData.append("file", file);
fetch("/upload", {
method: "POST",
body: formData
})
.then(res => res.json())
.then(data => console.log(data))
.catch(err => console.error(err));
}
upload();
To access uploaded files, call the getFiles() method on the request service from within your controller:
public function uploadAction()
{
// Get file instance from input named "document"
$file = $this->request->getFiles('document');
if ($file) {
$file->getName(); // Original filename
$file->getUniqueName(); // Unique filename (preserves extension)
$file->getType(); // Guessed MIME type
$file->getTmpName(); // Temporary file path
$file->getError(); // Native PHP upload error code
$file->getSize(); // File size in bytes
// ...
}
// ...
}
If no file was uploaded, getFiles() returns an empty array.
Regardless of input name or nesting depth, this method always returns a normalized and predictable collection of uploaded files, unlike the native $_FILES superglobal.
To persist uploaded files, use the dedicated uploader component:
<?php
namespace Site\Controller;
use Krystal\Http\FileTransfer\FileUploader;
use Krystal\Application\Controller;
final class User extends AbstractController
{
public function upload()
{
$files = $this->request->getFiles('files'); // If parameter provided, only from that input data is exacracted
if ($files) { // If there's a file
$destination = $this->appConfig->getUploadsDir(); // e.g /data/uploads/
$uploader = new FileUploader();
$uploader->upload($destination, $files); // Returns boolean value
// Other methods
$uploader->getUploadedFiles(); // Returns array of uploaded files
$uploader->getFailedFiles(); // Returns array of failed files
}
}
}