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";
require_once "DB.php";

$wacs = new Wacs;

The same code segment implemented in perl looks like:

use Wacs;
use DBI;
[Note]Note

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.

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
$dbhandle= DB::connect( $wacs->conf_get_attr("database","phpdbconnect") );
if( DB::iserror($dbhandle))
{
        die("Can't connect to database\nReason:".$dbhandle->getMessage."\n");
}
$dbhandle->setFetchMode(DB_FETCHMODE_ORDERED);

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 connect function which takes an argument of how to connect to the database. The Php version takes a single argument, which is stored in our configuration files as phpdbconnect and includes the whole username, password and database specification in a single lump. The Perl version asks for three: the database specification, the username and finally the password. The configuration file knows these as 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. We then just tell the PHP DB interface how we wish it to organise the returned data; the perl DBI default is pre-determined and is what we want.

[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.

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 both 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->query( $query );

The method is a little different in perl in that it is seperated into two steps; as a result it looks like this...

# 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;
[Note]Note

The query structure is very similar between Php and perl apart for the two step process of validating and then seperately executing the query in perl. This is mostly down to different traditions that exist for database accesses in each language. The net result is similar in technical terms and identical in output terms

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 fetchRow which returns the next row as an array of values, which we assign into the variable $results each time. In Perl, the function we're using is called fetchrow_array because perl offers us a choice in the type of data we are returned and in this case we want a numerically indexed array.

[Note]Note

There are other approaches to getting back the data, including having it returned in one big lump (such as with the Php call getAll()) - this has been avoided as some WACS installations might have tens of thousands of matching records for some queries.

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->fetchRow() )
{
        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 fetchRow 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>