Keeping it Small and Simple

2007.06.12

Building an object model in PHP #1: A database combo

Filed under: Object-oriented programming, PHP Programming — Lorenzo E. Danielsson @ 00:29

These days it seems to be increasingly common to hate PHP. If you look at some of the PHP code that has been written, perhaps one can understand why. But PHP code doesn’t have to look like a mess. In this series I will show you how to evolve an object model in PHP.

It may very well be that PHP sucks, but that is of no concern to me. PHP exists and I am interested in programming. I aim to teach you how it can be used. I assume you can figure out for yourself whether or not PHP is a good tool for whatever task you have.

What we are going to do is develop a fragment of an object model representing HTML elements. I know, it has been done already, and a number of times. Some of the implementations are much better than I could ever hope to accomplish. But we are learning, so we don’t really care about that. We will place ourselves in a possible world where no such model has been developed.

Experts in object-oriented programming know that the best worlds are the ones that are created by intelligent design. Normally this design is done using UML (the Useless Modeling Language), a tool created by a group commonly referred to as “the 666 Amigos”. UML is a part of a plot to take the fun out of programming.

Jokes aside, UML is a useful tool, especially when projects become large. But I am looking through the eyes of a programmer so I will not go into UML. If you are serious about object-oriented programming, then learning UML is not going to hurt you one bit. Go out and buy yourself a book on the subject.

As I am going to try to show off some OO techniques in PHP, I will occasionally take the OO concept too far. I will implement objects for just about everything. When you are back in the real world, you will have to judge how far it makes sense to use objects. It is very possible that in some cases it doesn’t.

A final note: I will be using some features that are only available in PHP5. You can do object-oriented programming in PHP4 as well, but it will look slightly different.

The problem

At this current point in time I’m not really interested in developing any object model at all. My problem is simple: I have been working on a web application for a while and I’m beginning to realize that I am doing a lot of CRYing (Constantly Repeating Yourself). Looking into the code a bit, I have noticed that I have quite a few database lookup tables. In my web application, I usually represent this as combo boxes (if you don’t like the word combo box then you may s/combo box/whatever you like/g).

These combo boxes have a few things in common. They are populated by simple SELECT queries that return two columns: an column that uniquely identifies the row (an ID column) and one that contains some form of textual representation of the row. For example, to populate a combo box showing departments within an organization I might use the following SQL statement:


select id, department from departments;

For people I might use:


select id, concat(firstname, ' ', lastname) as name from friends;

Furthermore I only have MySQL installed on my system. Any time I sell my application I install MySQL for the client. I have heard that there are other database servers out there but I am (at this stage) satisfied with supporting only MySQL. Not a single one of my clients has complained about it so far.

This gives us a starting point for developing a first class.

The combo box

Below is a first implementation of a combo box class.

<?php

class ComboBox {
    private $db;
    private $sql;

    public function __construct($sql) {
        $this->sql = $sql;
        $this->db = mysql_connect("localhost", "dbuser", "n0tap455w0rd");
        mysql_select_db("storage");
    }

    public function __toString() {
        $result = mysql_query($this->sql);
        $html = '<select>';
        while ($row = mysql_fetch_array($result)) {
            $html .= '<option value="' . $row[0] . '">'
                . $row[1] . '</option>';
        }
        $html .= '</select>';
        return $html;
    }
}

?>

As you can see, the ComboBox class currently has two methods: __construct and __toString. Both of these are special (PHP tends to use double underscore for special methods). The first one, __construct is the class constructor, that is the method that is run when a new instance of this class is first created. The __toString is used to convert an instance of this class into a string.

In the constructor we set up a connection to the database as well as store the SQL statement that will be used to populate the combo box. The __toString method simply returns the HTML to generate the combo box. Note that it only returns the HTML as a string. To actually make the combo box appear on a page you would have to echo the string.

There are a number of shortcomings with the ComboBox class as it currently stands. The database connection is hard-coded inside the class. There is absolutely no error-handling done. But at this stage I don’t care. Remember that the current requirement was just to create a re-usable combo box that gets its list items from the result of a database query. And it does just that, as long as we stick to one particular database.

We can test the combo box class with something like the following:

<?php
require_once("./combo.php");
$cb1 = new ComboBox("select id, department from departments");
$cb2 = new ComboBox("select id, concat(firstname, ' ', lastname) as name "
    . "from friends");
?>

<html>
<head>
<title>Testing: Combo box</title>
</head>
<body>
<p>Let's see if we can't generate a combo box or two.</p>
<?= $cb1 ?><br />
<?= $cb2 ?><br />
</body>
</html>

