Search This Blog

Monday, May 31, 2010

Ownership and Permissions

You also can get information on file ownership and permissions. On a UNIX system like Linux, all files are associated with a specific user and a specific group of users, and assigned flags that determine who has permission to read, write, or execute their contents. Some Windows file systems and operating systems work this way, and some don't. For example, the Windows NT File System (NTFS) enables you to assign owners and permissions, while Windows 98 doesn't.


User groups are defined in UNIX so that permissions can be easily extended to a certain set of users without extending them to everyone on the system. For example, several users who are working on the same project might want to share files with each other, but with no one else.


Each of these three permissions (read, write, and execute) can be granted to (or withheld from):
  • The file owner: By default, the user whose account was used to create the file.
  • A group of users: By default, the group to which the owner belongs.
  • All users: Everyone with an account on the system
Users and groups in UNIX are identified by ID numbers as well as names. If you want to get information on a user by his ID number, you can use the posix_getpwuid() function, which returns an associative array with the following references:


Name
Description
name
The shell account username of the user
passwd
The encrypted user password
uid
The ID number of the user
gid
The group ID of the user
gecos
A Comma-separated list containing the user's full name, office phone, office number, and home phone number. On most systems, only the user's full name is available
Dir
The absolute path to the home directory of the user
shell
Absolute path to the user's default shell


Another PHP function, posix_getgrgid(), returns an associative array on a group identified by a group ID. It contains the following elements of the group structure:


Name
Description
Name
The name of the group
Gid
The ID number of the group
members
The number of members belonging to the group


You can use the following three functions to get at this information from your PHP scripts. Each takes a single argument, a filename string.
  • fileowner(): Returns the user ID of the owner of the specified file.
  • filegroup(): Returns the group ID of the owner of the specified file.
  • filetype(): Returns the type (fifo, char, dir, block, link, file, or unknown) of the specified file.
For example, you can use filetype() to check whether a given filename is a file or a directory (make sure you have a file named counter.php in the folder; create it if you need to):


<?php
//file type.php
$filename = "./counter.php";
$filegroup = filegroup($filename);
$fileowner = fileowner($filename);
$filetype = filetype($filename);

if ($filetype == 'dir') {
   echo "$filename is a directory.";
} else if ($filetype == 'file') {
   echo "$filename is a file.<br>";
} else {
   echo "$filename is neither a directory nor a file.<br>";
}

echo "It belongs to user no.$fileowner and group no.$filegroup.<br>";
echo "<br>The user $fileowner info<br>";
$user_info_array = posix_getpwuid($fileowner);

foreach ($user_info_array as $key => $val) {
   echo "$key => $val<br>";
}

echo "<br>The group $filegroup info<br>";
$group_info_array = posix_getgrgid($filegroup);

foreach ($group_info_array as $key => $val) {
   echo "$key => $val<br>";
}
?>

Notice the error that results from attempting to use on Windows a function that only works on Linux machines. You can either find a Linux machine to run this on, or move on to the next example to get similar information about Windows files.

The is_dir() and is_file() Functions

Often you must know whether a file is a directory (remember, directories and files are all files, but directories are a special kind of file). For example, suppose you want to create a system that travels through the hierarchy of folders, you'd need to detect when a file was actually a folder, so you could attempt to enter the folder and continue traveling the hierarchy. By the same token, if you want to view only the files in a folder, you'd need to detect when a file is a file. PHP has two functions to help you out:
  • is_dir() is designed specifically to work with directories. It returns True if the given filename refers to a directory.
  • is_file() returns True if the given filename refers to a regular file.
It's very simple to rewrite the previous script so that it produces the same results using these functions:


<?php
$filename = "./counter.php";
if(is_dir($filename)) {
 echo "$filename is a directory.";
} else if (is_file($filename)) {
 echo "$filename is a file.";
} else {
 echo "$filename is neither a directory nor a file.";
}
?>

As noted earlier, if you use fileatime() in Windows, it returns the last modified date; and because Windows doesn't support file ownership, filegroup() and fileowner() both return zero.


