Java 2 Ada - Tag php2010-10-23T06:29:00+00:00Stephane Carrezurn:md5:d12e23c53b2436d6becce3d51ddbdf38AWABoost your php web site by installing eAcceleratorurn:md5:ea38d34e376bcb890aac4bf100ff2fdc2010-10-23T06:29:00+00:002010-10-23T06:29:00+00:00Stephane CarrezApacheperformancephpsymfony
<div class="post-text"><p>This article explains how to boost the performance of a PHP site by installing a PHP accelerator software.</p><h2>Why is PHP slow</h2><p>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.</p><h2>eAccelerator to the rescue</h2><p><code>eAccelerator</code> 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 <code>apache2</code> processes through a shared memory segment.</p><h2>Installing eAccelerator</h2><p>First get eAccelerator sources at <a href="http://eaccelerator.net/">http://eaccelerator.net/</a></p><p>Then extract the <code>tar.bz2</code> file on your server:</p><pre><code>$ tar xvjf eaccelerator-0.9.6.1.tar.bz2
eaccelerator-0.9.6.1/
eaccelerator-0.9.6.1/COPYING
...
</code></pre><h2>Build eAccelerator module</h2><p>Before building the module you must first run the <code>phpize</code> command to prepare the module before compilation:</p><pre><code>$ cd eaccelerator-0.9.6.1/
$ phpize
</code></pre><p>Then, launch the configure script:</p><pre><code>$ ./configure --enable-eaccelerator=shared \
--with-php-config=/usr/bin/php-config
</code></pre><p>Finally build the module:</p><pre><code>$ make
</code></pre><h2>Install eAccelerator</h2><p>Installation is done by the next steps:</p><pre><code>$ sudo make install
</code></pre><p>Don't forget to copy the configuration file (have a look at its content but in most cases it works as is):</p><pre><code>$ sudo cp eaccelerator.ini /etc/php5/conf.d/
</code></pre><h2>Restart Apache server</h2><p>To make the module available, you have to restart the Apache server:</p><pre><code>$ sudo /etc/init.d/apache2 restart
</code></pre><h2>Performance improvements</h2><p>What performance gain can you expect... That will depend on the PHP software and the page. It's easy to have an idea.</p><p>To measure the performance improvement, you can use the <a href="http://httpd.apache.org/docs/2.2/programs/ab.html">Apache benchmarking tool</a>. Do a performance measurement on the web site before the installation and another one after. Be sure to benchmark the same page.</p><p>The following command will benchmark the <a href="http://mysite.mydomain.com/index.php">http://mysite.mydomain.com/index.php</a> page 100 times with only one connection.</p><pre><code>$ ab -n 100 http://mysite.mydomain.com/index.php
</code></pre><p>Below is an extract of the percentage of the requests served within a certain time (ms) for one of my web page served by <a href="http://dotclear.org/">Dotclear</a>:</p><pre><code> 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)
</code></pre><p>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.</p></div> Upgrading Symfony projects to release 1.2urn:md5:ad0247ca1a15460e482d1eac65c0b4092009-10-04T19:43:26+00:002009-10-04T19:43:26+00:00Stephane CarrezUbuntuphppropelsymfony
<div class="post-text"><p>I had two <a href="http://www.planzone.com">projects</a> to upgrade on my desktop running Ubuntu 8. The upgrade process is described in <a href="http://www.symfony-project.org/installation/1_2/upgrade">Upgrading Projects from 1.1 to 1.2</a> but even if you follow it, this is really not easy.</p><h2>Symfony Installation</h2><p>First, I had to remove a previous symfony installation:</p><pre><code> sudo pear uninstall symfony/symfony
</code></pre><p>For some reasons, I had still the Propel plugin installed, so I've removed what remains with the command;</p><pre><code> $ sudo rm -rf /usr/share/php/symfony
</code></pre><p>Then, I've installed the new Symfony version with pear:</p><pre><code> $ sudo pear install symfony/symfony-1.2.3
</code></pre><p>(The Symfony installation is described in <a href="http://www.symfony-project.org/installation/1_2">Installation 1.2</a>).</p><h3>Upgrade to 1.2</h3><p>After Symfony installation, I've started to migrate my applications to the new version. You have to run these commands in each project:</p><pre><code> $ php symfony project:upgrade1.2
$ php symfony propel:build-model
</code></pre><p>The <b>prope:build-forms</b> command failed because I had to remove old plugins and update the <b>sfGuardPlugin</b>:</p><pre><code> $ symfony plugin:install sfGuardPlugin
$ php symfony propel:build-forms
$ php symfony propel:build-filters
</code></pre><p>Then clear Symfony cache (the sudo was necessary because some files are owned by <i>www-data</i> user)</p><pre><code> $ sudo php symfony cc
</code></pre><h3>Problems and fixes</h3><p>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.</p><p>After upgrading, I got this error:</p><pre><code> Fatal error: Call to undefined method DebugPDOStatement::setInt()
in /home/.../lib/model /AssertViewerPager.php on line 35
</code></pre><p>This is due to the upgrade of Propel from 1.2 to 1.3. Propel is now using <a href="http://fr.php.net/manual/fr/book.pdo.php">PHP Data Objects</a>, which is a very good thing. The following must be changed:</p><pre><code> $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";
}
</code></pre><p>into</p><pre><code> $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";
}
</code></pre><p>After fixting the database issues, I found another cryptic error:</p><pre><code> 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
</code></pre><p>It was caused by the <b>sfGuardPlugin</b> which was not updated correctly. I had to remove it and install it from scratch.</p><p>Another error caused by the migration:</p><pre><code> Fatal error: Call to undefined method
BasePeer::getmapbuilder()
in /home/.../lib/model /om/
BaseLogEmitterPeer.php on line 66
</code></pre><p>To solve this, I had to remove the 'om' directory and rebuild the model with:</p><pre><code> symfony propel:build-model
</code></pre><p>There could be other issues that I've not reported... In any case, making sure that everything works after the upgrade is painful.</p></div> Symfony cryptic error: Error importing plugins/sfPropelPlugin/lib/propel/builder/SfPeerBuilder.phpurn:md5:e4da57123169d9f05b6ae2b5c5cb6bb62009-05-17T20:59:02+00:002009-05-17T20:59:02+00:00Stephane Carrezphppropelsymfony
<div class="post-text"><p>After the migration to Symfony 1.2, when I wanted to use the command <code>symfony propel:build-model</code>, I was getting the following cryptic error:</p><pre><code>>> 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
</code></pre><p>After searching for some time, I've found that this error comes from a bad definition in my '<code>config/propel.ini</code>' file. The propel.ini file contained definitions like:</p><pre><code>propel.builder.peer.class = \
plugins.sfPropelPlugin.lib.propel.builder.SfPeerBuilder
propel.builder.object.class = \
plugins.sfPropelPlugin.lib.propel.builder.SfObjectBuilder
</code></pre><p>And they should be defined as follows:</p><pre><code>propel.builder.peer.class = \
plugins.sfPropelPlugin.lib.builder.SfPeerBuilder
propel.builder.object.class = \
plugins.sfPropelPlugin.lib.builder.SfObjectBuilder
</code></pre><p>Once the propel configuration was fixed, the symfony command was working again.</p><p>If you have this kind of problem, I suggest the following:</p><ul><li>Create an empty blank application with Symfony (<code>symfony init-app test</code>)</li><li>Make a diff of the '<code>config</code>' directory between your application and the blank application</li><li>Report any suspicious change to fix your configuration</li></ul></div> Fixing the setError and hasError migration issue in Symfony 1.2urn:md5:ae513c89e060f0758dce2555ed8cb4732009-05-17T20:57:45+00:002009-05-17T20:57:45+00:00Stephane Carrezphpsymfonyupgrade
<div class="post-text"><h2>Foreword on error management in the controller and the view</h2><p>The controller verifies the form parameters and it can check some error message. For this, Symfony 1.0 was providing the <b>setError</b> method that you use as follows:</p><pre><code>$this->getRequest()->setError('username',
"Sorry, usernames may contain only letters... and dashes.");
</code></pre><p>Then, in the view template file, you would use the <b>form_error</b> helper to check and report the error message.</p><pre><code><div class="username">
<label for="username">User name: </label>
<div class="content">
<?php echo form_error('username') ?>
<?php echo input_tag('username') ?>
</div>
</code></pre><p>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.</p><h3>Step 1: Provide your own sfWebRequest</h3><p>First, we override the <b>sfWebRequest</b> to implement the missing operations and we use the <b>setAttribute</b> method to associate the message with the error field. To avoid conflicts with other attributes, the error fields are prefixed with <code>error_</code>.</p><pre><code>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);
}
}
</code></pre><h3>Step 2: Update factories.yml</h3><p>Now we have to tell Symfony to use our own request class:</p><pre><code>all:
request:
class: myRequest
</code></pre><p>Then, clear the symfony cache by running the command:</p><pre><code>$ symfony cc
</code></pre><h3>Conclusion</h3><p>The fix is really simple and if you find other methods that you use and have been removed (<b>hasErrors</b>, <b>getErrors</b>, ...) you just have to add them to you request class!</p></div>