Jump to content
Larry Ullman's Book Forums

Using The Fgetcsv Function In Chapter 11


Recommended Posts

Hello all - I am having a huge stumper. I have have been diligently working on the Pursue question for chapter 11 that requires the script to guarantee unique usernames in register.php

 

Here is my script. I just can't figure out what I am doing wrong and why this won't work for me. I have modified it so many times! Here is what I have right now.

 

This is what is being returned with this script:

Register

 

You are now registered!

 

Warning: fgetcsv(): 3 is not a valid stream resource in C:\xampp\htdocs\registerpursue.php on line 55

 

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>Register</title>
<style type="text/css" media="screen">
 .error { color: red; }
</style>
</head>
<body>
<h1>Register</h1>
<?php // Script 11.6 - register.php
/* This script registers a user by storing their information in a text file and creating a directory for them. */
error_reporting(E_ALL);
// Identify the directory and file to use:
$dir = '../users/';
$file = $dir . 'users.txt';
if ($_SERVER['REQUEST_METHOD'] == 'POST')
{ // Handle the form.
$problem = FALSE; // No problems so far.
// Check for each value...
if (empty($_POST['username']))
{
 $problem = TRUE;
 print '<p class="error">Please enter a username!</p>';
}
if (empty($_POST['password1']))
{
 $problem = TRUE;
 print '<p class="error">Please enter a password!</p>';
}
if ($_POST['password1'] != $_POST['password2'])
{
 $problem = TRUE;
 print '<p class="error">Your password did not match your confirmed password!</p>';
}

if (!$problem)
{ // If there weren't any problems...

 if (is_writable($file))
 { // Open the file.


ini_set('auto_detect_line_endings', 1);
$open = fopen("$file", 'a+');  //opens my data file
while ($line = fgetcsv($open, 200, "\t" )!== FALSE) {  //breaks my string from $file into parts

$n = count($line);

 if  ($_POST['username'] == ($line[$n])) {//checks to see if user entered data matches what's in the text file 
 $problem = TRUE;
 print '<p>That username has been taken, please try again</p>';   //if above is true, print this statement

 break;

   }
   fclose($open);

   // Create the data to be written:
   $subdir = time() . rand(0, 4596);
   $data = $_POST['username'] . "\t" . md5(trim($_POST['password1'])) . "\t" .
   $subdir . PHP_EOL;
   // Write the data:
   file_put_contents($file, $data, FILE_APPEND | LOCK_EX);

   // Create the directory:
   mkdir ($dir . $subdir);
   // Print a message:
   print '<p>You are now registered!</p>';

  }

 } else { // Couldn't write to the file.
  print '<p class="error">You could not be registered due to a system error.</p>';
 }

} else { // Forgot a field.
 print '<p class="error">Please go back and try again!</p>';
}
} else { // Display the form.
// Leave PHP and display the form:
?>
<form action="registerpursue.php" method="post">
<p>Username: <input type="text" name="username" size="20" /></p>
<p>Password: <input type="password" name="password1" size="20" /></p>
<p>Confirm Password: <input type="password" name="password2" size="20" /></p>
<input type="submit" name="submit" value="Register" />
</form>
<?php } // End of submission IF. ?>
</body>
</html>

Link to comment
Share on other sites

First thing I saw was the '!==' for this condition:

$open = fopen("$file", 'a+');  //opens my data file
while ($line = fgetcsv($open, 200, "\t" )!== FALSE) {

 

As for the rest of your code (put together)...

$open = fopen("$file", 'a+');  //opens my data file
while ($line = fgetcsv($open, 200, "\t" )!== FALSE) {  //breaks my string from $file into parts
$n = count($line);
 if  ($_POST['username'] == ($line[$n])) {
 $problem = TRUE;
 print '<p>That username has been taken, please try again</p>';
 break;
}
fclose($open);

 

Break down:

 

line 1: You do not need a+, although Mr. Ullman says it is most forgiving, you can get by with r. If you used a+ to be less restrictive to debug, that is understandable, but if that is the case, then you should have also appended a 'b' (binary). Regardless of what you use, I would recommend appending the 'b'.

 

line 2: Problem mentioned above. Also, if you look at your code

$line = fgetcsv($open, 200, "\t" )!== FALSE

You are saying that $line is being assigned to fgetcsv($open, 200, "\t"), which is a TRUE statement (all assignments are true). Then you are using a comparative operator (!= or ==, I cannot tell) for a TRUE statement against FALSE. So say you were using !=, you are saying TRUE is not equal to FALSE, a true statement, so the '!= FALSE' would be redundant? And say you were using ==, you are asking if TRUE is equal to FALSE? My working code does not include the != or == FALSE part.

 

Line 3: You assign $n to the amount of values in the array $line (whose values are the username, password, and subdirectories of a user, separated by tabs). Therefore, $n would be equal to three, yes? I will refer to this line while breaking down line four.

 

Line 4: This is where you start checking if the value that you just read from the file is equal to the value submitted by the user, by using an if statement. You compare $_POST['username'] with $line[$n]. $_POST['username'] is pretty much out of our hands; we cannot control what it is equal to. But you are using two variables with $line[$n] - $line and $n. Think about what these values are equal to. To be honest, this was one of the most confusing pieces of the code to me.

$line is an array assigned values of 200 bytes (or till the end of the line) of a line. The line may contain something like this:

lingolatz 0ea4a0db010efd4ed3ca4ebee723b65c 13217357292811

So $line[0] would equal lingolatz,

$line[1] would equal 0ea4a0db010efd4ed3ca4ebee723b65c, and

$line[2] would equal 13217357292811.

As mentioned earlier, $n is equal to three (unless you included more items in the users.txt file). Therefore $line[$n] is equal to $line[3], and that does not even exist. Can you tell, now, what should be used to be compared to $_POST['username']? You also use extra, unneeded parentheses to surround '$line[$n]'.

 

The rest of your code has no problems as far as I can tell.

  • Upvote 1
Link to comment
Share on other sites

Thank you lingolatz!

This helped immensly - however, I am still a little unclear when trying to follow your direction on the

 

Here is what I have based on what you stated....

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>Register</title>
<style type="text/css" media="screen">
 .error { color: red; }
</style>
</head>
<body>
<h1>Register</h1>
<?php // Script 11.6 - register.php
/* This script registers a user by storing their information in a text file and creating a directory for them. */
error_reporting(E_ALL);
// Identify the directory and file to use:
$dir = '../users/';
$file = $dir . 'users.txt';
if ($_SERVER['REQUEST_METHOD'] == 'POST')
{ // Handle the form.
$problem = FALSE; // No problems so far.
// Check for each value...
if (empty($_POST['username']))
{
 $problem = TRUE;
 print '<p class="error">Please enter a username!</p>';
}
if (empty($_POST['password1']))
{
 $problem = TRUE;
 print '<p class="error">Please enter a password!</p>';
}
if ($_POST['password1'] != $_POST['password2'])
{
 $problem = TRUE;
 print '<p class="error">Your password did not match your confirmed password!</p>';
}

if (!$problem)
{ // If there weren't any problems...

 if (is_writable($file))
 { // Open the file.


ini_set('auto_detect_line_endings', 1);
$open = fopen("$file", 'r');  //opens my data file
while ($line = fgetcsv($open, 20, "\t" )) {  //breaks my string from $file into parts

$n = count($line);

 if  ($_POST['username'] == ($line[0]){
  $problem = TRUE;
 print '<p>That username has been taken, please try again</p>';   //if above is true, print this statement
 if($_POST['password1'] == ($line[1])
 {
  $problem = TRUE;
 print '<p>That username has been taken, please try again</p>';   //if above is true, print this statement
 if($_POST['password2'] == ($line[2]) {//checks to see if user entered data matches what's in the text file 
 $problem = TRUE;
 print '<p>That username has been taken, please try again</p>';   //if above is true, print this statement

 break;

   }
  }
   fclose($open);

   // Create the data to be written:
   $subdir = time() . rand(0, 4596);
   $data = $_POST['username'] . "\t" . md5(trim($_POST['password1'])) . "\t" .
   $subdir . PHP_EOL;
   // Write the data:
   file_put_contents($file, $data, FILE_APPEND | LOCK_EX);

   // Create the directory:
   mkdir ($dir . $subdir);
   // Print a message:
   print '<p>You are now registered!</p>';



 } else { // Couldn't write to the file.
  print '<p class="error">You could not be registered due to a system error.</p>';
 }

} else { // Forgot a field.
 print '<p class="error">Please go back and try again!</p>';
}
} else { // Display the form.
// Leave PHP and display the form:
?>
<form action="registerpursue.php" method="post">
<p>Username: <input type="text" name="username" size="20" /></p>
<p>Password: <input type="password" name="password1" size="20" /></p>
<p>Confirm Password: <input type="password" name="password2" size="20" /></p>
<input type="submit" name="submit" value="Register" />
</form>
<?php } // End of submission IF. ?>
</body>
</html>

 

You also said I have an unneeded parentheses to surround '$line[$n]'....

($_POST['username'] == ($line[$n]))

 

Isn't that correct syntax? The parantheses surrounds the whole thing and then opening and closing ones for ($line[$n])?

Link to comment
Share on other sites

or maybe this would be better...Still gives me an error

Parse error: syntax error, unexpected T_VARIABLE in C:\xampp\htdocs\registerpursue.php on line 59 - but is this more on the right track?

ini_set('auto_detect_line_endings', 1);
$open = fopen("$file", 'r');  //opens my data file
while ($line = fgetcsv($open, 20, "\t" )) {  //breaks my string from $file into parts

$n = count($line);

 if  ($_POST['username'] == ($line[0] $line[1] $line[2])){
  $problem = TRUE;
 print '<p>That username has been taken, please try again</p>';   //if above is true, print this statement


 break;
 }
 }
   fclose($open);

Link to comment
Share on other sites

Well - that didn't make sense either! I don't want the username compared to the passwords... So then I tried this...

And my script worked without errors, but I am still able to enter duplicate usernames.... HELP!


ini_set('auto_detect_line_endings', 1);
$open = fopen("$file", 'r');  //opens my data file
while ($line = fgetcsv($open, 20, "\t" )) {  //breaks my string from $file into parts

$n = count($line);

 if  ($_POST['username'] == ($line[0])){
  $problem = TRUE;
 print '<p>That username has been taken, please try again</p>';   //if above is true, print this statement
 } 

 break;

 }
   fclose($open);

Link to comment
Share on other sites

Hello April,

 

I know you're working very hard to figure all this out, and that's quite admirable. But it would seem that the very best debugging technique you should apply is to step away from the computer. When you're really working on something, and it's all jumbled in your head, it can be hard to see the forest for the trees, and changes you make under those conditions often exacerbate the problem rather than solve it. My second piece of advice would be for you, when you are in front of the computer, to apply "rubber duck debugging": get a rubber duck, set it in front of the computer, and explain to it what each line of code is doing. By doing so, you'll be able to reassure yourself as to what's correct and hopefully catch what doesn't make sense. From the messages you've posted in these forums, I really, really think you'd benefit from picking up both habits.

 

In terms of this particular bit of code (the last bit you posted)...

- you shouldn't put $file in quotes in your fopen() line as that's entirely unnecessary.

- there's no need to assign the count() of $line to $n, as $n is not used.

- to debug this, add this code before your loop:

print "

username = -{$_POST['username']}-

";

This confirms the value received from the form.

Then, immediately within the while loop, add this:

 

print "

line[0] = -{$line[0]}-

";

This confirms what the value of $line[0] is within the loop for each stored item. The use of the - before and after the value will reveal whether there are extra spaces involved or not.

 

Remember that using print statements to verify what the exact values of variables are is one of the most important debugging techniques.

  • Upvote 1
Link to comment
Share on other sites

Hi Larry - Thank you for your advice.

I did apply those debugging techniques. I found that username is correct. I also found that $line is returning the first word in my text file - so it really not "looping" through the text file to find matches. That is helpful information.

So the question is - how do I get things to loop through - I thought that is what my count() was doing.

 

Can you direct me that way?

Link to comment
Share on other sites

No, your count() wasn't doing anything useful at all. I suspect the problem is in the use of 20 for the second argument of fgetscsv(). I'm not sure why you're using that value or where you came up with it, but you should reread p 327 or the PHP manual's page for fgetscsv().

 

Also, for future reference, it would be beneficial to those trying to help if you had included, for example, the results (i.e., output) of the debugging code I just recommended. I *think* I know what you're saying, but my advise here is based upon an interpretation of what you mean, not on the actual results.

Link to comment
Share on other sites

I am a little closer think.

 

I used this code

ini_set('auto_detect_line_endings', 1);
$open = fopen($file, 'r');  //opens my data file

while ($line = fgetcsv($open, 200, "\t" )) {  //breaks my string from $file into parts
 if  ($_POST['username'] == ($line[0])){
  $problem = TRUE;
 print '<p>That username has been taken, please try again</p>';   //if above is true, print this statement
 }

 break;

 }
   fclose($open);

 

This code was debugged and I ran the form, I rec'd this as the return... That was telling me that it wasn't looking throughout the text file and was only looking at just the first word in the text file.

line[0] = -April-

You are now registered!

 

Then Larry I have been practicing your debugging techniques and I read through the chapter on arrays again. Here is the code I have written...

 

ini_set('auto_detect_line_endings', 1);
$open = fopen($file, 'r');  //opens my data file

while ($line = fgetcsv($open, 200, "\t" )) {  //breaks my string from $file into parts

for($n=0; $n <= count($line); $n++) {

 if  ($_POST['username'] == ($n[0])){
  $problem = TRUE;
 print '<p>That username has been taken, please try again</p>';   //if above is true, print this statement
 }
 }
 break;

 }
   fclose($open);

 

The problem is that - it is not recognizing unique usernames. I can enter april in as my username 100 times and it will always tell me that I've been registered.

 

When i debugged this using this line

print "<p>line[n] = -{$line[$n]}-</p>";

 

My output was this. (the notice is a repercussion of this line print "<p>line[n] = -{$line[$n]}-</p>"; so I'm not worried about that. So from this debugging - I am getting the feeling that it's not scanning my entire text file. It's only scanning the first line.

 

Register

 

line[n] = -April-

line[n] = -2a2d595e6ed9a0b24f027f2b63b134d6-

line[n] = -13214649081347-

 

Notice: Undefined offset: 3 in C:\xampp\htdocs\registerpursue.php on line 59

 

line[n] = --

You are now registered!

 

So my question is - how do I get this script to read and scan every word in the text file? that is what I thought i was doing with the for loop - but it's not doing that.... It only took the first 3 words. I really don't mean to keep bothering the forum lines, but I really do want to understand this.....

Link to comment
Share on other sites

Okay, a couple of debugging options here:

 

for($n=0; $n

 

First of all, I'll just say outright that you can't do $n

Now, just after that you have:

 

if ($_POST['username'] == ($n[0])){

 

If you apply "rubber duck debugging" to that line, tell me about the $n variable: what it is, where it comes from, what kind of value it will have.

Or you could have the PHP script tell you exactly what $n is by doing:

print "

n = -{$n}-

";

just before that line.

 

You could also look at your code. Previously you used this and it worked:

print "

line[n] = -{$line[$n]}-

";

But now you're using this, and it doesn't work:

 

if ($_POST['username'] == ($n[0])){

 

What's different?

 

Finally, going back to "rubber duck debugging", how about explaining to me why you want to loop through each word in each row? What is the value of that?

Link to comment
Share on other sites

Hi Larry - I took a break like you said, and I feel like I'm so close! But...... This script returns about 100 lines of

This username is already used. Please enter a unique username!

 

But that's because I've been entering 'april' for the last 100 times I've been testing the script. I tried a new name and the first time I entered the name it said that I've been registered, and the second time it told me This username is already used. Please enter a unique username!

You are now registered!

 

So it's returning two of my statements that contradict each other.

 

This is further than I've ever gotten before - because I could never get it to return that at all - but why is it repeating? I have the break; for the while

 

Also I did this print "<p>line = -{$line}-</p>"; and found that line = -Array-. I don't even know what that means - but I think that's good - because then my text file is now in an array, but it's not split by the delimiter like I want.

 

So to sum it up - it seems to be returning the message This username is already used. Please enter a unique username! for as many times as that name is entered and it also contradicts that message at the end by saying You are now registered!

 

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>Register</title>
<style type="text/css" media="screen">
 .error { color: red; }
</style>
</head>
<body>
<h1>Register</h1>
<?php // Script 11.6 - register.php
/* This script registers a user by storing their information in a text file and creating a directory for them. */
error_reporting(E_ALL);
// Identify the directory and file to use:
$dir = '../users/';
$file = $dir . 'users.txt';
if ($_SERVER['REQUEST_METHOD'] == 'POST')
{ // Handle the form.
$problem = FALSE; // No problems so far.
// Check for each value...
if (empty($_POST['username']))
{
 $problem = TRUE;
 print '<p class="error">Please enter a username!</p>';
}
if (empty($_POST['password1']))
{
 $problem = TRUE;
 print '<p class="error">Please enter a password!</p>';
}
if ($_POST['password1'] != $_POST['password2'])
{
 $problem = TRUE;
 print '<p class="error">Your password did not match your confirmed password!</p>';
}

if (!$problem)
{ // If there weren't any problems...

 if (is_writable($file))
 { // Open the file.


ini_set('auto.detect_line_endings', 1);
$open = fopen($file, 'r');//Check if username is duplicate:

 while($line = fgetcsv($open, 500, "\t")) {//Loop through the file:
  for($n=0; $n < count($line); $line++) {
  break;
  }//End of For

   if( ($line[0] == $_POST['username']) && ($line[0] == $_POST['username'])){
  print '<p class="error">This username is already used. Please enter a unique username!</p>';
   }


  }//End of while
   fclose($open);

   // Create the data to be written:
   $subdir = time() . rand(0, 4596);
   $data = $_POST['username'] . "\t" . md5(trim($_POST['password1'])) . "\t" .
   $subdir . PHP_EOL;
   // Write the data:
   file_put_contents($file, $data, FILE_APPEND | LOCK_EX);

   // Create the directory:
   mkdir ($dir . $subdir);
   // Print a message:
   print '<p>You are now registered!</p>';



 } else { // Couldn't write to the file.
  print '<p class="error">You could not be registered due to a system error.</p>';
 }

} else { // Forgot a field.
 print '<p class="error">Please go back and try again!</p>';
}
} else { // Display the form.
// Leave PHP and display the form:
?>
<form action="registerpursue.php" method="post">
<p>Username: <input type="text" name="username" size="20" /></p>
<p>Password: <input type="password" name="password1" size="20" /></p>
<p>Confirm Password: <input type="password" name="password2" size="20" /></p>
<input type="submit" name="submit" value="Register" />
</form>
<?php } // End of submission IF. ?>
</body>
</html>

Link to comment
Share on other sites

This is the code I used, I didn't read this post at all and thought 'd have a stab at it myself! It seems to work but as always are there any areas I coud improve on Larry?

 

By the way I'm also learning from the book and I'm at the exact same position of the book as you April. Like Larry said it is smetimes best to step away from the computer and just think about the logic in plain english, I did this with this exercise after getting myself into a pickle and finally worked it out (I think!)

 
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Register</title>
</head>
<style>
.error { color: red; }
</style>
<body>
<?php
//Identify directory and file to write to
$dir = '../users/';
$file = $dir . 'users.txt';
//Set flag
$problem = FALSE;
//Handle form
if ( ($_SERVER['REQUEST_METHOD'] == 'POST') ) {

//Validate Username
if ( empty($_POST['username']) ) {
 print '<p class="error">Please enter a username</p>';
 $problem = TRUE;
}

//Validate Password
if ( empty($_POST['password']) ) {
 print '<p class="error">Please enter a password</p>';
 $problem = TRUE;
}

//Validate Password
if ( ($_POST['password'] != $_POST['password1']) ) {
 print '<p class="error">Please make sure passwords match</p>';
 $problem = TRUE;
}

ini_set('auto_detect_line_endings', 1);

//Open file
$fp = fopen($file, 'rb');

//Read file
while ( $line = fgetcsv($fp, 200, "\t") ) {

 //Check for username pairs
 if ( $line[0] == $_POST['username'] ) {

  $problem = TRUE;

  print '<p>Username already exists, please choose another username</p>';

  break;

 }
}

fclose($fp);

//If fields are valid
if ( !$problem ) {

 //Is file writable
 if ( is_writable($file)) {

  //Create the data
  $subdir = time() . rand(0, 4999);
  $data = $_POST['username'] . "\t" . md5(trim($_POST['password'])) . "\t" . $subdir . PHP_EOL;

  //Write the data
  file_put_contents($file, $data, FILE_APPEND | LOCK_EX );

  //Create Directory
  mkdir($dir . $subdir);

  print '<p>Thankyou, you have been successfully registered</p>';

 } else {

  print '<p class="error">Error writing to file</p>';

 }

} else {

 print '<p class="error">Please enter a valid username and password</p>';

}

} else { //Display form
?>
<form action="register.php" method="post">
<p>Username: <input type="text" name="username" size="20" /></p>
<p>Password: <input type="password" name="password" size="20" /></p>
<p>Confirm password: <input type="password" name="password1" size="20" /></p>
<p><input type="submit" name="submit" value="Register" /></p>
</form>
<?php } //End of IF statement ?>
</body>
</html>

Link to comment
Share on other sites

 Share

×
×
  • Create New...