Linux Online Advertisement
[ Register ]

[ Applications ]
[ Documentation ]
[ Distributions ]
[ Download Info ]
[ General Info ]
[ Book Store ]
[ Courses ]
[ News ]
[ People ]
[ Hardware ]
[ Vendors ]
[ Projects ]
[ Events ]
[ User Groups ]
[ User Area ]

Beginning Linux Programming

[ About Us ]
[ Home Page ]
[ Advertise ]

Intermediate Level User Linux Course

Web development

We've dealt with how to set up and configure Apache and MySQL. Now we can use these tools in connection with others to provide web services to our office, company or to the world.

PHP

PHP is a web scripting language created by Rasmus Lerdorf to solve some problems he was having with is personal home page (the initials of which are PHP). The developers of PHP maintain that these initials stand for PHP Hypertext Processor in the GNU tradition of recursive acronyms, so we'll take their word for it and leave it at that. As Shakespeare famously pointed out, A rose by any other name would smell as sweet, so whatever PHP stands for, it's a great tool for web development and more importantly, its learning curve is not that steep compared to other programming languages.

Note

PHP is currently at version 4 with version 5 reportedly on the way soon (at the time of this writing). Version 4 is a complete re-write of the original PHP language carried out by Zend Technologies Ltd, a company based in Israel. We will be dealing with PHP version 4 in this lesson.

Installation of PHP

I hope the readers will forgive me for repeating many things, but as we have seen with many other programs and server daemons, most major Linux distributions will allow you to install PHP and some accompanying modules with minimal effort.

Apache module or stand-alone CGI

PHP can be installed as a module of Apache (known as mod_php) or as a stand-alone executable binary, also known as CGI. There are pros and cons to both. I have worked with both systems and from a development point of view, I prefer compiling it into Apache as a modules, mainly for performance and security reasons. PHP.net has some good pages to explain some of these issues in more detail.

Your distribution will normally give you the option of installing this in one way or another. Installing PHP on your own from source code is also possible. Using PHP as an Apache module requires you to recompile Apache itself and this goes somewhat beyond what we can efficiently deal with in this lesson. Suffice it to say that I have not had to install PHP in this way in over 3 years, so chances are that you will just indicate that you want to use PHP during your distribution's install process and you will have it when you're finished.

You may also find that some add-ons to PHP might come in handy. There are modules that work with MySQL, PostgreSQL, IMAP and some other programs plus enhancement tools like PEAR. It is my experience with Debian GNU/Linux that you can easily choose these add-ons during the installation process and you can then use them after without much futzing around.

Just to make sure that PHP is installed to your liking, you can have a look at your Apache httpd.conf file. Look for the following lines:

LoadModule php4_module /usr/lib/apache/1.3/libphp4.so

And

<IfModule mod_dir.c>
    DirectoryIndex index.html index.php index.htm index.shtml index.cgi
</IfModule>

Also:

# And for PHP 4.x, use:
    #
    AddType application/x-httpd-php .php
    AddType application/x-httpd-php-source .phps

These lines are the basic requirements so that PHP scripts are processed by Apache. If Apache is running and you've been able to locate these lines, then we're all set to start web development with PHP.

Your first PHP script

