SimpleshoPHP

Detailed Analysis - Part 1

index.php

Well, here we go... Here's the index page for the SimpleshoPHP demo, with comments.

<?php

Note that our "index.html" is actually a PHP file, and the very first line is a PHP start tag. We have to configure Apache to be aware of this; by default, Apache does not assume that a plain .html file may be interpreted as PHP. You can make your index.html file do a "redirect" to main.php, or you can tell Apache that index.php is valid as an IndexDocument with this minor change to httpd.conf.

Now, back to index.php:

require_once("startme.inc");

This is a way to reuse a block of code that we're going to need in almost every page of our store. It's equivalent to saying "include this chunk," but by saying it's required, it's a bit stronger than merely including it. So, what's in "startme.inc"?

session_start();

PHP 4.2.x has great session handling capabilities. If it detects that you have cookies enabled, it will use them; if not, it will put a session identifier into a "hidden" form variable. The session_start() command will start a new session, or intelligently continue one that was already set up. To make a session go away, close your browser completely and open it again.


$connection = mysql_connect("localhost", "MyUsername", "MyPassword")
        or die ("Couldn't connect to server.");
$db = mysql_select_db("bookstore", $connection)
        or die ("Couldn't select database.");

A more advanced user of PHP would stick the $connection variable into an object and pass that around between the pages that make up the store. We're going to be lazy and sloppy and let each page create its own database connection -- but we're not going to paste the username and password into every page; we're going to maintain them in one place, the startme.inc file.


error_reporting(0);

$ArrayList = array("_GET", "_POST", "_SESSION", "_COOKIE", "_SERVER");
foreach($ArrayList as $gblArray)
{
   $keys = array_keys($$gblArray);
   foreach($keys as $key)
   {
       $$key = trim(${$gblArray}[$key]);
   }
}

This is a workaround. When PHP 4.2 came out, the default for the system setting register_globals was changed from ON to OFF, which broke a lot of old code. Specifically, defining a variable name in one PHP page no longer meant you could use it in another page, even one in the same session. It was a necessary change, though, and you won't have much luck convincing your admin to turn register_globals back on. This workaround picks up certain sets of variables and lets us pass them between our PHP pages even though register_globals is off.

Connecting to a MySQL database with PHP is about as easy as can be:

$connection = mysql_connect("localhost", "dbusername", "dbpassword")
  	or die ("Couldn't connect to server");

$db = mysql_select_db("thestore", $connection)
	or die ("Couldn't select database.");

I hope I don't have to mention that "MyUsername" and "MyPassword" are not the actual username and password for my database? $connection contains the result of the operation mysql_connect() and when we refer to it below, we will have access to what is on the other end of the connection.

That brings us back to the code that is unique to "main.php."

  
$_SESSION['xvalid'] = 1;

We're giving the PHP session management a little grist for its mill -- if we skip the main page and dive deeper in the system, "session_start()" will still create a session for us, and that's not what we really want. By requiring this _SESSION variable to be set later on, we will know if someone is playing silly games with our system. As a _SESSION variable it is relatively immune to spoofing.

All the things that begin with dollar signs are variables; they have a name so we can refer to them, and a value that we can change to carry data around. Programmers sometimes refer to a variable as a "bucket" -- as in the real world, what is in the bucket is at least as important as the bucket itself. PHP variable names are "case sensitive," which means that "$MYVAR" is not the same as "$myvar" -- they are two different buckets.

$sql = "select distinct dept from books where stock > 0";

$sql_result = mysql_query($sql,$connection) 
	or die ("Couldn't get list!");
?>

We made the database connection in startme.inc; now we are using it. The $sql variable is just a string, the same one you would type into a command window at the "mysql" prompt. The command is actually sent to MySQL by the "mysql_query" function, using the $connection we opened above, and the results come back to us in the $sql_result variable. We will use those result below to generate HTML output. If the query fails, PHP stops right here and instead displays that error message, "Couldn't get list!"

