How to work with $_SESSION?

Hello,
I have a website with data stored in a DB.
Everything worked well untill now when I added registration, login logout.

I registered a user and logged in

DB_Screenshot_1

I wrote echo $_SESSION[‘username’]; and I got user7

Here is the code of a file which generates lots of warnings and gives no result

<?php
    include_once 'trades_header.php';
	include_once 'all.inc.php';
	include_once 'footer.php';
?>

The first warning says:

Warning: Invalid argument supplied for foreach() in C:\wamp\www\trade analyzer login\trades_header.php on line 55

The first line in trades header.php after <?php is

session_start();

Here are lines 52 to 55 in trades_header:

$items = get_all_couples();
//Reset counter
$j=0;
foreach($items as $row)

and here is the function:

function get_all_couples()
{	
	global $db;
	
	try
	{
		$sql = "SELECT DISTINCT item FROM filtered WHERE username = ':username' AND item != '0' ORDER BY item ASC";
		$stmt = $db->prepare($sql);
		$stmt->bindParam(':username', $username, PDO::PARAM_STR);
		$stmt->execute();
		
		if($stmt->rowCount() == 0)
		return 0;
		else
		return $stmt->fetchAll(PDO::FETCH_ASSOC);
		
	}
	catch(Exception $e) 
	{
	   return false;        
	}
}

All my problems started when I added the username.
There is no doubt that I miss some crucial concepts pertaining to the SESSION

Please help me

How do I do it right?

In here

$stmt->bindParam(':username', $username, PDO::PARAM_STR);

where is your $username variable coming from? You don’t pass it into the function, and you don’t declare it as global.

Slightly off-topic, but I really dislike global variables. To me, functions should be able to work anywhere, and not rely on external variables to be named in a certain way. So you should pass in $db and $username as parameters, not declare them as global.

Your function code can return a zero if no matching data was found, an array of fetched data, or a false if there is a query error. Your code that calls the function must handle each of those values. Since it currently just tries to loop over the returned value like it is always a non-empty array, you are getting a follow-on php error, having nothing to do with the actual problem, for two of those three values.

Start by simplifying the error handling, so that your main code only has to deal with error free execution. For a database dependent web page, most database statement errors are fatal, i.e. the web page doesn’t work and there’s nothing the visitor to the page can do about the problem (and you certainly don’t want to tell hackers that something they did caused a specific type of error, as that will just encourage them to do more of the same.) The exception to this rule is for inserting/updating user supplied data. In this case, a duplicate or out of range value might be something that the visitor can correct. This is the only case where having database statement error handling logic in your code ‘adds value’ to what you are doing. You would catch the exception, detect if the error number is for something that your code can handle, and set up a message telling the visitor what was wrong with the data that they submitted. In all other cases, just let php catch and handle the database exceptions, where it will use its error related settings to control what happens with the actual error information (database errors will ‘automatically’ get displayed or logged the same as php errors.) When learning, developing, and debugging code/queries, you would display all php errors (which now includes database statement errors.) When on a live/public web server, you would log all php errors (which now includes database statement errors.)

This will reduce the values that your code must deal with to just two.

Next, the fetchAll() method returns an empty array if there is no matching data, so there’s no need for any of the rowCount() logic. Just return the value from the fetchAll() method. This will give you either an empty array or an array containing data. Your main code should then test the returned value and output an appropriate message if there is nothing to display.

BTW - if you set the default fetch mode to assoc when you make the database connection, you won’t have to specify it in each fetch… statement.

There’s also no good reason to use explicit binding (the bindParam() statement), and in fact because it works by using a reference to the variable, doesn’t produce a php error if the variable doesn’t exist. Use the much simpler implicit binding, by supplying an array of the input data to the ->execute([…]) method call.

Your get_all_couples() function, which doesn’t seem to be well named, should accept all inputs as call-time parameters. This will make the function general purpose. Your current question/problem seems to be about using the session variable to supply the username for the query. If you hard-code this inside the function, the function can only find data that matches the currently logged in user. If you instead supply this input as a call-time parameter, the function can now find data that matches the user given to it when it is called.