Try it Out: Get Information on a Windows File
Start example
Here's a sample script that displays some properties on a given file:


<?php
//file_win_info.php
function date_str($timestamp) {
   $date_str = getdate($timestamp);
   $year = $date_str["year"];
   $mon = $date_str["mon"];
   $mday = $date_str["mday"];
   $hours = $date_str["hours"];
   $minutes = $date_str["minutes"];
   $seconds = $date_str["seconds"];
   return "$hours:$minutes:$seconds $mday/$mon/$year";
}
function file_info($file) {

   $file_info_array["filesize"] =
                  number_format(filesize($file)) . " bytes.";
   $file_info_array["filectime"] = date_str(filectime($file));
   $file_info_array["filemtime"] = date_str(filemtime($file));
   if(!isset($_ENV[WINDIR])) {
      $file_info_array["fileatime"] = date_str(fileatime($file));
      $file_info_array["filegroup"] = filegroup($file);
      $file_info_array["fileowner"] = fileowner($file);
}
$file_info_array["filetype"] = filetype($file);

return $file_info_array;
}

$filename = "./count.dat";
$file_info_array = file_info($filename);

echo "<center>Stats for $filename</center>";
foreach($file_info_array as $key=>$val) {
   echo ucfirst($key) . "=>". $val . "<br>";
}
?>


End example

How it Works

Taking some lines of code from the last version of the hit counter, you can put them in a complete function named date_str(), which returns a formatted date string:


function date_str($timestamp) {
   $date_str = getdate($timestamp);
   $year = $date_str["year"];
   $mon = $date_str["mon"];
   $mday = $date_str["mday"];
   $hours = $date_str["hours"];
   $minutes = $date_str["minutes"];
   $seconds = $date_str["seconds"];

   return "$hours:$minutes:$seconds $mday/$mon/$year";
}

The next file_info() function introduces a set of new file-related functions that return useful information on a given file:


function file_info($file) {
   $file_info_array["filesize"] =
                  number_format(filesize($file)) . " bytes.";
   $file_info_array["filectime"] = date_str(filectime($file));
   $file_info_array["filemtime"] = date_str(filemtime($file));

if(!isset($_ENV[WINDIR""])) {
   $file_info_array["fileatime"] = date_str(fileatime($file));
   $file_info_array["filegroup"] = filegroup($file);
   $file_info_array["fileowner"] = fileowner($file);
}

$file_info_array["filetype"] = filetype($file);
return $file_info_array;
}

The environment variable $_ENV[WINDIR] is only set on the Windows platform; you access it via $_ENV and use it as a flag to denote whether the script is being run on a Windows platform. If it is set in the preceding code, you don't read the three fields that Windows doesn't support. Please note that the old $HTTP_ENV_VARS could be used as well, so long as you used the global keyword on it to make it visible inside your function. Although that still works, it's deprecated in favor of the $_ENV superglobal. An advantage of using the superglobal $_ENV is that you don't have to declare it as global to access from inside functions.


Finally, you specify a file, call the file_info() function, and print out the contents of each element in the array it returns:


$filename = "./count.dat";
$file_info_array = file_info($filename);

echo "<center>Stats for $filename</center>";
foreach($file_info_array as $key=>$val) {
   echo ucfirst($key) . "=>". $val . "<br>";
}

Monday, May 24, 2010

Getting Information on Files

Earlier in this chapter, you saw how you could run into problems if you ran the counter script and the file count.dat didn't exist, and you made sure you performed some basic error checking when you opened the file. That's a very simple approach—if something does go wrong, the user won't necessarily know what the problem is.


PHP provides some functions that enable you to access useful file information. For example, rather than just spewing out a standard error message when you fail to open a file, you can use file_exists() to discover whether the file exists. If it doesn't, you can infer that the current user is the first visitor to the site and create the data file. You can say:


file_exists("/home/chris/count.dat")

This returns True if count.dat exists in /home/chris/, and False otherwise. The error checking for your hit counter might now take this form:


<?php
//hit_counter_10.php
$counter_file = "./count.dat";
if(file_exists($counter_file)) {
   if(!($fp = fopen($counter_file, "r+")))
              die("Cannot open $counter_file");
   $counter = (int) fread($fp, filesize($counter_file));
   $counter++;
   rewind($fp);
}
else {
   if(!($fp = fopen($counter_file, "w")))
               die("Cannot open $counter_file");
   $counter = 1;
}

echo "You're visitor No. $counter.";

fwrite($fp, $counter);
fclose($fp);
?>

This is just one of a number of functions that return helpful information on a given file. In a similar fashion, you can use the filesize() function to determine exactly how many bytes should be read from the counter data file. Just as with file_exists(), this function takes a filename string argument directly instead of a file handle:


filesize("/home/chris/count.dat")

This returns the size of the specified file in bytes, or False upon error. You could use filesize() to determine how many bytes should be read from the data file to get the number of hits:


<?php
//hit_counterll.php
$counter_file = "./count.dat";
if(file_exists($counter_file)) {
   if(!($fp = fopen($counter_file, "r+")))
                  die("Cannot open $counter_file");
   $counter = (int) fread($fp, filesize($counter_file));
   $counter++;
   rewind($fp);
}
...

Time-Related Properties

Besides their contents, files have other properties that can provide useful information. These will principally depend on the operating system in which they are created and modified. On UNIX platforms such as Linux, for example, properties include creation date, modification date, last access date, and user permissions. With just a little extra code, you can make the hit counter show when the last access was made. The function fileatime() returns the last access time for a file in a UNIX timestamp format, and returns the last date on which the file was modified in a Windows format.
A UNIX timestamp is a long integer whose value can be interpreted as the number of seconds between the UNIX Epoch (January 1, 1970) and a specified time and date.
PHP provides two other time-related file functions:
  • filectime() returns the time at which the file was last changed as a UNIX timestamp. A file is considered changed if it is created or written, or when its permissions have been changed.
  • filemtime() returns the time at which the file was last modified as a UNIX timestamp. The file is considered modified if it is created or has its contents changed.
