. 82
( 132 .)


store session data in a MySQL database, you can use the functions in Appendix H.

To write variables to a database you must put them in a format that makes sense to
the database. That is what the session_encode function does. You can see examples
of this in Appendix H.

$str = session_encode()

This function reverses the process of encoding, so that the variable is turned into a
representation that PHP can work with. You can see examples of this in Appendix H.
486 Part IV: Not So Simple Applications

Dealing with the credit-card processor
You are going to need to get some information directly from the entity processing
the transaction. Most processing companies that we™ve seen work similarly. You
send them a request with all the expected credit-card information ” number, expi-
ration date, address, and so forth ” and they send you some codes in response.
Your PHP script needs to compare the codes it receives with the values you get
from the processing agency.
For this application we use (well, pretend to use) Authorize.Net as the credit-card
processor, which seems to work just fine.

Code Breakdown
As with the catalog, here you start by looking at the classes that come into play in
this application. Again, the files accessed via the URLs are very high-level files; all
the tough work is done in the class files.
As already mentioned, one of the goals of this application is to make use of the
classes you created in the catalog. You want to write as little new code as possible,
so the new classes here inherit the methods and properties in the classes you™ve
already created.
One class from Chapter 12 doesn™t quite do enough for inclusion in the cart. That
is the Base class. In this chapter you™re going to create another Base class with some
extended functionality. Then all you have to do is make sure that the categories that
extend Base call your new version. This is easily done with require_once() state-
ments. In your classes.php file you include the CartBase class, and then, when a
class that extends CartBase is included, the extending class sees the new class.
When you look at the classes.php file, remember that the entire content of each of
the included files is sucked into this file when the main file is parsed by PHP.

These classes have methods that look very much like the methods in the Category,
Product, and other classes from Chapter 12. Those worked well because products
have a natural hierarchy: Categories contain products, products contain styles, and
styles contain substyles. For a shopping cart a hierarchy of user information exists:
A user can have many addresses, many orders can go to an address, and many
items can belong in a single order. These relationships are represented and man-
aged in this application by the Tree class.
We start by looking at the Request class, another general purpose class similar
to Base and Tree, that handles HTTP requests.
Chapter 14: Shopping Cart 487

In the two example classes we provide for packaging up your user™s credit card
information all neatly for some card processing company, there™s just one line that
does quite a bit:

$results = Request::post($url, $args);

This sends an array of data and a URL off to the Request class, and receives the
OK or not-OK from the card processor. Looks fairly simple on the outside, which is
somewhat the point. But there™s quite a bit going on inside.
This is especially true for POST requests (GET requests, being ultimately just long
URLs, are easier to manage.) In previous versions, you had to explicitly open a
socket to port 443 yourself, and do most of the work of reading data, checking for
blocks, and so on. PHP 5 largely does that for you now. Instead, you need to set up
what are called “stream context options.” These are parameters to tell the PHP code
handling the communication with the server what kind of content headers to send,
where your SSL certificates are, among other things. Here™s what that code looks
like in the Request class:

$context = stream_context_create(
array(˜http™ => array(
˜method™ => ˜POST™
, ˜user_agent™ => ˜Mad/Fish 1.0™
, ˜http™
, ˜header™
, “Content-type: {$content_type}\r\n”
.™Content-length: ˜.strlen($request_content).”\r\n”
, ˜http™
, ˜content™
, $request_content

We begin by creating a stream context of type ˜http,™ telling it that we will be doing
a POST, and setting up a user agent. Then we spell out the content type and length ”
the type for a POST request is typically “application/x-www-form-encoded.” Finally,
we attach the actual content of the post ” the URL-encoded version of the array we
passed in as an argument to this function.
488 Part IV: Not So Simple Applications

Then to do the POST and get a response, we have this tricky bit of coding:

$results= file_get_contents($url,false,$context);

Yes, that™s it. Just like reading in a file, or downloading a web page from a URL.
This will return the body of the response. To see the HTTP headers that came with
it, you™ll need to check the global variable $http_response_header.

We™ve set up this shopping-cart application to use a generalized tree structure to
represent relationships among database records ” individual records can have par-
ents and children. Therefore, a Tree class exists for making queries against the
database and interpreting the results in terms of tree structure.
Essentially, you pass to the node() method (shown following) the kind of object
you want to organize in a hierarchy, which determines what table is queried, what
the key field name is, and what class is created for each child record found.

function node($o=NULL)
if ($o !== NULL)
if (is_object($o) && is_a($o, ˜base™))
$class = get_class($o);
if ($this->node !== NULL
&& $o !== $this->node
&& $class == $this->node_class
$props = array_merge(
, get_object_vars($o)
foreach ($props as $k => $v)
$o->$k = $v;
$this->node = $o;
$this->node_class = get_class($o);
$this->idfield = $o->idfield;
$this->table = $o->table;
Chapter 14: Shopping Cart 489

return FALSE;
elseif ($this->node === NULL)
return FALSE;
return $this->node;

Once you have a tree in place in your database, you can use the predict_
children() method to pull up all the descendants of a given ID value (or set of
values) at one time, out to an arbitrary number of generations: the depth. The depth
can be sent as a parameter, but it may be a better idea to let the method go to the
default value set in the class constant Depth (referred to in code as Tree::Depth).
You can set Tree::Depth to match the structure you™ve established for your data.
So what does a predict_children() query do? A query out to a depth of 3
looks like this:

select g0.product_id as g0_id
, g1.product_id as g1_id
, g2.product_id as g2_id
, g3.product_id as g3_id
from products g0
left join products g1 on g0.product_id = g1.parent_id
left join products g2 on g1.product_id = g2.parent_id
left join products g3 on g2.product_id = g3.parent_id
where g0.product_id in (1)

and the results look like this:

| g0_id | g1_id | g2_id | g3_id |
| 1| 3| 5| 8|
| 1| 3| 5| 9|
| 1| 3| 6 | NULL |
| 1| 3| 7 | NULL |
| 1| 4| 10 | NULL |
| 1| 4| 11 | NULL |
| 1| 4| 12 | NULL |
| 1| 22 | NULL | NULL |
490 Part IV: Not So Simple Applications

In other words:

— Record 1 has three children: 3, 4, 22

— Record 3 has three children: 5, 6, 7

— Record 5 has two children: 8, 9

— Record 4 has three children: 10, 11, 12

The predict_children() method represents these data as a tree, creating a new
object for each record. The actual data are stored in the $all_data property; the
other properties are arrays of references into $all_data.
You know that the tree has been represented completely if the last column is
NULL for all the records. If it™s not, that can mean that more child records remain to
be found, and the function can run again starting at that depth. In terms of the
shopping cart application, this means we can extract the entire contents of our Tree

The Address class exists for the purpose of manipulating the Address table ” a
design characteristic of applications like this. The key function is write_to_db(),
which verifies that a user_id value has been provided, and that either modifies an
existing record or creates a new one corresponding to it.

class Address extends CartBase
var $table = ˜addresses™;
var $idfield = ˜address_id™;
var $what = ˜address™;
var $fields = array(


var $id = NULL;
var $address_id = NULL;
var $user_id = NULL;
var $address1 = NULL;
var $address2 = NULL;
var $city = NULL;
var $state = NULL;
var $zip = NULL;
var $phone = NULL;
Chapter 14: Shopping Cart 491

var $save_as_new = NULL;

// Methods:

function write_to_db()
if (empty($this->user_id))
$this->error = ˜user_id required to save address™;
trigger_error($this->error, E_USER_WARNING);
return FALSE;
if ($this->save_as_new)
// if the save_as_new property is set,
// then create a new address record by setting
// the id to NULL
$this->address_id = NULL;

// update the addresses table
$result = parent::write_to_db();
return $result;

The Order class corresponds to the Order table and so features fields and methods
that adjust and examine the contents of that table. It also contains the


. 82
( 132 .)