Setting Up Lighttpd with PHP-FPM

PHP-FPM can provide an alternative to spawn-fcgi when setting up Lighttpd with PHP. It has several advantages over using spawn-fcgi, among them:

  • It can dynamically scale and spawn new processes as needed.
  • It can gracefully respawn PHP processes after a configuration change.
  • It comes with an init.d script, so there is no need to write your own.
  • It can log slow PHP script execution (similar to MySQL’s slow query log).

Installation

PHP-FPM is available from the Ubuntu (since 12.04) and Debian repositories, so all you need to do is:

$ sudo apt-get install php5-fpm

Configuration

PHP-FPM works with process pools. Each pool spawns processes independently and has different configurations. This can be useful to separate the PHP processes of each user or major site on the server. PHP-FPM comes with a default pool configuration in /etc/php5/fpm/pool.d/www.conf. To create new pools, simply copy the default pool configuration and edit it. At a minimum, you will need to set the following:

  • Pool name – [www]. I name mine according to the user the pool serves.
  • user – I set the user to the appropriate user, and leave group as www-data.
  • listen = /var/run/php.$pool.sock – Unix sockets have lower overhead than TCP sockets, so if both Lighttpd and PHP run on the same server, they are preferable. $pool will be expanded to your pool name. Also, it is more secure to create the sockets in a directory that is not globally writable (such as /tmp/), so /var/run is a good choice.
  • listen.owner should match the PHP user, while listen.group should match the group Lighttpd runs in, so both have access to the socket.

If you copied www.conf to create a new configuration, you will need to rename it to something like www.conf.default in order to disable it.

In the Lighttpd configuration, you need to add the following to each vhost that uses PHP:

fastcgi.server    = ( ".php" => 
        ((
                "disable-time" => 0,
                "socket" => "/var/run/php.pool.sock",
        ))
)

Here, pool in the socket configuration is replaced by the matching pool name in the PHP-FPM configuration. Overriding disable-time and setting it to 0 is suitable in the case where you have only one PHP backend and it’s local. In this scenario, attempting to connect to the backend is cheap, and if it gets disabled, no requests will get through anyway.

Useful Files

  • /etc/php5/fpm/pool.d – The PHP-FPM pool configuration directory.
  • /var/log/php5-fpm.log – The PHP-FPM error log. It will display errors and warnings notifying you when pm.max_children has been reached, when processes die unexpectedly, etc.

Configuring Lighttpd for CakePHP

I tried a few days ago to install a CakePHP project of mine on a lighttpd web server. As expected, its clean URLs didn’t work, so I set out to find a solution.

One possible solution is the one outlined in the CakePHP manual. The solution uses the mod_magnet module (which basically runs a Lua script to do the rewriting), and I found it to be overkill. I was looking for a simple solution based only on mod_rewrite, something like the solution for WordPress.
Continue reading Configuring Lighttpd for CakePHP

Question Marks Instead of Non-ASCII Chars When Using Gettext in PHP

Yesterday I’ve ported a PHP website to use Gettext for localization (l10n). After reading through the Gettext documentation and going through the documentation on the PHP site, I’ve managed to get everything working (almost). I had one problem: all the non-ASCII characters (accented Latin chars, Japanese, and Chinese) were displayed as question marks (?) instead of in the correct form. This happened despite my using UTF-8 encoded files.

While some people (e.g. this one) suggested that it’s not possible to use non-ASCII characters when using UTF-8 encoded message files, there is a solution, and it’s quite simple. All you have to do is call bind_textdomain_codeset and pass it UTF-8 as charset.

Trailing Whitespace Causes a Session to Be Destroyed in CakePHP

While working on a new project using CakePHP, I’ve come across a weird problem. In one of the controllers, the session always came out as invalid, as

$this->Session->valid()

always returned false. I tried debugging this weird stuff, and it looked like all the session variables were unset. Using

$this->Session->error()

to get the last error returned “Config doesn’t exist”. After further debugging, “Config” turned out to be an internal array that is saved by CakePHP to the session and holds various internal data (some of it is used for validation, like a user-agent hash). I kept printing more and more debugging data, as well as looking at CakePHP’s trac system.

I found an interesting bug (ticket #4217), and it looked very promising, as it almost fully described my problem. Unfortunately, the solution offered didn’t seem to work for me. But it inspired me to try starting the session manually using session_start() instead of using Cake’s startup and activate methods of the Session Component.

I found out that session_id() returned an empty string. Luckily, calling session_start() directly from the controller gave me a lead. The session seemed to work well, but a nasty error about headers already being sent showed up.

A little more investigation led me to realize that I had a trailing newline after my closing PHP tag in that controller file. Deleting this trailing whitespace completely solved the problem. There was no need anymore to manually start the session. It’s pretty annoying that such a small thing as a trailing newline can cause such seemingly unrelated problems in CakePHP’s session handling.

Maybe CakePHP should add a little debug notice when the session doesn’t start because headers were already sent. This can be done by modifying the else statement in the __startSession() method in cake/lib/session.php (line 557 in version 1.2.0.6311). I wonder what the reason was for not informing the developer when such an event happens, as I don’t see why someone would deliberately try to start the session after sending the headers. I think it only happens by mistake, at least most of the time.

Vim Macros for Wrapping Strings for Gettext

I’m working on a website, and we decided to localize it using GNU gettext. Soon enough, I found it tiring to wrap each string manually in _( and ), and also to do it in Smarty (using {t}string{/t}). So I decided that I needed a macro that would let me highlight the string that needs translation, and the macro would wrap it for me.

I ended up writing two macros: one for PHP files (but it’s also good for C/C++, etc.) and one for Smarty.

:vmap tg di_(<ESC>pa)<ESC>
:vmap ts di{t}<ESC>pa{/t}<ESC>

To use these macros, just highlight the string for translation in Vim’s visual mode and press tg (or ts), and your string will be wrapped for translation.

Multibyte String Truncate Modifier for Smarty – mb_truncate

When working with Smarty, a PHP templating engine, I discovered that while the regular truncate modifier works great on ASCII strings, it doesn’t work with multibyte strings, i.e., UTF-8-encoded strings. This leads to problems in internationalization (i18n), as UTF-8 is the popular encoding for non-Latin alphabets nowadays. The problem can be solved by modifying the built-in truncate modifier and creating a new one that takes an additional argument, the charset of the string, and acts accordingly. The new modifier, mb_truncate, is implemented below.
Continue reading Multibyte String Truncate Modifier for Smarty – mb_truncate