In this Tuts+ Premium tutorial we will learn how to work with file operations using PHP. This is one of the most fundamental subjects of server side programming in general. Files are used in web applications of all sizes. So let's learn how to read, write, create, move, copy, delete files and more.
Screencast Preview

Copying, Moving and Deleting Files
We are going to start learning with these operations first, because they are very simple one liners, and they do not involve dealing with file pointers.
Copying a file
1 |
|
2 |
$file = "test.txt"; |
3 |
$new_file = "test_copy.txt"; |
4 |
|
5 |
copy($file, $newfile); |
Moving a File
1 |
|
2 |
$file = "test.txt"; |
3 |
$new_file = "test_copy.txt"; |
4 |
|
5 |
rename($file, $newfile); |
Note that the function is called "rename" and not "move". Internally, renaming a file is the same as moving it to a new location.
Same code works when changing the directory of the file:
1 |
|
2 |
$file = "test.txt"; |
3 |
$new_file = "different/folder/test_copy.txt"; |
4 |
|
5 |
rename($file, $newfile); |
Deleting a File
There is no such function as "delete". Here we call the "unlink" function.
1 |
|
2 |
$file = "test.txt"; |
3 |
|
4 |
unlink($file, $newfile); |
Opening and Reading a File
Now it is time to start dealing with some file pointers. There is more than one way of reading the contents of a file in PHP, but we are going to start with the most basic method first. Later in the article we will learn about the alternative methods.
There are three file functions we will use here: fopen, fread and fclose.
1 |
|
2 |
$file = "test.txt"; |
3 |
|
4 |
// open the file
|
5 |
$fp = fopen($file, "r"); |
6 |
|
7 |
// read 5k data from the file
|
8 |
$contents = fread($fp, 5000); |
9 |
|
10 |
// close the file
|
11 |
fclose($fp); |
When opening the file, we include a second parameter, which determines the type of access. In this case "r" stands for "read only". There are other types that we need to use for writing and appending that we will cover later in the article.
The second important thing to pay attention to is the second parameter in the fread function. This determines the length of the data (in bytes) to read from the file. To read the entire contents of the file, we need to pass the total file size. The filesize function does the trick:
1 |
|
2 |
$file = "test.txt"; |
3 |
|
4 |
// open the file
|
5 |
$fp = fopen($file, "r"); |
6 |
|
7 |
// read 5k data from the file
|
8 |
$contents = fread($fp, filesize($file)); |
9 |
|
10 |
// close the file
|
11 |
fclose($fp); |
Full Screencast

