CS71 – Ass1 – Finance – Part 2 – Classes

I am doing the Harvards building dynamic websites called CS-75 (also could be called E-75), because someone told me about it and I just thought might as well, it is all learning 🙂 even if allot of it you may already know.

Here is the classes part of the project, I have only done two, one for the users which allow for registration/forgotten password/login etc and another for the stock function like buying and selling stock.

To start with here is the users.php file, this file will allow the user to login and setup the SESSION information,

<?php
// could have setup the contrustor to have a session ID of the user.
// I went for this of passing in the users ID in the parameters, encase I wanted to pass in a different user within a admin screen!.
 
class User {
	// LoginCheck will check against the database the users creditials, please note that the string is send in clear text over the port/IP address from the php server to mysql database.
	public	function LoginCheck($varuser, $varpassword, $checkPassword = false)
	{
		global $db;
		if ($checkPassword == false)
			$sqlquery = sprintf("select uid from users where username = '%s' and pass =  AES_ENCRYPT('%s', '%s%s') and guid = 0", $varuser, $varuser, $varuser, $varpassword);
		else 
			$sqlquery = sprintf("select uid from users where username = '%s' and guid = 0", $varuser);
		$result = $db->query($sqlquery);
		if ($db->rowsNumber($result) === 1)
		{
			$output = $db->arrayResults($result);
			$_SESSION["authenticated"] = true;
			$_SESSION["username"] = $output["uid"];
			$db->freeResult($result);
			return true;
		}
		return false;
	}

here is the registration method, that will return values that are useful for the error reporting part of the web site

	// RegisterUser 
	// paramters : $varuser = email address
	//						$varpassword = password
	// return values
	// 					-2 : check password
	// 					-1 : not a valid email address
	// 					0 : already registered
	// 					1 : email check
	public	function RegisterUser($varuser, $varpassword)
	{
		global $db;
 
		if (!$this->CheckPassword($varpassword))
			return -2;
		// first lets check for a valid email address
		preg_match("/(\w+@[a-zA-Z_]+?\.[a-zA-Z]{2,6})/", $varuser, $matches);
		if (isset($matches[0]))
		{
			// valid email address
			$sqlquery = sprintf("select uid from users where username = '%s'", $varuser);
			$result = $db->query($sqlquery);
			if ($db->rowsNumber($result) ===1)
			{
				$output  = $db->arrayResults($result);
				$db->freeResult($result);
				if ($output["uid"] > 0) return 0;
			}
			// so there is no user with that email address,
			// if there is not a trigger on the database
//			$sqlquery = sprintf("insert into users (username, pass) values ('%s',AES_ENCRYPT('%s','%s%s'))", $varuser, $varuser, $varuser, $varpassword);
			$sqlquery = sprintf("insert into users (username, pass,guid) values ('%s',AES_ENCRYPT('%s','%s%s'), uuid())", $varuser, $varuser, $varuser, $varpassword);
			$db->query($sqlquery);
			$sqlquery = sprintf("select guid from users where username = '%s'", $varuser);
			$result = $db->query($sqlquery);
			if ($db->rowsNumber($result) ===1)
			{
				$output = $db->arrayResults($result); 
				$guid = $output["guid"];
				$db->freeResult($result);
				// email the client a 
				$subject = 'Registration required for stocks program';
				$message = 'Please click on the link to register with the site';
				$message .= "<a href=\"http://{$_SERVER["SERVER_NAME"]}{$_SERVER["PHP_SELF"]}?uid=$varuser&validid=$guid\">http://{$_SERVER["SERVER_NAME"]}{$_SERVER["PHP_SELF"]}?uid=$varuser&validid=$guid</a>";
				$headers = 'From: noresponse@codingfriends.com' . "\r\n" . 'X-Mailer: PHP/' . phpversion();
 
				//mail($varuser, "Register with the stocks site", $message, $headers);
				// debugging, just output the message
				echo $message;
				return 1;
			}
		}
		else
			return -1;
	}

encase the user has javascript turned off and the javascript is not checking to make sure that the user is passing in a password that is at least 6 characters long and also has 1+ numeric/aplha characters.

	// checks password to be a minimum of 6 letters and also contains at least 
	// 1+ aplha/numeric
	private function CheckPassword($varpassword)
	{
		$varpassword = trim($varpassword);
		if (strlen($varpassword) >=6)
		{
			// if not all aplha or digit characters then return true else false
			if (!ctype_alpha($varpassword))
				if (!ctype_digit($varpassword))
					return true;
			return false;
		}	
		else
			return false;	
	}

Here the user will be emailed, if you uncomment the code, a link that will allow to change the password

