A First WACS Program

Modules: Importing

The very first step is to import the WACS API modules into your program file along with those standard modules needed to access the database. These files should be in the right location already and should just be found without any additional specification of where they are.

Example 2.1. WACS Module Import

require_once "wacs.php";

$wacs = new Wacs;

The same code segment implemented in perl looks like:

use Wacs;
use DBI;
[Tip]Tip

The PHP interface requires an Object Handle to use when accessing the WACS module which we're simply calling $wacs. Perl doesn't need such a construct - there is simply the one instance.

You will note that in Perl we import a database abstraction library called DBI that allows us to make connections to and ask questions of an external relational database. In Php there is a very similar system available called PDO (Php Data Objects) but it is normally included by default and is installed as a part of the language itself using the extensions mechanism.

[Note]Note

In past times, we used to use an external module called DB.php but this had become unreliable and we have re-written all the code to use PDO. You might still find occasional examples that reference it.

Configuration And Security

The second step is to read the standard WACS configuration file to find out where everything is, and then check that this user is allowed to access the WACS system. This is a two step process, and the reading of the configuration file must be done first; otherwise WACS doesn't know where to look for the security files it needs to determine whether this user should be given access or not.

Example 2.2. Config and Security

// read the Wacs configuration files
$wacs->read_conf();

// check the auth(entication and authorisation) of this user
$wacs->check_auth( $_SERVER['REMOTE_ADDR'], 1 );

and here is the same thing again in the perl dialect:

# read the Wacs configuration files
read_conf;

# check the auth(entication and authorisation) of this user
check_auth( $ENV{"REMOTE_ADDR"}, 1 );

Initialising Database Connection

The third step is to initialise the database connection. Since some databases require an environment variable to determine where their configuration files have been stored, this needs to be set first. Wacs provides for this and this code will create that environment variable, if needed, and then proceed to establish the database connection itself.

Example 2.3. Database Connection Initialisation

// database initialisation
// - establish environment variable
$dbienv = $wacs->conf_get_attr("database","dbienvvar");
if( ! empty( $dbienv ))
{
        putenv($dbienv."=".$wacs->conf_get_attr("database","dbienvvalue"));
}
// - connect to the database
try {
       $dbhandle= new PDO(
                 $wacs->conf_get_attr("database","phppdoconnect"),
                 $wacs->conf_get_attr("database","dbuser"),
                 $wacs->conf_get_attr("database","dbpass") );
}
catch( PDOException $e )
{
        die("Can't connect to database\nReason:".$e->getMessage."\n");
}

and here's how we do it in perl:

# database initialisation
# - establish environment variable
$dbienv = conf_get_attr( "database","dbienvvar" );
if( $dbienv ne "" )
{
        $ENV{$dbienv}= conf_get_attr( "database","dbienvvalue" );
}
# - connect to the database
$dbhandle=DBI->connect( conf_get_attr("database","dbiconnect"),
                        conf_get_attr("database","dbuser"),
                        conf_get_attr("database","dbpass") ) ||
die("Can't connect to database\nReason given was $DBI::errstr\n");

OK, let's just study this code for a moment. It first calls the WACS API function conf_get_attr with the section parameter of database as it wants database related configuration information, and an argument of dbienvvar. The WACS API function conf_get_attr is short for configuration get attribute and returns the value of the configuration file parameter of that name or it's default value. The dbienvvar means database interface environment variable. A typical value for this might be something like ORACLE_HOME which is the environment variable that Oracle 10g and 11i requires to be set in order to find it's current configuration.

The next line of the code checks to see if we got back an actual variable name (eg ORACLE_HOME) or an empty string (ie nothing). If we were given a valid variable name, then we're going to need to set it the value it should be, which again we can get from the configuration file, this time called dbienvvalue which is short for database interface environment value (as distinct from the variable name we just looked up). A likely value for this might be /usr/local/oracle. Obviously if we're given no variable name to set, there's no point looking for a value for it! Conversely we are assuming that having bothered to name the variable in the configuration file, also put in a valid value for it - this code could break if the variable name is specified but not it's value.

