PHP File handling
Convenience functions
Raw direct IO
file_get_contents and file_put_contents provide the ability to read/write from/to a file to/from a PHP string in a single call. file_put_contents can also be used with the FILE_APPEND bitmask flag to append to, instead of truncate and overwrite, the file. It can be used along with LOCK_EX bitmask to acquire an exclusive lock to the file while proceeding to writing. Bitmask flags can be joined with the | bitwise-OR operator.
$path = "file.txt";
// reads contents in file.txt to $contents
$contents = file_get_contents($path);
// let's change something... for example, convert the CRLF to LF!
$contents = str_replace("\r\n", "\n", $contents);
// now write it back to file.txt, replacing the original contents
file_put_contents($path, $contents);
FILE_APPEND is handy for appending to log files while LOCK_EX helps prevent race condition of file writing from multiple processes. For example, to write to a log file about the current session:
file_put_contents("logins.log", "{$_SESSION["username"]} logged in", FILE_APPEND | LOCK_EX);
CSV IO
fgetcsv($file, $length, $separator)
The fgetcsv parses line from open file checking for csv fields. It returns CSV fields in an array on success or FALSE on failure. By default, it will read only one line of the CSV file.
$file = fopen("contacts.csv","r");
print_r(fgetcsv($file));
print_r(fgetcsv($file,5," "));
fclose($file);
contacts.csv
Kai Jim, Refsnes, Stavanger, Norway
Hege, Refsnes, Stavanger, Norway
Output
Array
(
[0] => Kai Jim
[1] => Refsnes
[2] => Stavanger
[3] => Norway
)
Array
(
[0] => Hege,
)
(
[0] => Kai Jim
[1] => Refsnes
[2] => Stavanger
[3] => Norway
)
Array
(
[0] => Hege,
)
Reading a file to stdout directly
readfile copies a file to the output buffer. readfile() will not present any memory issues, even when sending large files, on its own.
$file = 'monkey.gif';
if (file_exists($file)) {
header('Content-Description: File Transfer');
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename="'.basename($file).'"');
header('Expires: 0');
header('Cache-Control: must-revalidate');
header('Pragma: public');
header('Content-Length: ' . filesize($file));
readfile($file);
exit;
}
Or from a file pointer
Alternatively, to seek a point in the file to start copying to stdout, use fpassthru instead. In the following example, the last 1024 bytes are copied to stdout:
$fh = fopen("file.txt", "rb");
fseek($fh, -1024, SEEK_END);
fpassthru($fh);
Reading a file into an array
file returns the lines in the passed file in an array. Each element of the array corresponds to a line in the file, with the newline still attached.
print_r(file("test.txt"));
test.txt
Welcome to File handling
This is to test file handling
Output
Array
(
[0] => Welcome to File handling
[1] => This is to test file handling
)
Deleting files and directories
Deleting files
The unlink function deletes a single file and returns whether the operation was successful.
$filename = '/path/to/file.txt';
if (file_exists($filename)) {
$success = unlink($filename);
if (!$success) {
throw new Exception("Cannot delete $filename");
}
}
Deleting directories, with recursive deletion
On the other hand, directories should be deleted with rmdir. However, this function only deletes empty directories. To delete a directory with files, delete the files in the directories first. If the directory contains subdirectories, recursion may be needed.
The following example scans files in a directory, deletes member files/directories recursively, and returns the number of files (not directories) deleted.
function recurse_delete_dir(string $dir) : int {
$count = 0;
// ensure that $dir ends with a slash so that we can concatenate it with the filenames directly
$dir = rtrim($dir, "/\\") . "/";
// use dir() to list files
$list = dir($dir);
// store the next file name to $file. if $file is false, that's all -- end the loop.
while(($file = $list->read()) !== false) {
if($file === "." || $file === "..") continue;
if(is_file($dir . $file)) {
unlink($dir . $file);
$count++;
} elseif(is_dir($dir . $file)) {
$count += recurse_delete_dir($dir . $file);
}
}
// finally, safe to delete directory!
rmdir($dir);
return $count;
}