home | O'Reilly's CD bookshelfs | FreeBSD | Linux | Cisco | Cisco Exam  

Writing Apache Modules with Perl and C
By:   Lincoln Stein and Doug MacEachern
Published:   O'Reilly & Associates, Inc.  - March 1999

Copyright 1999 by O'Reilly & Associates, Inc.


   Show Contents   Previous Page   Next Page

Appendix F - HTML::Embperl--Embedding Perl Code in HTML
Storing Persistent Data

While hidden fields are useful when working with forms, it's often necessary to store persistent data in a more general way. Embperl utilizes Apache::Session to do this job. Apache::Session is capable of storing persistent data in memory, in a text file, or in a database. More storage methods may be supported in the future. Although you can simply call Apache::Session from an Embperl page, Embperl can call it for you. All you need to do is to put user data in the hash %udat. The next time the same user requests any Embperl page, %udat will contain the same data. You can use this approach to keep state information for the user, and depending on your expire settings, you can also keep state between multiple sessions. A second hash, %mdat, can be used to keep state for one page for multiple users. A simple example would be a page hit counter:

The page is requested [+ $mdat{counter}++ +] times
since [+ $mdat{date} ||= localtime +]

This example counts the page hits and shows the date when the page is first requested. (See the hangman game at the end of this appendix for more examples of %udat and %mdat.) You don't need to worry about performance--as long as you don't touch %udat or %mdat, no action is taken.

Modularization of Embperl Pages

   Show Contents   Go to Top   Previous Page   Next Page

If you are working on a complete site and not just a few pages, there are always elements which occur in every page or in many pages. Instead of copying the source code to every page, you can include Embperl modules in your pages, so you'll have to write the source only once. Such a module could be a header, a footer, a navigation bar, etc. Embperl is capable of not only including such partial pages but also passing arguments. Here is an example that tells the navigation bar which element to highlight:

[- @buttons = ('Index', 'Infos', 'Search') -]
[$if $buttons[$col] eq $param[0]$] <bold> [$endif$]
<a href="[+ $buttons[$col] +].html"> [+ $buttons[$col] +] </a>
[$if $buttons[$col] eq $param[0]$] </bold> [$endif$]

Now if you are on the "Infos" page, you can include the navigation bar as follows:

[- Execute ('navbar.html', 'Infos') -]

This will include the navigation bar, which is stored in the file navbar.html, and pass as its first parameter the string Infos. The navigation bar module itself uses a dynamic table to display one column, which contains the text and a link, for every item in the array @buttons. Also, the text that is equal to text passed as a first parameter is displayed in bold. There is also a long form of the Execute call, which allows you to control all aspects of executing the module.


   Show Contents   Go to Top   Previous Page   Next Page

Debugging of CGI scripts is always a difficult task because the execution is controlled by the web server and, for the most part, you can't use a debugger. Embperl helps you debug your Embperl pages by creating a detailed log file. The log file shows you what Embperl does as it processes your page. Depending on the debug flag settings, Embperl logs the following:

  • Source

  • Environment

  • Form data

  • Evals (source and result)

  • Table processing

  • Input tag processing

  • HTTP headers

To make debugging even easier, you can tell Embperl to display a link at the top of each page to your log file while you are debugging a page. If you follow the link, Embperl will show the portion of the log file that corresponds to that request. The log file lines are displayed in different colors to give you a better overview. With these links to the log file enabled, every error displayed in an error page is also a link to the corresponding position in the log file, so you can easily locate where things are going wrong.

Querying a Database

   Show Contents   Go to Top   Previous Page   Next Page

Often it's necessary to query a database when generating a dynamic page. We have already seen in our discussion of dynamic tables how this can be done using the DBI database interface. Since the tasks needed in a web page are often the same, there is a module called DBIx::Recordset, which simplifies commonly needed tasks:

[-*set = DBIx::Recordset -> Search ({%fdat,
                                   ('!DataSource'   => $DSN,
                                    '!Table' => $table,
                                    '$max'   => 5,)}) ; -]
  <td>[+ $set[$row]{id} +]</td>
  <td>[+ $set[$row]{name} +]</td>
[+ $set -> PrevNextForm ('Previous Records',
                        'Next Records',
                        \%fdat) +]

The Search() method in this example will take the values from %fdat and use them to build a SQL WHERE expression. This way, what you search for depends on what is posted to the document. For example, if you request the document with http://host/mydoc.html?id=5, the above example will display all database records where the field id contains the value 5. The result of the query can be accessed as an array (this does not mean that the whole array is actually fetched from the database). Alternatively, you can directly access the current record just by accessing the fields, as shown here:

set[5]{id}   access the field 'id' of the sixth found record
set{id}      access the field 'id' of the current record

While normal DBI lets you access your data by column numbers, DBIx::Recordset uses the field names. This makes your program easier to write, more verbose, and independent of database changes.

The PrevNextButtons function can be used to generate a button for showing the previous record or the next record. PrevNextButtons generates a small form and includes all necessary data as hidden fields. To get it to work, simply feed this data to the next Search request.

There are also methods for Insert, Update, and Delete. For example, if %fdat contains the data for the new record, the following code will insert a new record into the database:

[-*set = DBIx::Recordset -> Insert ({%fdat,
                                    ('!DataSource'   => $DSN,
                                     '!Table' => $table)}) ; -]

DBIx::Recordset can also tie a database table to a hash. You need to specify a primary key for the table, which is used as a key in the hash:

$set{5}{name}    access the name with the id=5
                (id is primary key)

There are more features of DBIx::Recordset, such as handling linked tables, which makes it useful even in pages that do not use Embperl.


   Show Contents   Go to Top   Previous Page   Next Page

Another topic of interest in web environments is security. When running under mod_perl, all Perl code shares the same interpreter. This means that every application can access data from every other application. Embperl maintains a separate namespace for every document, which prevents accidentally overwriting other applications' data but provides no real security. You can still access anything you like if you explicitly specify a package name.

Therefore, Embperl incorporates Safe.pm, which makes it impossible to access any packages other than your own. This can be used, for example, to calculate something in a Perl module and then pass the results to an Embperl document. If the Embperl document runs in a safe namespace, it can access the data it has received from the browser, but it can't access data outside itself. Therefore, you can safely let different people create the layouts for Embperl pages.

Safe.pm also permits the administrator to disable any set of Perl opcodes. This gives you the power to decide which Perl opcodes are permitted for use by the page creators.

   Show Contents   Go to Top   Previous Page   Next Page
Copyright 1999 by O'Reilly & Associates, Inc.