The second section of these code segments is to do with the establishment of a connection to the database and is a little different between the two versions. Both systems use a handle for the database connection, which we call $dbhandle - imaginative name huh? In both cases the respective database APIs provide a way to open the connection to the database and return us the handle we will use for future actions. In the Php case, we create a new object of type PDO with the necessary three parameters to make the connection. In Perl we simply call the DBI connect function with those same three pieces of information.

These three pieces are: the database specification, the username and finally the password. The configuration file knows these as phppdoconnect or dbiconnect, dbuser and dbpass respectively.

The final bit copes with putting out some kind of error message, at least showing the point of failure, if we are unable to establish a connection to the database. The methods are very slightly different, but the effect is very much the same between the two versions.

[Tip]Tip

Note that you might wish to have completed the output of the HTML header section and started the body by this point so that should the database connection fail, the error message will be visible. We will show you how to present the error to the user in a better way later on.

Fetching Some Records

The next step in the process is to use the database connection we've established to actually make a request of the database. For now don't worry about what that request is or how we've written it - we'll come back to that topic in detail later in this chapter.

Look at the mechanics of how we're issuing the request and getting back the results. What we're going to ask the database for is a list of those girls who are marked as Favourite Solo models. We chose this because all three of the models in our current samples directory are marked as this and so even if you only have our sample records loaded, you should find some matches.

Example 2.4. Database Query

// do db select
//                0      1        2          3
$query = "select mname, modelno, mbigimage, mimage from ".
         $wacs->conf_get_attr("tables","models").
         " where mflag = 'S' order by mname";
$cursor = $dbhandle->prepare( $query );
$cursor->execute();

And the perl version pretty much the same:

# do db select
#                 0      1        2          3
$query = "select mname, modelno, mbigimage, mimage from ".
          conf_get_attr("tables","models").
          " where mflag = 'S' order by mname";
$cursor = $dbhandle->prepare( $query );
$cursor->execute;

In both cases we're putting together an SQL query that reads:

select mname, modelno, mbigimage, mimage
from models
where mflag = 'S'
order by mname

This query asks the database to fetch the four named items: mname, modelno, mbigimage, and mimage from the database table called models where the field mflag has a value of the capital letter S and to sort the results it returns to us by the value in the field called mname. It may not surprise you to learn that mname is the model's name, modelno is our reference number for her, mbigimage is the (location of the) large size headshot of her and mimage is the (location of the) smaller size headshot of her.

You may have noticed that the only part of this that wasn't copied verbatim from the code is the from models bit and that there we've used the WACS API call conf_get_attr to get the actual name of the database table concerned from the main WACS configuration file. This is actually important and it's strongly recommended that you do use this form when creating SQL queries. If you really insist on knowing why, take a look at the section on the tables part of the wacs.cfg configuration file in the WACS configuration guide.