	public function ForgottenUser($varuser)
	{
		global $db;
 
		$sqlquery = sprintf("update users set guid = uuid() where username = '%s'", $varuser);
		$result = $db->query($sqlquery);
		if ($db->rowsAffected() === 1)
		{
			$db->freeResult($result);
			$sqlquery = sprintf("select guid from users where username = '%s'", $varuser);
			$result = $db->query($sqlquery);
			$output = $db->arrayResults($result);
			$db->freeResult($result);
 
			$subject = 'Forgotten password';
			$message = 'Please click on the link to get back your password with the site';
			$message .= "<a href=\"http://{$_SERVER["SERVER_NAME"]}{$_SERVER["PHP_SELF"]}?uid=$varuser&validid={$output["guid"]}\">http://{$_SERVER["SERVER_NAME"]}{$_SERVER["PHP_SELF"]}?uid=$varuser&validid={$output["guid"]}</a>";
			$headers = 'From: noresponse@codingfriends.com' . "\r\n" . 'X-Mailer: PHP/' . phpversion();
 
			//mail($varuser, "Register with the stocks site", $message, $headers);
			// debugging, just output the message
			echo $message;
			return true;
		}
		$db->freeResult($result);
		return false;
	}

here this is checking to make sure that the value that was passed to the user to either registrar/forgotten password that is it the same as what is in the database and thus change that value to 0 to denote that the user is valid.

	public function CheckUserGUID($varuser, $guid)
	{
		global $db;
 
		$retValue = false;
		$sqlquery = sprintf("select guid from users where username = '%s'", $varuser);
		$result = $db->query($sqlquery);
		if ($db->rowsNumber($result) === 1)
		{
			$output = $db->arrayResults($result);
			if ($guid == $output["guid"])
				$retValue = true;
			$sqlquery = sprintf("update users set guid = 0 where username = '%s'", $varuser);
			$db->query($sqlquery);
		}
		$db->freeResult($result);
		return $retValue;
	}

If the user wants to update there password, here we alter the database values to for there password and using the AES_ENCRYPT function within MySQL to encrypted the password.

	public function ChangePassword($varuserID, $varpassword)
	{
		global $db;
 
		if (!$this->CheckPassword($varpassword))
			return false;
 
		$sqlquery = sprintf("select username from users where uid = %s", $varuserID);
		$result = $db->query($sqlquery);
		if ($db->rowsNumber($result) === 1)
		{
			$output = $db->arrayResults($result);
			$varuser = $output["username"];
			$db->freeResult($result);
 
			$sqlquery = sprintf("update users set pass = AES_ENCRYPT('%s', '%s%s') where username = '%s' and guid = 0", $varuser, $varuser, $varpassword, $varuser);
			$result = $db->query($sqlquery);
			return true;
			// if the password is the same, then the line is not updated and thus may come across as a weird message.
//			if ($db->rowsAffected() ===1)
//				return true;
		}
		$db->freeResult($result);
		return false;
	}

this is the method that will return the value of the users cash that they have left

	public function GetCash($varuser)
	{
		global $db;
		$returnValue = 0;
		$sqlquery = sprintf("select cash from users where uid = %d", $varuser);
		$result = $db->query($sqlquery);
		if ($db->rowsNumber($result) ===1)
		{
			$results = $db->arrayResults($result);
			$returnValue = $results["cash"];
		}
		$db->freeResult($result);
		return $returnValue;
	}

last but not the least, here is destroying the session data, so that the user is logout.

	// logout the user!
	public 	function Logout()
	{
		session_destroy();
	}
};
?>

The next class is the stock details, I called it getstocks.php, to start with I am getting the stock details from the yahoo site, with using the fopen of the yahoo.

