2.3 Static and Dynamic

First, let's point out some often misunderstood concepts. Everybody speaks about static pages and dynamic ones. However, the real point is: static contents or dynamic contents?

2.3.1 Static and dynamic contents

Static contents are inserted by the site administrator and don't need frequent changes. You want to share a given amount of information, and publish them. Every user will see the same information, and the same user will see the same information upon subsequent visits (unless site administrator changes them in the meantime of course). As an example, all contents on G-Cows site are static.

Dynamic contents changes on every request. As an example, a web based mail application will generate the message list every time you log in. This list must be created on the fly.

2.3.2 Static and dynamic pages

A static page is a file consisting in HTML tags sent by the web server when a browser send a request for it. Every user requesting that file will get the same page, and the same user requesting that file upon subsequent requests will get the same page. Page will change if and only if the site owner changes the file stored in the web server.

A dynamic page is a page consisting in HTML tags and small scripts. The web server doesn't send the page 'as is': it first looks for the scripts and executes them. These scripts can perform actions (e.g. send an e-mail, access a database server etc.) and/or display some output. The web server will send the user a page consisting in HTML tags and the scripts' output.

It's easy to see that dynamic contents need dynamic pages. Static contents instead can be generated with static pages or dynamic ones.

2.3.3 Hybrid approaches

There are also hybrid approaches: Client-side scripting and HTML preprocessing.

A Client-side script is a script embedded in the HTML page that will be executed by user's browser, not by the web server. Pages with client-side scripts can be considered static, since there is no interpretation by the web server or dynamic, since part of the output is generated by a script. The most frequent choice for client-side scripting is JavaScript.

