Log in

View Full Version : Strange error re user_id when adding entries to database



Anne Arbor
01-26-2014, 05:19 AM
I have a simple PHP program and a small MySQL database. When I add a new entry to the database, using a form, I often get the following error: "Undefined index: user_id"

My personal user id in the database is 1. When I get this error, it usually means that the entry has been entered in the database as if from user 0. There is no actual user 0. Sometimes the entry is entered in the database as if from user 0 (who doesn't exist); sometimes as if from user 1 (me). I cannot detect any pattern in when it goes one way or the other.

Does that make any kind of sense to anyone? Is there a way that I could plug this hole so that there would be no future entries attributed to user 0? and a way to ensure that all my future entries are attributed to user 1?

- - - - -

In case it helps, my database looks something like this:

1st table - users:



Field Type Null Default Auto_increment

user_id smallint(5) No yes auto_increment
email varchar(40) No
password varchar(40) No
first_name varchar(15) No
last_name varchar(30) No
active char(32) Yes
registration_date datetime No



2nd table - temperatures:



Field Type Null Default

user_idm smallint(5) No
temp_rating char(2) No
notes text Yes NULL
time_entered datetime No
time_entered2 timestamp No
CURRENT_TIMESTAMP

james438
01-26-2014, 06:41 PM
Information about your database is helpful, but the PHP that you are using to enter the data into your database would be more helpful and what we really need to see here.

Anne Arbor
01-26-2014, 08:49 PM
Thank you, James. I was hoping it was a database problem. But no such luck. ;-)

Here's what I have, with all its faults:



<?php // add_entry.php

ob_start();
require_once ('./includes/config.inc.php');
include ('./includes/header.html');
$page_title = 'Add New Entry';

?>

<!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>Add New Entry</title>
</head>

<body>

<style type="text/css" media="screen">@import "./includes/layout.css";</style>

</head>

<body>

<?php // Ullman Script 12.6 - entry.php #2

/* This script adds a log entry to the database. It now does so securely! */

if (isset($_POST['submitted'])) { // Handle the form.

require_once ('../mysql_connect_temp1.php'); // Connect to & select the database.

// Validate and secure the form data:
$problem = FALSE;
if (!empty($_POST['temp_rating'])) {
$temp = mysql_real_escape_string(trim($_POST['temp_rating']));
$notes = mysql_real_escape_string(trim($_POST['notes']));

} else {
print '<p style="color: red;">Please submit a temperature rating.</p>';
$problem = TRUE;
}

if (!$problem) {

// Define the query:
$query = "INSERT INTO temps (user_idm, temp_rating, notes, time_entered)
VALUES ('$_SESSION[user_id]', '$temp', '$notes', NOW())";

// Execute the query:
if (@mysql_query($query)) {
print '<p>The entry has been added!</p>';

} else {
print '<p style="color: red;">Could not add the entry because:<br />' . mysql_error() . '.</p><p>The query being run was: ' . $query . '</p>';
}

} // No problem!

mysql_close();
} // End of form submission IF. // Display the form:
?>

<p><br><br>
<FORM action="add_entry.php" method="post">
<center><TABLE border="1">
<TR bgcolor="#CCCCFF"> <TH>Name</TH> <TH>Value</TH>
</TR>

<TR>
<TD>Temp rating:</TD>
<TD>
<select name="temp_rating">
<option></option>
<option>0 F</option>
<option>10 F</option>
<option>20 F</option>
<option>30</option>
<option>40</option>
<option>50</option>
<option>60</option>
<option>70</option>
<option>80</option>
<option>90</option>
<option>100</option>
</select>
</TD>
</TR>

<TR>
<TD colspan="2">Notes:<BR>
<textarea name="notes" cols="50" rows="4"></textarea>
</TD>
</TR>
<TR>

<TD colspan="2" align="center">
<input type="submit" name="submit" value="Post this entry!">
<input type="hidden" name="submitted" value="true" />

</TD>
</TR>

</TABLE></center>
</FORM>

<?php
include ('./includes/footer.html');
?>

</BODY> </HTML>

Anne Arbor
01-26-2014, 08:54 PM
James, just so you know, my actual program does not work with temperatures. I've been trying to preserve a bit of privacy about the real topic. The temperature analogy is very close, however, and the code provided here is otherwise absolutely identical.