<?php
	class StocksDetails {
 
		public function GetStocksFromYahoo($varsearch)
		{
			$returnvalue = 0;
			$handle = fopen("http://download.finance.yahoo.com/d/quotes.csv?s=$varsearch&f=sl1d1t1c1ohgv&e=.csv", "r");
			while ($row = fgetcsv($handle))
			{
				if ($row[0] == $varsearch)
					$returnvalue = $row[1];
			}
			fclose($handle);
			return $returnvalue;
		}

This method will build up a array of the users stock details and also the present value of the stock symbol.

		// display all of the users stock details, 
		public function ReturnAllStocks($varusernameID)
		{
			global $db;
 
			$sqlquery = sprintf("select symbol, quantity from stocks where uid = '%s'", $varusernameID);
			$result = $db->query($sqlquery);
			$insI = 0;
			while ($row = $db->arrayResults($result))
			{
				$returnArr[$insI++] = array($row["symbol"], $row["quantity"],$this->GetStocksFromYahoo($row["symbol"]));
			}
			$db->freeResult($result);
			return $returnArr;
		}

This method will sell the stock that the user has, to start with need to update the users cash flow for the sale of the stock, and then delete the actual stock from stocks table in the database that is linked to the user.

		public function SellStock($varusernameID, $stockID)
		{
			global $db;
 
			$db->startTransaction();
			try {
//	need to pull back users stock quantity and then delete it from the list and update the cash within the users table.
				$sqlquery = sprintf("update users,stocks set users.cash = users.cash + (%f * stocks.quantity) where users.uid = stocks.uid and users.uid = %d and stocks.symbol = \"%s\"",$this->GetStocksFromYahoo($stockID), $varusernameID, $stockID);
				$result = $db->query($sqlquery);
				if ($db->rowsAffected() == 1)
				{
					$sqlquery = sprintf("delete from stocks where uid = %d and symbol = \"%s\"", $varusernameID, $stockID);
					$result2 = $db->query($sqlquery);
					if ($db->rowsAffected() != 1)
						throw new Exception("Error updating the stock details");
				}
				else
					throw new Exception("Error updating users cash");
 
				$db->commitTransaction();
			} catch (Exception $e)
			{
				echo "Possible error : {$e->getMessage()}";
				$db->rollbackTransaction();
			}
		}

this method is the opposite of the above, where we are buying stock, we have to check the users balance/cash to make sure that they are able to, and then go though the process of updating the users table and the stocks, I am using the START TRANSACTION from within MySQL database InnoDB so that if any of process errors I can rollback the updates to the database tables.

		// buy the stock into the users 
		public function BuyStock($varusernameID, $stockID, $stockQuantity)
		{
			global $db;
 
			$valueOfStock = $this->GetStocksFromYahoo($stockID) * $stockQuantity;
			if ($valueOfStock > 0)
			{
				try {
					$db->startTransaction();
					// update the users cash, whilst making sure that there is enought !!.
					$sqlquery = sprintf("update users set cash = cash - (%f) where uid = %d and cash > %f", $valueOfStock, $varusernameID, $valueOfStock);
					$db->query($sqlquery);
					// if there was enought money, place the update into the stocks table now!.
					if ($db->rowsAffected() ==1)
					{
						// now update the stock database table, could have used on duplicate key here.. 
						$sqlquery = sprintf("update stocks set quantity = quantity + %d where uid = %d and symbol = '%s'",$stockQuantity, $varusernameID, $stockID);
						$db->query($sqlquery);
						// there was no stock of that type already, then just insert
						if ($db->rowsAffected() == 0)
						{
							$sqlquery = sprintf("insert into stocks values (%d,\"%s\", %d)", $varusernameID, $stockID, $stockQuantity);
							$db->query($sqlquery);
						}
					}
					else 
						throw new Exception("Not enought money!");
					$db->commitTransaction();
				} catch (Exception $e)
				{
					echo "Possible error : {$e->getMessage()}";
					$db->rollbackTransaction();
					return 0;
				}
			}
			else
			{
				echo "Possible error : Getting values from Yahoo stock";
				return 0;
			}
			return $valueOfStock;
		}

Here, I am getting the news from of the symbol stock from the yahoo rss links.

		public function ArrayOfStockDetails($stockID)
		{
			$xmlDoc =  simplexml_load_file("http://finance.yahoo.com/rss/headline?s=$stockID");
			$xpath = $xmlDoc->xpath("//channel/item");
			$insI = 0;
			foreach ($xpath as $key)
			{
				$arrayRet[$insI++] = array("Date" => (string)$key->pubDate,
															  "Link" => (string)$key->link,
															"Title" =>  (string)$key->title);
			}
			return $arrayRet;
		}
	};
?>

Next going to do the basic php file to load up the class files and connect to the database, with also the javascript code.

CS75 – Assignment 0 – Three Aces Menu

I am doing the Harvards building dynamic websites called CS-75 (also could be called E-75), because someone told me about it and I just thought might as well, it is all learning 🙂 even if allot of it you may already know.