Understanding File Pointers
Since we started working with file pointers, let's take a moment and try to understand them better.
A file pointer, also known as a file "handle", is a variable that refers to a file. The exact value of this variable is unimportant; all we need to know is that it points to a specific file. The pointer is usually obtained by opening a file. In PHP we use the fopen function.
Even though PHP has an excellent garbage collector that closes all open file pointers at the end of a scripts execution, it is considered a good practice to close them manually using the fclose function.
1 |
|
2 |
$fp = fopen($file, 'r'); |
3 |
// ...
|
4 |
|
5 |
// always close files
|
6 |
fclose($fp); |
Position in a File
The file pointer actually does more than just pointing to a file. It also points to a specific position inside that file. In most cases, when you open a file, the file pointer points to the beginning (or position 0) in that file. We will cover this concept in more detail when we talk about "seeking" later in the article.
Writing To a File
Just like reading files, there is more than one way of writing to a file. First we will see the basic way, and later in the article we will look at alternatives.
The following code writes the contents of the $data variable into the test.txt file.
1 |
|
2 |
$file = "test.txt"; |
3 |
$data = "0123456789abc"; |
4 |
|
5 |
// open the file for writing
|
6 |
$fp = fopen($file, "w"); |
7 |
|
8 |
// write data to the file
|
9 |
fwrite($fp, $data); |
10 |
|
11 |
// close the file
|
12 |
fclose($fp); |
This time we used a different flag for opening the file. The "w" flag opens the file for writing, and overwrites all existing data in the file. If there was anything in the test.txt file, it will be replaced with the string in $data.
The fwrite function was very simple, we just passed the file pointer and the data to be written to the file. There is an optional third parameter that determines the length of data to be written:
1 |
|
2 |
$file = "test.txt"; |
3 |
$data = "0123456789abc"; |
4 |
|
5 |
// open the file for writing
|
6 |
$fp = fopen($file, "w"); |
7 |
|
8 |
// write data to the file (only 5 bytes)
|
9 |
fwrite($fp, $data, 5); |
10 |
|
11 |
// close the file
|
12 |
fclose($fp); |
If you run the code above, the contents of test.txt will only be "01234".
Creating a New File
The examples above also work for creating new files. If the file test.txt does not exist, it will be created automatically, just by the fopen function call.
If all you want to do is to create a new blank file, without writing any data in it, this code will work:
1 |
|
2 |
$file = "does_not_exist.txt"; |
3 |
|
4 |
// creates and opens the file
|
5 |
$fp = fopen($file, "w"); |
6 |
|
7 |
// close the file
|
8 |
fclose($fp); |
Seeking
The act of moving the file pointers location is called "seeking". To seek we can use the function fseek, and to get the position of a given pointer, we can use the function ftell.
Before we use seeking, let's create a file with 10 bytes of data in it.
1 |
|
2 |
$file = "test.txt"; |
3 |
$data = "0123456789"; |
4 |
$fp = fopen($file, "w"); |
5 |
fwrite($fp, $data); |
6 |
fclose($fp); |
Now let's see how these functions behave. Here are a few different examples:
1 |
|
2 |
$file = "test.txt"; |
3 |
|
4 |
// opening for reading, pointer should be at 0
|
5 |
$fp = fopen($file, "r"); |
6 |
|
7 |
echo "open: "; |
8 |
// outputs 0
|
9 |
echo ftell($fp)."\n"; |
10 |
|
11 |
// -----
|
12 |
|
13 |
// seek to byte 4
|
14 |
fseek($fp, 4); |
15 |
|
16 |
echo "seek 4: "; |
17 |
// outputs 4
|
18 |
echo ftell($fp)."\n"; |
19 |
|
20 |
// -----
|
21 |
|
22 |
// you can go out of bounds
|
23 |
fseek($fp, 9000); |
24 |
|
25 |
echo "out of bounds: "; |
26 |
// outputs 9000
|
27 |
echo ftell($fp)."\n"; |
28 |
|
29 |
// ----
|
30 |
|
31 |
// this is how you get to the end
|
32 |
fseek($fp, 0, SEEK_END); |
33 |
|
34 |
echo "end: "; |
35 |
// outputs 10
|
36 |
echo ftell($fp)."\n"; |
37 |
|
38 |
// ----
|
39 |
|
40 |
// move relative to current position
|
41 |
// negative numbers work
|
42 |
fseek($fp, -3, SEEK_CUR); |
43 |
|
44 |
echo "go back by 3: "; |
45 |
// outputs 7
|
46 |
echo ftell($fp)."\n"; |
47 |
|
48 |
// ----
|
49 |
|
50 |
// or seek relative to the end
|
51 |
// again, negative works
|
52 |
fseek($fp, -3, SEEK_END); |
53 |
|
54 |
echo "go back by 3 from the end: "; |
55 |
// outputs 7
|
56 |
echo ftell($fp)."\n"; |
57 |
|
58 |
// ----
|
59 |
|
60 |
// does the same thing as seeking to 0
|
61 |
rewind($fp); |
62 |
|
63 |
echo "rewind: "; |
64 |
// outputs 0
|
65 |
echo ftell($fp)."\n"; |
66 |
|
67 |
fclose($fp); |
Other Operations Moving File Pointer Position
The file pointer position can move by operations other than fseek.
For example, reading moves the position:
1 |
|
2 |
$file = "test.txt"; |
3 |
|
4 |
$fp = fopen($file, "r"); |
5 |
|
6 |
// reading 6 bytes
|
7 |
$data = fread($fp, 6); |
8 |
|
9 |
// outputs 6
|
10 |
echo ftell($fp); |
11 |
|
12 |
fclose($fp); |
Writing also moves it:
1 |
|
2 |
$file = "test.txt"; |
3 |
|
4 |
$data = "0123456789"; |
5 |
|
6 |
$fp = fopen($file, "w"); |
7 |
|
8 |
// writing 10 bytes
|
9 |
fwrite($fp, $data); |
10 |
|
11 |
// outputs 10
|
12 |
echo ftell($fp); |
13 |
|
14 |
fclose($fp); |
File Access Modes
Now that we are familiar with the reading, writing and seeking concepts, it is time better understand the different file access modes. When opening a file with the fopen function, the second parameter must be provided, which is the file access mode. There is a table in the PHP manual describing them:



By looking at these, you can see that we have more options than we talked about so far. Here are some important factors to consider about picking the right option:
- Opening with 'r+' will fail, if the file is not writable, even if you just want to read and not write to it.
- Both 'w' and 'w+' will completely delete the contents of an existing file as soon as you open it. If you intend to add data, you need to use 'a' or 'a+'.
- If you want to create new files, and also prevent accidentally overwriting and existing file, use 'x' or 'x+'.
- When working with binary files, such as images, add the letter 'b' after the mode. For example: 'rb' or 'r+b'
Getting File Information
There are many pieces of information we can obtain about a file besides just its contents (size, last access time, modify time etc...) The main function used for this is stat.
It returns the information in an array:
1 |
|
2 |
$data = stat("test.txt"); |
3 |
|
4 |
print_r($data); |
5 |
/* prints:
|
6 |
|
7 |
Array
|
8 |
(
|
9 |
[0] => 2
|
10 |
[1] => 0
|
11 |
[2] => 33206
|
12 |
[3] => 1
|
13 |
[4] => 0
|
14 |
[5] => 0
|
15 |
[6] => 2
|
16 |
[7] => 10
|
17 |
[8] => 1264374556
|
18 |
[9] => 1264374556
|
19 |
[10] => 1264373231
|
20 |
[11] => -1
|
21 |
[12] => -1
|
22 |
[dev] => 2
|
23 |
[ino] => 0
|
24 |
[mode] => 33206
|
25 |
[nlink] => 1
|
26 |
[uid] => 0
|
27 |
[gid] => 0
|
28 |
[rdev] => 2
|
29 |
[size] => 10
|
30 |
[atime] => 1264374556
|
31 |
[mtime] => 1264374556
|
32 |
[ctime] => 1264373231
|
33 |
[blksize] => -1
|
34 |
[blocks] => -1
|
35 |
)
|
36 |
|
37 |
*/
|
The data is in 12 pieces and is repeated, first with numeric keys, and then again with string keys.
The PHP manual has a table explaining each of them:



Alternative Functions
As I mentioned earlier, there are other alternative functions that can be used for reading and writing files. Let's go over some of them.
file_get_contents
This function returns the entire contents of a given file. Also you do not need to deal with file pointers at all.
1 |
|
2 |
$file = "test.txt"; |
3 |
|
4 |
$contents = file_get_contents($file); |
The function accepts four more additional parameters as described in the PHP manual. For example, to read only a portion of the file, you can set the fourth and fifth parameters:
1 |
|
2 |
$file = "test.txt"; |
3 |
|
4 |
// reads 5 bytes of data, starting at byte 3
|
5 |
$contents = file_get_contents($file, NULL, NULL, 3, 5); |
file
Yes, this function is simply named file, and it again returns the entire contents of a file. But this time, the contents are split into an array, by the newline character.
Let's create a file with the following contents:
1 |
|
2 |
abc |
3 |
123 |
4 |
empty line after this |
5 |
|
6 |
last line |
Now use the function:
1 |
|
2 |
$file = "test.txt"; |
3 |
|
4 |
$contents = file($file); |
5 |
|
6 |
print_r($contents); |
7 |
|
8 |
/* prints
|
9 |
|
10 |
Array
|
11 |
(
|
12 |
[0] => abc
|
13 |
|
14 |
[1] => 123
|
15 |
|
16 |
[2] => empty line after this
|
17 |
|
18 |
[3] =>
|
19 |
|
20 |
[4] => last line
|
21 |
)
|
22 |
|
23 |
|
24 |
*/
|
It may not be obvious, but there is a newline character at the of each array element. If you don't want them, you can either trim them manually, or use the FILE_IGNORE_NEW_LINES flag:
1 |
|
2 |
$file = "test.txt"; |
3 |
|
4 |
$contents = file($file, FILE_IGNORE_NEW_LINES); |
5 |
|
6 |
print_r($contents); |
7 |
|
8 |
/* prints
|
9 |
|
10 |
Array
|
11 |
(
|
12 |
[0] => abc
|
13 |
[1] => 123
|
14 |
[2] => empty line after this
|
15 |
[3] =>
|
16 |
[4] => last line
|
17 |
)
|
18 |
|
19 |
*/
|
If you do not want the empty lines either, you can use the FILE_SKIP_EMPTY_LINES flag:
1 |
|
2 |
$file = "test.txt"; |
3 |
|
4 |
$contents = file($file, FILE_SKIP_EMPTY_LINES | FILE_IGNORE_NEW_LINES); |
5 |
|
6 |
print_r($contents); |
7 |
|
8 |
/* prints
|
9 |
|
10 |
Array
|
11 |
(
|
12 |
[0] => abc
|
13 |
[1] => 123
|
14 |
[2] => empty line after this
|
15 |
[3] => last line
|
16 |
)
|
17 |
|
18 |
*/
|
file_put_contents
Again, there is no need to use file pointers. This function simply writes the given data to the given file:
1 |
|
2 |
$file = "test.txt"; |
3 |
$data = "0123456789"; |
4 |
|
5 |
file_put_contents($file, $data); |
filesize, fileatime, filemtime
These are alternatives to the stat function for getting file information.
1 |
|
2 |
$file = "test.txt"; |
3 |
|
4 |
// gets the file size
|
5 |
echo filesize($file); |
6 |
|
7 |
// gets the last time the file was modified
|
8 |
echo filemtime($file); |
9 |
|
10 |
// gets the last time the file accessed
|
11 |
echo fileatime($file); |
File Permissions
Unix-like systems have a quite detailed file permissions standard. Windows systems have it a little simpler. The whole subject of file permissions can be lengthy and a whole separate article can be written on it. So instead, we will only look at two simple concepts of file permissions: being "readable", and being "writable".
Checking for Permissions
Your script may or may not have read and/or write access to a file due to various reasons. Before attempting to open a file for reading or writing, it is wise to check if you have permission to do so:
1 |
|
2 |
$file = "test.txt"; |
3 |
|
4 |
if (is_readable($file)) { |
5 |
// file is readable
|
6 |
}
|
7 |
|
8 |
if (is_writable($file)) { |
9 |
// file is writable
|
10 |
}
|
Setting the Permissions
To set the permissions of a file, we use the chmod function:
1 |
|
2 |
$file = "test.txt"; |
3 |
|
4 |
// makes the file read-only
|
5 |
chmod($file, 0444); |
6 |
|
7 |
// makes the file readable and writable
|
8 |
chmod($file, 0666); |
The above code should work on both Unix and Windows systems. However, you may not be able to set the permissions, if you do not own the file or have any permissions to it in the first place.
To better understand chmod and file permissions in general, you can check out these links:
- Understanding file permissions on Unix: a brief tutorial
- File and Folder Permissions in Windows
- chmod Wikipedia entry
- Manual page for chmod
Using Loops for Big Files
Last thing we are going to talk about is a quick tip for handling big files. This has some performance implications.
Easiest way to read and output a file is this:
1 |
|
2 |
$file = "test.txt"; |
3 |
|
4 |
echo file_get_contents($file); |
But that causes the entire contents of the file to be loaded into the memory. It will persist as long as it is being downloaded by the web surfer. If you have multiple people downloading files at the same time, this can consume your web servers memory very quickly.
A better idea for handling this would be to use a loop to read only small chunks of the file at a time. For the loop condition we will utilize the feof function:
1 |
|
2 |
$file = "test.txt"; |
3 |
|
4 |
$fp = fopen($file, 'r'); |
5 |
|
6 |
while (!feof($fp)) { |
7 |
|
8 |
echo fread($fp, 4092); |
9 |
|
10 |
}
|
11 |
|
12 |
fclose($fp); |
The code above will only load 4kb of data at once into the memory, and this will greatly reduce the memory usage for big file outputs. The feof function returns true when we reach the end of the file, and this breaks out of the loop.