traq
01-26-2014, 09:44 PM
If you don't wish to share your actual code, you will need to make sure your example code demonstrates the same problem, in the same way. This includes your database schema, etc..

"undefined index" means that you're trying to get "user_id" from an array, but the array has no such index. At the most basic level, you could (should) simply check if this is the case, and provide a value if needed. e.g.:
$user_id = isset( $_SESSION["user_id"] )? $_SESSION["user_id"]: 1;

However, the fact that you expect the user_id to be in the session would imply that the user is supposed to be logged in (or authenticated in some way). If there is no user_id in the session, I would worry that this has not happened: therefore, I would not accept the form submission at all, because it might come from an unauthorized user.

Anne Arbor
01-26-2014, 10:03 PM
Traq, thank you very much for your reply. The only change from my actual code is that I am using "temp" here and the actual code uses a different word. Otherwise, everything is identical.

My site does have a user registration feature and a log-in page. Those work almost correctly -- except that sometimes after I log in and make an entry, the entry gets entered properly for me as 'user_1' and sometimes gets entered improperly as 'user_0', even though there is no actual user_0.

I'd be glad of any way to solve this problem. One way that had occurred to me was there might be some way of plugging the "user_0" hole. Is there some way that I could assign that to someone (even me, using a different name)?

Anne Arbor
01-26-2014, 10:10 PM
Traq, you seem to be suggesting that the problem might lie in the log-in code, so I'll supply that here:



<?php // Ullman Script 11.4 (DWS) - login.php

ob_start(); // Start output buffering.

require_once ('./includes/config.inc.php');

$page_title = 'Log in';

include ('./includes/header.html');

?>

<!--

<!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=iso-8859-1" />
<title>Login</title>
</head> -->

<body>

<?php

if (isset($_POST['submitted'])) {

require_once ('../mysql_connect_temp1.php');

if (!empty($_POST['email'])) {
$e = escape_data($_POST['email']);

} else {

echo '<p><font color="red">You forgot to enter your email address.</font></p>';
$e = FALSE;

}

if (!empty($_POST['password'])) {
$p = escape_data($_POST['password']);

} else {
echo '<p><font color="red">You forgot to enter your password.</font></p>';
$p = FALSE;
}

if ($e && $p) {

$query = "SELECT user_id, first_name FROM users WHERE email='$e' AND password=SHA('$p')";

$result = @mysql_query ($query); // Run the query.

$row = mysql_fetch_array ($result, MYSQL_NUM); // Return a record, if applicable.

if ($row) { // A record was pulled from the database.

// Set the session data & redirect.

session_start();
$_SESSION['user_id'] = $row[0];

$_SESSION['first_name'] = $row[1];

ob_end_clean(); // Delete the buffer.

// Redirect the user to the loggedin.php page.
// Start defining the URL.

$url = 'http://' . $_SERVER['HTTP_HOST'] . dirname($_SERVER['PHP_SELF']);

// Check for a trailing slash.

if ((substr($url, -1) == '/') OR (substr($url, -1) == '\\') ) {

$url = substr ($url, 0, -1); // Chop off the slash.
}

// Add the page.
$url .= '/loggedin.php';

header("Location: $url");

exit(); // Quit the script.

} else {

// No record matched the query.

echo '<p><font color="red">The email address and password entered do not match those on file.</font></p>';

// Public message.
echo '<p><font color="red">' . mysql_error() . '<br /><br />Query: ' . $query . '</font></p>';

// Debugging message.
}

} else { // Errors.

echo '<p><font color="red">Please try again.</font></p>';

} // End of if ($e && $p) IF.

mysql_close();

} // End of the main Submit conditional.

// Display the form.


?>

<h2>Login</h2>

<form action="login.php" method="post">

<p>Email Address: <input type="text" name="email" size="20" maxlength="40" value="<?php if (isset($_POST['email'])) echo $_POST['email']; ?>" /> </p>

<p>Password: <input type="password" name="password" size="20" maxlength="20" /></p>

<p><input type="submit" name="submit" value="Login" /></p>

<input type="hidden" name="submitted" value="TRUE" />

</form>

</body>

</html>


<?php

include ('./includes/footer.html');

ob_end_flush(); // Send everything to the Web browser.

?>

Anne Arbor
01-26-2014, 11:08 PM
And here, fwiw, is the actual script for entering the data. A registered user arrives at this page after logging in.