Anyway, to setup the CS75 on my personal laptop ( I am running Ubuntu 10.4) I did a couple of alternations for me so that the local apache instance looks like I am doing it under the top level domain of CS75 :).. so for this assignment Three Aces menu I am calling the website http://threeaces.cs75 on my local host, so to setup that I altered the /etc/hosts file and added

127.0.0.1       threeaces.cs75

so that local IP will also have threeaces.cs75 🙂 and now need to alter apache to allow me to run that as a virtualhost on my laptop, I added the file within

sudo vim /etc/apache2/sites-available/cs75_threeaces

with adding in

<VirtualHost *:80>
       ServerAdmin emailaddress@here.com
       ServerName  threeaces.cs75
       DocumentRoot /var/www/cs75/ass0
       <Directory /var/www/cs75/ass0>
               Options Indexes FollowSymLinks MultiViews
               AllowOverride None
               Order allow,deny
               allow from all
       </Directory>
 
       ErrorLog /var/log/apache2/error.log
       LogLevel warn
       CustomLog /var/log/apache2/access.log combined
 
</VirtualHost>

Which points to the /var/www/cs75/ass0 as the base directory for this assignment :).

I have included the assignments 0 PDF for more information, but basically this assignment is about playing with simpleXML to load in a menu (that you have created from the menu list of the restaurant Three Aces) which is in XML format, you get to pick the format, but here is mine (it has the extras link within the menu->type extras attribute which link to the extras at the bottom of the menu format)

<?xml version="1.0" encoding="ISO-8859-1"?>
<menu>
	<type name="Pizzas" extras="Extra Cheese">
		<item name="Tomato &amp; Cheese">
			<price type="Small">5.50</price>
			<price type="Large">9.75</price>
		</item>
		<item name="Onions">
			<price type="Small">6.85</price>
			<price type="Large">10.85</price>
		</item>
	</type>
	<type name="Salads">
		<item name="Garden">
			<price type="Small">3.50</price>
			<price type="Large">4.50</price>
		</item>
		<item name="Greek">
			<price type="Small">4.50</price>
			<price type="Large">5.50</price>
		</item>
	</type>
	<type name="Grinders">
		<item name="Meatless">
			<price type="Small">4.50</price>
			<price type="Large">4.95</price>
		</item>
		<item name="Hamburger">
			<price type="Small">4.50</price>
			<price type="Large">4.95</price>
		</item>
	</type>
	<type name="Special Dinners">
		<item name="Chicken Wing Dinner">
			<price>7.25</price>
		</item>
		<item name="Gyro Plate">
			<price>7.25</price>
		</item>
	</type>
	<extras name="Extra Cheese">
		<price type="Small">1.25</price>
		<price type="Large">1.85</price>
	</extras>
</menu>

You can implement it anyway that you want to, but to not have two same names of the food for different sizes. So that is why I have a the item name with the price list(s) underneath. It is only part of the menu that I implemented above.

The next part is to only have part of the menu on the main home page, so I am just displaying the main types of food options, I have included the source file for all of the files in the zip file above.

<form name="types" method="get" action="types.php">
<?php
// display the user the different types of foods that are avaible to order
$xml = simplexml_load_file("menu.xml");
// list the different types on the menu
foreach ($xml->type as $types)
{
	echo "<input type=\"radio\" name=\"type\" value=\"" .$types->attributes()->name .  "\"/>".$types->attributes()->name . "<br/>" ;
}
?>
<input type="submit"/>
</form>

Once the user has selected the type of food that they want, you next to have display the items of food within that area, so I am passing in the $_GET string from the index page the type of food, so within the types.php I pick up the type and then display all of the food items within that area. I did do some javascript that will make sure that there is a valid integer value within the, also at the start of the code I am pulling any extra values from the menu to populate the extras option.

<form name="types" method="get" action="checkout.php" onsubmit="return checkout()">
<?php
// display the different items within that type area of foods, with also there different prices and sizes
// normal = normal size for the default size
$xml = simplexml_load_file("menu.xml");
$type = $_GET["type"];
echo $type. "<table border=1>";
echo "<input type=\"hidden\" value=\"".str_replace(" ", "@",$type)."\" name=\"type\"/>";
$result = $xml->xpath("//type[@name='$type']");
if (sizeof($result[0]) == 0)
	echo "<br/><br/>Please go back to home, because cannot find that type<br/><br/>";
