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:
- SQLTemplate_DBDriver �driver� � sets the APR driver to be used
- SQLTemplate_DBOpts �option-string� � options for the driver (e.g. database name, username, password…)
- SQLTemplate_Caching �on|off� � enable/disable configuration caching
- SQLTemplate_CacheDir �path� � directory in which to save cached configurations
- <SQLRepeat �query�> � new section, as described below
- <SQLCatSet �sep� �query�> � new section, as described below
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!
Leave a Reply