Working with files is an important part of any programming language, and PHP is no different. Whatever your reasons are for wanting to manipulate files, PHP will happily accommodate them through the use of a handful of functions. You should have read and be comfortable with the concepts presented in the first five sections of this book before beginning here.

Folders

edit

To display the current directory: dirname().

To change the directory: chdir().

To make a new directory: mkdir().

fopen() and fclose()

edit

fopen() is the basis for file manipulation. It opens a file in a certain mode (that you specify) and returns a handle. Using this handle you can read or write to the file, before closing it with the fclose() function.

Example Usage
<?php
$handle = fopen('data.txt', 'r'); // Open the file for reading
fclose($handle); // Close the file
?>


In the example above you can see the file is opened for reading by specifying 'r' as the mode. For a full list of all the modes available to fopen(), you can look at the PHP Manual page.

Opening and closing the file is all well and good, but to perform useful operations, you need to know about fread() and fwrite().

When a PHP script finishes executing, all open files are automatically closed. So although it is not strictly necessary to close a file after opening it, it is considered good programming practice to do so.

Reading

edit

Reading can be done in a number of ways. If you just want all the contents of a file available to work with, you can use the file_get_contents() function. If you want each line of the file in an array, you can use the file() command. For total control over reading from files, fread() can be used.

These functions are usually interchangeable and each can be used to perform each other's function. The first two do not require that you first open the file with fopen() or then close it with fclose(). These are good for quick, one-time file operations. If you plan on performing multiple operations on a file it is best to use fopen() in conjunction with fread(), fwrite() and fclose() as it is more efficient.

An example of using file_get_contents()

  Code:

<?php
$contents = file_get_contents('data.txt');
echo $contents;
?>

  Output:

I am the contents of data.txt
This function reads the entire file into a string and from then on you can manipulate it as you would any string.
An example of using file()

  Code:

<?php
$lines = file('data.txt');
foreach($lines as $Key => $line) {
	$lineNum = $Key + 1;
	echo "Line $lineNum: $line";
}
?>

  Output:

Line 1: I am the first line of file
Line 2: I am the second line the of the file
Line 3: If I said I was the fourth line of the file, I'd be lying
This function reads the entire file into an array. Each item in the array corresponds to a line in the file.
An example of using fread()

  Code:

<?php
$handle = fopen('data.txt', 'r');
$string = fread($handle, 64);
fclose($handle);

echo $string;
?>

  Output:

I am the first 64 bytes of data.txt (if it was ASCII encoded). I
This function can read up to the specified number of bytes from the file and return it as a string. For the most part, the first two functions will be preferable, but there are occasions when this function is needed.

As you can see, with these three functions you are able to easily read data from a file into a form that is convenient to work with. The next part shows how these functions can be used to do the jobs of the others, but this is optional. You may skip it and move onto the writing section, if you are not interested.

<?php
$file = 'data.txt';

function detectLineEndings($contents) {
	if(false !== strpos($contents, "\r\n")) return "\r\n";
	else if(false !== strpos($contents, "\r")) return "\r";
	else return "\n";
}

/* This is equivalent to file_get_contents($file), but is less efficient */
$handle = fopen($file, 'r');
$contents = fread($handle, filesize($file));
fclose($handle);

/* This is equivalent to file($file), but requires you to check for the line-ending
type. Windows systems use \r\n, Macintosh \r and Unix \n. File($file) will
automatically detect line-endings whereas fread/file_get_contents won't */
$lineEnding = detectLineEndings($contents);
$contents = file_get_contents($file);
$lines = explode($lineEnding, $contents);

/* This is also equivalent to file_get_contents($file) */
$lines = file($file);
$contents = implode("\n", $lines);

/* This is equivalent to fread($file, 64), if the file is ASCII encoded */
$contents = file_get_contents($file);
$string = substr($contents, 0, 64);
?>


Writing

edit

Writing to a file is done by using the fwrite() function in conjunction with fopen() and fclose(). As you can see, there aren't as many options for writing to a file as there are for reading from one. However, PHP 5 introduces the function file_put_contents() that simplifies the writing process somewhat. This function will be discussed later in the PHP 5 section, as it is fairly self-explanatory and does not require discussion here.

The extra options for writing don't come from the amount of functions, but from the modes available for opening the file. There are three different modes you can supply to the fopen() function, if you wish to write to a file. One mode, 'w', wipes the entire contents of the file, so anything you then write to the file will fully replace what was there before. The second mode, 'a', appends stuff to the file so anything you write to the file will appear just after the original contents of the file. The final mode 'x' only works for non-existent files. All three writing modes will attempt to create the file, if it doesn't exist whereas the 'r' mode will not.

An example of using the 'w' mode

  Code:

<?php
$handle = fopen('data.txt', 'w'); // Open the file and delete its contents
$data = "I am new content\nspread across\nseveral lines.";
fwrite($handle, $data);
fclose($handle);

echo file_get_contents('data.txt');
?>

  Output:

I am new content
spread across
several lines.
An example of using the 'a' mode

  Code:

<?php
$handle = fopen('data.txt', 'a'); // Open the file for appending
$data = "\n\nI am new content.";
fwrite($handle, $data);
fclose($handle);

echo file_get_contents('data.txt');
?>

  Output:

I am the original content.

I am new content.
An example of using the 'x' mode

  Code:

<?php
$handle = fopen('newfile.txt', 'x'); // Open the file only, if it doesn't exist
$data = "I am this file's first ever content!";
fwrite($handle, $data);
fclose($handle);

echo file_get_contents('newfile.txt');
?>

  Output:

I am this file's first ever content!

Of the three modes shown above, 'w' and 'a' are used the most, but the writing process is essentially the same for all the modes.

Reading and Writing

edit

If you want to use fopen() to open a file for both reading and writing all you need to do is put a '+' on the end of the mode. For example, reading from a file requires the 'r' mode. If you want to read and write to/from that file you need to use 'r+' as a mode. Similarly you can read and write to/from a file using the 'w+' mode. However, this will also truncate the file to zero length. For a better description visit the fopen() page that has a very useful table describing all the modes available.

Error Checking

edit

Error checking is important for any sort of programming, but when working with files in PHP it is especially important. This need for error checking arises mainly from the filesystem the files are on. The majority of webservers today are Unix-based and so, if you are using PHP to develop web-applications, you have to account for file permissions. In some cases PHP may not have permission to read the file and so, if you've written code to read a particular file, it will result in an ugly error. More likely is that PHP doesn't have permission to write to a file and that will again result in ugly errors. Also, the file's existence is (somewhat obviously) important. When attempting to read a file, you must make sure the file exists first. On the other side of that, if you're attempting to create and then write to a file using the 'x' mode, then you must make sure the file doesn't exist first.

In short, when writing code to work with files, always assume the worst. Assume the file doesn't exist and you don't have permission to read from/write to it. In most cases this means you have to tell the users that, in order for the script to work, they need to adjust those file permissions so that PHP can create files and read from/write to them, but it also means that your script can adjust and perform an alternative operation.

There are two main ways of error checking. The first is by using the '@' operator to suppress any errors when working with the file and then checking, if the result is false or not. The second method involves using more functions like file_exists(), is_readable() and is_writeable().

Examples of using the '@' operator
<?php
$handle = @ fopen('data.txt', 'r');
if(!$handle) {
	echo 'PHP does not have permission to read this file or the file in question doesn\'t exist.';
} else {
	$string = fread($handle, 64);
	fclose($handle);
}

$handle = @ fopen('data.txt', 'w'); // The same applies for 'a'
if(!$handle) {
	echo 'PHP either does not have permission to write to this file or
it does not have permission to create this file in the current directory.';
} else {
	fwrite($handle, 'I can has content?');
	fclose($handle);
}

$handle = @ fopen('data.txt', 'x');
if(!$handle) {
	echo 'Either this file exists or PHP does not have permission to
create this file in the current directory.';
} else {
	fwrite($handle, 'I can has content?');
	fclose($handle);
}
?>
As you can see, the '@' operator is used mainly when working with the fopen() function. It can also be used in other cases, but is generally less efficient.


Examples of using specific checking functions
<?php
$file = 'data.txt';

if(!file_exists($file)) {
	// No point in reading since there is no content
	$contents = '';
	
	// But might want to create the file instead
	$handle = @ fopen($file, 'x'); // Still need to error-check
	if(!$handle) {
		echo 'PHP does not have permission to create a file in the current directory.';
	} else {
		fwrite($handle, 'Default data');
		fclose($handle);
	}
} else {
	// The file does exist so we can try to read its contents
	if(is_readable($file)) {
		$contents = file_get_contents($file);
	} else {
		echo 'PHP does not have permission to read that file.';
	}
}

if(file_exists($file) && is_writeable($file)) {
	$handle = fopen($file, 'w');
	fwrite($handle, 'I can has content?');
	fclose($handle);
}
?>


You can see by that last example that error-checking makes your code very robust. It allows it to be prepared for most situations and behave accordingly, which is an essential aspect of any program or script.

Line-endings

edit

Line-endings were mentioned briefly in the final example in the 'Reading' section of this chapter and it is important to be aware of them when working with files. When reading from a text file, it is important to know what types of line-endings that file contains. 'Line-endings' are special characters that try to tell a program to display a new line. For example, Notepad will only move a piece of text to a new line, if it finds "\r\n" just before the new line (it will also display new lines, if you put word wrap on).