else
{
	$extras = $result[0]->attributes()->extras;
	if (strlen($extras) > 0)
	{
		$resultExtras = $xml->xpath("//extras[@name='$extras']");
		if (sizeof($resultExtras[0]) > 0)
		{
			$extraName = $resultExtras[0]->attributes()->name;
			for ($i=0; $i < sizeof($resultExtras[0]); $i++)
			{
				$thePrice =  $resultExtras[0]->price[$i];
				$theType = ($resultExtras[0]->price[$i]->attributes()->type ? $resultExtras[0]->price[$i]->attributes()->type : "Normal");
				$theExtras[$i] = array((string)$thePrice, (string)$theType);
			}
		}
	}
	foreach ($result[0] as $items)
	{
		echo "<tr><td>".$items->attributes()->name."</td><td>Price</td><td>Quantity</td>";
		if (strlen($extras)) echo "<td>$extras</td>";
		echo "</tr>";
		for ($i=0; $i < sizeof($items->children()); $i++)
		{
			echo "<tr><td>". $items->price[$i]->attributes()->type . 
				"</td><td>" . $items->price[$i].
				"</td><td><input type=\"text\" size=\"1\" value=\"\" name=\"".str_replace(" ", "@",$items->attributes()->name)."_".$i."\"/></td>";
			if (strlen($extras))
			{
				foreach ($theExtras as $typeCheck)
				{
					if ($typeCheck[1] == $items->price[$i]->attributes()->type)
					{
						echo "<td><input type=\"checkbox\" name=\"".str_replace(" ","_",$extras)."_".$i."\" value=\"".str_replace(" ", "@",$items->attributes()->name)."_".$i."\"/></td>";
						break;
					}
				}
			}
			echo "</tr>";
		}
	}
}?>
</table>
<input type="submit" name="submit"/>
</form>

Because there is a basket of items that the customer will like to buy, I am also storing the basket details within the $_SESSION within a multi array, the array looks like something like

Array
(
    [Pizzas] => Array
        (
            [Tomato  Cheese] => Array
                (
                    [Small] => Array
                        (
                            [price] => 5.5
                            [quantity] => 6
                        )
                )
        )
)

So in the next page, checkout.php I need to update the basket details and also then display what the customer has already picked (with giving the options to update the quantity of goods and also to remove them if they did not want it), the first part loads the new item(s) into the basket and the second part of the code below will display the basket to the user, and call the update.php file if any quantities/remove of items need to be done.

<form name="types" method="get" action="update.php">
<table>
<?php
// display the updated details of the order with the new order addon's 
// and also any previous details of the order, if you want to place the order
// click the order now link below.
$xml = simplexml_load_file("menu.xml");
// load basket from the session
$basket = $_SESSION["basket"];
$type = $_GET["type"];
$type = str_replace("@", " ", $type);
 