In order to get things working you will have to modify the connection statements in the ComboBox class as well as the SQL statements in the test page. The connection should be to a database that you have on your system and the SQL statements should select from tables in that actual database.

Conclusion

So far we have developed a single class, so we can’t really talk about an object model at all. In the next tutorial things will begin to change as I get a troublesome client who does not want to use MySQL. Also, I find myself wanting to be able to use the combo box in another web application, so there are more reasons to make the class more generic.


Update 2007.06.12: Fixed typo in title.

2007.06.02

Alternating row color in HTML/CSS (with PHP)

Filed under: CSS, HTML, PHP Programming — Lorenzo E. Danielsson @ 00:28

I frequently get asked how to create an HTML in which the rows have alternating background color. Most of the people who ask me this are PHP programmers or at least striving to be, so I’m going to use PHP for the example, but the principle is of course valid regardless of implementation language.

First of all, create two CSS classes, row0 and row1. Set the background-color of each to be a different value. Here is an example:

.row0 {
    background-color: #ffffff;
}

.row1 {
    background-color: #f0f0f0;
}

You will probably be creating your table rows inside a loop of some form. You will need to have flag that will hold either 0 or 1, depending on the CSS class to use for the current table row. How do we make sure that the value is always 0 or 1? Simple. Assuming the name of our flag variable is $rowclass then we just do:


$rowclass = 1 - $rowclass;

Since this will evaluate to 0 if $rowclass is 1, or 1 if $rowclass is 0 it does exactly what we want. I think it is easier to understand how it works if you see it used. So take a look at the following:

<html>
<head>
<title>Table example</title>
<link rel="stylesheet" type="text/css" href="style.css" />
</head>

<body>
<table>
<tr>
<th>Toon</th>
</tr>

<?php
$toons = array("Bugs Bunny", "Daffy Duck", "Tom Cat", "Jerry Mouse");
$rowclass = 0;
foreach ($toons as $toon) {
?>
    <tr class="row<?= $rowclass ?>">
    <td><?= $toon ?></td>
    </tr>
<?php
    $rowclass = 1 - $rowclass;
} ?>

This assumes that you called your stylesheet file style.css and placed it in the same directory as the php file. If you did that you should see a table with four rows, where rows one and three have a white background and rows two and four have a light gray background.

This simple example iterates through the array $toons, but you could do the same thing if your data is the result of a database query.

While we’re at it, I also get asked how to make the rows change background color when the mouse moves over them. That isn’t difficult either. Just add the following to your stylesheet:

.row0 {
    background-color: #ffffff;
}

tr.row0:hover {
    background-color: #ffff00;
}

.row1 {
    background-color: #f0f0f0;
}

tr.row1:hover {
    background-color: #ffff00;
}

Adding the :hover part specifies the style to apply when the mouse is over a table row. This is frequently used to style hyper-links, but it can be used for table rows as well.

2007.05.19

Plotting trigonometrical functions in PHP

Filed under: Graphics Programming, Mathematics, PHP Programming — Lorenzo E. Danielsson @ 02:48

If you’ve ever tried software applications like MRTG or Zabbix you will have seen that these programs are able to generate graphs that are displayed on a web page. If you wonder how it’s done, I hope that this short tutorial will give you an idea.

