A Blog Less Ordinary

The blog of Dave Ingram

Modules for Apache and PHP

The number of projects I have in mind just keeps growing… I really need to get something together to organise them, and remember them! But here are two more to add to the list: an Apache module for dynamic configuration generation and a PHP framework inside an extension. Read on for a monster post with more information…

Apache module

The first thing I have in mind is to create an Apache 2.2 module that uses the APR (Apache Portable Runtime) to make configuration file templating easy.

On my server, there are a lot of very similar Apache configuration files. At the moment, they are semi-automatically generated by running a Perl script that reads the configuration from a database and then creates the output files as required. This is all well and good, but changing anything requires running the Perl script, regenerating all of the configuration files, then prodding Apache to reload its configuration. There has to be a better way, especially one that doesn’t require elevated permissions like that.

Enter my proposed solution: mod_sqltemplate. This module will add a few extra settings and (at least) two extra sections to the standard Apache configuration. The propsed features are:

SQLRepeat

The SQLRepeat section is probably going to be the most-used new directive. It provides templating functionality similar to mod_macro, although the template variables are taken from the column names of the query rather than being explicitly specified. The section will then be repeated for each row of the resultset, substitutions occurring as required.

Perhaps an example would help:

<SQLRepeat "SELECT id,hostname,domain FROM apache_hosts">
<VirtualHost *:80>
  DocumentRoot /var/www/${hostname}.${domain}
  ServerName ${hostname}.${domain}
  # ...
</VirtualHost>
</SQLRepeat>

For this example, I’ve avoided the configuration directives for clarity, assuming they are specified elsewhere. Supposing the database table contains simply:

ID hostname domain
1 www example.com
3 test example.com
12 yourhost somewhere.local

Then this will expand to be equivalent to:

<VirtualHost *:80>
  DocumentRoot /var/www/www.example.com
  ServerName www.example.com
  # ...
</VirtualHost>
<VirtualHost *:80>
  DocumentRoot /var/www/test.example.com
  ServerName test.example.com
  # ...
</VirtualHost>
<VirtualHost *:80>
  DocumentRoot /var/www/yourhost.somewhere.local
  ServerName yourhost.somewhere.local
  # ...
</VirtualHost>

And so on. These sections can be nested, so if you wanted to store a list of aliases as well:

ID hostid alias target
1 1 /icons /var/www/_share/icons
2 1 /example /var/www/_share/foo
5 12 /bar /var/www/_share/foo

You could use something like this:

<SQLRepeat "SELECT id,hostname,domain FROM apache_hosts">
<VirtualHost *:80>
  DocumentRoot /var/www/${hostname}.${domain}
  ServerName ${hostname}.${domain}
  # ...
  <SQLRepeat "SELECT alias,target FROM apache_aliases WHERE hostid=?" ${id}>
    Alias "${alias}" "${target}"
  </SQLRepeat>
</VirtualHost>
</SQLRepeat>

which would then expand to:

<VirtualHost *:80>
  DocumentRoot /var/www/www.example.com
  ServerName www.example.com
  # ...
    Alias "/icons" "/var/www/_share/icons"
    Alias "/example" "/var/www/_share/foo"
</VirtualHost>
<VirtualHost *:80>
  DocumentRoot /var/www/test.example.com
  ServerName test.example.com
  # ...
</VirtualHost>
<VirtualHost *:80>
  DocumentRoot /var/www/yourhost.somewhere.local
  ServerName yourhost.somewhere.local
  # ...
    Alias "/bar" "/var/www/_share/foo"
</VirtualHost>

So far, so good. Something to note is that the variable names above did not conflict. If we had included “id” in the list of columns for the inner SQLRepeat, then included ${id}, the module should use the innermost scope. Disambiguation should be obtained by table alias prefixing (e.g. ${apache_hosts.id} or ${apache_aliases.id}). Full variable names should always be specified for clarity. This also shows that if the query returns no results, the repeating section is not included, as you would expect. Also note that queries that use variable substitution should use the prepared statement format, with variables specified afterwards. Variables will be included directly, so anything that might contain spaces must be quoted.

However, there are some directives like ServerAlias that must be specified once with a list of space-separated values. Enter SQLCatSet

SQLCatSet

This acts in a similar way to SQLRepeat, but it concatenates all of the results for each field before substituting, and it includes its content at most once. If there are no results for the query, then it will not include its content. For example, with a server aliases table like:

ID hostid alias
1 3 test2.example.com
2 3 test.example.net
5 12 *.somewhere.local

You could use something like this:

<SQLRepeat "SELECT id,hostname,domain FROM apache_hosts">
<VirtualHost *:80>
  DocumentRoot /var/www/${hostname}.${domain}
  ServerName ${hostname}.${domain}
  <SQLCatSet " " "SELECT alias FROM apache_server_aliases WHERE hostid=?" ${id}>
    ServerAlias ${alias}
  </SQLCatSet>
  # ...
  <SQLRepeat "SELECT alias,target FROM apache_aliases WHERE hostid=?" ${id}>
    Alias "${alias}" "${target}"
  </SQLRepeat>
</VirtualHost>
</SQLRepeat>

which would then expand to:

<VirtualHost *:80>
  DocumentRoot /var/www/www.example.com
  ServerName www.example.com
  # ...
    Alias "/icons" "/var/www/_share/icons"
    Alias "/example" "/var/www/_share/foo"
</VirtualHost>
<VirtualHost *:80>
  DocumentRoot /var/www/test.example.com
  ServerName test.example.com
    ServerAlias test2.example.com test.example.net
  # ...
</VirtualHost>
<VirtualHost *:80>
  DocumentRoot /var/www/yourhost.somewhere.local
  ServerName yourhost.somewhere.local
    ServerAlias *.somewhere.local
  # ...
    Alias "/bar" "/var/www/_share/foo"
</VirtualHost>

This would seem to cover the major functionality needed for automatically generating configurations in a really flexible way, although I’m sure more will probably be needed at some point… like SQLIf, perhaps.

PHP Framework

It occurs to me that having a framework’s code in PHP itself could mean that it is quite slow. It would be interesting to put together a framework in C and embedded as a module, to see if there are any memory/speed improvements possible. It might just be a case of implementing an ORM (Object Relational Model) system to begin with. Obviously, this idea is nowhere near as fully-formed!

One Response to Modules for Apache and PHP

David Durant says: August 13, 2008 at 22:24

The configuration template module looks so extremely useful I would be surprised if such a project doesn’t already exist. I’d have a good dig around to look for similar things before starting anything new.

Leave a Reply

Your email address will not be published. Required fields are marked *

*

*

 

GitHub Google+ Twitter