Reading and Writing Text Files in PHP
Files and Directories with Security Considerations in PHP - Part 1
Foreword: In this part of the series, I explain how to read, edit and write text files in PHP.
By: Chrysanthus Date Published: 19 Jan 2019
Introduction
Resource
In order for you to use a file in the hard disk or some other drive, you have to do what is called opening the file. For this, the content of the file is copied into memory. This area in memory that has the content of the file is called a stream (precisely a buffer). Whatever you want to do to the file (modifying the file, adding text to the end of the file, or just reading the file) you do to the stream. After that you have to do what is called closing the file. When a file is closed, the content of the stream is copied to the file in the disk, if necessary. Any modification of stream content or adding of text to the end of the stream is reflected in the file in the disk after closing.
A file may not exist in the disk. This means you have to create it. For this purpose, you still have to use the opening process (see below). A stream for the newly (not existing) opened file is created. You send information to the stream. When you close the file, effectively closing the stream, the content of the stream is copied to the disk. Closing a file means putting an end to the association between the stream and the corresponding file in the disk, after the content of the stream has just been copied to the file, which might or might not have existed, in the disk.
Note: the content of the stream is the file content copied from disk, if the file existed in the disk before it was opened. While you are editing a file, the content may also be saved.
In order to open, edit and close a file, you need what is known as a resource. A resource is a special kind of reference holding variable. There are different kinds of resources - see later.
The simplified syntax to open a file is,
resource fopen($filename, $mode)
The function returns a file pointer resource on success, or FALSE on error. The first argument in the parentheses is the file name in quotes (can include directory path). I will explain the role of the mode (second) argument as we go along.
The simplified syntax to close a file is,
bool fclose($handle)
where $handle is the returned resource above. Any file opened has to be closed, after editing.
The function returns TRUE on success or FALSE on failure.
Text files are created in lines of text. The following simplified syntax reads a line of text from a file:
string fgets($handle)
This function returns the line of text including the newline character (\n). It will return FALSE if there is no more line to read. It will still return FALSE if an error occurs. Note that the content of a line may be NULL or 0. So to test for FALSE, use === or !== .
The following simplified syntax writes a line of text to a file:
int fwrite ($handle, $string)
where $handle is the same resource from fopen(), and $string is in quotes, optionally ending with a newline character (\n). The function returns the number of bytes written, or FALSE on error. Note that the integer returned may be zero. So to test for FALSE, use === or !== .
The following are the possible values for the $mode variable above:
'r': Open for reading only; place the file pointer at the beginning of the file.
'r+': Open for reading and writing; place the file pointer at the beginning of the file.
'w': Open for writing only; place the file pointer at the beginning of the file and truncate the file to zero length. If the file does not exist, attempt to create it.
'w+': Open for reading and writing; place the file pointer at the beginning of the file and truncate the file to zero length. If the file does not exist, attempt to create it.
'a': Open for writing only; place the file pointer at the end of the file. If the file does not exist, attempt to create it.
'a+': Open for reading and writing; place the file pointer at the end of the file. If the file does not exist, attempt to create it.
'x': Create and open for writing only; place the file pointer at the beginning of the file. If the file already exists, the fopen() call will fail by returning FALSE and generating an error of level E_WARNING. If the file does not exist, attempt to create it. This is equivalent to specifying O_EXCL|O_CREAT flags for the underlying open(2) system call.
'x+': Create and open for reading and writing; otherwise it has the same behavior as 'x'.
'c': Open the file for writing only. If the file does not exist, it is created. If it exists, it is neither truncated (as opposed to 'w'), nor the call to this function fails (as is the case with 'x'). The file pointer is positioned on the beginning of the file. This may be useful if it's desired to get an advisory lock (see flock()) before attempting to modify the file, as using 'w' could truncate the file before the lock was obtained (if truncation is desired, ftruncate() can be used after the lock is requested).
'c+': Open the file for reading and writing; otherwise it has the same behavior as 'c'.
Writing a File
The following code creates a new file and writes lines of text to it.
<?php
if (!$handle = fopen('temp1.txt', 'w'))
{
echo "Cannot open file 'temp1.txt'";
}
else
{
if (fwrite($handle, "The first line.\r\n") === false)
{
echo "Cannot write 'The first line.\n' to temp.txt";
}
if (fwrite($handle, "The second line.\r\n") === false)
{
echo "Cannot write 'The second line.\n' to temp.txt";
}
if (fwrite($handle, "The third line.\r\n") === false)
{
echo "Cannot write 'The third line.\n' to temp.txt";
}
echo 'Data written';
fclose($handle);
}
?>
The file content is:
The first line.
The second line.
The third line.
The expression,
$handle = fopen('temp1.txt', 'w')
results in $handle, which is either the resource or false. If it is false, then !$handle is true. Note the mode that has been used in the above code.
Reading a File
The following code opens and reads the lines of text from a file:
<?php
if (!$handle = fopen('temp1.txt', 'r'))
{
echo "Cannot open file 'temp1.txt'";
}
else
{
while (($buffer = fgets($handle)) !== false)
{
echo $buffer, "<br>";
}
fclose($handle);
}
?>
The output is:
The first line.
The second line.
The third line.
Note the mode that has been used in the above code. The expression,
$buffer = fgets($handle)
results in $buffer, which either has the line of text or "false". It is compared to false with the Not Identical operator. The while-loop reads lines of text until the end-of-file. After the reading of each line, the file pointer points to the next line.
"Append" means add lines to the bottom. The following code appends a fourth line to the file, temp1.txt :
<?php
if (!$handle = fopen('temp1.txt', 'a'))
{
echo "Cannot open file 'temp1.txt'";
}
else
{
if (fwrite($handle, "The fourth line.\r\n") === false)
{
echo "Cannot write 'The first line.\n' to temp.txt";
}
echo 'Data written';
fclose($handle);
}
?>
Note the mode that has been used in the above code. The file content becomes,
The first line.
The second line.
The third line.
The fourth line.
Editing a File
To edit a file, open the file in the 'r+' mode. Change the line, while it is read. The following code illustrates this, changing the second line:
<?php
if (!$handle = fopen('temp1.txt', 'r+'))
{
echo "Cannot open file 'temp1.txt'";
}
else
{
while (($buffer = fgets($handle)) !== false)
{
if ($buffer === "The second line.\r\n")
{
$stringlength = strlen($buffer);
fseek($handle, -$stringlength, SEEK_CUR);
$buffer = "The B line.\r\n";
fwrite($handle, $buffer);
echo $buffer, "<br>";
}
else
{
echo $buffer, "<br>";
}
}
fclose($handle);
}
?>
Whenever a line is read, the file pointer is made to point to the next line. The following code segment takes back the file pointer to the beginning of the line read:
$stringlength = strlen($buffer);
fseek($handle, -$stringlength, SEEK_CUR);
The new file content is:
The first line.
The B line.
ne.
The third line.
The fourth line.
This means that the last characters, 'ne.\r\n' of the second line were not removed. I will explain how to solve this problem in a later series.
Consider the funcrion:
int file_put_contents($filename, $string)
This function is identical to calling fopen(), fwrite() and fclose() successively to write data to a file. If filename does not exist, the file is created. Otherwise, the existing file is overwritten.
The function returns the number of bytes (characters) that were written to the file, or FALSE on failure. The function may also return a non-Boolean value which evaluates to FALSE, such as 0 or "". Use === or !== for testing the return value of this function.
The following code illustrates its use:
<?php
$str = "The first line.\r\nThe second line.\r\nThe third line.\r\n";
if (file_put_contents('temp1.txt', $str) !== false)
{
echo 'file created and written to, successfully';
}
?>
Consider the funcrion:
string file_get_contents($filename)
This function is identical to calling fopen(), fgets() and fclose() successively to read all file data into a string.
The function returns the read data or FALSE on failure (the returned string may be "").
The following code illustrates its use:
<?php
$str;
if (($str = file_get_contents('temp1.txt')) !== false)
{
echo $str;
}
?>
Security Considerations
The fopen() Function
The syntax for this function is:
resource fopen ( string $filename , string $mode [, bool $use_include_path = FALSE [, resource $context ]] )
fopen() binds a named resource, specified by filename, to a stream.
It returns a file pointer resource on success, or FALSE on error.
Problem really occurs when you are using modes, 'r', 'r+', 'w', 'w+', 'a', 'a+'.
Different operating system families have different line-ending conventions. When you write a text file and want to insert a line break, you need to use the correct line-ending character(s) for your operating system. Unix based systems use \n as the line ending character, Windows based systems use \r\n as the line ending characters and Macintosh based systems use \r as the line ending character.
If you use the wrong line ending characters when writing your files, you might find that other applications that open those files will "look funny".
Windows offers a text-mode translation flag ('t') which will transparently translate \n to \r\n when working with the file.
Prevention: use only the 'r', 'r+', 'w', and 'a' modes and be careful how you code.
The fclose() function
The syntax is:
bool fclose ( resource $handle )
Returns TRUE on success or FALSE on failure.
When testing whether a file has been closed, do not test for 1 instead of true or test for 0 instead of false.
The fgets() function
The syntax is:
string fgets ( resource $handle [, int $length ] )
Gets the next line from file pointer.
Returns a string of up to length - 1 bytes read from the file pointed to by handle. If there is no more data to read in the file pointer, then FALSE is returned.
If an error occurs, FALSE is returned.
Problem: False is returned for no-more-data-left or error. Prevention: discard any data read when false is returned.
The fwrite() function
The syntax is:
int fwrite ( resource $handle , string $string [, int $length ] )
fwrite() writes the contents of string to the file stream pointed to by handle.
fwrite() returns the number of bytes written, or FALSE on error.
Prevention: To check for 0 byte written, test for 0 with === . To check for error, test for false with === . Do not use == for 0 and false.
Note: Writing to a network stream may end before the whole string is written. Return value of fwrite() may be checked.
Note: On systems which differentiate between binary and text files (i.e. Windows) the file must be opened with 'b' included in fopen() mode parameter.
Note: If handle was fopen()ed in append mode, fwrite()s are atomic (unless the size of string exceeds the filesystem's block size, on some platforms, and as long as the file is on a local filesystem). That is, there is no need to flock() a resource before calling fwrite(); all of the data will be written without interruption.
Note: If writing twice to the file pointer, then the data will be appended to the end of the file content.
Let us take a break here and continue in the next part of the series.
Chrys
Related Links
Basics of PHP with Security ConsiderationsWhite Space in PHP
PHP Data Types with Security Considerations
PHP Variables with Security Considerations
PHP Operators with Security Considerations
PHP Control Structures with Security Considerations
PHP String with Security Considerations
PHP Arrays with Security Considerations
PHP Functions with Security Considerations
PHP Return Statement
Exception Handling in PHP
Variable Scope in PHP
Constant in PHP
PHP Classes and Objects
Reference in PHP
PHP Regular Expressions with Security Considerations
Date and Time in PHP with Security Considerations
Files and Directories with Security Considerations in PHP
Writing a PHP Command Line Tool
PHP Core Number Basics and Testing
Validating Input in PHP
PHP Eval Function and Security Risks
PHP Multi-Dimensional Array with Security Consideration
Mathematics Functions for Everybody in PHP
PHP Cheat Sheet and Prevention Explained
More Related Links