$result = $xml->xpath("//type[@name='$type']");
if (sizeof($result[0]) == 0)
{
	if (strlen($type) > 0)
		echo "<br/><br/>Please go back to home, because cannot find that type<br/><br/>";
}
else
{
	$extras = $result[0]->attributes()->extras;
	if (strlen($extras) > 0)
	{
		$resultExtras = $xml->xpath("//extras[@name='$extras']");
		if (sizeof($resultExtras[0]) > 0)
		{
			$extraName = $resultExtras[0]->attributes()->name;
			for ($i=0; $i < sizeof($resultExtras[0]); $i++)
			{
				$newname = str_replace(" ", "_" , $extras) . "_" . $i;
				$$newname = $_GET[$newname];
				if (strlen($$newname) > 0)
				{
					$thePrice =  $resultExtras[0]->price[$i];
					$theType = ($resultExtras[0]->price[$i]->attributes()->type ? $resultExtras[0]->price[$i]->attributes()->type : "Normal");
					$theExtras[$i] = array($$newname, (string)$thePrice, (string)$theType);
				}
			}
		}
	}
 
	foreach ($result[0] as $items)
	{
		for ($i=0; $i < sizeof($items->children()); $i++)
		{
			$newname = $items->attributes()->name."_".$i;
			$newname = str_replace(" ", "@", $newname);
			$$newname = $_GET[$newname];
			if ($$newname > 0)
			{
				$priceType = $items->price[$i]->attributes()->type;
				if (strlen($priceType) <=1)
					$priceType = "Normal";
				$extraPrice = 0;
				foreach ($theExtras as $extraAdd)
				{
					if ($extraAdd[0] == $newname)
						$extraPrice = $extraAdd[1];
				}
				$basket[(string)$type][(string)$items->attributes()->name][(string)$priceType . ($extraPrice > 0 ? (string)" (".$extras.")" : "")] = 
							array("price" =>(float) ($items->price[$i]) + (float)$extraPrice, 
									"quantity" => (int)($$newname 
										+ $basket[(string)$type][(string)$items->attributes()->name][(string)$priceType]["quantity"]) );
				// going with the theory of adding to the previous quantity
				echo "Have added " . $type . " (". $items->attributes()->name . " " . $priceType . ")<br/>";
			}
		}
	}
	$_SESSION["basket"] = $basket;
}
// lets print out the basket in a form of the user to confirm and also remove / alter the quantity
$totalPrice = 0;
echo "<table border=1><tr><td width=50>Type</td><td width=50>Item</td><td width=50>Size</td><td>Price</td><td>Quantity</td><td>Total Price</td><td>Remove</td></tr>";
foreach ($basket as $type => $typevalue)
{
	echo "<tr><td colspan=\"6\">$type</td>";
	foreach ($typevalue as $item => $itemvalue)
	{
			echo "<tr><td></td><td colspan=\"5\">$item</td></tr>";
			foreach ($itemvalue as $size => $sizevalue)
			{
				$namevalue = $type."_".$item."_".$size;
				$namevalue = str_replace(" ","@",$namevalue);
				echo "<tr><td></td><td></td><td>$size</td>";
				echo "<td>&pound;".number_format($sizevalue["price"],2) . "</td><td><input size=5 value=" . $sizevalue["quantity"]  . " name=\"".$namevalue."_quantity\"/></td><td>&pound;".number_format($sizevalue["price"] * $sizevalue["quantity"],2) ."</td><td><input type=\"checkbox\" name=\"".$namevalue."_remove\"/></td></tr>";
				$totalPrice +=$sizevalue["price"] * $sizevalue["quantity"];
			}
	}
	echo "</tr>";
}
echo "</table>";
 
echo "The total price is &pound;".number_format($totalPrice,2)."<br/>";
?>
</table>
<input type="submit"/>
</form>

here is the update.php, this will update the basket from the requested action from the above php file.

<?php
// this will update the session basket from the checkout.php page
session_start();
$xml = simplexml_load_file("menu.xml");
// load basket from the session
$basket = $_SESSION["basket"];
foreach ($_GET as $key => $value)
{
	$key = str_replace("@", " ", $key);
	list($type, $item, $size,$command) =  split("_", $key);
	if ($command== "quantity" && $value > 0)
	{
		$basket[$type][$item][$size]["quantity"] = $value;
	}
	elseif ($command =="remove")
	{
		unset($basket[$type][$item][$size]);
		// clean out the type -> item if none more left
		if (sizeof($basket[$type][$item])==0)
			unset($basket[$type][$item]);
		// clean out the type if none left
		if (sizeof($basket[$type])==0)
			unset($basket[$type]);
	}
}
$_SESSION["basket"] = $basket;
// redirect back to checkout.php
header('Location: checkout.php');
?>

and redirects back to checkout.php.

The last page displays the order in total and also says thanks very much for the order :).. and deletes the basket details :).

<table>
<?php
// display the order details and also thanks for placing the order
$basket = $_SESSION["basket"];
// lets print out the basket in a form of the user to confirm and also remove / alter the quantity
$totalPrice = 0;
echo "<table border=1><tr><td width=50>Type</td><td width=50>Item</td><td width=50>Size</td><td>Price</td><td>Quantity</td><td>Total Price</td></tr>";
foreach ($basket as $type => $typevalue)
{
	echo "<tr><td colspan=\"6\">$type</td>";
	foreach ($typevalue as $item => $itemvalue)
	{
			echo "<tr><td></td><td colspan=\"5\">$item</td></tr>";
			foreach ($itemvalue as $size => $sizevalue)
			{
				echo "<tr><td></td><td></td><td>$size</td>";
				echo "<td>&pound;".number_format($sizevalue["price"],2) . "</td><td>" . $sizevalue["quantity"]  . "</td><td>&pound;".number_format($sizevalue["price"] * $sizevalue["quantity"],2) ."</td></tr>";
				$totalPrice +=$sizevalue["price"] * $sizevalue["quantity"];
			}
	}
	echo "</tr>";
}
echo "</table>";
 
echo "The total price is &pound;".number_format($totalPrice,2)."<br/>";
session_destroy();
?>
</table>

The file above does include all of the php files from the above comments and the PDF of the assignment. You can also view the assignment live on my CS – 75 assignment 0 test area.