/* This script adds a log entry to the database. */

if (isset($_POST['submitted'])) {

require_once ('../mysql_connect_temp1.php');

$problem = FALSE;
if (!empty($_POST['temp_rating'])) {
$temp = mysql_real_escape_string(trim($_POST['temp_rating']));
$notes = mysql_real_escape_string(trim($_POST['notes']));

} else {
print '<p style="color: red;">Please submit a temp rating.</p>';
$problem = TRUE;
}

if (!$problem) {

// Define the query:
$query = "INSERT INTO temps (user_idm, temp_rating, notes, time_entered)
VALUES ('$_SESSION[user_id]', '$temp', '$notes', NOW())";

// Execute the query:
if (@mysql_query($query)) {
print '<p>The entry has been added!</p>';

} else {
print '<p style="color: red;">Could not add the entry because:<br />' . mysql_error() . '.</p><p>The query being run was: ' . $query . '</p>';
}

} // No problem!

mysql_close();
} // End of form submission IF.

// The script next displays the form. . . . .


?>

traq
01-27-2014, 12:49 AM
Traq, you seem to be suggesting that the problem might lie in the log-in code, so I'll supply that here:
More likely, it lies in your session management, and is not being carried between requests reliably. For example, this:
// Set the session data & redirect.
session_start;
should be giving you an error message along the lines of
Notice: Use of undefined constant session_start - assumed 'session_start' …
(problem being, you forgot the parenthesis after the function name, and so PHP thought you were trying to use a constant and not a function.)

In fact, I don't see any part of that code where you start a session successfully, so I'd expect there to be no session at all. The fact that you say the problem is intermittent would indicate that a session is started successfully sometimes, however.


And here, fwiw, is the actual script for entering the data. A registered user arrives at this page after logging in.
That may be part of the problem: just because the user is supposed to arrive at this page after logging in doesn't mean that they can't end up there some other way. It also does not guarantee that they are logged in when they arrive - if the login is successful but the session is not, then you'd still be taken to that page, but there would be no way of knowing if you were logged in.

The solution to this is to check if the user is logged in on the same page they're supposed to be logged in on. Write a script (or just a function) that does only that, and run it at the top of any page that requires you to be logged in. Stop the script if the check fails, show the page if it is successful.

This will also tell you whether your login script is working properly, or if the problem lies elsewhere.


I'd be glad of any way to solve this problem. One way that had occurred to me was there might be some way of plugging the "user_0" hole. Is there some way that I could assign that to someone (even me, using a different name)?
You could, of course, add a new user record to your database and manually assign that user id. I would definitely not recommend this, since it would cause every not-logged-in user to be treated as if they were you (and I'm assuming you're the "admin"). That would be a potentially disastrous security hole: 0, especially in dynamically typed languages like PHP, is widely used as a "FALSE"-ish value. That, combined with the fact that MySQL auto-increments usually start with 1, leads a lot of CMS's to use "1" as the super-admin id, and "0" as the not-logged-in id. You don't want those two confused.

BTW, are you using a CMS? or is this something custom written for you? by you?

Anne Arbor
01-27-2014, 01:25 AM
Traq, this is a program that I cobbled together several years ago, relying on a fairly popular "Write your own program in PHP and MySQL" textbook.
It seemed like a miracle to me that it worked at all and I was thrilled that it did. Now, several years later, I'm trying to "bring it up to code," so to speak, and then I'd like to add more features to it.

I'll have to re-read your answer a few times and see how much I can understand. I will say that *most* of the time a session does start; just not always.

Anne Arbor
01-27-2014, 01:33 AM
Traq, thank you for your help. Looking at the actual code on the server, the session function is written correctly, i.e., session_start();

After I posted the code here, I went back over it and removed a lot of the comments. The parentheses probably got removed then. Several times something got accidentally removed and I would restore it. Here I apparently restored the ";" but not the parentheses.

traq
01-27-2014, 02:36 AM
Did you write a function that checks if a user is logged in? If not, then you can probably use something like:
function checkLoggedIn(){
return ! empty( $_SESSION["user_id"] );
}then, on pages where the user must be logged in:
<?php
session_start();

// make sure the function definition is loaded (i.e., include as necessary)
if( ! checkLoggedIn() ){
// you're not logged in: go log in
header( "http://example.com/log-in" );
exit;
}