Lastly, the database table you have shown should NOT use the username to relate data back to the user it belongs with. You should use the user’s id (auto-increment integer primary index.) This will result in the least amount of data storage, the fastest queries, and allow the username to be edited without breaking the relationship in the data.

2 Likes

In looking at the code/query further, you have single-quotes around the prepared query place-holder in the sql query statement. When you use a prepared query, you do not put (leave) any single-quotes around the place-holders.

Your function code, using the suggestions given, should simply be -

// i don't know what this function name means. the query is getting unique item(s) matching a user.
// all inputs to the function should be via call-time parameters
function get_all_couples($db,$user_id)
{
	// the user_id should be used to relate data
	// do you actually have rows with zero values for the item?
	// don't put quotes around numbers either
	$sql = "SELECT DISTINCT item FROM filtered WHERE user_id = ? AND item != 0 ORDER BY item ASC";
	$stmt = $db->prepare($sql);
	$stmt->execute([$user_id]);

	return $stmt->fetchAll();
}

Hello,
I tried your solition. It doesn’t work but I think I found the problem - yet I can’t solve it myself :

When I login I get a message using $_SESSION[‘firstname’] that I am logged in. I used it in the headline on index.php and it worked

Yet on anothed page which is in the same folder as index php the first lines are :slight_smile:

<?php
  if(!isset($_SESSION)){
	  echo 'Oh no ';
     session_start();
}
	  if(isset($_SESSION)){
	 echo 'ok';
  }
  $username = $_SESSION['username'];
 echo 'CURRENT USER:'.$username;

Here is the screenshot of the output
I believe that once the session problem will be solved all errormessages will be gone

How do I make the session work everywhere in the folder ?

Just because the session is set, doesn’t means it contains the elements you are referencing. You stated at one point you are using _SESSION['firstname'], then you are trying to use _SESSION[‘username’]. Do you actually have both of those stored in the session variable? What does adding the following show -

var_dump($_SESSION);

Check the online PHP user manual and notice how If anything is output to the screen before session_start (); is called an error will be thrown.

Try this instead:

<?php
  if(!isset($_SESSION)){
	  // echo 'Oh no ';
     session_start();
 }
	  // if(isset($_SESSION)){ MUST BE STARTED
	 echo 'ok - SESSION STARTED ==> ' .__line__;
 }//endif
  //Should be checked first using isset(...)
  $username = $_SESSION['username'];
 echo 'CURRENT USER:'.$username;
die;// temporary halt, remove after desired result is obtained

Here is what I get on index.php :
array (size=5) ‘user_id’ => string ‘2’ (length=1) ‘firstname’ => string ‘james’ (length=5) ‘lastname’ => string ‘bond’ (length=4) ‘username’ => string ‘user7’ (length=5) ‘password’ => string ‘$2y$10$1lHtmcOMM6jChyIO2ElNL.TSrQ/rZtcahx8MNAze.f4kg1SJPBK4.’ (length=60)

and here is what I get on the page I displayed :

array (size=0) empty

Here is a screenshot of the result :

How are you navigating from the index.php page to the page in question?

You are likely changing the web page’s host-name/sub-domain (a www. vs no www) and the session id cookie no longer matches and you are starting a new, and empty, session.

Another possibility is you have some code that’s clearing the session, that you are managing to execute.

Try this because it adds line feed and makes it easier to read:

<?php 
echo '<pre>'; 
  var_dump($_SESSION); 
echo '</pre>';

array (size=5) 
‘user_id’      => string ‘2’ (length=1) 
‘firstname’   => string ‘james’ (length=5) 
‘lastname’   => string ‘bond’ (length=4)
‘username’  => string ‘user7’ 
(length=5) ‘password’ => string  ‘$2y$10$1lHtmcOMM6jChyIO2ElNL.TSrQ/rZtcahx8MNAze.f4kg1SJPBK4.’ (length=60)

Looks as though $username is set on the index.php page but not on included/required pages.