PROJECT 3 — SESSION SHOPPING CART
Starter files for PHP the TPRM Way, Chapter 14


WHAT IS IN THIS FOLDER

  cart.php            The page. It starts the session, handles the POST actions,
                      and renders the cart. Every action is a TODO.
  cart_functions.php  Cart operations and totals. Every body is a stub that
                      returns a placeholder.
  catalog.php         The server-side product catalog, keyed by stable product
                      id. This is the fixture data, and it is also the only
                      place a price is allowed to come from.
  README.txt          This file.

These are starter files, not a solution. Start the server in this folder with

  php -S localhost:8000

and open http://localhost:8000/cart.php. You get the catalog and an empty cart,
and the buttons do nothing yet.

Running php cart.php from the terminal works too and behaves as a first visit,
which is a quick way to check for a parse error without opening a browser.


WHAT TO BUILD

A small product catalog and a shopping cart. The catalog lives on the server. The
per-visitor cart state lives in the PHP session.

The Chapter 11 mini-project kept one preference in the session. This project
keeps a collection in it, and it adds the requirement that makes carts
interesting: the browser sends a product id and a quantity, and the price comes
from the server. Every total is derived, never submitted.

The cart itself is deliberately small:

  $_SESSION["cart"] = ["notebook" => 2, "lamp" => 1];

Product id to quantity, and nothing else. No name, no price, no line total. Those
are all derived from catalog.php at render time, so a price change is a change in
one file rather than a hunt through everybody's session data.


BUILD MILESTONES

  1. Define a server-side product catalog keyed by stable product ids. Store
     product name and price on the server. This is done for you in catalog.php.
  2. Call session_start() before output and initialize an empty cart when
     necessary.
  3. Add items by validated product id and quantity. Do not accept a
     client-supplied price as authoritative.
  4. Render the cart by looking up each product in the server-side catalog.
  5. Support update quantity, remove item, and clear cart actions.
  6. Calculate line totals and the cart total from server-side prices.
  7. Escape product and user-visible text appropriately and validate every
     action.
  8. Test repeated refreshes, an empty cart, invalid ids, zero and negative
     quantities, and two separate browser sessions if that is practical.


ACCEPTANCE TESTS

  Add              Valid product id and quantity update the session cart.
  Invalid id       An unknown client-supplied id is rejected.
  Price integrity  Changing a form field cannot override the server-side catalog
                   price.
  Remove           Removing the final item returns to a clean empty-cart state.
  Session          The cart persists across requests in the same session.

Price integrity is the row worth doing by hand. Open the page, use your browser's
developer tools to add a hidden price field to the add form, set it to 0.01, and
submit it. Try to buy a lamp for one cent, and make sure you cannot. If your
program reads a price from anywhere except catalog.php, this test finds it.

Invalid id is the same test from the other side. Change the product id in the
form to something that is not in the catalog and confirm that the cart refuses it
rather than storing an id it cannot price later.

For Session, refresh the page several times and watch the cart survive, then open
the same address in a different browser and watch it start empty. A session
identifies state for one visitor, and it decides nothing about what that visitor
is allowed to do.


THE TRUST RULE

The browser controls what gets submitted. A product identifier can be a request.
Prices and authorization decisions must come from trusted server-side state.

Everything in this project follows from that one sentence. The client says which
product, and the server says what it costs.


STRETCH GOALS

  - Add a maximum quantity rule.
  - Add a session flash message after each cart change, shown once and then
    cleared with unset().
  - Persist only a final order to a database in Project 4, rather than treating
    the session as permanent storage.

Keep session data small, and do not treat it as storage. It belongs to one
server-side session and it is meant to be temporary.