Notice that all of this has taken place on the server -- we have not yet sent anything to the waiting browser. That's actually rather nice, since our username and password are much less exposed that way. A "view source" command should not expose any of this machinery to the curious. But now it's time to shift gears and start thinking in terms of HTML. We exit from PHP mode with the closing tag ?> and whatever comes next will be sent as regular HTML text...

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html><head>
<link rel="stylesheet" href="simple.css">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta name="description" content="Silk Purse Books features vintage
paperbacks, old and unusual hardcovers, and books with a certain
character.  Many of them are rescued library discards, thrift
store books, and from private collections.">
<meta name="keywords" content="Silk Purse Books, vintage paperbacks,
old books, rescued books, private collections, unusual books">
<title>Silk Purse Books - silkpursebooks.com</title>

All of this is standard HTML pencil-sharpening. To make a page compliant with W3C standards, we are expected to declare a DOCTYPE and a MIME Content-Type. It's just good manners. We're also going to make use of a modest Cascading Style Sheet, one which simply sets our preferred fonts and colors, and gives us a bit of a margin around paragraphs. If we were really going to be 21st Century web developers, all of the formatting we do below with tables would be done with CSS instead... unfortunately, we can't count on people viewing our site with 21st Century browsers, so as mentioned earlier, we're going to use tables out the proverbial wazoo.

</head>
<body bgcolor="#ffffff" >
<!--- page --->
<?php $location="Main"; include("top.inc");?>

Eh, what's this? Another "include" file. I'm going to be using the same "top" matter over and over again, so rather than change all my pages every time I make a modest change to the headers (or build a complete new store) I'm going to tell PHP to include a small text file at this point. If we weren't already using PHP, we could do this as a "server side include" but since we are using PHP, the ability to include common template files is a freebie. Here's what this one contains:

--- Begin "top.inc"

<!--- top --->
<table width="90%" align=center border=0 cellpadding=10>
<tr>
<td colspan=3>
 <table><tr>
 <td><img src="images/simpleshophp.jpg" alt="SimpleshoPHP DEMO" align=center>
 <td width=150px> 
 <td align=right><a href="http://simpleshop.org/">Powered by<br>SimpleshoPHP</a>
 </tr>
 <tr>
 <td><h3><? echo "$location";?></h3>
 <td> 
 <td> 
 <td> 
 </table>

--- End of "top.inc"

Notice the 'echo "$location";' near the end? It helps your users if you give them some indication of where they are. (There is also the question of "how you got here." That's sometimes called a "breadcrumb," from the story of Hansel and Gretel. You'll see an example of that on the browse page.) I wanted this breadcrumb to appear in the space that is covered by "top.inc" -- but the whole point of using top.inc is that it is the same for all the pages that use it. What to do, what to do? Use PHP, that's what. It allows us to pass in a variable (named $location in this case). Set that variable before we include the "top.inc" file, and PHP will display it in the appropriate spot. Problem solved.

We have just finished the "banner" area of our basic layout -- although you see that it has tables within tables, the basic layout we described at the very beginning of the overview still applies. Next comes the "left" panel. We start a new row in the main, outermost table:

<tr>
<!--- left --->
<td align=top bgcolor="#e7f0ff">
<h4>Browse by department:</h4>
<ul>
<?php
while ($row = mysql_fetch_array($sql_result)) {
	$category = $row["dept"];
	$link = urlencode($category);
	echo "
	<li><a href=\"browse.php?dept=$link\"><em>$category</em></a>
	";
}
?>
</ul>
</td>

There's where we make use of our SQL query results. SQL returns a whole set of results; PHP is more of a conventional or procedural language, and presents those results by looping through an array. The "while" command is a nice bridge between those two worlds; you don't have to count the rows and manage a counter as you would in BASIC. You just tell PHP to keep displaying rows while there are still rows to process.

We set up the department names as plain text rather than funky code words so we can simply pull them out of the database and present them without jumping through hoops or translating them through yet another table. We also use almost the same value as a parameter called $link, which we will pass to the "browse.php" page (which we will get to in the next installment).