If someone writes a text file on a Windows system, the chances are that each line will end with "\r\n". Similarly, if they write the file on a Classic Macintosh (Mac OS 9 and under) system, each line will probably end with "\r". Finally, if they write the file on a Unix-based system (Mac OS X and GNU/Linux), each line will probably end with "\n".

Why is this important? Well, when you read a file into a string with file_get_contents(), the string will be one long line with those line-endings all over the place. Sometimes they will get in the way of things you want to do with the string so you can remove them with:

<?php
$string = str_replace(array("\n", "\r"), '', $string);
?>

Other times you may need to know what kind of line-ending is being used throughout the text in order to be consistent with any new text you add. Luckily, in 99% of cases, the line-endings will never change type throughout the text so the custom function 'detectLineEndings' can be used as a quick way of checking:

<?php
function detectLineEndings($string) {
	if(false !== strpos($string, "\r\n")) return "\r\n";
	else if(false !== strpos($string, "\r")) return "\r";
	else return "\n";
}
?>

Most of the time though, it is just sufficient to be aware of their existence within the text so you can adjust your script to cope properly.

Binary-safe

edit

So far, all of the text seen in this chapter has been assumed to be encoded in some form of plaintext encoding such as UTF-8 or ASCII. Files do not have to be in this format, however, and in fact there exist a huge number of formats that aren't (such as pictures or executables). If you want to work with these files you have to ensure that the functions you are using are 'binary-safe'. Previously you would have to add 'b' to the end of the modes you used to tell PHP to treat the file as a binary file. Failing to do so would give unexpected results and generally 'weird-looking' data.

Since about PHP 4.3, this is no longer necessary as PHP will automatically detect, if it needs to open the file as a text file or a binary file and so you can still follow most of the examples shown here.

Working with binary data is a lot different to working with plaintext strings and characters and involves many more functions that are beyond the scope of this chapter. However, it is important you know about these differences.

Serialization

edit

Serialization is a technique used by programmers to preserve their working data in a format that can later be restored to its previous form. In simple cases this means converting a normal variable such as an array into a string and then storing it somewhere. That data can then be unserialized and the programmer will be able to work with the array once again.

There is a whole chapter devoted to Serialization in this book as it is a useful technique to know how to use effectively. It is mentioned here as one of the primary uses of serialization to store data on plain files when a database is not available. It is also used to store the state of a script and to cache data for quicker access later, and files are one of the preferred media for this storage.

In PHP, serialization is very easy to perform through use of the serialize() and unserialize() functions. Here follows an example of serialization used in conjunction with file functions.

An example of storing user details in a file so that they can be easily retrieved later.

  Code:

<?php
/* This part of the script saves the data to a file */
$data = array(
	'id' => 114,
	'first name' => 'Foo',
	'last name' => 'Bartholomew',
	'age' => 21,
	'country' => 'England'
);
$string = serialize($data);

$handle = fopen('data.dat', 'w');
fwrite($handle, $string);
fclose($handle);

/* Then, later on, we retrieve the data from the file and output it */
$string = file_get_contents('data.dat');
$data = unserialize($string);

$output = '';
foreach($data as $key => $datum) {
	$field = ucwords($key);
	$output .= "$field: $datum\n";
}

echo $output
?>

  Output:

Id: 114
First Name: Foo
Last Name: Bartholomew
Age: 21
Country: England

PHP 5

edit

There is one particular function specific to files that was introduced in PHP 5. That was the file_put_contents() function. It offers an alternative method of writing to files that does not exist in PHP 4. To see how it differs, it is easiest to just look at an example.

Examples showing writing to a file in PHP 4 and the equivalent in PHP 5 with the file_put_contents() function
<?php
$file = 'data.txt';
$content = 'New content.';

// PHP 4, overwrite entire file with data
$handle = fopen($file, 'w');
fwrite($handle, $content);
fclose($handle);

// PHP 5
file_put_contents($file, $content);

// PHP 4, append to a file
$handle = fopen($file, 'a');
fwrite($handle, $content);
fclose($handle);

// PHP 5
file_put_contents($file, $content, FILE_APPEND);
?>
file_put_contents() will also attempt to create the file, if it doesn't exist and it is binary-safe. There is no equivalent of the 'x' mode for file_get_contents().


file_put_contents() is almost always preferable over the fopen() method except when performing multiple operations on the same file. It is more preferable to use it for writing than file_get_contents() is for reading and for this reason, a function is provided here to emulate the behaviour of file_put_contents() for PHP 4:

<?php
if(!function_exists('file_put_contents')) {
	function file_put_contents($file, $data, $append = false) {
		if(!$append) $mode = 'w';
		else $mode = 'a';
		
		$handle = @ fopen($file, $mode);
		if(!$handle) return false;
		
		$bytes = fwrite($handle, $data);
		fclose($handle);
		
		return $bytes;
	}
}
?>