Those of us who have tried to learn a programming language know that you usually start off with the archetypal 'Hello World' programs (or their variants ('Hello Mars', 'Hello Jupiter', 'Hello Vulcan'). We all know what these are going to do so we'll start with a more practical example. Let's write a script to have PHP give us information about itself.

Example 1. Hello PHP!

<?php

// this is a comment
/* this is
 * another 
 * comment */

// this will show us information about PHP

phpinfo();

?>

This will give us a long page with detailed information about PHP and how it works with our server, what it will do for us, etc. I'd say it's a tad more interesting than 'Hello world!'. Even this small script gives us a lot of information about actually writing our own scripts. First, you will notice that PHP scripts begin with <:?php. This basically tells Apache to process the rest of the script as PHP. Skipping down to the end, you'll see the reverse: ?> which logically means, end of script. You will also notice that you can use two types of commentary. I prefer the // for short commentary and /* *text *here */ for longer explanations. This is, of course, a personal preference. Lines of code in php must end in a semi-colon (;). These are the basics. We'll see some more requirements as we move along with the lesson.

HTML with PHP

Web browsers are designed to render content written in HTML. However, pure HTML is just static content. Web scripting languages like Perl and in particular PHP, which was designed after the WWW hit the public consciousness and is therefore designed particularly for it, can really help us to make sure the information we're providing is up-to-date. PHP can take real-time information from a MySQL database and display it in an HTML format and provide what is called "dynamic" content. Before we actually talk about providing this content, let's see how PHP displays HTML.

Example 2. Hello PHP!

<?php

/* script to output simple HTML
 * using PHP */

// show the page
print "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">\n";
print "<html>\n";
print "<head>\n";
print "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=ISO-8859-1\">\n";
print "<meta NAME=\"Author\" content=\"Michael J Jordan\">\n";
print "<meta NAME=\"description\" content=\"php\">\n";
print "<meta NAME=\"keywords\" content=\"php, html\">\n";
print "<title>Welcome to PHP</title></head>\n";
print "<body bgcolor=\"#ffffff\">\n";

print "<h2>Welcome to PHP!</h2>"\n";

print "</body>\n";
print "</html>\n";

?>

True to my tradition of being my own worst critic, this code is neither terribly efficient nor elegant, but it does show more important information about how PHP works. I chose to use this because it shows how PHP acts like a traditional programming language. Like C, for example, you'll find that you need to place things that will be show to the user like so:

print "something";

You could also alternatively use:

echo("something");

This code is not efficient because it doesn't do anything that your run-of-the-mill page in pure HTML could do. As a matter of fact, PHP will let us write pure HTML if we want.

Example 3. Hello again PHP!

<?php

/* script to output simple HTML
 * using HTML in a PHP script */

// create page

?>


<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<meta NAME="Author" content="Michael J Jordan">
<meta NAME="description" content="php">
<meta NAME="keywords" content="php, html">
<title>Welcome to PHP</title></head>

<body bgcolor="#ffffff">

 <?php print "<h2>Welcome to PHP!</h2>"; ?>

</body>
</html>

<? // the end ?>

This is really nothing more than scattering PHP throughout an HTML script. But we could add to it to make it a little more dynamic.

Example 4. And the time is...

<?php

/* script to output the time
 * using a PHP script */

// get time and create variable

$myTime = date("H:i:s");

// create page

?>


<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<meta NAME="Author" content="Michael J Jordan">
<meta NAME="description" content="php">
<meta NAME="keywords" content="php, html">
<title>Welcome to PHP</title></head>

<body bgcolor="#ffffff">

 <?php 
         print "<h2>Welcome to PHP!</h2>"; 
         print "<h3>The time is <b>$myTime</b></h3>";
?>
 

</body>
</html>

<? // the end ?>

Here we've used the MySQL 'date' function to get the local time. We've converted the result into a variable called $myTime and used this to show the date to users. There are a lot of different options for showing the date and the time. Check out PHP.net's fine manual page for more options. Speaking of options, that's what PHP is all about - options! How about choosing a greeting based on the hour that people visit your page:

Example 5. Greeting a user

<?php

/* script to output the time
 * using a PHP script */

// get time and create variable

$myTime = date("H:i:s");

// give the user a special greeting
$myHour = date("H");

if (($myHour > "06") && ($myHour < "12"))

{
     $greeting = "Good Morning";
}

else if (($myHour > "12") && ($myHour < "18"))

    {

       $greeting = "Good Afternoon";

     }

else if (($myHour > "18") && ($myHour < "22"))

     {
       $greeting = "Good evening";

      }

else 

     {
$greeting = "Hey! You're up late!";

    }


// create page

?>


<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<meta NAME="Author" content="Michael J Jordan">
<meta NAME="description" content="php">
<meta NAME="keywords" content="php, html">
<title>Welcome to PHP</title></head>

<body bgcolor="#ffffff">

 <?php 
         print "<h2>Welcome to PHP!</h2>"; 
         print "<h3>The time is <b> $myTime </b></h3>";
         print "<h3> $greeting </h3>";
?>
 

</body>
</html>

<? // the end ?>

As you can see, we're making this page a little more dynamic each time. Greeting the user is interesting, but it doesn't really represent anything that you couldn't do with JavaScript, for example. But let's imagine that those variables, instead of just displaying the date, were actually showing information from a database - perhaps our LP collection database we saw in the MySQL section. With PHP, you can do that.

True dynamic content

The above examples were true dynamic content in that we didn't have to manually change the time (which would be a chore indeed!) or the greeting. But in terms of providing dynamic content, they represent only the most infinitesimal piece of the tip of the proverbial iceberg. Where we start seeing more of the iceberg is when we use data from MySQL or another database package. Here's a fairly simple script to show the data in our LP database:

Example 6. Hello World - here's my album collection

<?php

/* script to list the LPs in our collection
 * uses DB album TABLE collection */

// get the HTML doctype declaration and metatags
    require("./includes/doctype.inc.php");
// get our database connection information
    require("./includes/dbinfo.inc.php");
    mysql_selectdb("album");
    

// start page
print "<title>My Album Collection</title>\n";
print "</head>\n";
print "<h1 align=\"center\">My Album Collection</h1>\n";
print "<table width=\"98%\" cellspacing=\"2\" cellpadding=\"2\" align=\"center\">\n";
print "<tr><th>Artist</th><th>Title</th><th>Label</th><th>Released</th></tr>\n";    
$query = mysql_query("SELECT artist, title, label, date_format
(released, '%d %M %Y') AS Release_Date FROM collection ORDER BY artist");

 if (mysql_num_rows($query) != 0)

{
while ($row = mysql_fetch_array($query))

   {
        $artist = $row["artist"];
        $title = $row["title"];
        $label = $row["label"];
        $released = $row["Release_Date"];
 
print "<tr>\n";
print "<td align=\"center\"><font size=\"2\">$artist</font></td>\n";
print "<td align=\"center\"><font size=\"2\">$title</font></td>\n";
print "<td align=\"center\"><font size=\"2\">$label</font></td>\n"; 
print "<td align=\"center\"><font size=\"2\">$released</font></td></tr>\n";

  }
}

else 

{

  print "<tr><td colspan=\"2\">Sorry, no LPs found</td></tr>\n";

}


print "</table>\n";
print "<p></p>\n";

require ("endofpage.inc.php");

// that's all folks!

?>

Besides being a leap forward compared with our script that showed you the time, it's also quite a bit more efficient. First, we've taken our HTML doctype declaration and metatags, which should appear in every HTML document, and we've put them in separate file. That saves us from having all of that information in every PHP script we write. Secondly, the database connection information goes into a separate file as well. This is also for efficiency, but more for security. It contains our password and that kind of information should never be put into the script itself. As you can see, to require that a file be included in your script, use this syntax:

require ("/some/where/somefile");

What I've included in the three files we require is:

Example 7. doctype.inc.php

print "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">\n";
print "<html>\n";
print "<head>\n";
print "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=ISO-8859-1\">\n";
print "<meta NAME=\"Author\" content=\"Michael J Jordan\">\n";
print "<meta NAME=\"description\" content=\"php\">\n";
print "<meta NAME=\"keywords\" content=\"php, html\">\n";

Example 8. dbinfo.inc.php

$db = mysql_connect("localhost", "USER", "PASSWORD") 
	or die ("There was a database error");

Example 9. endofpage.inc.php

print "</body>\n";
print "</html>";

The first and third files are rather self explanatory. The one in the middle is also fairly straightforward. It includes our database connection information and some error handling. If the MySQL daemon were to shut down unexpectedly your users would get some meaningful message (the message is entirely up to you).

So if you load this main file into in your server's web directory, anywhere Apache is configured to serve web pages, and then put the required files where the path says they're supposed to be located, then you'll be able to see something like this:

      
                         My Album Collection


Artist              Title                  Label           Released


Beatles           Yesterday and Today     Capitol        01 June 1966
Elvis Costello     Armed Forces           Rhino          05 January 1979
Police             Zenyatta Mondatta      A&M        03 October 1980

The meaty part of the script, so to speak, comes in the middle. First, we start with the MySQL query:

$query = mysql_query("SELECT artist, title, label, date_format
(released, '%d %M %Y') AS Release_Date FROM collection ORDER BY artist");

This is a normal MySQL query with the little trick we learned about showing the date in a more pleasant form.

Next, we make sure that there are actually entries in the table. PHP has a statement here, mysql_num_rows, that looks for the number of entries or rows in our table. So we set up a conditional statement that says if the number of rows isn't zero, then we should fetch information from the table and present it.

 
if (mysql_num_rows($query) != 0)

{

We then "fetch" the rows from the MySQL table and convert them into an array of values:

while ($row = mysql_fetch_array($query))

   {
        $artist = $row["artist"];
        $title = $row["title"];
        $label = $row["label"];
        $released = $row["Release_Date"];

We have also converted the entries into variables to display in the table.

After we render the HTML table with the variables, we add an else statement in case we have no entries in our database table

else 

{

  print "<tr><td colspan=\"2\">Sorry, no LPs found</td></tr>\n";

}

That's providing dynamic content in a nutshell.

Updating/Editing content

Providing dynamic web content also means being able to add to it and edit it if necessary. We'll need to develop scripts to let us do this. Here's one that gets that job done:

Example 10. Manage my album collection

<?php

/* script to edit/add/delete to the LPs in our collection
 * uses DB album and TABLE collection */

// get the HTML doctype declaration and metatags
    require("doctype.inc.php");
// get our database connection information
    require("dbinfo.inc.php");
    mysql_selectdb("albums");
    
// start doing stuff
/* if we find an ID
 * in the hidden ID field
 * it means we're doing an edit */

// the submit button will be present with edits or adds
if ($submit) {

  /* if there's an ID number in the hidden field
   * we're editing. If not, we're adding */

   if ($ID) {
 
 $query = "UPDATE collection SET title='$title', artist='$artist', label='$label',
 genre='$genre', condition='$condition', worth='$worth', notes='$notes', 
 released='$released', added='$added', opened='$opened' WHERE ID=$ID";
} else {
 $query = "INSERT INTO collection (title, artist, label, genre, condition, worth, 
 notes, released, added, opened) VALUES ('$title', '$artist', '$label', '$genre',
 '$condition', '$worth', '$notes', '$released', '$added', '$opened')";
}
// do the query
$result = mysql_query($query);
        
        print "Database changed.";
        print "<p><a href=$PHP_SELF>Enter or edit more 
        information</A>";
         } elseif ($delete) {
        // delete the record we choose
        $query = "DELETE FROM collection WHERE ID=$ID";
        $result = mysql_query($query);
        print "<p>That entry is goooooooone!\n";
        print "<a href=\"$PHP_SELF\">Add or Edit LP</a>\n";
 
} else {
    // no ID == new entry
       if (!$ID) {
    // print our list of albums 
    $result = mysql_query("SELECT * FROM collection ORDER BY title");
    print "<table border=\"0\" bgcolor=\"#FFF0F0\" width=\"98%\" 
    cellspacing=\"1\" cellpadding=\"2\">\n";

// output the list
while ($myrow = mysql_fetch_array($result)) {

          print "<tr><td><b>Album: </b>";
          printf("<a href=\"%s?ID=%s\">%s </a></TD><TD>
          <B>%s</B>\n", $PHP_SELF, $myrow["ID"], $myrow["title"], 
          $myrow["artist"]);
          print "</td><td>\n";
          // think twice before you offer this to users!!
          print "<font color=\"990000\"><b>Delete (careful!):
          </b></font>";
          printf("<a href=\"%s?ID=%s&delete=yes\">YES</a>
          </td></tr>\n", $PHP_SELF, $myrow["ID"]);

}

 print "</table>";

}

?>

<p>

<a href="<?php echo $PHP_SELF?>">Add LP</a>

<p>

<form method="post" action="<?php echo $PHP_SELF?>">

<?php

if ($ID) {

// if editing - select a record

$query = "SELECT * FROM collection WHERE ID=$ID";
$result = mysql_query($query);
$myrow = mysql_fetch_array($result);

// this hidden field contains the ID of the entry

?>

<input type="hidden" name="ID" value="<?php echo $ID ?>">

<?php
// the HTML fields
}

?>


<h2>LP collection</h2>
<h4>Use 'add LP' to add an LP or click on the name to modify an 
existing one</h4>

<p>
<table border="0" bgcolor="#ffcc99" align="center" width="80%">
<tr><td><b>Title: </b></td><td align="left">
<input type="Text" name="title" size="80" maxlength="80" 
value="<?php echo $myrow["title"] ?>">
</td></tr> 
<tr><td><b>Artist: </b></td><td align="left">
<input type="Text" name="artist" size="80" maxlength="80" 
value="<?php echo $myrow["artist"] ?>">
</td></tr>
<tr><td><b>Label: </b></td><td align="left">
<input type="Text" name="label" size="40" maxlength="40" 
value="<?php echo $myrow["label"] ?>">
</td></tr>

<tr><td><b>Genre: </b></td><td align="left">
<input type="Text" name="genre" size="40" maxlength="40" 
value="<?php echo $myrow["genre"] ?>">
</td></tr>
<tr><td><b>Condition: </b></td><td 
align="left">
<input type="Text" name="condition" size="20" maxlength="20" 
value="<?php echo $myrow["condition"] ?>">
</td></tr>
<tr><td><b>Worth: </b></td><td align="left">
<input type="Text" name="worth" size="10" maxlength="10" 
value="<?php echo $myrow["worth"] ?>">
</td></tr>
<tr><td><b>Released: </b></td><td 
align="left">
<input type="Text" name="released" size="10" maxlength="10" 
value="<?php echo $myrow["released"] ?>">
</td></tr>
<tr><td><b>Added: </b></td><td align="left">
<input type="Text" name="added" size="10" maxlength="10" 
value="<?php echo $myrow["added"] ?>">
</td></tr>
<tr><td><b>Opened: </b></td><td align="left">
<select name="opened">
<option value="<?php echo $myrow["opened"] ?>"> 
<?php echo $myrow["opened"] ?> </option>
<option value="yes">yes</option>
<option value="no">no</option>
</select>
</td></tr>
<tr><td colspan="2">
<b>Notes:</b><br><textarea name="notes" cols="80" 
rows="10" wrap="virtual">
<?php echo $myrow["notes"] ?></textarea>
</td></tr>
</table>

<p>
<input type="submit" name="submit" value="ok">
<input type="reset" name="reset">
</form>

<?php

}

require("endofpage.inc.php");

?>

This will give you a way to update your data. However, there are a couple of things wrong with this script. One thing is the HTML, which is not really written to be aesthetically pleasing. But since this isn't a course in HTML, then we can overlook that. The most important thing about this script is that it doesn't do any error checking. First, it doesn't warn you if you've left a field blank. Though that's fairly important, what's more important from a security point of view is the need to check the fields for illegal characters. Characters like *<>!#\ (which look a lot like comic book swearing) can be used in conjunction with other code to use your script to attack the machine. Although this is probably only going to be used by you and you really aren't going to try to crack into your own machine, it's good to get into the habit of writing good, secure scripts. You never know when you might be called on to do this for a public website. So let's add a little basic security. Before we let anyone submit information, we'll give it a check.

Example 11. Re-written section

 if ($submit) {
	    // make all fields into one long string
            $myFields = array($title, $artist, $label, $genre, $condition, 
            $worth, $notes);            
            $careful = implode(" ", $myFields);
             // check for strange characters for security reasons
             if (!ereg("[*+#\|<>@$%]+", $careful))
		 {

  /* if there's an ID number in the hidden field
   * we're editing. If not, we're adding */

                   if ($ID) {
 
 $query = "UPDATE collection SET title='$title', artist='$artist', label='$label',
  genre='$genre', condition='$condition', worth='$worth', notes='$notes', 
  released='$released', added='$added', opened='$opened' WHERE ID=$ID";
} else {
$query = "INSERT INTO collection (title, artist, label, genre, condition, 
 worth, notes, released, added, opened) VALUES ('$title', '$artist', '$label', 
 '$genre', '$condition', '$worth', '$notes', '$released', '$added', '$opened')";
}
// run SQL against the DB
$result = mysql_query($query);
        
        print "Database changed.";
        print "<p><a href=$PHP_SELF>Enter or edit 
        more information</A>";
	       }
	// warn the user who enters illegal characters
               else  {
            print "<title>Update/Add to Album 
            Collection</title></head>\n";
            print "<body bgcolor=\"#fff0f0\">\n";
            print "<h2 align=\"center\">Error</h2>\n";
            print "<h3 align=\"center\">Please do not use non-standard 
            characters</h3>\n";
            print "<h4 align=\"center\">Use your back button to 
            return</h4>\n";
		  }

        } elseif ($delete) {
        // delete a record

I've re-written the section between if ($submit) { and } elseif ($delete) { We need another 'if' condition to check the fields in which you can freely enter information. I have left out the Released, Added, Worth and Opened fields. These should be changed to drop down menus. This will eliminate the need for checks there, as the information you can enter is limited to what the menus give you.

To make this more efficient, I have converted the contents of the fields into one long string, courtesy of the PHP function 'implode'. Then we'll give an error message if someone enters characters like these *+#\|<>@$%. So this limits the user to alpha-numerical characters and punctuation which shouldn't do any harm.

More or less, we have a script that will add, edit and delete data. Combined with the script before to show our data, we've got a web-based data processing system written in PHP. The scripts are far from finished, however. We need some error checking there to make sure people don't leave things blank, use obscene words or any other limitations we might want. We can make it a whole lot nicer looking too. I'll let you do these things as a homework assignment!

Web development with Perl

We've already talked a bit about Perl as a tool to help with system administration, (which is what it was originally designed for) but it's also the most widely-used language for web development on Linux systems. Since the advent of the World Wide Web, more features have been added to Perl to improve its use as a web scripting language.

Some time ago, I wrote some scripts to create a web-based knowledge base of Linux configuration information, mostly for my personal use. It's sort of a cross between a set of how-tos and a FAQ. I'll be using these scripts as examples in this lesson.

Note

Many people prefer PHP because it was created after the Web came into being and since Perl came before, it supposedly is playing catch-up, according to PHP fans. Strictly speaking, the two are capable of doing the same things. I have found that Perl requires more coding than PHP in general and you need to know something about accompanying code known as Perl modules in order for you to be really proficient in Perl. PHP seems to have more features built-in to it and a quick perusal of the PHP manual will get you up to speed on them. Perl also may a bit more imposing if you're new to programming. PHP on the other hand, is not well suited to tasks such as system administration, though PHP developers are also trying to take PHP in that direction. These are, of course, are my personal observations and so should be taken with that in mind.

MySQL revisited

MySQL and Perl also make a good combination, like MySQL and PHP. Before we start out creating our knowledge base, we'll need to set up a database and table. I named the database 'kbase' and created this table:

+------------+---------------+------+-----+---------+----------------+
| Field      | Type          | Null | Key | Default | Extra          |
+------------+---------------+------+-----+---------+----------------+
| how_to     | varchar(255)  | YES  |     | NULL    |                |
| level      | varchar(30)   | YES  |     | NULL    |                |
| category   | varchar(30)   | YES  |     | NULL    |                |
| answer     | text          | YES  |     | NULL    |                |
| keywords   | varchar(100)  | YES  |     | NULL    |                |
| importance | int(11)       | YES  |     | NULL    |                |
| rating     | int(11)       | YES  |     | NULL    |                |
| modified   | timestamp(14) | YES  |     | NULL    |                |
| ID         | int(11)       |      | PRI | NULL    | auto_increment |
+------------+---------------+------+-----+---------+----------------+

The how_to field is the field that will contain our question: 'How do I configure ....' . I've given this plenty of room. 'level' corresponds to the difficulty of the configuration task. 'category' responds to the area we're dealing with (mail, x-window, kernel). The 'answer' is the explanation of how to do it and I have allowed a whole text field. 'keywords' we can use for future searches when our data gets voluminous. 'importance' is a field for giving a value to judge how often we're going to doing these things. The 'rating' is a system of showing how often we really have needed to look something up. I put that there for my personal curiosity. You may find other field names more meaningful. Now that you know all about MySQL, you are free to change them. Our first script will be one to enter information in the table.

Adding data

Before we start writing a script in Perl, we should find out where Perl is hiding. This is important. A hosting service I once used upgraded their server and Perl went from being /usr/bin/perl to /some/where/else/perl. My scripts broke and my website visitors started mailing me. The most common location for Perl in my experience is /usr/bin/perl, and that's what will appear first in our script.

#! /usr/bin/perl -w

The -w part means 'warn'. This will show us warnings if our code has some rough edges to it. Let's look at the whole script:

Example 12. add.pl

#! /usr/bin/perl -w 

use strict;
use DBI;
use CGI;
use CGI qw(:standard :netscape :shortcuts);
use CGI::Carp qw(fatalsToBrowser);

# little routine to include external scripts

sub include { 
# Include files in this directory
 
   my $file = shift or return; 
   my $path = '/home/mike/www/dev/perl/incluir/'; 
 
   open  INC, "$path/$file" or return $!; 
   read  INC, my $str, -s INC; 
   close INC; 
 
   return $str; 
}

## include these files

my $MyWWW = "/dev/perl";
my $imagenes = "$MyWWW/graficos";

my $inc_dtype = include('doctype.inc'); 
my $inc_head = include('header.inc');
my $inc_add = include('adding.inc');
my $inc_foot = include('footer.inc');
my $inc_endpg = include('endpg.inc');

## add basic password protection

open PWD, "/home/mike/perl-lib/dbinfo";
my ($user, $pass) = split(/:/, <PWD>, 2);
close PWD;
chomp($user, $pass);



# print HTML headers

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

# print page

print qq |

$inc_dtype 
<title>Linux Knowledge Base</title>
<body bgcolor="#ffffff">
$inc_head
$inc_add

|;

# see if we have the HTML fields

if (param()) {

# open DB connection

my $dbh = DBI->connect('DBI:mysql:dbname=kbase', $user, $pass); 
my $how_to = param ("how_to");
my $level = param ("level");
my $category = param ("category");
my $answer = param ("answer");
my $keywords = param ("keywords");
my $importance = param ("importance");
# my $rating = param ("rating");

# start counting visitor rating at 0 (of course)

my $rating="0";

if (($answer =~/Microsoft/i) || ($answer =~/Gates/i))

{

print qq |

<b>Eeek! Microsoft!</b>
<p>Are you sure you want that in there?


$inc_finpg

|;

}

    else {

my $sql = "INSERT INTO questions (how_to, level, category, answer, keywords, 
importance, rating) VALUES (";
$sql .= $dbh->quote($how_to) . ",";
$sql .= $dbh->quote($level) . ",";
$sql .= $dbh->quote($category) . ",";
$sql .= $dbh->quote($answer) . ",";
$sql .= $dbh->quote($keywords) . ",";
$sql .= $dbh->quote($importance) . ",";
$sql .= $dbh->quote($rating);
$sql .= ")"; $dbh->do ($sql); 

print qq | 
 
<p><br>
<table align="center" border=0 cellspacing=2 cellpadding=4 width=70% 
bgcolor="#990000">
<tr><td bgcolor="#ffffff"><font color="#990000"><B>FYI: 
</B></td></tr>
<tr><td bgcolor="#ffffff"><p>The how-to:<br> 
<font color="#0000cd"><b>$how_to</b></font>
<br> has been added to our knowledge base.</td></tr>
</table>
<p><br>

<h3 align="center">Add another</h3>
<h3 align="center">or <a href="../bin/list.pl">see the list</a></h3>

|;

    }

# Disconnect from the database
$dbh->disconnect;

} 

# show our data form

print qq |

    <form method="get">|;

# entry fields


print qq |
<p>
<table align="center" border=0 cellspacing=2 cellpadding=4 
width=80% bgcolor="#000080">
<tr><td bgcolor="#9ed5d4" colspan="2">
<font color="#990000"><b>How do you: </b></font><br>
<textarea name="how_to" cols="60" rows="3" wrap="virtual"></textarea>
</td>
<tr><td bgcolor="#9ed5d4">
<font color="#990000"><B>Difficulty Level: </B></font>
<select name="level">
<option value="NA"> NA</option>
<option value="easy"> easy </option>
<option value="involved"> involved </option>
<option value="difficult"> difficult </option>
<option value="very difficult"> very difficult </option>
</select>
</td>
<td bgcolor="#9ed5d4">
<font color="#990000"><B>Category: </B></font>
<input type="text" name="category" size="50"></td></tr>
</select></td></tr>
<tr><td bgcolor="#9ed5d4" colspan="2"><font color="#990000">
<B>Frequency of use: </B></font><font size=2></font> 
<select name="importance">
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
    </select> 
<font size="2">(We will use this - 1 = a lot, 2 = sometimes 3 = not much)
</font>
</td></tr>
<tr><td bgcolor="#9ed5d4" colspan="2">
<font color="#990000"><b>Keywords: </b></font>
<input type="text" name="keywords" size="70"></td></tr>
<tr><td bgcolor="#9ed5d4" colspan="2">
<font color="#990000"><b>Explanation: </b></font>
<br><textarea name="answer" cols=60 rows=20 wrap="virtual">
</textarea><br>
</td></tr>
<tr><td align="left"><input type="submit" name="submit" </td>
<td colspan="2" align="right"><input type="reset"></td></tr>
</table>
</form>
<p><br>

$inc_foot
$inc_endpg


    |;

As you can see, there are some significant differences compared with our PHP scripts before. First, you'll see that we need to tell the script to use some Perl modules. Perl modules is a system that exists to re-use code so you don't have to write the same things over and over again in your code.

Note

There are a lot of modules available via the CPAN (Comprehensive Perl Archive Network) system. The easiest way to get Perl modules is to fire up a 'cpan' terminal and install them through there. To open a CPAN terminal, type: perl -MCPAN -e shell. If it's the first time, you'll have to answer a few questions about your system in order to set it up. Then you can install new modules with a command like: install ModuleName

First, we've said we want to use the modules 'strict'. Basically, it requires you to declare variables first before using them. You'll notice the variables in the script are first declared as 'my $something'. This makes the script run faster and in the long run makes it easier to spot mistakes. If you type a variable wrong in the script, then the script will stop and tell you that it can't find it. Next, we see 'use DBI'. This is the module 'DataBase Interface' that allows us to connect to and use MySQL. The next are the various parts of the CGI module we're going to use. I should actually say that we could use them. This script really doesn't take advantage of these that much. That is to say, I could use the CGI module write my HTML with a more Perl like syntax - something like

$q->h1('hello world'),
instead of using the normal tags as we have here. This is a question of choice and my brain seems to work better when HTML is HTML. The last one, fatalsToBrowser, sends error output to the browser with a nice message asking you to contact the webmaster of the site. This can come in handy because you may actually be sleeping and the server might encounter problems. If some nice website visitor chooses to paste in the error message, you might have otherwise lost information about what was going on at the time. You can actually customize these messages. See 'man MODULE_NAME' for more informations on the Perl modules installed on your system.

The next system is actually a little kludge I found (my thanks to whomever wrote this) to allow me to use external files like PHP uses 'require/include'. Files in /home/mike/www/dev/perl/incluir/ can be included in the script. This allows me to keep my static content in another place. With this we can re-use HTML. Next, we declare these files for use later.

Next, we have another kludge to keep the passwords out of the web directory's hierarchy. The dbinfo file just has one line which is USER:PASSWORD. This little routine separates them and passes it to two variables which we'll use to connect to our database. If you skip over the block where we start creating the page, you'll see the connection info and the 'param' fields which are there so our HTML fields will pass the info in them to our table. But before we pass the information, we should do a little error handling. Due to the nature of the web app we're creating, we can't be too selective about letting strange characters into the database. After all, this is about configuration, so we'll definitely need to enter characters that we would have considered illegal in the PHP examples. I've added a little humorous block here as an example.

Next you'll find where we've given a little notice when we've added the info to our database. After, we break our connection to the database. Next we put the html table where our fields are for entering information. You've may have noticed that 'print qq | |; will let us put HTML code in between without escaping the quotes with forward slashes (\) as we had to in HTML. After we add a last few include files, we're finished with our basic script to add information.

Editing entries

It's not enough to enter the information. We'll need to retrieve it and to edit it if necessary (and it usually is). Let's look at some scripts for doing those tasks.

Example 13. publist.pl

#!/usr/bin/perl

use strict;
use DBI;
use CGI;
use CGI::Carp qw(fatalsToBrowser);

# needed for inclusion

sub include { 
#--------------------------------------------------- 
# Pass in a file name to include. 
 
   my $file = shift or return; 
   my $path = '/home/mike/www/dev/perl/incluir/'; 
 
   open  INC, "$path/$file" or return $!; 
   read  INC, my $str, -s INC; 
   close INC; 
 
   return $str; 
}

## add basic password protection

open PWD, "/home/mike/perl-lib/dbinfo";
my ($user, $pass) = split(/:/, <PWD>, 2);
close PWD;
chomp($user, $pass);

# Connect to DB

my $dbh = DBI->connect('DBI:mysql:basecon', $user, $pass)
    or die "Couldn't open database: $DBI::errstr; stopped";

# Prepare SQL query
# this is ordered by 'rating'
my $sth = $dbh->prepare("SELECT * FROM questions ORDER BY rating")
or die "Couldn't prepare statement:$DBI::errstr; stopped";

# Execute the query

$sth->execute() or die "Couldn't execute statement: $DBI::errstr; stopped";

## include files

my $MyWWW = "/dev/perl";
my $imagenes = "$MyWWW/graficos";

my $inc_dtype = include('doctype.inc'); 
my $inc_head = include('header.inc');
my $inc_intro = include('intro.inc');
my $inc_foot = include('footer.inc');
my $inc_endpg = include('endpg.inc');


# print HTML headers

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

## start showing the page
print qq |

$inc_dtype 
<title>Linux Knowledge Base </title>
<body bgcolor="#ffffff">
$inc_head
$inc_intro 


|;


print "<ul>\n";

# Fetch and print rows from table

while ( my ($how_to, $ID) = $sth->fetchrow_array() ) 
{
    print STDOUT "<li><a href=\"see.pl?ID=$ID\">$how_to</a>\n";
}

print "</ul>\n";


print qq |

$inc_foot
$inc_endpg

    |;


# Disconnect
$dbh->disconnect();

This script isn't all that different. Instead of submitting information, it retrieves it. It passes the titles of the how-tos to a bulleted list. This serves as a link to a script that will show us more detail.

Example 14. see.pl

#! /usr/bin/perl -w 

use strict;
use DBI;
use CGI;
use CGI qw(:standard :netscape :shortcuts);
use CGI::Carp qw(fatalsToBrowser);


# to include files

sub include { 
#--------------------------------------------------- 
# Pass in a file name to include. 
 
   my $file = shift or return; 
   my $path = '/home/mike/www/dev/perl/incluir/'; 
 
   open  INC, "$path/$file" or return $!; 
   read  INC, my $str, -s INC; 
   close INC; 
 
   return $str; 
}

## add basic password protection

open PWD, "/home/mike/perl-lib/dbinfo";
my ($user, $pass) = split(/:/, <PWD>, 2);
close PWD;
chomp($user, $pass);


## include files

my $MyWWW = "/dev/perl";
my $imagenes = "$MyWWW/graficos";

my $inc_dtype = include('doctype.inc'); 
my $inc_head = include('header.inc');
my $inc_intro = include('intro.inc');
my $inc_foot = include('footer.inc');
my $inc_endpg = include('endpg.inc');


# print HTML headers

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

# print page

print qq |

$inc_dtype 
<title>Linux Knowledge Base</title>
<body bgcolor="#ffffff">
$inc_head

|;
 
# Connect to the database.

my $dbh = DBI->connect('DBI:mysql:dbname=basecon', $user, $pass); 	       
my $query = new CGI;
my $ID = $query->param('ID');
my $how_to = $query->param('how_to');
my $answer = $query->param('answer');
my $rating = $query->param('rating');

my $sql = "SELECT * FROM questions WHERE ID = $ID;";
my $sth = $dbh->prepare($sql);
$sth->execute();
my $ref = $sth->fetchrow_hashref();
my $rank = $ref->{'rating'};

# this adds points to the database for ranking purposes

my $score = 1;
my $ranking = $rank + $score;
my $sql2 = "UPDATE questions SET rating='$ranking' WHERE ID='$ID';"; 
$dbh->do($sql2);

# show our data form

print qq |
<p><br>
<p><b>How to:  </b>$ref->{'how_to'}
<p><br>
<p><b>Answer:  </b><br><i>$ref->{'answer'}</i>
<p><br>
<p align="right"><a href="publist.pl">return to list of how-to</A>
<p><br>
$inc_foot
$inc_endpg

    |;

This is a slight variation on our previous script. It shows us the HOW-TO question and the answer. The variation is a routine to add a point to the ranking every time we view it. This is a little add-on to get a ranking of how many times somebody looked at this particular entry.

What's left is a means to edit the entries. This script will do the job.

Example 15. edit.pl

#! /usr/bin/perl -w 

use strict;
use DBI;
use CGI;
use CGI qw(:standard :netscape :shortcuts);
use CGI::Carp qw(fatalsToBrowser);


# to include files

sub include { 
#--------------------------------------------------- 
# Pass in a file name to include. 
 
   my $file = shift or return; 
   my $path = '/home/mike/www/dev/perl/incluir/'; 
 
   open  INC, "$path/$file" or return $!; 
   read  INC, my $str, -s INC; 
   close INC; 
 
   return $str; 
}

## add basic password protection

open PWD, "/home/mike/perl-lib/dbinfo";
my ($user, $pass) = split(/:/, <PWD>, 2);
close PWD;
chomp($user, $pass);

## include files

my $MyWWW = "/dev/perl";
my $imagenes = "$MyWWW/graficos";

my $inc_dtype = include('doctype.inc'); 
my $inc_head = include('header.inc');
my $inc_pie = include('footer.inc');
my $inc_endpg = include('endpg.inc');


# print HTML headers

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

# print page

print qq |


$inc_dtype 
<title>Linux Knowledge Base</title>
<body bgcolor="#ffffff">
$inc_head
 
    |;

# Connect to the database.

my $dbh = DBI->connect('DBI:mysql:dbname=basecon', $user, $pass); 	       
my $query = new CGI;
my $ID = $query->param('ID');

my $sql = "SELECT * FROM questions WHERE ID = $ID;";
my $sth = $dbh->prepare("$sql");
$sth->execute();
my $ref = $sth->fetchrow_hashref();

# show our data form
print qq |

<form action="update.pl" method="POST">

    |;

# entry fields


print qq |

<h2 align="center">Edit HOW-TOs</h2>
<p align="center"><font color="#990000"><b><i>$ref->
{'how_to'}</i></b></font>
<p><br>
<input type="hidden" name="ID" size="2" value="$ref->{'ID'}">
<table align="center" border=0 cellpadding=4 cellspacing=2 width=70% 
bgcolor="#foe68c">
<tr><td bgcolor="#FFFACD" colspan="2" align="left" valign="top">
<font COLOR="#990000"><B>How-to: </B></font></td>
<td bgcolor="#FFFACD" colspan="2"><textarea name="how_to" cols="40" rows="4" wrap="virtual">$ref->{'how_to'}</textarea></td></tr>
<tr><td bgcolor="#FFFACD">
<font COLOR="#990000"><B>Difficulty Level: </B></font></td>
<td bgcolor="#FFFACD">
<select name="level">
<option value="$ref->{'level'}">$ref->{'level'}</option>
<option value="NA"> NA</option>
<option value="easy"> easy </option>
<option value="involved"> involved </option>
<option value="difficult"> difficult </option>
<option value="very difficult"> very difficult </option>
</select>
</td>
<td bgcolor="#FFFACD">
<font COLOR="#990000"><B>>Category: </B></font></td>
<td bgcolor="#FFFACD">
<input type="text" name="category" size="50" value="$ref->{'category'}">
</td></tr>
</td></tr>
<tr><td bgcolor="#fffacd" width=50%><font COLOR="#990000">
<B>Importance: </B></font></td>
<td bgcolor="#FFFACD" colspan="3" align="left" valign="top">
<select name="importance">
<option value="$ref->{'importance'}">$ref->{'importance'}</option>
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
</select>
<BR><font size="3">(We will use this - 1 = a lot, 2 = sometimes 3 = not much)</font>
</td>
<tr><td bgcolor="#FFFACD"><font color="#990000"><B>Keywords: </B></font></td>
<td bgcolor="#FFFACD" colspan="3"> 
<input type="text" name="keywords" size="70" value="$ref->{'keywords'}">
</td></tr>
<tr><td colspan=4 bgcolor="#FFFACD"><font color="#990000">
<B>Answer: </B></font><BR><textarea name="answer" cols=60 rows=20 wrap="virtual">$ref->{'answer'}</textarea><br>
</td></tr>
<tr><td colspan="2" align="right"><input type="submit" 
name="submit" value="OK"></td></tr>
</table>
</form>
<p><br>

$inc_pie
$inc_finpg

    |;

The above script will pass this information on to the next one which will incorporate or changes into the database.

Example 16. update.pl

#! /usr/bin/perl -w 

use strict;
use DBI;
use CGI;
use CGI qw(:standard :netscape :shortcuts);
use CGI::Carp qw(fatalsToBrowser);

## add basic password protection

open PWD, "/home/mike/perl-lib/dbinfo";
my ($user, $pass) = split(/:/, <PWD>, 2);
close PWD;
chomp($user, $pass);


my $dbh = DBI->connect('DBI:mysql:dbname=basecon', $user, $pass); 
my $how_to = param ("how_to");
my $level = param ("level");
my $category = param ("category");
my $answer = param ("answer");
my $keywords = param ("keywords");
my $importance = param ("importance");
my $ID = param ("ID");
# we shouldn't need to touch the rating points
# my $rating = param ("rating");

# to include files

sub include { 
#--------------------------------------------------- 
# Pass in a file name to include. 
 
   my $file = shift or return; 
   my $path = '/home/mike/www/dev/perl/incluir/'; 
 
   open  INC, "$path/$file" or return $!; 
   read  INC, my $str, -s INC; 
   close INC; 
 
   return $str; 
}

## include files

my $MyWWW = "/dev/perl";
my $imagenes = "$MyWWW/graficos";

my $inc_dtype = include('doctype.inc'); 
my $inc_add = include('add.inc');
my $inc_head = include('header.inc');
my $inc_foot = include('foot.inc');
my $inc_endpg = include('endpg.inc');

## print HTML headers
print "Content-type: text/html\n\n";


print qq |

$inc_dtype 
<title>Linux Knowledge Base</title>
<body bgcolor="#ffffff">
$inc_cab
|;

if (($answer =~/Microsoft/i) || ($answer =~/Gates/i))

{

print qq |

<b>What are they doing in a Linux knowledge base???</b>
<p><br>
<p>Why don't you try to <a href="edit.pl?ID=$ID">edit this page again</a>

$inc_finpg

|;

}

    else {
 
my $sql = "UPDATE questions SET how_to='$how_to', level='$level', 
   category='$category', answer='$answer', keywords='$keywords', 
   importance='$importance' WHERE ID='$ID';";
   $dbh->do("$sql");

# Disconnect from the database
 
 $dbh->disconnect;

# print message when we've updated lessons page

print qq | 

<p><br>
<table border="0" cellpadding="3" width="98%" bgcolor="#990000" align="center">
<tr><td colspan=2 bgcolor="#fffacd" bgcolor="#ffffff">
<font color="#990000"><u>Question:</u><b>$how_to</b>
</font></td></tr>
<tr><td WIDTH="33%" bgcolor="#ffffff"><u></u>Difficulty Level:</td>
<td bgcolor="#fffacd"> <font color="#990000">$level</font></td>
</tr>
<tr><td WIDTH="33%" bgcolor="#ffffff"><u>Category</u>: </td>
<td bgcolor="#fffacd"><font color="#990000">$category</font></td>
</tr>
<tr><td WIDTH="33%" bgcolor="#ffffff"><u></u>Importance:<br>
<font SIZE="2">(We will use this - 1 = a lot, 2 = sometimes 3 = not much)
</font></td><td bgcolor="#fffacd"><font color="#990000"> 
$importance</font></td></tr>
<tr><td WIDTH="33%" bgcolor="#ffffff"><u></u>Keywords:<br>
</font></td><td bgcolor="#fffacd"><font color="#990000"> 
$keywords</font></td></tr>
<tr><td colspan=2 bgcolor="#ffffff"><u>Answer</u>:</td>
</tr>
<tr><td colspan=2 bgcolor="#fffacd">
<font color="#990000"> $answer</font></td></tr>
</table>
<p><br>
<p>If you see things you want to change
<a href="edit.pl?ID=$ID">use this form</a>
<p>Or <a href="add.pl">add another</a>
<p><br>

$inc_foot
$inc_endpg

|;

}

We could make the edit scripts more efficient by actually combining them all into one, but this modular approach is a good way of showing how the different things work. As with our PHP section, this should serve as a starting point for Perl web development. If you'd like to improve on these scripts, there's another homework assignment for you.



Comments: feedback (at) linux.org
Advertising: banners (at) linux.org
Copyright Linux Online Inc.
Compilation ©1994-2008 Linux Online, Inc.
All rights reserved.