The getdate() function is also very useful when working with timestamps. It returns an associative array containing the date information present in a timestamp. The array includes such values as the year, the month, the day of the month, and so on. You can set a variable ($my_date) equal to the value returned by getdate(), and then access the month, for example, with $my_date['month].


Try it Out: Display the Last Access Time to the Counter Data File
Start example
This example uses the getdate() function to read the date values from the fileatime() function for the file named in the variable $counter_file.


<?php
//last_counter_access.php
$counter_file = "./count.dat";
if(file_exists($counter_file)) {
   $date_str = getdate(fileatime($counter_file));
   $year = $date_str["year"];
   $mon = $date_str["mon"];
   $mday = $date_str["mday"];
   $hours = $date_str["hours"];
   $minutes = $date_str["minutes"];
   $seconds = $date_str["seconds"];

   $date_str = "$hours:$minutes:$seconds $mday/$mon/$year";

   if(!($fp = fopen($counter_file, "r+")))
                     die("Cannot open $counter_file");
   $counter = (int) fread($fp, filesize($counter_file));
   $counter++;

   echo "You're visitor No. $counter.";
   echo "The last access was made at $date_str";
   rewind($fp);
   }
   else {
      if(!($fp = fopen($counter_file, "w")))
                     die("Cannot open $counter_file");
      $counter = 1;
      echo "You're visitor No. $counter.";
   }

   fwrite($fp, $counter);
   fclose($fp);

   ?>



End example

How it Works

If the specified count.dat file exists, the script gets the last access time for the data file as a UNIX timestamp. This is converted to an associative array containing date information with the getdate() function, values are extracted from the appropriate elements, arranged, and stored in a string variable:


$counter_file = "./count.dat";
if(file_exists($counter_file)) {
   $date_str = getdate(fileatime($counter_file));
   $year = $date_str["year"];
   $mon = $date_str["mon"];
   $mday = $date_str["mday"];
   $hours = $date_str["hours"];
   $minutes = $date_str["minutes"];
   $seconds = $date_str["seconds"];

   $date_str = "$hours:$minutes:$seconds $mday/$mon/$year";

When you create, write to, or read from a file, UNIX considers the file to have been accessed (Windows is a bit different, and may consider the file changed only under other circumstances, and as we mentioned previously, may show the file creation time instead of the last access time). For this reason, you call fileatime() before reading from the data file. Still conditional on the actual existence of the count.dat file, you open it and read its entire contents into the variable $counter, increment that variable, and print it out along with the formatted date information. Then, rewind() the file position indicator (so that you're ready to write the new count) and close the body of the if statement:


  if(!($fp = fopen($counter_file; "r+")))
                    die("Cannot open $counter_file");
  $counter = (int) fread($fp, filesize($counter_file));
  $counter++;

  echo "You're visitor No. $counter. "
  echo "The last access was made at $date_str";
  rewind($fp);
}

Now look at the else clause; if the file count.dat didn't already exist, you create it with fopen(), specifying write-only. Initialize the counter at 1 and incorporate it in a displayed message:


else {
   if(!($fp = fopen($counter_file, "w")))
                     die("Cannot open $counter_file");
   $counter = 1;
   echo "You're visitor No. $counter.";
}

Finally, write the new counter value to the count.dat file, and close it with fclose():


fwrite($fp, $counter);
fclose($fp);

Monday, May 17, 2010

Random Access to File Data

As you may have realized, it'd be more efficient if you opened the file for both read and write operations with a single fopen() call. However, using the functions you've met so far, you can only manipulate data sequentially, that is, in the same order that it is (or will be) arranged in the file. This puts a major limitation on what you can usefully achieve by doing it this way; once the file position indicator passes a certain point in a file, you need to close and reopen the file before you can access data at that point. Unless your data access is terribly well organized in advance (not likely in real-life situations, where it's often impossible to predict what you might need to do), you'd end up opening your data file as many (if not more) times as before.
What you really need is some way to move the file position indicator around in the file without having to close and reopen the file. There are a couple of functions that do just that:
  • fseek()
  • ftell()
PHP provides a number of functions that are designed to let you read from or write to specific positions within a file. Specifying a file handle (such as $fp) and an integer offset (5 in the following example) as arguments, fseek() will move the file position indicator associated with fp to a position determined by offset. By default, the offset is measured in bytes from the beginning of the file. Here's an example:


fseek($fp, 5);
$one_char = fgetc($fp);

This code places the file's file position indicator associated with handle $fp just after the fifth byte in that file. The call to fgetc() therefore returns the contents of the sixth byte. A third optional argument (called whence in the documentation) can be specified with any of the following values to calculate the relative offset:
  • SEEK_SET: The beginning of the file + offset.
  • SEEK_CUR: Current position + offset (default).
  • SEEK_END: End of the file + offset.
fseek() is rather unusual because it's an integer PHP function that returns 0, not 1, upon success (it also returns -1 upon failure). You can't use this function with files on remote hosts opened through either an HTTP, URL, or FTP.


The ftell() function takes a file handle and returns the current offset (in bytes) of the corresponding file position indicator. For example:


$fpi_offset = ftell($fp);
rewind()

This is similar to the rewind button on your cassette player—it takes a file handle and resets the corresponding file position indicator to the beginning of the file. You can say:


rewind($fp);
which is functionally equivalent to:
fseek($fp, 0);

As you saw earlier, the fpassthru() function outputs file data from the current file position onward. If you have already read data from a file but want to echo the file's entire contents, you need to call rewind() first.


You can use rewind() to revise the counter script so that it only has to open the data file once, for both reading and writing:


<?php
//hit_counter09.php
$counter_file = "./count.dat";
if(!($fp = fopen($counter_file, "r+"))) die ("Cannot open $counter_file.");
$counter = (int) fread($fp, 20);
$counter++;

echo "You're visitor No. $counter.";
rewind($fp);
fwrite($fp, $counter);
fclose($fp);
?>

As you sec, the data file is only opened once, in read and write mode. After reading the last access number from the file and displaying it, you rewind the file to reset the file position indicator.


Try it Out: Navigate Within a File
Start example
Here's another example that uses these three navigating functions (fseek(), ftell(), and rewind()):


<?php
//nav_file.php
$name_field_len = 15;
$country_code_field_len = 2;
$country_field_len = 20;
$email_field_len = 30;

if(!($fp = fopen("./address.dat", "r")))
      die ("Cannot open the address data file.");
do {
   $address = '';
   $.field = fread($fp, $name_field_len);
   $address .= $field;
   $field = fread($fp, $country_code_field_len);
   $address .= $field;
   $field = fread($fp, $country_field_len);
   $address .= $field;
   $field = fread($fp, $email_field_len);
   $address .= $field;
   echo "$address<BR>";
} while ($field);

rewind($fp);

echo "<BR>";

fseek($fp, $name_field_len);

do {
   $country_code = fread($fp, $country_code_field_len)
   fseek($fp, ftell($fp) + $country_feild_len +
                           $email_field_len +
                           $name_field_len + 1);
   //NB: change '+1' to '+2' on Win32 platforms
   echo ''$country_code<BR>";
} while ($country_code) ;

fclose($fp);
?>

This script assumes you have an address book data file called address.dat in the current directory that looks like this (note that you must be very careful about the number of characters—even blank characters—in each field):


Wankyu Choi      KRRepublic of Korea   wankyu@whatyoumaycallit.com
James Hetfield   USUnited States       james@headbangers.com
Nomura Sensei    JPJapan               nomura@nosuchsite.com

Here's the output from a test run:


Wankyu Choi KRRepublic of Korea wankyu@whatyoumaycallit.com
James Hetfield USUnited States james@headbangers.com
Nomura Sensei JPJapan nomura@nosuchsite.com

KR
US
JP

Records in this data file are separated by a newline character. (As mentioned earlier, a newline character is CR LF for Windows platforms, CR for Mac, and LF for Linux.) Each field has a set length: 15 characters for the name field, two characters for the country code field, and so on.


End example

How it Works

The example starts by opening address.dat for reading in the current directory. First, it displays all the records as they are. When fread() reaches end-of-file, $field is set to False and the first loop terminates:


if(!($fp = fopen("./address.dat", "r")))
                die ("Cannot open the address data file.");
do {
   $address = ' ' ;
   $field = fread($fp, $name_field_len);
   $address .= $field;
   $field = fread($fp, $country_code_field_len);
   $address .= $field;
   $field = fread($fp, $country_field_len);
   $address .= $field;
   $field = fread($fp, $email_field_len);
   $address .= $field;
   echo "$address<BR>";
} while ($field);

Then the data file rewinds, and the file position indicator moves to the end of the first name field entry, so that you're ready to read and display the country code field values:


rewind($fp);
fseek($fp, $name_field_len);

A do...while loop is initiated. Within it, you assign the country code data to a variable with fread(), move the file position indicator to the start of the next country code field, and finally output the country code that was just read. You start by assigning a value to $country_code:


do {
   $country_code = fread($fp, $country_code_field_len);

Determine the exact position of the next country code field as follows:
  • Get the current position of the file position indicator, as returned by ftell($fp).
  • Add to this the total length of the remaining fields and a trailing newline character.
By using this as the second argument in a call to fseek(), you set the file position indicator to the appropriate point in the file:


fseek($fp, ftell($fp) + $country_field_len + $email_field_len +
                                                  $name_field_len + 1);

For Windows platforms, the length of a CRLF combination should be given as 2 instead of 1 (1 is fine for Linux and Mac). You echo the recorded value and close the loop, which cycles as long as $country_code is True:


      echo "$country_code<BR>";
} while($country_code);

Finally, the file is closed:


fclose($fp);

Monday, May 10, 2010

Reading Entire Files

There are also some functions that provide access to the complete contents of files in one go. These include:
  • file()
  • fpassthru()
  • readfile()
The file() function takes just one argument: a string containing the name of a file. For example:


file("/home/chris/myfile.txt")

returns the entire contents of myfile.txt (in the directory /home/chris/) as an array, using the newline character (CR LF on the Windows platform) to delimit elements. The newline character remains attached at the end of each line stored in the array. This function doesn't require you to specify a file handle because you can refer to the filename explicitly; it automatically opens, reads, and, once it's done, closes the file.


Here's the file() version of the original hit counter:


<?php
//hit_counter06.php
$counter_file = "./count.dat";
$lines = file($counter_file);
$counter = (int) $lines[0];

$counter++;

echo "You're visitor No. $counter.";

if(!($fp = fopen($counter_file, "w"))) die ("Cannot open $counter_file.");
fwrite($fp, $counter);
fclose($fp);
?>

The entire contents of the data file are read into the array $lines and only the first element of the array (the first line of the file) is extracted. You could actually do just the same using a mixture of feof() and fgets() like this:


$fp = fopen ("./count.dat", "r");
while (!feof($fp)) $lines[] = fgets($fp, 1024);
fclose ($fp);

But you don't need to because the file() function does it all for you. As with fopen(), it can also fetch files on a remote host:


$file_lines = file( "http://www.whatyoumaycallit.com/index.html" );
foreach($file_lines as $line) echo $line;

Although this function can be very useful for reading the entire contents of a file, you should exercise caution when using it—if it tries to read a very large file, it may end up consuming all the memory allocated to PHP. That wouldn't be a good move.


The fpassthru() function is the one to use if all you want to do is read and print the entire file to the Web browser. It takes a single argument, a file handle, which it uses to read the remaining data from a file (that is, from the current position until end-of-file). It then writes results to standard output. Here's the corresponding version of the hit counter:


<?php
//hit_counter07.php
$counter_file = ./count.dat";
if(!($fp = fopen($counter_file, "r"))) die ("Cannot open $counter_file.");
$counter = (int) fread($fp, 20);
fclose($fp);

$counter++;

if(!($fp = fopen($counter_file, "w"))) die ("Cannot open $counter_file.");
fwrite($fp, $counter);
fclose($fp);

if(!($fp = fopen($counter_file, "r"))) die ("Cannot open $counter_file.");
echo "You're visitor No. ";
fpassthru($fp);
?>

Only data from the current file position onward is written. If you read a couple of lines from a file before calling fpassthru(), for example, only the subsequent contents of the file will be printed. The file is closed when the function finishes reading, so there's no need to call fclose()—in fact, if you do, you get a warning that the file pointer is not valid.


The readfile() function enables you to print the contents of a file without even having to call fopen(). It takes a filename as its single argument, reads the whole file, and then writes it to standard output, returning the number of bytes read (or False upon error). Apply it like this:


<?php
//hit_counter08.php
$counter_file = "./count.dat";
if(!($fp = fopen($counter_file, "r"))) die ("Cannot open $counter_file.");
$counter = (int) fread($fp, 20);
fclose($fp);

$counter++;

if(!($fp = fopen($counter_file, "w"))) die ("Cannot open $counter_file.");
fwrite($fp, $counter);
fclose($fp);

echo "You're visitor No. " ;
readfile($counter_file);
?>

As in the previous script, all read and write operations on the data file take place before the incremented counter is echoed out.

Monday, May 3, 2010

Reading and Writing Characters in Files

What if you wanted to read the entire contents of a file, or perhaps analyze it one character at a time. You could do both of these using fread() without too much trouble; for example:


do {$one_char = fread($fp, 1); $counter .= $one_char; } while ($one_char);

However, PHP provides a set of functions that make this sort of thing much more straightforward:
  • fgetc()
  • feoff()
  • fgets()
  • fgetcsv()
  • fputs()
The fgetc() function can be used to read from files one character at a time.fgetc() takes a single argument, a file handle fp, and returns just one character from the file it points to; it returns False when it reaches the end of the file. This is basically the same as the fread():


$one_char = fgetc($fp)
is equivalent to saying:
$one_char = fread($fp,1)

What if you modify the original hit counter example to use fgetc()? You could have:


<?php
//hit_counter03.php
$counter_file = "./count.dat";
if(!($fp = fopen($counter_file, "r"))) die ("Cannot open $counter_file.");

do {
   $one_char = fgetc($fp);
   $counter .= $one_char;
} while($one_char);
$counter = (int) $counter;
fclose($fp);
...

You use a while loop to read the entire contents of the data file because fgetc() reads from the file one character at a time. And you need to know when to stop reading, so you assign the last-read character to a dummy variable, and when that's False, you can stop looping. But there's a flaw. As soon as you read either "0" or " " from the file, the loop condition fails and you won't read any more data. If you plan to attract more than nine hits, this is going to be a big problem.


Fortunately, there's another way to do this, using the feof() function.


The feof() function serves a single, simple purpose: it returns True on reaching the end of a specified file (or if an error occurs) and returns False otherwise. It takes just one argument—the relevant file handle:


feof($fp)

So you can use its logical negative as the condition in the loop, to test whether the file position indicator has reached the end of the file:


<?php
//hit_counter04.php
$counter_file = "./count.dat";
if(!($fp = fopen($counter_file, "r"))) die ("Cannot open $counter_file.");
while(!feof($fp)) $counter .= fgetc($fp);
$counter = (int) $counter;
fclose($fp);

$counter++;

echo "You're visitor No. $counter.";
$fp = fopen($counter_file, "w");
fwrite($fp, $counter);
fclose($fp);
?>

The feof() function tells the while statement when to quit (when the end of the file is reached after reading through it character by character with the fgetc() function), and the counter is forced to an integer data type using the (int) statement.


If you use fgetc() to try to read a large file, though, the script's going to take ages to run because it reads only one character at a time. Fortunately, PHP provides fgets() to help you read sets of characters. This function takes two arguments, fp and length, and returns a string of maximum length (length -1) in bytes, as read from the file pointed to by fp. It stops reading for any one of three reasons:
  • The specified number of bytes has been read.
  • A new line is encountered.
  • The end of the file is reached.
The difference between fgets() and fread() is that fgets() stops reading when it reaches end-of-line and reads up to length -1 bytes, while fread() reads past end-of-line and reads up to length bytes. You can apply fgets() to the hit counter like this:


<?php
//hit_counter05.php
$counter_flie = "./count.dat";
if(!($fp = fopen ($counter_file, "r"))) die ("Cannot  open  $counter_file.");
$counter = (int) fgets($fp, 20);
fclose ($fp);

...

Because the counter needs only one line from the data file, the call to fgets() returns the last counter value, as you want it to.


If you've ever done any work with importing and exporting data, you know about the comma-separated-value (CSV) format for data. (CSV even has its own file extension: .csv.) In CSV files, data values are separated or delimited by commas, and string values are often contained or enclosed within double quotes in between the commas. The fgetcsv() function reads the data in files with the assumption that it is properly formatted in CSV and puts the data it finds in a line into an array. Naturally, once you have an array of data you can easily manipulate it.


The fgetcsv() function must be given a valid file handle, a numerical value higher than the length of each line (including the end-of-line characters), and you can optionally specify data delimiters (the default is comma) and data enclosures (the default is double-quotes). The following code snippet shows how you might retrieve a line of data values from a file in CSV format:


$file_handle = fopen("my_csv_text_file.csv", "r");
$my_data_values_array = fgetcsv($file_handle, 1000, ",");

The result would be an array named $my_data_values_array, and if the first data value in the line of text was someone's first name (Bob, for example) then $my_data_values_array[0] would equal "Bob". Note that a blank line within the text file is treated as a single NULL field, not an error.


The fputs() function is simply an alias for fwrite(); the two are functionally identical.