HTML preprocessing is a very interesting technique (G-Cows is based upon it, so I'm a little biased...). HTML files are processed by a software tool running on the machine used by site's author and then uploaded to a server. HTML is still generated from scripts but this happens when developing and updating the site. The server sends a static page.

2.3.4 Which One?

Suppose you are creating a web site and you want to display a common header on all pages. There are different ways to do this.

  1. You can simply type the common header in every page; your HTML files will begin like this:

    <h1>Header</h1>
    <p>This is a common header repeated on every page.</p>
    ...
    
  2. You can insert a `client-side script': a script embedded in the HTML page that will be executed by user's browser. Using JavaScript the page could look like this:

    <script language="JavaScript" src="/header.js"></script>
    

    Here, header.js must be a JavaScript program printing the common header.

    Notice that now, if you want to modify your header, you don't need to update every page, but only the file header.js.

  3. You can insert a `server-side script': a script embedded in the HTML page that will be executed on the web server before it sends the page to the user. Using PHP the page will look like this:

    <?php include ('header.php'); ?>
    

    Again, header.php must be a PHP script printing the common header.

In my opinion, none of these approaches is acceptable. `Client-side' scripting languages are affected by different implementations on different browsers (and different versions of the same browser). Moreover, many browsers don't support these languages; this is especially true for browsers used by visually impaired users.

When possible, I try to use `client-side' scripting only for non-critical tasks like swapping of an image when mouse pointer is moved over it: if the script fails user will only lose a visual effect, not page's content.

`Server-side' scripting languages result in a higher server load respect to HTML pages since the server has to parse the whole file and execute the code before sending it to the client. They should only be used when page content is dynamic: whenever the content depends from user's navigation, you must create it on the server just before sending the page. As an example, if we decided to add a random quote on the page, the page should be generated by the server `on the fly'. But this is not our case: we want to include a file; the content of our page is fixed and it's useless to generate it again and again each time the page is requested.

So, the first choice seems to be the only acceptable one: we should include a verbatim copy of the common header in every file. Of course, this doesn't mean we can't find a smooth way to generate these pages, as long as we generate the pages while creating the site, not every time the page is requested. That's where G-Cows and HTML preprocessing come in help: you will simply write an HTML page with an embedded script and run Cows; Cows will execute the script and generate a standard HTML file. Now you can place the file on the server so you get the advantages of the first approach (a standard HTML file is delivered quickly with small server load) and the advantages of the other two approaches (you don't need to `copy and paste' the same header in every file).

This is a trivial example but the same principle applies to file sizes, dates of last modify, creation of complex menus and navigation bars. The idea behind this is that server-side technology should only be used to add dynamic features. Nowadays, many sites rely on it also to make site administration easier - even when contents are static - resulting in useless server overwork and losing a lot of advantages related to static pages (see Section 2.3.6).

2.3.5 Combining

Many people speak about static versus dynamic. This is a non-argument since both techniques can be easily combined to get the best from the two worlds.

We've already said that dynamic contents need dynamic pages, while static contents can be generated with static pages or dynamic ones. Of course you can also mix both approaches inserting, as an example, both Cows and PHP scripts. Then you run cows on the machine you're using to create the site: Cows scripts will be replaced by their output while PHP scripts will be copied verbatim. Now, you place the resulting files on the server and PHP scripts will be executed on each request, wile the Cows scripts have already been resolved and are out of play.

Let's consider three different scenarios.

Sites with completely static contents.

These sites should be made, in my opinion with completely static pages.

Sites with at least one dynamic information in each page.

Of course, every page will need to be dynamic. You may consider to use an HTML preprocessor for layout elements, navigation bars, history stacks etc. and use a server side scripting language for dynamic parts. This can be a good idea or not; some of the benefits of static sites are lost due to the fact that no page is completely static (i.e. a plain HTML file), especially if the dynamic parts are quite complex (database connections etc.).

Sites with both static and dynamic contents.

Many sites consist in both static and dynamic content. In these cases, if you combine both approaches, your site will consist in dynamic pages and static ones (in the previous scenery, all pages were dynamic, even if some parts (e.g. navigation bars) were static. The mixed approach is, in my opinion, the best choice.

2.3.6 Advantages of static pages

2.3.6.1 CPU and Bandwidth consumption

One of the most (ab)used arguments against HTML preprocessing is that nowadays, even the less expensive personal computer has so much RAM and CPU to easily serve sites consisting in completely dynamic pages: band saturation will probably occur long before the lack of other resources. This is a very vague argument: I created a site consisting in a lot of articles saved into plain text files and divided into directories according to the given topic. Navigation bars display the number of articles belonging to a given topic so with a completely dynamic site every page served would need to look into many directories to count files stored within. We could of course find a smoother way to retrieve these information but this could be impractical. Surely, it would be harder than using an HTML preprocessor.

Moreover dynamic pages are neither cached by users' browsers nor by proxy servers. This is necessary since dynamic pages are meant to store dynamic contents, whose validity is by definition limited to the moment they are sent to you. So, dynamic generation of static contents also has a negative drawback on your band consumption (or, given a fixed or saturated bandwidth, on load time of your pages).

2.3.6.2 No need for a web server

Dynamic pages need a web server to be viewed. Static pages can be seen with a simple browser, which is installed on nearly every personal computer.

If your site is only made of static pages, you can create it without installing a web server and a scripting language extension module or an application server and, if you're part of a team, you can easily allow other people to see your work by sending them a bunch of HTML files, without asking them to install a web server and other tools.

If your site is only made of static pages, you can distribute your site on a CD-ROM or allow users to download a compressed archive and browse it later. As an example, if you create a site for a program you can include a copy in the package so users will be able to see it off-line.

Sites created by volunteers (e.g. sites for charities or no-profit projects) often need contributors. Some people can be tempted to offer their help for editing and testing phases, but some of them would be probably stopped if asked to install and configure a rather complex software tool like a web server (and possibly an application server).

2.3.6.3 Separation between content and layout.

Quoting a discussion on the Gimp-web mailing list about using static or dynamic pages for their site:

... there should be a clear separation between the contents (body of the page) and the templates or layout stuff (headers, footers, menus and other decorations), so we need some kind of build system that generates the static pages by merging the contents with the layout. One could also do that at run-time (when the page is fetched) by using SSI, but this is much less efficient: a page is edited at most once per day, but is requested several thousand times. It is better to rebuild the page once when it is updated, than doing the same work several thousand times when it is requested (in addition to the advantages of making tests easier, as mentioned yesterday).

2.3.6.4 Unix tools

Many tools for HTML preprocessing (G-Cows among them) allow to use all your favorite Unix tools while creating a site: you can traverse the whole site with `find', extract information with `grep', build complex pipelines and so on...

You can also include external scripts and programs written in every language whose interpreter or compiler is installed on your machine. Some tasks can be very easy to accomplish with a small shell script; other may require more complex languages.

2.3.6.5 Paranoid checks

You can perform strict checks on unexpected situations; if scripts are executed at request time, you must browse the whole site to see if checks raise errors. On the contrary, if you use a static scripting language in conjunction with an automatic build system (e.g. Make), as you add a check all pages interested by it will be rebuild and errors (if any) will be shown.

Of course, before releasing a site, you'll browse every page to make sure everything is OK, but it's very useful to catch errors as soon as possible, without building other work upon them.

2.3.6.6 More automatization

Despite of the amount of RAM or CPU of your server, dynamic scripting will always limit the number of elements generated programmatically. On the contrary, HTML preprocessing allows to automatize everything you want, without worrying about loss of performances.

As an example, I always create CSS files with G-Cows by defining some variables storing color codes (e.g. light_blue="#50648c") at the beginning of the file and using them everywhere. So, it's very easy to replace a color. I've also created a site where every section has a different color scheme; this is easily achieved by defining a single template for CSS files and `cloning' it with different colors for each section. With dynamic scripting I wouldn't find this approach acceptable since every page requested would lead to the generation of more dynamic pages: the page itself, and the CSS files.

I also tend to insert image dimensions via small scripts so, when I change an image, I don't need to manually update these values. Of course I would never do it with server side technology since the small advantages wouldn't pay the server overload due to reading dimensions from image files.

2.3.6.7 Freedom in programming

Despite of the amount of RAM or CPU of your server, dynamic scripting will always influence your coding style. As an example, suppose to define a data structure representing your site's structure and to extract some information from it: navigation bars, an history stack, and so on. If your scripts are executed once for each request, You'll probably try to build these elements in an efficient way, probably by traversing the whole structure only once and building navigation bars, history stack etc..

On the contrary, if your scripts are executed during the development cycle, you'll be free to choose the more readable coding style. As an example, you may split these tasks into multiple files, each one traversing the whole structure to extract a single information.

2.3.6.8 Security

Sometimes you may need two pages with a slightly different behavior. As an example, common users may be able to perform a given search on a database and site's administrators can be allowed to search and edit these information.

So, you need two almost equals pages, and if the search mask is not trivial, you'll probably want to write a single page, belonging differently according to the situation (common user or administrator).

Of course, these pages will be dynamic, since data are loaded from a database and results are generated on the fly. So you can use a completely dynamic page, or a mixed approach: navigation bars can be generated with G-Cows or another HTML preprocessor, leaving only the database lookup code dynamic. But most of all, the conditional statement telling if the data can be edited or not, should be static. You can create two pages including the same common file, passing a parameter reflecting the editing capability. So you end up with two single pages, one allowing editing the other allowing read-only queries. Once you check these pages and upload them to the server, you can be sure that nothing will go wrong. If the parameter reflecting the editing capability is evaluated dynamically, on each request, you must be sure that your code is strong enough to face every situation: your page will be requested thousand times with different queries; are you sure that your code won't allow editing when it shouldn't?

I sleep better knowing that the edit/not edit issue has been resolved statically and two different pages are on my server: one in a public area and the other in a restricted area reserved to administrators.

2.3.6.9 Different configurations

If you develop a web site on a machine and then upload it on a production server, you'll probably need different configurations. As an example, parameters for database connection can be different (at least passwords should be).

I define these variables in two conditional blocks: according to the value of a given variable, a set of variables (suitable for development and/or testing) or the other (suitable for production) is defined.

2.3.6.10 Everything is one command away.

G-Cows allow to add rules to the automatically created Makefile, so you can provide rules for uploading your site on a remote ftp server via sitecopy, or synchronizing your local tree on remote server with ssh and rsync. I like to be able to simply type make and have my site locally updated, typing make sync and having the remote version synchronized.

2.3.6.11 Programmatically created PHP.

Some times ago I needed a small PHP library to handle complex forms. My idea was to call a single PHP function passing form's specifications (e.g. An input text accepting up to 20 characters, an optional text area accepting up to 1000 characters, a menu taking its values from a database with a given query etc.)

The library would need to display the fields when called the first time, perform all the checks when form is submitted, redisplay the form with user's values and errors highlighted, or perform a given action (e.g. write user's data in a database). The library should also allow to edit records already in the database.

As you can easily see, this is not a trivial task, since there are many input types belonging in different ways (checkboxes, radio buttons, text fields). Values can be selected from a database or hard-coded in your page (a menu with two items Yes/No will be probably hard-coded while a list of messages will be probably read from a database). Moreover, there are different modes: display, edit values from a database, edit user's values after an error.

Two things scared me about this approach:

  • This library would be overkill for simple forms with a pair of fields, and no need for editing capabilities (I don't consider 'computers are so powerful nowadays' a valid argument for doing things inefficiently).

  • Since I give sources to my clients, everybody could have access to the whole library; once found a bug in the check subroutines all other clients would be exposed (note that this is not a problem in Open Source Software since many people can look at the code and fix security bugs but this library would be used by no more then 10 people).

So, I decided to write a G-Cows library which generates very simple PHP statements performing the needed checks and displaying the input fields. Let's' see the different behavior of the two approaches limiting our analysis to checks.

Using PHP only, upon each request, the library would loop over input fields, and according to the field type perform the given checks.

Using G-Cows and PHP, Cows loops over the fields (while developing the site, not upon each request) and create very simple PHP statements like

if (condition)
  error
else if (another condition)
  error
...

The resulting PHP code is very long and repetitive but it's impossible not to see a bug, and your underlying logic isn't exposed.

Clients with some programming background like this approach because they can understand the scripts and eventually modify it without dealing with the complexity of my library. Of course every change should be done on the Cows source, not at the PHP level.

This manual can be downloaded from http://www.g-cows.org/.