Once we've created the SQL query, we feed it to the database routines. The first step is to pass in the SQL query and have the database perform that search on the database. Once the query has been executed, we want to pull back the matching records (or rows in database parlence) for each model. In both Php and Perl we're calling a routine that returns to us a single row from the database (a single model's record in this case) each time it's called. When we run out of records, a null return is given and our while loop ends. In Php, the function to do this is called using fetch which returns the next row in the way we request. Here we ask for it to be returned to us as an array of values by specifying PDO::FETCH_NUM . We assign this array of values into the variable $results each time. In Perl, the function we're using is called fetchrow_array because perl gives us the choice of how we want the data returned to us in the name of the function. There are other function names in perl that provide other layouts of the data - a PDO::FETCH_ASSOC argument to fetch in php is much the same as using fetchrow_hashref in perl.

[Note]Note

There are other approaches to getting back the data, including having it returned in one big lump - this has been avoided as some WACS installations have tens of thousands of matching records for some queries. It is good practice and perfectly OK to handle one record at a time and the database routines can cope well with this.

Showing The Results

The final step is to actually generate some output from the data we've fetched from the database. We're going to do this as an unordered list in HTML, so we're going to be adding a little formating to the output as we retrieve each record.

Example 2.5. Outputing The List

print "<ul>\n";
while( $results = $cursor->fetch(PDO::FETCH_NUM) )
{
        print "<li>";
        print "<a href=\"".$wacs->conf_get_attr("server","cgiurl");
        print "wacsmpthumbs/".$results[1]."\">";
        print $results[0]."</a></li>\n";
}
print "</ul>\n";

and here's the perl version...

print "<ul>\n";
while( @results = $cursor->fetchrow_array )
{
        print "<li>";
        print "<a href=\"".conf_get_attr("server","cgiurl");
        print "wacsmpthumbs/".$results[1]."\">";
        print $results[0]."</a></li>\n";
}
print "<ul>\n";

We start off by printing out the HTML instruction to start an unordered list (<ul>) in a line on it's own. We then start a while loop which goes through each entry until it's done them all. Both versions use the database cursor object ($cursor) to fetch the next record (aka row) from the database using the fetch(PDO::FETCH_NUM) or fetchrow_array method and assigning it into the array $results (or in perl @results). The act of the assignment fails when there are no more records to fetch and the while loop will terminate. The construct here is based upon the fact that both languages have seperate operators for assignment (=) and comparison (== and eq) and so the code is unambiguous (at least to the php and perl interpreters it is!).

Once inside the body of the while loop we print out the start of list entry tag (<li>) and start in on making use of the data. In the quest to make this example a little bit more satisfying, we've tried to make sure this application does something vaguely useful. A simple list of names is all well and good, but we wanted it to actually do something! So what we've done here is to create a link around each models name that points to her model page as displayed by the standard WACS tools. The raw HTML to achieve this would look like:

<a href="http://www.mywacsserver.com/cgi-bin/wacsmpthumbs/123">
Sarah</a>

So we're left with a slight problem here in that we don't know in advance (trust me on this) what the WACS server is called, we don't know what the models are called and we don't know what their numbers are. We have no idea if we have a model number 123 or not and whether she's called Sarah; but the WACS system should be able to fill in all the blanks for us.

The first part of the code merely prints out the start of the HTML <a href="> and then we ask the WACS configuration system what it's externally visible URL for cgi-bin programs is. We do this using the conf_get_attr call again, telling it we want an answer in the section server of the URL for cgi scripts aka cgiurl. On the next line of the example we put the name of the WACS application we want to link to, in this case wacsmpthumbs. Since the way we tell wacsmpthumbs what we want it to look up is to add a slash and then the model number to the URL, we add a slash (/) on the end and then the number.

[Tip]Tip

You may have noticed that we added a comment on the line above the SQL select statement with 0,1,2,3 with each number above the field name in the query. This was a shorthand to ourselves to remind us what the index number in the array is for each of those database fields.

Since the order of the fields we asked for was mname, modelno, mbigimage and then mimage, the results in the array will be the same - element 0 will be the mname, element 1 will be the model number, and so on. In both cases we're dealing with a single-dimensional array. The first field we want to go into the URL for wacsmodelthumbs is the model number, so that will be element 1 (not zero) therefore we write $results[1]. We then finish off the URL reference by closing the quotes (") and the > tag.

We then want to print the model's name which will be element 0 in our arrays, put out the closing anchor tag (</a>) and then finish off the unordered line entry with the end line tag (</li>). We then print out a new line so the generated page is easier to read. The moving on to the next record will be done as a by-product of the test for the next iteration around the while loop. Once we exit the loop, we finish off the HTML unordered list.

Finishing Off

To just finally finish it off, we need to add a few more pieces just to make it work. For the Php version, we need to declare it as being a php program with <?php at the very start of the file, with a matching ?> at the very end. For perl, we need to declare it as a perl script with the very first line being just #!/usr/bin/perl. Additionally for perl, we need to output the mime content type declaration so that the web browser knows what kind of object it's being passed - this is done simply with:

print "Content-Type: text/html\n";
print "\n";

Next we need a couple of lines of HTML preamble near the beginning (as mentioned before, just before the database connection code so we could see any error message that appears):

<html>
<head>
<title>MySimple: Index Of Favourites</title>
</head>
<body>

Similarly at the end, we just need to finish the page off with the html tail piece:

</body>
</html>