I’m going to use PHP for this example, but it wouldn’t be difficult to adapt to mod_perl, mod_ruby or possibly mod_pascal (is there such a thing??). To begin with we will need a web server (I’m using Apache2 with PHP support. If you are using Debian or one of its derivates, aptitude install libapache2-mod-php5 should do the trick (you could also use libapache2-mod-php4 if you prefer). You will also need the PHP GD (www.libgd.org library for dynamically creating images. You get that with aptitude install php5-gd.

The GD library allows to create an image, use various functions to draw on the image and finally stream the image to the client (web browser). GD itself is written in C, but there are bindings for several languages available. There are also various utility libraries available that build on top of GD, that make it easier to use GD for specific purposes, but it’s always a good idea to understand how things work at a lower level, so in this tutorial I will look at GD itself.

The procedure is quite simple:

  1. Create an image (either a new blank image or load an existing image)
  2. Perform drawing operations on the image
  3. Stream the image data to the client

In this example, I’m going to plot the two trigonometrical functions sine and cosine. We will create two files. I called the first on trigplot.php (you can call it whatever you want), and it looks like this:

<?php

header("Content-type: image/png");

function draw_axes($image, $width, $height, $color) {
    $xaxis = $height / 2;
    $yaxis = $width / 2;

    imageline($image, $yaxis, 0, $yaxis, $height-1, $color);
    imageline($image, 0, $xaxis, $width-1, $xaxis, $color);
}

function plot_sin($image, $height, $color) {
    for ($x = 0; $x < 360; ++$x) {
        $amplitude = $height/2 - 10;
        $y = $height/2 + $amplitude * sin(deg2rad($x-180));
        imagesetpixel($image, $x, $y, $color);
    }
}

function plot_cos($image, $height, $color) {
    for ($x = 0; $x < 360; ++$x) {
        $amplitude = $height/2 - 10;
        $y = $height/2 + $amplitude * cos(deg2rad($x-180));
        imagesetpixel($image, $x, $y, $color);
    }
}

$width = 360;
$height = 200;
$image = imagecreate($width, $height) or die("Failed to create stream");
$bgcolor = imagecolorallocate($image, 255, 255, 255);
$axiscolor = imagecolorallocate($image, 0, 0, 0);
$curvecolor1 = imagecolorallocate($image, 0, 0, 255);
$curvecolor2 = imagecolorallocate($image, 0, 255, 0);

draw_axes($image, $width, $height, $axiscolor);
plot_sin($image, $height, $curvecolor1);
plot_cos($image, $height, $curvecolor2);
imagepng($image);
imagedestroy($image);

?>

(Sorry for the lack of line numbering, Vim gave me errors when I tried to run :TOhtml with line numbering enabled.)

Notice the header call. This is important. We are sending image data to the web browser, so we must notify it of the content type, so that it will now what to do with the incoming data. The rest should be fairly straightforward I hope (make sure you have you PHP documentation open so that you can read up on the functions that are used). Of course you don’t have separate code this short into functions, but I did it just to show that this is a PHP file like any other and that you can use normal PHP constructs in it.

You could now upload this file to a location where you web server is able to serve it, and you should see a very simple graph containing to curves in different colors. As far as the web browser is concerned, this is an image it is displaying, similar to the way the browser will display any other image. The browser is not at all concerned with the fact that this image is, in fact, dynamically created.

If, instead of seeing an image in your browser, you get something about the image not being displayed because it contains errors then skip down to the section called A little note on debugging.

But how about if you would want to include the graph in an existing web page? That is not a problem at all. Here is a sample, you could call it index.html if you want.

<html>
<head>
<title>Trigonometry plotter</title>
</head>

<body>
<center><b>Trigonometry Functions</b></center>
<img src="trigplot.php">
</body>
</html>

Notice that I am using a normal HTML IMG tag, and pay attention to the SRC attribute. That is really all there is to it (I’m not calling the blog Keeping it Small and Simple for nothing).

A little note on debugging

If a syntax error creeps into your image generation code, then you will just get a blank image if you browse index.html. If you browse trigplot.php, on the other hand, you will get a message that the image cannot be displayed because it contains errors. To get some useful information about the error, browse trigplot.php and get your browser to view the source. It will contain error messages from PHP, along with line numbers. With that your debugging almost becomes trivial.

You will not see any error messages if you try to view the source of index.html. You must browse something like http://localhost/phpsamples/trigplot/trigplot.php to be able to see the error messages that PHP sends you.

Ideas for exercises

For those who feel so inclined I’ve added a few ideas for exercises based on my sample. You can try them if you don’t have anything better to do. Feel free to post your solutions as comments. (No, I’m not going to give you a degree in computer science. 😀 )

  1. Add a border around the graph.
  2. Add labels to the curves (something like y = sin(x) and y = cos(x)). Make sure these get well positioned.
  3. The axes are currently just straight lines. Make them look more like the ones you would find in your average school math book. Add arrows, labels etc.
  4. All drawing in the original is done by GD. Use a drawing program to create an image that contains labeled axes and has a border around it. In your PHP code, use the imagecreatefrompng() function to load the image and then perform you function plotting on it. Make sure you get your positioning right.
  5. Add y = tan(x) to the graph. What special condition do you need to take into consideration when plotting this function?
  6. Add a grid. Choose a light gray color so that it doesn’t dominate the graph.
  7. Create a form that asks the user for information like amplitude, start angle, end angle, wavelength etc, and uses this information to plot the functions.
  8. In the plot functions, highlight the maximum and minimum points of the sine and cosine functions.
  9. Write a Perl program that uses an Inline Asm section to dynamically generate PHP code that dynamically creates graphs of trigonometric functions. 😉

These are just a few examples that I can think of.

Addendum

Spot the error in the code? Hint: think Cartesian vs. screen coordinates. You can fix that by changing $y = $height/2 + ... to $y = $height/2 - ... in the two functions plot_sin and plot_cos.


Update 2007.05.23: Noticed an error in the code.

Blog at WordPress.com.