// now do everything else

Anne Arbor
01-27-2014, 03:03 AM
Traq, that looks very cool. My understanding of PHP is so threadbare that it will take me a while to try it, but I really look forward to doing so.

I hope to try this later tonight or tomorrow, and will post the result.

Thank you so much.

On edit: Would I define the function on the log_in page? at the top? (or doesn't it matter?) And then actually use it on the logged_in and add_entry pages?

Anne Arbor
01-27-2014, 03:34 AM
Traq, I may owe you a pretty big apology. I was trying *not* to post code that wasn't strictly relevant, so as not to clutter things up. The risk is, of course, that one leaves something essential out. I might have done that, entirely inadvertently. I'm sorry if that turns out to be true.

I've posted the 'log_in' page and the 'add_entry' page. I thought that would be enough. There is another page which would go, chronologically speaking, in between those two: the 'logged_in' page.

I'll post the code below. The reason that there are three pages is because the book I was relying on used three pages and I wasn't sure how to combine any of them.

*Important* - the code below supposedly redirects any user who is not logged in back to the log_in page. That has never happened to me.




<?php # Script 11.5 (DWS) - loggedin.php

// Refers to: first_name; login.php.

ob_start(); // Start output buffering.

require_once ('./includes/config.inc.php');
// Include the configuration file for error management and such.

$page_title = 'Logged in';

include ('./includes/header.html');
// Set the page title and include the HTML header.

?>

<!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=iso-8859-1" />
<title>Logged In!</title>
</head>

<body>

<?php

// Greet the user by name.

if (isset($_SESSION['first_name']) ) {

echo "You are now logged in, {$_SESSION['first_name']}. <br><br><br>

If you would like to post a new entry, go to the <a href='add_entry.php'>New entry</a> page.</p>";

} else {

// If no session value is present, redirect the user.

ob_end_clean(); // Delete the buffer.

// Start defining the URL.

$url = 'http://' . $_SERVER['HTTP_HOST'] . dirname($_SERVER['PHP_SELF']);

// Check for a trailing slash.

if ((substr($url, -1) == '/') OR (substr($url, -1) == '\\') ) {

$url = substr ($url, 0, -1); // Chop off the slash.

}

$url .= '/login.php'; // Add the page.

header("Location: $url");

exit(); // Quit the script.

}

echo '</body>
</html>';

ob_end_flush();

// Send everything to the Web browser.

?>

traq
01-27-2014, 04:02 AM
Would I define the function on the log_in page? at the top? (or doesn't it matter?) And then actually use it on the logged_in and add_entry pages?
To make it as portable as possible, I would define it in its own script (or, if you already have a script that does nothing but define functions). This way, you can simply include the code wherever you need it.


I was trying *not* to post code that wasn't strictly relevant, so as not to clutter things up. The risk is, of course, that one leaves something essential out. I might have done that, entirely inadvertently. I'm sorry if that turns out to be true.
While that script does seem to do basically what my function describes, the difference is that your script is a page that you have to visit, while the function is used directly on the page in question. And that's an important distinction: if it's on a different page (more specifically, a different request), then the check is useless, because it's not dealing with the page you actually want to protect.

Anne Arbor
01-27-2014, 08:01 PM
I haven't tried the new function yet because I need to set aside a block of time for that, and the opportunity is eluding me so far today.

Last night, after re-reading traq's comments and suggestions, I went back to the original error message, about the 'undefined index.' That error points to line 50 in my code. Plugging the code into my IDE, line 50 and its nearest neighbor say the following:



$query = "INSERT INTO moods (user_idm, mood_rating, notes, time_entered)
VALUES ('$_SESSION[user_id]', '$mood', '$notes', NOW())";


Looking at that code, I wonder if there is a problem in the way that the first VALUE is written. Ordinarily a value in square brackets would have single quotes around it. Here that is not true: user_id has no quotes at all. I thought about modifying it so as to enclose user_id in quotes but then I would have multiple layers of quotes and probably run into other problems. Should that first variable be re-written and, if so, how?

Anne Arbor
01-27-2014, 10:57 PM
I tried adding the suggested code, or something like it. Here is the code I added:



session_start();

function checkLoggedIn(){
return !empty($_SESSION["user_id"] };

if(!checkLoggedIn() ){
header("http://www.templog.com/login.php");
exit;
}
else {
echo '<p>Still logged in, and that's important.</p>';
}



I added the 'else' clause so that I could have a positive indicator of being logged in.

The result, though, was that I got a completely blank page.

On edit: I have fooled around quite extensively with the added code to see if I could get it to work, or at least not to blank out the page altogether. The page goes blank if I have start_session(); in it (even without any of the other new code).

The page is okay when I add function checkLoggedIn(){ }

However, as soon as I add return !empty($_SESSION["user_id"] between the curly brackets, it again goes blank.

Anne Arbor
01-27-2014, 11:04 PM
Oops (accidental post)

Anne Arbor
01-28-2014, 01:43 AM
Something good might have happened, although I'm not entirely sure why.

For the moment, I've commented out all the 'new' code. I went back to my 'logged_in' page, grabbed some of that code and copied it to the add_entry page. Then I added about 20 new entries. All were entered into the database under the correct user_id -- which is a much more robust and reliable showing than I've probably ever had before.

Here is the code I added. I think it would serve the same function as the proposed code to check on log-in. For some reason, it has worked in the sense that the user is addressed by the correct name, and the entries placed in the form were inserted properly into the database.



if (isset($_SESSION['first_name']) ) {

echo "You are now logged in, {$_SESSION['first_name']}. <br><br><br>

If you would like to post a new entry, go to the <a href='add_entry.php'>New entry</a> page.</p>";


Why would this snippet of code work (to a degree, at least), while the other blanked out the page entirely?

traq
01-28-2014, 01:58 AM
$query = "INSERT INTO moods (user_idm, mood_rating, notes, time_entered)
VALUES ('$_SESSION[user_id]', '$mood', '$notes', NOW())";

Looking at that code, I wonder if there is a problem in the way that the first VALUE is written. Ordinarily a value in square brackets would have single quotes around it. Here that is not true: user_id has no quotes at all. I thought about modifying it so as to enclose user_id in quotes but then I would have multiple layers of quotes and probably run into other problems. Should that first variable be re-written and, if so, how?
YES, the index should be quoted.
NO, that's not what was causing your problems.

When you write a string without quotes, PHP recognizes it as the name of a constant. If there is no such constant in your script, then PHP throws an error (E_NOTICE), and then assumes that you really meant it to be a string. So, in the end, it is treated as a string (as you expected), but there was an error and some unnecessary computational expense along the way.




session_start();

function checkLoggedIn(){
return !empty($_SESSION["user_id"] };
if(!checkLoggedIn() ){
header("http://www.templog.com/login.php");
exit;
}
else {
echo '<p>Still logged in, and that's important.</p>';
}
The result, though, was that I got a completely blank page.
You are missing the closing paren and semicolon after your call to empty. The semicolon after the curly brace (which closes the function definition) is unnecessary.

When php gives you a blank white page, it's usually an indication that a) there was a fatal error, and b) you have error reporting disabled. During development, it's a good idea to go to your php.ini file and turn error reporting on.
error_reporting = E_ALL | E_STRICT
display_errors = OnIn this case, that would have shown you a message similar to
Parse error: syntax error, unexpected '}', expecting ')' …

Anne Arbor
01-28-2014, 02:13 AM
I've modified the code to say this:



function checkLoggedIn(){
return !empty($_SESSION["user_id"]);
}

if(checkLoggedIn() ) {
echo '<p>Still logged in, and that is important.</p>';
}



I've checked it twice, and still get a blank screen. I'm sure this code is good, but I cannot make it work for me.

traq
01-28-2014, 02:27 AM
If you're still getting a blank screen, it's likely that there is another parse error somewhere.

Do you use a code editor with syntax highlighting? code intelligence? Such things are quite awesome

Anne Arbor
01-28-2014, 02:37 AM
Traq, I've been working at this for several hours now and should probably let it go for a while. I'm grateful to you for all your help and, although the problem is not yet solved, I have been learning new things at each step as a result of your suggestions.

(Yes, my IDE is quite a good one and does give me quite a lot of feedback on errors of all kinds.)

I have another question that's been bothering me for a long time. I'll post that in a separate thread for a fresh start.

traq
01-28-2014, 03:01 AM
No problem. I'll take a look at your script if you like. If you prefer not to share it, that's fine too, of course.

I'll look for your other question.