Java 2 Ada

Boost your php web site by installing eAccelerator

By stephane.carrez 1 comments

This article explains how to boost the performance of a PHP site by installing a PHP accelerator software.

Why is PHP slow

PHP is an interpreted language that requires to parse the PHP files for each request received by the server. With a compiled language such as Java or Ada, this long and error prone process is done beforehand. Even if the PHP interpretor is optimized, this parsing step can be long. The situation is worse when you use a framework (Symfony, CakePHP,...) that requires many PHP files to be scanned.

eAccelerator to the rescue

eAccelerator is a module that reduces this performance issue by introducing a shared cache for the PHP pre-compiled files. The module somehow compiles the PHP files in some internal compiled state and makes this available to the apache2 processes through a shared memory segment.

Installing eAccelerator

First get eAccelerator sources at http://eaccelerator.net/

Then extract the tar.bz2 file on your server:

$ tar xvjf eaccelerator-0.9.6.1.tar.bz2
eaccelerator-0.9.6.1/
eaccelerator-0.9.6.1/COPYING
...

Build eAccelerator module

Before building the module you must first run the phpize command to prepare the module before compilation:

$ cd eaccelerator-0.9.6.1/
$ phpize

Then, launch the configure script:

$ ./configure --enable-eaccelerator=shared \
    --with-php-config=/usr/bin/php-config

Finally build the module:

$ make

Install eAccelerator

Installation is done by the next steps:

$ sudo make install

Don't forget to copy the configuration file (have a look at its content but in most cases it works as is):

$ sudo cp eaccelerator.ini  /etc/php5/conf.d/

Restart Apache server

To make the module available, you have to restart the Apache server:

$ sudo /etc/init.d/apache2 restart

Performance improvements

What performance gain can you expect... That will depend on the PHP software and the page. It's easy to have an idea.

To measure the performance improvement, you can use the Apache benchmarking tool. Do a performance measurement on the web site before the installation and another one after. Be sure to benchmark the same page.

The following command will benchmark the http://mysite.mydomain.com/index.php page 100 times with only one connection.

$ ab -n 100 http://mysite.mydomain.com/index.php

Below is an extract of the percentage of the requests served within a certain time (ms) for one of my web page served by Dotclear:

         Without        with
        eAccelerator  eAccelerator
 50%       383           236
 66%       384           237
 75%       387           238
 80%       388           239
 90%       393           258
 95%       425           265
 98%       536           295
 99%       796           307
100%       796           307 (longest request)

The gain varies from 38% to 60% so it is quite interesting. The other benefit is that the variance is also smaller meaning that requests are served globally in the same time.

1 comments
To add a comment, you must be connected. Login to add a comment

Fixing the setError and hasError migration issue in Symfony 1.2

By stephane.carrez 1 comments

Foreword on error management in the controller and the view

The controller verifies the form parameters and it can check some error message. For this, Symfony 1.0 was providing the setError method that you use as follows:

$this->getRequest()->setError('username',
  "Sorry, usernames may contain only letters... and dashes.");

Then, in the view template file, you would use the form_error helper to check and report the error message.

<div class="username">
  <label for="username">User name: </label>
  <div class="content">
  <?php echo form_error('username') ?>
  <?php echo input_tag('username') ?>
</div>

The goal is to keep the controller and the view as simple as possible by using the same mechanisms. Here is how in two steps.

Step 1: Provide your own sfWebRequest

First, we override the sfWebRequest to implement the missing operations and we use the setAttribute method to associate the message with the error field. To avoid conflicts with other attributes, the error fields are prefixed with error_.

class myRequest extends sfWebRequest {
    public function hasError($name) {
        return $this->getAttribute("error_" . $name) != null;
    }
    public function setError($name, $msg) {
        $this->setAttribute("error_" . $name, $msg);
    }
    public function getError($name) {
        return $this->getAttribute("error_" . $name);
    }
}

Step 2: Update factories.yml

Now we have to tell Symfony to use our own request class:

all:
  request:
    class: myRequest

Then, clear the symfony cache by running the command:

$ symfony cc

Conclusion

The fix is really simple and if you find other methods that you use and have been removed (hasErrors, getErrors, ...) you just have to add them to you request class!

1 comments
To add a comment, you must be connected. Login to add a comment

Symfony cryptic error: Error importing plugins/sfPropelPlugin/lib/propel/builder/SfPeerBuilder.php

By stephane.carrez 3 comments

After the migration to Symfony 1.2, when I wanted to use the command symfony propel:build-model, I was getting the following cryptic error:

