facebook google plus twitter
Webucator's Free PHP Tutorial

Lesson: File System Management

Welcome to our free PHP tutorial. This tutorial is based on Webucator's Introduction to PHP Training course.

Most Web applications use databases to store large amounts of data. However, in some cases, it will be necessary to store data in or access data from files.

Lesson Goals

  • Read from files on the server.
  • Write to files on the server.
  • Upload files to the server.
  • Obtain information about files on the server.
  • Access and work with directories on the server.

Opening a File

fopen()

Syntax

fopen (path_to_file, file_mode)

path_to_file can either be a relative or an absolute path.

File Modes
File Mode Description
r open for reading
w open for writing (erases existing content); creates new file if one doesn't exist
a open for appending to end of content; creates new file if one doesn't exist
x create and open for writing (new in PHP 4.3.2); fails and returns false if file already exists
r+ open for reading and writing (erases existing content when file is written to)
w+ open for writing and reading (erases existing content on opening or creates new file if one doesn't exist
a+ open for appending and writing; creates new file if one doesn't exist
x+ create and open for writing and reading (new in PHP 4.3.2); fails and returns false if file already exists

File Permissions

Files that do no have the appropriate permissions settings will fail to open. In this case, the fopen() function will return false and a warning will be given. Use conditional processing as shown below to handle this situation.

$myFile = @fopen('MyFile.txt','a');
if (!$myFile)
{
	echo '<b>Sorry, but the file cannot be opened.</b>';
}
else
{
	// code for processing file
}

The @ symbol in front of first line of code is used to suppress errors. Any errors can then be handled more gracefully.

Reading from a File

Opening a file for reading involves three steps:

  1. Open the file.
  2. Read the file.
  3. Close the file.

fgets()

fgets() is used to read a file one line at a time. It requires one argument: the resource or "handle" for the file and accepts a second argument: the length of the line. It will continue reading the line until the length - 1 have been read or it reaches the end of the line or the end of the file. If the second argument is not included, it will continue reading until it reaches the end of the line.

Examine the file shown below.

Code Sample:

Files/Demos/Employees.txt
Nancy	Davolio	Sales Representative	ndavolio@northwind.com
Andrew	Fuller	Vice President, Sales	afuller@northwind.com
Janet	Leverling	Sales Representative	jleverling@northwind.com
Margaret	Peacock	Sales Representative	mpeacock@northwind.com
Steven	Buchanan	Sales Manager	sbuchanan@northwind.com
Michael	Suyama	Sales Representative	msuyama@northwind.com
Robert	King	Sales Representative	rking@northwind.com
Laura	Callahan	Inside Sales Coordinator	lcallahan@northwind.com
Anne	Dodsworth	Sales Representative	adodsworth@northwind.com

Employees.txt is a tab-delimited text file. Each line is formatted as follows:

FirstName\tLastName\tTitle\tEmail\n

The file is divided into "columns" using tabs (\t) and each "row" is separated by a newline character (\n). The code below opens Employees.txt, reads and displays each line, and closes the file.

Code Sample:

Files/Demos/Employees.php
<!DOCTYPE HTML>
<html>
<head>
<meta charset="UTF-8">
<title>Employees</title>
</head>
<body>
<h1>Employees</h1>
<?php
	$myFile = @fopen("Employees.txt", 'r');
	
	if (!$myFile)
	{
		echo '<p>Cannot open file.';
	}
	else
	{
		while (!feof($myFile))
		{
			$employee = fgets($myFile, 999);
			echo $employee.'<br>';
		}
		fclose($myFile);
	}
?>
</body>
</html>
Other options for reading from files
Function Description
fgetss() Like fgets() but it strips out HTML and PHP tags.
fgetcsv() Like fgets() but it splits the file on a specified delimiter rather than a newline character.
readfile() Opens a file, sends its contents to the browser, and closes the file.
file() Opens a file, splits it into an array on newline characters, and closes the file.

Writing to a File

Opening a file for writing involves three steps:

  1. Open the file.
  2. Write to the file.
  3. Close the file.

fwrite()

Syntax

fwrite(file_pointer,output_string)

The output_string is the text to write to the file. See the following example of writing to a file.

$outputString='text to write';
$myFile = @fopen('Employees.txt', 'a');
fwrite($myFile, $outputString);
fclose($myFile);

Writing to a File

Duration: 10 to 15 minutes.

In this exercise you will write code to append entries to the Employees.txt.

  1. Open Files/Exercises/AddEmployee.php in your editor.
  2. Create short versions of the form variables.
  3. Write code to save the entry in Employees.txt, which is in the same directory. The steps involved are:
    1. Open Employees.txt for appending. Be sure to suppress errors.
    2. Write a condition that checks to see if the file failed to open.
    3. If it did open, write the output string to the file and close the file.

Solution:

Files/Solutions/AddEmployee.php
<!DOCTYPE HTML>
<html>
<head>
<meta charset="UTF-8">
<title>Employees</title>
</head>
<body>
<?php
if (array_key_exists('Submitted',$_POST))
{	
//Create short versions of the Form Variables
	$firstName = $_POST['FirstName'];
	$lastName = $_POST['LastName'];
	$title = $_POST['Title'];
	$email = $_POST['Email'];

	$outputString = "$firstName\t$lastName\t$title\t$email\n";
				  
//Open Employees.txt for appending
//Be sure to suppress errors
	$myFile = @fopen('Employees.txt', 'a');
	
//Write condition to check if file cannot be opened
	if (!$myFile)
	{
		echo '<p><strong> We cannot open the file
				at this time. Please try again later.
				</strong></p></body></html>';
	}
	else
	{
		echo '<h1 align="center">Employee added</h1>';
		
		//Write $outputString to the file
		fwrite($myFile,$outputString);
		
		//Close the file
		fclose($myFile);
			
		echo '<a href="Employees.php">Employees.php</a>';
	} 
}
else
{
?>

---- C O D E   O M I T T E D ----

<?php
}
?>
</div>
</body>
</html>

File Locking

flock()

flock() is used to lock a file so that two or more people do not get access to it at the same time. This helps protect the file from being corrupted. flock() takes two arguments: a file handler and a lock type. flock() does not work with NFS or other networked file systems or with such as FAT that does not support file locking.

Lock Type Explanation
LOCK_SH Reading lock. Others can read file.
LOCK_EX Exclusive lock. The file cannot be opened by others.
LOCK_UN Unlocks file.
LOCK_NB If a file is already locked by another user, flock() waits to get a lock. LOCK_NB tells it not to wait.

The code below shows how we should change Files/Solutions/AddEntry.php to protect Employees.txt from being corrupted.

Code Sample:

Files/Demos/Locking.php
---- C O D E   O M I T T E D ----
		flock($myFile, LOCK_EX);
		fwrite($myFile,$outputString);
		flock($myFile, LOCK_UN);
		fclose($myFile);
---- C O D E   O M I T T E D ----

Uploading Files via an HTML Form

In order to upload files via an HTML form, the form tag's method must be set to "post" and the enctype must be set to "multipart/form-data" as shown below.

For file uploads to work, the file_uploads flag in php.ini must be turned on.

Syntax

<form method="post" enctype="multipart/form-data">

The following example demonstrates how to safely allow the user to upload a file to the server.

Code Sample:

Files/Demos/FileUpload.php
<!DOCTYPE HTML>
<html>
<head>
<meta charset="UTF-8">
<title>Resume Upload</title>
</head>
<body style="text-align:center">
<?php
	if (!array_key_exists('Submitted',$_POST)) {
?>
<h2>Resume Upload Form</h2>
<form method="post" enctype="multipart/form-data">
<input type="hidden" name="Submitted" value="true">
<table border="1">
<tr>
	<td>First Name</td>
	<td><input type="text" name="FirstName" size="20"></td>
</tr>
<tr>
	<td>Last Name</td>
	<td><input type="text" name="LastName" size="20"></td>
</tr>
<tr>
	<td>Resume</td>
	<td><input type="file" name="Resume"></td>
</tr>
<tr>
	<td colspan="2" align="center"><input type="submit" value="Upload"></td>
</tr>
</table>
</form>
<?php
} else {
	//process the  form
	$resumeFile = $_FILES['Resume']['tmp_name'];
	$fileSize = $_FILES['Resume']['size'];
	$fileType = $_FILES['Resume']['type'];
	$fileError = $_FILES['Resume']['error'];

	$resumeName=$_POST['FirstName'] . '_' .
			$_POST['LastName'] . '_Resume.txt';
	if ($fileError)
	{
		echo "We could not upload the file:<br>$fileError";
		endPage();
	}
	elseif ($fileType != 'text/plain')
	{
		echo "You have attempted to upload a file of type: $fileType.
				<br>Only text files allowed.";
		endPage();
	}

	$fileSavePath = 'Resumes/' . $resumeName;	
	if (is_uploaded_file($resumeFile))
	{
		if (!move_uploaded_file($resumeFile,$fileSavePath))
		{
			echo 'Could not save file.';
			endPage();
		}
	}
	else
	{
		//This case happens if somehow the file
		//we are working with was already on the server.
		//It's to stop hackers.
		echo 'Hey, what is going on here?
					Are you being bad?';
		endPage();
	}
	$resume=makeFileSafe($fileSavePath);
?>
	<h2>Thanks!</h2>
	<b>We got your resume.</b><hr>
	<form>
	<textarea cols="60" rows="20"><?php echo $resume ?></textarea>
	</form>
	</p>
<?php
}

function endPage()
{
	echo '</body></html>';
	exit;
}

function makeFileSafe($filePath)
{
	$fP = @fopen($filePath,'r+');
	if (!$fP)
	{
		return "Could not read file";
	}
	$contents = fread($fP,filesize($filePath));
	$contents = strip_tags($contents);
	rewind($fP);
	fwrite($fP,$contents);
	fclose($fP);
	return $contents;
}
?>
</body>
</html>

The first thing to notice about this page is that it submits to itself. The first time it is loaded, it will show the form. When the form is submitted, it will attempt to upload and save the user's resume.

  1. The form also has an input field of type file that is used to browse for the file to upload.
  2. When the form is submitted, the script assigns values to short named variables.
  3. The next block of code is the if-elseif-elseif statement, which checks for errors. If it finds any, it displays an appropriate message and calls the endPage() user function, which just closes the HTML page.
  4. The next piece of code attempts to upload the file:
    if (is_uploaded_file($resumeFile))
    {
    	if (!move_uploaded_file($resumeFile,$fileSavePath))
    	{
    		echo 'Could not save file.';
    		endPage();
    	}
    }
    else
    {
    	//This case happens if somehow the file
    	//we are working with was already on the server.
    	//It's to stop hackers.
    	echo 'Hey, what is going on here?
    				Are you being bad?';
    	endPage();
    }
  5. The last bit of PHP code on the page calls the makeFileSafe() user function which opens the resume file, strips out all the tags from its contents and closes it.

Getting File Information

The following code sample illustrates how to get information about a file using PHP.

Code Sample:

Files/Demos/FileInfo.php
<!DOCTYPE HTML>
<html>
<head>
<meta charset="UTF-8">
<title>File Details</title>
</head>
<body>
<?php
	$file = 'Resumes/J_C_Resume.txt'; // Path to the file
	$fileName = basename($file); // Just the file name
	echo '<h1>Details of file: ' . $fileName.'</h1>';
	
	echo '<h2>File data</h2>';
	echo 'File last accessed: ' . 
			date('j F Y H:i', fileatime($file)) . '<br>';
	echo 'File last modified: ' .
			date('j F Y H:i', filemtime($file)).'<br>';
	echo 'File type: ' . filetype($file).'<br>';
	echo 'File size: '.filesize($file).' bytes<br>';
	
	echo '<h2>File tests</h2>';
	echo 'is_dir: ' .
		(is_dir($file)? 'true' : 'false') . '<br>';
	echo 'is_file: ' .
		(is_file($file)? 'true' : 'false').'<br>';
	echo 'is_readable: ' . 
		(is_readable($file)? 'true' : 'false').'<br>';
	echo 'is_writable: ' . 
		(is_writable($file)? 'true' : 'false').'<br>';
?>
</body>
</html>

The functions used in this script are described in the following table.

Function Description
basename() Strips off the path and returns the file name.
fileatime() Returns the last accessed time of the file.
filemtime() Returns the last modified time of the file.
filetype() Returns the type of file (e.g, file or dir).
filesize() Returns the size of the file in bytes.
is_dir() Returns true if the passed value is a directory, false if it isn't.
is_file() Returns true if the passed value is a file, false if it isn't.
is_readable() Returns true if the file is readable, false if it isn't.
is_writable() Returns true if the file is writable, false if it isn't.

More File Functions

A few more file functions are shown below:

Function Description
file_exists(path_to_file) Checks to see if a file exists.
filesize(path_to_file) Returns the size of file in bytes.
unlink(path_to_file) Deletes the file.
copy() Copies a file. Takes two arguments: the path to the source file and the destination to copy the source file to.
rename() Moves a file. Takes two arguments: the path to the source file and the destination to move the source file to. If the path and destination are the same with the exception of the filename, rename() simply renames the file.

Directory Functions

The following table shows some of the more common directory functions.

Function Description
mkdir() Creates a directory.
rmdir() Deletes a directory.
opendir() Opens a directory for reading.
readdir() Reads the contents of an open directory.

Getting a Directory Listing

To get a directory listing, use the opendir() function to open the directory and the readdir() function to read its contents. Then loop through its contents with a while loop outputting the name of each file and folder.

Creating a Resume Management Page

Duration: 20 to 30 minutes.

In this exercise, you will create a simple resume management page that will list all the resumes currently in the resumes folder and allow you to remove resumes from the folder.

  1. Open Files/Exercises/FileListing.php in your editor. Much of the file is done already.
  2. Complete the fileDetails() function so that it will properly display the details of the passed file.
  3. At the end of the if block of the fileDetails() function there is a "Delete File" link. Modify this link so that it passes the file path in the "Delete" variable via the query string.
  4. Write the browseDir() function.
  5. Write the deleteFile() function.

Solution:

Files/Solutions/FileListing.php
<!DOCTYPE HTML>
<html>
<head>
<meta charset="UTF-8">
<title>File Listing</title>
</head>
<body>
<?php
	browseDir('Resumes');
	if (array_key_exists('File',$_GET))
	{
		fileDetails($_GET['File']);
	}
		
	if (array_key_exists('Delete',$_GET))
	{
		if(deleteFile($_GET['Delete']))
		{
			echo 'File Deleted';
		}
		else
		{
			echo 'Could not delete file';
		}
	}

	function fileDetails($filePath)
	{
		if (is_file($filePath))
		{
			$baseName=basename($filePath);
			echo "<h1>Details of file: $baseName</h1> 			
				<h2>File data</h2>
				File last accessed: " . 
					date('j F Y H:i', fileatime($filePath)) . '<br>
				File last modified: ' . 
					date('j F Y H:i', filemtime($filePath)) . '<br>
				File type: ' . 
					filetype($filePath) . '<br>
				File size: ' .
					filesize($filePath) . ' bytes<br>';					
			echo "<a href='FileListing.php?Delete=$filePath'>Delete File</a>";
		}
		else
		{
			echo "$file_path is not a file.";
		}
	}
	
	function browseDir($directory)
	{
		$dir = opendir($directory);
		echo "<h2>$directory</h2>";
		echo 'Directory Listing:<br><hr><br>';
		while ($file = readdir($dir))
		{
			if(is_file($directory.'/'.$file))
echo "<a href='filelisting.php?File=$directory/$file'>$file</a><br>";
		}
		echo '<hr><br>';
		closedir($dir);
	}
	
	function deleteFile($filePath)
	{
		if (!is_file($filePath) || !@unlink($filePath))
		{
			return false;
		}
		else
		{
			return true;
		}
	}

?>
</body>
</html>