Project 2 - Simple Shopping Cart

Due April 26 before midnight


Implement a basic user login, logout, online store, and shopping cart in PHP using sessions and previously-learned PHP/MySQL concepts.

Screenshots


For this project, you will be creating a web-based store in PHP. It will utilize the concept of session cookies to manage user sessions. Unlike GET and POST, sessions are useful to store data that is persistent across multiple web pages for each unique client. This means that while a client's session is active, every page will have access to that client's session variables.
This is what makes shopping carts possible in web-based stores. When you add an item to your cart, it is stored on the server and can only be retrieved by the user that created the session. PHP stores session variables in the global array $_SESSION.

In general, session cookies are used to keep track of users who visit or log in to websites (See link about for intro to session cookies). Session cookies pose a potential security risk because if an attacker steals your session cookie, he can use it to log into on to the account that the session cookie is associated with. A common way to steal session cookies is with a XSS (Cross-Site Scripting) attack, which is one of the most commonly exploited website vulnerabilities. XSS attacks often use JavaScript to send a victim's session cookie to the attacker's remote server, where it can be processed by a PHP script and logged to a file.

The best way to prevent XSS attacks from accessing session cookies (or other cookies containing sensitive data) is setting the HTTP-Only flag on cookies. HTTP-Only cookies cannot be accessed by client-side languages (JavaScript).

PHP session cookies can be set to HTTP-Only a few different ways. It can be done in http server configuration files or directly in your PHP scripts. We will be doing it in the PHP script.

When programming websites that use cookies, it is helpful to be able to see the cookies that you have created. In Firefox, right click on a page, then click View Page Source, click details, and finally click view cookies. In Chrome, right click on a page, click inspect element, click Resources, and then click Cookies. There are also more capable cookie management plugins available on the web for most major browsers.

HTTP Redirects

You are going to need to redirect users to pages based on whether they are logged in or not. Redirects can be done by JavaScript or a server-side language like PHP. Use PHP for this assignment. PHP redirects are done by changing the "Location" HTTP header. This is done with the header() function.

header("Location: http://www.example.com/"); exit();
// or 
header("Location: ./home.php"); exit();

Passing GET Parameters in a URL

POST and GET methods can be used for sending data from a web browser to a server. We used POST in previous assignments that used forms. POST is useful for forms because the form data won't be sent in the URL and there is no limit to how much data you can send. While this is useful for forms, it is not useful when we want to be able to link to a URL with variable data. This is where GET comes in.

GET encodes variables names and values in the URL. For instance, you have probably seen a URL like the one below. The question mark ('?') is a delimiter between the address and the GET variables. Each GET variable is in the form of variable=value, separated by ampersands ('&').
http://forums.example.com/?forum=news&threadid=1234

In this assignment you will encode links to store items in a similar fashion. Example PHP code:
echo "<a href='store.php?item=$itemID'>$items['itemName']</a>"; You will also need to access GET variables and use them in your scripts. Example PHP code:
$itemID = htmlspecialchars($_GET['item']);

SQL Injections

SQL Injections refer to an injection attack wherein an attacker can execute malicious SQL statments that control a web application's database server. Sever side scripting languages are not able to determine whether or not the SQL query string is malformed; all they can do is send a string to the database server and wait for the interpreted response. Either use prepared statments to prevent SQL injections or real escape string all variable parameters used within the SQL query string.


Requirements

home.php

This page will contain a generic home page that will display either a 'guest' page or a 'logged-in' page. This will also include your navigation from nav.php (described below).
If a user has not logged in, display a page that contains a generic "Welcome GUEST" prompt and that the user must be logged in to preview the store. Also navigation to the 'login' page and a link to your final project proposal page.
Otherwise, display a page that contains a generic "Welcome $_SESSION['user_fname']" prompt and navigation to the 'store', to the user's 'shopping cart', to 'logout', and to your final project proposal page.

nav.php

If a user is not 'active' display a link to your home page, a link to the login page, and a link to your final proposal page. Otherwise, if a user is 'active' display a link to the home page, the store page, the shopping cart, to logout, and to your final project proposal page. This script will be included for each page.

login.php

This page will contain your navigation and a HTML form for user login data. If a user is currently 'active', redirect the user to the home page. Otherwise, display a form and have a text input for the user's username or email, a password input for the user's password, and a submit button. You will validate and authenticate a login request by extracting posted form data (user + password). Validate that there are no missing form fields. Query your 'Users' Table to select all fields where username or email equals the posted username or email. If there was no result set, error, username or email does not exists. Otherwise, authenticate the stored database password and posted password. If the passwords match, set a $_SESSION variable that indicates that a user has successfully logged in and is 'active'. Also store the user's username into the $_SESSION array, then redirect the user to the 'home' page. If a login error occurred, redirect the user to the 'login' page and display an error "invalid username, email, or password".

store.php

