PHP File Handling

Read and write files with file_get_contents and file_put_contents, process CSV files, work with folders and handle uploads safely.

Lesson 15 of 86Intermediate17 min read

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

PHP
$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_contents returns the file as a string, or false if it can't be read.
  • file_put_contents writes a string, replacing the file's contents, and creates the file if needed.
  • Add the FILE_APPEND flag to add to the end instead; add LOCK_EX as 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

FunctionAnswers
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:

PHP
$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:

PHP
$file = $_FILES['photo'];
// $file['name'], $file['tmp_name'], $file['size'], $file['error']

Uploads are a common attack route, so always:

  1. Check $file['error'] === UPLOAD_ERR_OK.
  2. Limit the size, for example 2 MB.
  3. Check the real type with finfo or mime_content_type, not the file name.
  4. Give the file a new, random name with a safe extension.
  5. 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_contents reads a whole file; file_put_contents writes one, with FILE_APPEND to add.
  • file() gives an array of lines; fopen, fgets and fgetcsv read large files piece by piece.
  • Check paths with file_exists, is_dir and is_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.

Example 1

A visitor counter

After four earlier visits, the file contains 4.

PHP
<?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";
Output
You are visitor number 5
Line-by-line explanation
  1. Line 4Read the current number, or start at 0 the very first time.
  2. Line 6Write the new number back, locking the file while writing.
Example 2

Totals from a CSV file

sales.csv has a header line item,qty,price and three rows: pen, notebook and lamp.

PHP
<?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);
Output
pen        12.00
notebook   14.00
lamp       24.50
TOTAL      50.50
Line-by-line explanation
  1. Line 3Read the first line separately to get the column names.
  2. Line 7array_combine pairs the column names with the values: ['item' => 'pen', ...].
  3. Line 8CSV values are strings, so convert them before doing maths.
  4. Line 10%-9s pads the name and %6.2f lines up the amounts.
Example 3

A safe image upload

The visitor chose a 180 KB JPEG called holiday.jpg.

PHP
<?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;
Output
Saved as a JPG image.
Line-by-line explanation
  1. Line 5Check that the upload itself worked.
  2. Line 7Limit the size: 2 MB is 2 x 1024 x 1024 bytes.
  3. Line 10Detect the real type from the file's content, not its name.
  4. Lines 15 to 16Save under a random name, with an extension chosen by us.

Common PHP Files Mistakes

Overwriting a log file

Don't
file_put_contents('log.txt', "Login failed\n");    // only the latest line survives
Do
file_put_contents('log.txt', "Login failed\n", FILE_APPEND | LOCK_EX);

Splitting CSV with explode

Don't
$fields = explode(',', '"Smith, Jane",42');   // 3 broken pieces
Do
$fields = str_getcsv('"Smith, Jane",42');      // ['Smith, Jane', '42']

Keeping the visitor's file name

Don't
move_uploaded_file($tmp, 'uploads/' . $_FILES['photo']['name']);
Do
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.

Fill in the blankseasy

Complete the line that adds a message to the end of log.txt.

file_put_contents('log.txt', "Saved\n", );
Multiple choiceeasy

What does file_get_contents return?

Predict the outputmedium

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];
Multiple choicemedium

Why use fgetcsv instead of explode for CSV files?

Multiple choicehard

Which check reliably tells you an uploaded file's real type?

PHP Files Quiz

5 questions to check what you learned.

  1. 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
  2. Which form attribute is required for file uploads?

    • method="get"
    • enctype="multipart/form-data"
    • accept="files"
    • action="upload"
  3. Which function moves an uploaded file to its final location?

    • rename()
    • copy()
    • move_uploaded_file()
    • file_put_contents()
  4. What type are the values returned by fgetcsv?

    • Numbers where possible
    • Objects
    • Strings
    • Booleans
  5. 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