Not every piece of data needs a database. A small site might keep a visitor counter in a text file, read product data from a CSV exported from a spreadsheet, write a log of errors, or let people upload a profile picture. PHP has a rich set of functions for all of these.
The easiest file functions work with a whole file at once: file_get_contents() reads a file into a string and file_put_contents() writes a string to a file. For larger files, or files you want to read line by line, PHP also provides the classic fopen, fgets and fclose functions, plus fgetcsv for spreadsheet data.
In this lesson you'll read and write text, append to log files, process a CSV file, check whether files and folders exist, and handle file uploads from a form, including the security checks that uploads absolutely need.
Reading and writing whole files
$text = file_get_contents(__DIR__ . '/notes.txt');
file_put_contents(__DIR__ . '/notes.txt', "New content\n");
file_put_contents(__DIR__ . '/log.txt', "Event\n", FILE_APPEND);file_get_contentsreturns the file as a string, orfalseif it can't be read.file_put_contentswrites a string, replacing the file's contents, and creates the file if needed.- Add the
FILE_APPENDflag to add to the end instead; addLOCK_EXas well (FILE_APPEND | LOCK_EX) so two visitors writing at the same moment don't mix their lines. file()returns an array of lines.file($path, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES)removes line endings and blank lines for you.
Checking files and folders
| Function | Answers |
|---|---|
file_exists($path) | Does the file or folder exist? |
is_file($path) / is_dir($path) | Is it a file / a folder? |
is_readable($path) / is_writable($path) | Can PHP read / write it? |
filesize($path) | Size in bytes |
mkdir($path, 0755, true) | Create a folder, including parents |
scandir($path) / glob('*.txt') | List a folder's contents |
unlink($path) | Delete a file |
Check before you act, and remember that the web server's user, not your own account, needs permission to write to a folder.
Line by line and CSV
For big files, read one line at a time so you don't load everything into memory:
$handle = fopen(__DIR__ . '/sales.csv', 'r');
while (($row = fgetcsv($handle)) !== false) {
// $row is an array of the fields on one line
}
fclose($handle);fgetcsv splits each line at the commas and handles quoted values such as "Smith, Jane" correctly, which a simple explode(',', ...) would break. fputcsv($handle, $fields) writes a row in the same format.
Handling uploads
A form needs method="post" and enctype="multipart/form-data" to send files. PHP then describes each upload in $_FILES:
$file = $_FILES['photo'];
// $file['name'], $file['tmp_name'], $file['size'], $file['error']Uploads are a common attack route, so always:
- Check
$file['error'] === UPLOAD_ERR_OK. - Limit the size, for example 2 MB.
- Check the real type with
finfoormime_content_type, not the file name. - Give the file a new, random name with a safe extension.
- Move it with
move_uploaded_file()into a folder that can't run PHP.
PHP Files Syntax
File functions
$text = file_get_contents($path);
file_put_contents($path, $text, FILE_APPEND | LOCK_EX);
$lines = file($path, FILE_IGNORE_NEW_LINES);
$h = fopen($path, 'r');
while (($row = fgetcsv($h)) !== false) { ... }
fclose($h);
move_uploaded_file($_FILES['f']['tmp_name'], $destination);PHP Files Summary
file_get_contentsreads a whole file;file_put_contentswrites one, withFILE_APPENDto add.file()gives an array of lines;fopen,fgetsandfgetcsvread large files piece by piece.- Check paths with
file_exists,is_dirandis_writable; build them with__DIR__. - Uploads arrive in
$_FILES; validate error, size and real type before saving. - Save uploads under a new random name with
move_uploaded_file.
PHP Files Examples
Each example shows the code, the output it produces and what each part does.
A visitor counter
After four earlier visits, the file contains 4.
<?php
$path = __DIR__ . '/counter.txt';
$count = file_exists($path) ? (int) file_get_contents($path) : 0;
$count++;
file_put_contents($path, $count, LOCK_EX);
echo "You are visitor number $count";You are visitor number 5Line-by-line explanation
- Line 4Read the current number, or start at 0 the very first time.
- Line 6Write the new number back, locking the file while writing.
Totals from a CSV file
sales.csv has a header line item,qty,price and three rows: pen, notebook and lamp.
<?php
$handle = fopen(__DIR__ . '/sales.csv', 'r');
$header = fgetcsv($handle);
$total = 0;
while (($row = fgetcsv($handle)) !== false) {
$sale = array_combine($header, $row);
$line = (int) $sale['qty'] * (float) $sale['price'];
$total += $line;
printf("%-9s %6.2f\n", $sale['item'], $line);
}
fclose($handle);
printf("%-9s %6.2f\n", 'TOTAL', $total);pen 12.00
notebook 14.00
lamp 24.50
TOTAL 50.50Line-by-line explanation
- Line 3Read the first line separately to get the column names.
- Line 7array_combine pairs the column names with the values: ['item' => 'pen', ...].
- Line 8CSV values are strings, so convert them before doing maths.
- Line 10%-9s pads the name and %6.2f lines up the amounts.
A safe image upload
The visitor chose a 180 KB JPEG called holiday.jpg.
<?php
$errors = [];
$file = $_FILES['photo'] ?? null;
if (!$file || $file['error'] !== UPLOAD_ERR_OK) {
$errors[] = 'Please choose a file.';
} elseif ($file['size'] > 2 * 1024 * 1024) {
$errors[] = 'The image must be 2 MB or smaller.';
} else {
$type = mime_content_type($file['tmp_name']);
$extensions = ['image/jpeg' => 'jpg', 'image/png' => 'png'];
if (!isset($extensions[$type])) {
$errors[] = 'Only JPEG and PNG images are allowed.';
} else {
$newName = bin2hex(random_bytes(16)) . '.' . $extensions[$type];
move_uploaded_file($file['tmp_name'], __DIR__ . '/uploads/' . $newName);
echo "Saved as a " . strtoupper($extensions[$type]) . " image.";
}
}
foreach ($errors as $e) echo $e;Saved as a JPG image.Line-by-line explanation
- Line 5Check that the upload itself worked.
- Line 7Limit the size: 2 MB is 2 x 1024 x 1024 bytes.
- Line 10Detect the real type from the file's content, not its name.
- Lines 15 to 16Save under a random name, with an extension chosen by us.
Common PHP Files Mistakes
Overwriting a log file
file_put_contents('log.txt', "Login failed\n"); // only the latest line survivesfile_put_contents('log.txt', "Login failed\n", FILE_APPEND | LOCK_EX);Splitting CSV with explode
$fields = explode(',', '"Smith, Jane",42'); // 3 broken pieces$fields = str_getcsv('"Smith, Jane",42'); // ['Smith, Jane', '42']Keeping the visitor's file name
move_uploaded_file($tmp, 'uploads/' . $_FILES['photo']['name']);move_uploaded_file($tmp, 'uploads/' . bin2hex(random_bytes(16)) . '.jpg');A file called shell.php could then be run on your server.
PHP Files in Real Projects
Files in PHP applications
Content management systems save uploaded images and generate thumbnails, shops import product lists from supplier CSV files every night, and applications write error logs that developers check when something goes wrong. Larger sites often store uploads in cloud storage, but the validation steps are exactly the same.
PHP Files FAQ
When should I use fopen instead of file_get_contents?
file_get_contents loads the whole file into memory at once, which is simple and perfect for small files. Use fopen with fgets or fgetcsv to process large files one line at a time, or when you need to keep a file open for several reads or writes.
How do I create a folder in PHP?
Use mkdir($path, 0775, true). The third argument creates any missing parent folders too. Check is_dir($path) first, or check the return value, because mkdir produces a warning if the folder already exists.
Why do I get "Permission denied" when writing a file?
PHP runs as the web server's user, often www-data, not as you. That user needs write permission on the folder. Give it access to just the folders that need writing, such as storage/ or uploads/, and never make whole folders writable by everyone with 777.
How do I list the files in a folder?
scandir($dir) returns every entry, including . and .., which you'll usually filter out. glob($dir . '/*.csv') returns only the paths that match a pattern, which is handy for processing every CSV file in an import folder.
How do I delete a file in PHP?
Call unlink($path), which returns true on success. Check is_file($path) first if a missing file is normal. Make sure the path comes from your own code, never directly from user input, so a visitor can't delete files outside the intended folder.
PHP Files Exercises
Answer each one and check it instantly. If you get stuck, open the hint before the answer.
Complete the line that adds a message to the end of log.txt.
file_put_contents('log.txt', "Saved\n", );
What does file_get_contents return?
colours.txt contains red, green and blue on separate lines. What does this output?
$lines = file('colours.txt', FILE_IGNORE_NEW_LINES);
echo count($lines) . ' ' . $lines[1];
Why use fgetcsv instead of explode for CSV files?
Which check reliably tells you an uploaded file's real type?
PHP Files Quiz
5 questions to check what you learned.
What does the FILE_APPEND flag do?
- Reads the file backwards
- Creates a backup
- Locks the file
- Adds new content to the end instead of overwriting
Which form attribute is required for file uploads?
- method="get"
- enctype="multipart/form-data"
- accept="files"
- action="upload"
Which function moves an uploaded file to its final location?
- rename()
- copy()
- move_uploaded_file()
- file_put_contents()
What type are the values returned by fgetcsv?
- Numbers where possible
- Objects
- Strings
- Booleans
Why give uploaded files a new random name?
- To save space
- Browsers require it
- To make them load faster
- The visitor's name could overwrite files or be a script that the server runs