If the user is NOT 'active', redirect the user to 'home.php'. If a user is 'active', this will display all of your products within your 'Products' table. This page will closely resemble your lab10 for displaying products stored in your database. You may optionally include a 'sort by' filter, but not required for this project. Within the page, display each product from your 'Products' table. For each product item, include a button called "View Item" that will link to this page with a query string with the product's id. For example, if a user clicks on "View Item" for the first product, the link for the anchor tag would be:
<a href='./store.php?q=1'>View Item</a>.
Process and output to the page as follows:
If $_GET['q'] is NOT set, query all of your products and display them to the page similar to lab10.
If $_GET['q'] is set and greater than 0, query your Products table to select the product where the product's id is equal to $_GET['q']. If the query value is less than 1 or the queried result set is empty, then display all the products (default). Otherwise, display only the queried product and a form with a hidden input with the value of the product's id with name set to 'store_prod_id' and submit button with value "Add To Cart". The form's method will be post and the action will call 'cart.php'. This will only pass the product's id within the POST parameters to 'cart.php'.

cart.php

If the user is NOT 'active', redirect the user to 'home.php'. If the user is 'active', this page will display the current items within the cart from $_SESSION['cart'].
An item to be added to the cart will be from a POST request passing a product's id to this script. If $_POST['store_prod_id'] is set, query your 'Products' table and SELECT the product's id where the product id is equal to the passed POST parameter. If the query was successful, store the product's id into a var called $added_product_id. Next, if $_SESSION['cart'] is not set, declare $_SESSION['cart'] = []; Then,
$_SESSION['cart'][$added_product_id] = isset($_SESSION['cart'][$added_product_id]) ? $_SESSION['cart'][$added_product_id]+1 : 1;
endif $_POST['store_prod_id'] was set.
At this moment, $_SESSION['cart'] is either empty or contains an associative array where each key is a product's id within the cart and the value stored at that key is the cart quantity for that product.
To display each item in the user's cart, you will output each item within a form, this form will either remove an item or update the cart quantity. Use the following pseudocode as a guide:

//display cart and subtotals
if $_SESSION['cart'] is not empty 
  $cart_total=0.0;
  echo html form action cart.php method POST
  for each in $_SESSION['cart'] as $prod_id =>$cartQnty
      query database's Products table and select name, sku, and price
      where prod_id is equal to $prod_id.
      if the query was successful, fetch the row by association.
        Display the name and sku first.
        Then display an html input type=number where name is equal to
        $prod_id, min=0, and value=$cartQnty 
        Finally calculate the queried price * $cartQnty
        and display the sub price for the item(s) in $monetary.00 format.
        Update the running cart_total with sub price
      else
        the query was unsuccessful, 
        optionally, could unset $_SESSION['cart'][$prod_id]
        or continue (skip)
   end_foreach
   echo the $cart_total in monetary format
   echo a submit button name = 'action' value = 'Empty Cart'
   echo a submit button name = 'action' value = 'Update Cart'
endif 
//end display cart and subtotals

Before the cart is displayed, there are two submit buttons with the name 'action'. If $_POST['action'] is set: if the value is equal to 'Empty Cart', unset your $_SESSION['cart'] variable, but if the value is equal to 'Update Cart'
foreach ($_POST as $key_prod_id=>$qnty) {
  $qnty = htmlspecialchars($qnty);
  if(array_key_exists($key_prod_id, $_SESSION['cart'])) {
    $_SESSION['cart'][$key_prod_id] = $qnty;
  }
}//endforeach
...
//display cart and subtotals

logout.php

If user is 'active', unset the $_SESSION variable and destroy the session. Display to the screen that the user is now logged out and navigation to 'home' or 'login'.

MySQL Products Table

This table may be reused from lab 10. Have at least 6 entries within this table. Refer to lab 10 for the table fields.

MySQL Users Table

Create the following table and manually enter two users via mysql> This table will contain a user_id, user_uname, user_email, user_password, user_fname (user_id unsigned int primary key not null auto_increment, and the rest varchars(255) ).
Within MySQL, manually insert a record to this table with the following values: test, test@test.com, testpassword, testfname. I will use these credentials to test and grade your project 2. You are welcome to add more users, but you are required to enter at least one more user into your table.

connect.php, css, js, images, tables.txt, extras

Your connect.php will instantiate your $db resource variable and connect your scripts to your database. You must include some styling such that the layout of all your pages are used by the same css file. Within your project2 directory, you must include an images subdir that will contain all of your product images files. Within a text file named tables.txt, perform a mysql describe Users; SELECT * FROM Users; describe Products; SELECT * FROM Products; and copy and paste the output to tables.txt. You may include any extra php, js, css, html, etc within your project, but must be within your project2 directory.

Requirements and Points Layout

RequirementsPoints
home.php4pts
nav.php3pts
login.php 7pts
store.php 10pts
cart.php 15pts
logout.php 3pts
Users + Products MySQL Table 5pts
connect.php, css, js, images, tables.txt, extras 3pts
Total 50pts

Have all of your files within your $~/3680_S19/project2/ depository directory