>> propel    Running "om" phing task
[phingcall] Error importing plugins/sfPropelPlugin/lib/propel/builder/SfPeerBuilder.php
Execution of target "om" failed for the following reason: /usr/share/php/symfony/plugins/sfPropelPlugin/lib/vendor/propel-generator/build- propel.xml:465:22: Execution of the target buildfile failed. Aborting.
   [phing] /usr/share/php/symfony/plugins/sfPropelPlugin/lib/vendor/propel-generator/build-propel.xml:465:22: Execution of the target  buildfile failed. Aborting.
   
 Some problems occurred when executing the task:
   build-propel.xml:465:22: Execution of the target buildfile failed. Aborting.
   Read the logs to fix them

After searching for some time, I've found that this error comes from a bad definition in my 'config/propel.ini' file. The propel.ini file contained definitions like:

propel.builder.peer.class  = \
  plugins.sfPropelPlugin.lib.propel.builder.SfPeerBuilder
propel.builder.object.class = \
  plugins.sfPropelPlugin.lib.propel.builder.SfObjectBuilder

And they should be defined as follows:

propel.builder.peer.class    = \
  plugins.sfPropelPlugin.lib.builder.SfPeerBuilder
propel.builder.object.class = \
  plugins.sfPropelPlugin.lib.builder.SfObjectBuilder

Once the propel configuration was fixed, the symfony command was working again.

If you have this kind of problem, I suggest the following:

  • Create an empty blank application with Symfony (symfony init-app test)
  • Make a diff of the 'config' directory between your application and the blank application
  • Report any suspicious change to fix your configuration
3 comments
To add a comment, you must be connected. Login to add a comment

Upgrading Symfony projects to release 1.2

By stephane.carrez 3 comments

I had two projects to upgrade on my desktop running Ubuntu 8. The upgrade process is described in Upgrading Projects from 1.1 to 1.2 but even if you follow it, this is really not easy.

Symfony Installation

First, I had to remove a previous symfony installation:

 sudo pear uninstall symfony/symfony

For some reasons, I had still the Propel plugin installed, so I've removed what remains with the command;

 $ sudo rm -rf /usr/share/php/symfony         

Then, I've installed the new Symfony version with pear:

 $ sudo pear install symfony/symfony-1.2.3    

(The Symfony installation is described in Installation 1.2).

Upgrade to 1.2

After Symfony installation, I've started to migrate my applications to the new version. You have to run these commands in each project:

 $ php symfony project:upgrade1.2
 $ php symfony propel:build-model

The prope:build-forms command failed because I had to remove old plugins and update the sfGuardPlugin:

 $ symfony plugin:install sfGuardPlugin
 $ php symfony propel:build-forms
 $ php symfony propel:build-filters

Then clear Symfony cache (the sudo was necessary because some files are owned by www-data user)

 $ sudo php symfony cc

Problems and fixes

Now come the issues!!!. You have to check you application and fix each problem. Unlike Ada or Java, there is no compilation check so every change of API is only detected when you execute the code.

After upgrading, I got this error:

 Fatal error: Call to undefined method DebugPDOStatement::setInt()
  in /home/.../lib/model /AssertViewerPager.php on line 35

This is due to the upgrade of Propel from 1.2 to 1.3. Propel is now using PHP Data Objects, which is a very good thing. The following must be changed:

 $statement = $con->prepareStatement("SELECT ID, VALUE 
                                       FROM Dictionary WHERE KEY = ?");
 $statement->setString(1, $value);
 $rs = $statement->executeQuery();
 while($rs->next()) {
   print "ID: " . $rs->getString("ID") . "  VALUE: " 
           . $rs->getString("VALUE") . "\n";
 }

into

 $statement = $con->prepare("SELECT ID, VALUE 
                                        FROM Dictionary WHERE KEY = ?");
 $statement->bindValue(1, $value);
 $statement->execute();
 while ($row = $statement->fetch()) {
   print "ID: " . $row['ID'] . "  VALUE: " . $row['VALUE'] . "\n";
 }

After fixting the database issues, I found another cryptic error:

 Catchable fatal error: Argument 1 passed to
   sfPatternRouting::configureRoute()
   must be an instance of sfRoute, string given,
  called in /usr/share/php/symfony/routing/
   sfPatternRouting.class.php on line 245 
 and defined in /usr/share/php/symfony/routing 
  /sfPatternRouting.class.php on line 256

It was caused by the sfGuardPlugin which was not updated correctly. I had to remove it and install it from scratch.

Another error caused by the migration:

 Fatal error: Call to undefined method 
   BasePeer::getmapbuilder() 
  in /home/.../lib/model /om/
  BaseLogEmitterPeer.php on line 66

To solve this, I had to remove the 'om' directory and rebuild the model with:

 symfony propel:build-model

There could be other issues that I've not reported... In any case, making sure that everything works after the upgrade is painful.

3 comments
To add a comment, you must be connected. Login to add a comment
  • Page 1