Why almost? We may have departments (like "Vintage Paperbacks") with embedded spaces -- and while some browsers will actually handle that, others won't. You'll find out because they pass back only the first word, "Vintage", which is not a valid department name. The PHP function urlencode() takes care of that for us by replacing the blank with a plus sign. When the parameter "Vintage+Paperbacks" is received by the server, it is properly treated (as "Vintage Paperbacks") and everyone's happy. Note that we keep what we display ($category) separate from the urlencoded value ($link), so we don't have users wondering why the plus sign is there, or what it is for.

<!--- main --->
<td valign=top>
<center>
<a href=bookbag.php>Your Bookbag</a>&nbsp;&nbsp;-&nbsp;&nbsp;
<a href=checkout.php>Check Out</a>&nbsp;&nbsp;-&nbsp;&nbsp;
<a href=aboutus.php>About Us</a>
<br>
<br>
<form name="main" action="browse.php" method=POST>
<strong>Browse by:</strong><br>
Author <input type=text size=64 name="aname"><br>
&nbsp;Title &nbsp;&nbsp;<input type=text size=64 name="bname"><br>
<input type="submit" value="Browse"></form>
</center>
<h3>THIS IS A DEMO</h3>
<p>
</td>

The main panel uses HTML input fields to provide two other ways to send data to the "browse.php" page. When we get there, you will see that it can work with the department, an author, or a book title. We also present some plain HTML-formatted text, since this is the index page for our site.

As a "tour de force" I built a right-hand panel to balance the site and provide a place for a (manually-entered) featured item:

<td bgcolor="#e7f0ff"><center>
<h4>Featured</h4><br>
<a href="http://polandonthepassaic.com/"><img src="images/potp.jpg"></a><br>
<i>Poland on the Passaic</i><h4>by<br>Bill Michalsky</h4>
</td>
</tr>

Please note that this is an offsite link to a book that is actually for sale, unlike the rest of the Demo Store items.

We give the page a finished look by spanning the bottom -- since the number of columns is not always the same, we have a "bottom3.inc" for the most common case, a three-column table. Rather than have multiple versions of the bottom text, we use an include within an include to bring in our actual text:

<!--- bottom --->
<? include("bottom3.inc"); ?>

Since we only use bottom3.inc on our three-column pages, we let it finish off our outermost table:

</p>
<pre>
<tr align=center>
<td colspan=3 bgcolor="#e7f0ff">
<p> </p>
<tr align=center>
<td colspan=3>
<? include("bottom.inc"); ?>
</td></tr></table>

Of course, that leaves bottom.inc:

<pre>
<center>
<h5>SimpleshoPHP from <a href="http://simpleshop.org">simpleshop.org</a> - 
Brass Cannon Consulting - PO Box 82783 Portland OR 97282</h5>
</center>

Finally, since you and I are putting all this work into our sites, let's make sure some clown doesn't try to copyright it out from under us:

<!--
  This site contains code from SimpleshoPHP, Free Software distributed
  under the LGPL as described at <http://opensource.org>.
  SimpleshoPHP Copyright 2003-2007, Brass Cannon LLC.
  Additions and changes Copyright 2007 (YOUR NAME HERE)
-->

</body></html>

It's important to include my copyright notice, because as the copyright holder, I'm the only one who can give you your license to use and modify this code. By respecting my rights, you protect your own. That's what Free (as in Liberty) Software is all about. See OpenSource.org for more information. I don't require this to be a live link or even visible on the rendered page, as long as it shows up when someone views the source. An HTML comment is all you need.

That's it for main.php. Next logical step is the browse.php page.


Summary

Detailed descriptions

SimpleshoPHP Home


You are invited to post comments or questions on the SimpleshoPHP forum at SourceForge.net.

SourceForge.net Logo


Copyright 2003-2005, Kevin Martin, dba Brass Cannon Consulting.
The project "SimpleshoPHP" is Free Software, distributed
under the LGPL as described at opensource.org