Greg Aker

With Great Power Comes Great Responsibility

How the decisions you make in your ExpressionEngine templates affect site performance.

Filed in: Blobbing, Performance, ExpressionEngine

October 28, 2010

A lot of us are drawn to ExpressionEngine because PHP isn't pre-requisite knowledge to make a kick-ass dynamic website. If we need to roll our own PHP we can if absolutely needed. PHP directly in the templates is fantastic for small chunks. I do want to illustrate the kind of issues that may arise if you are putting too much PHP into your templates, and show alternatives.

First, I need to do a custom query and want to use some CodeIgniter Helpers and Libraries too. If we were in straight CI, this would look pretty simple if broken between MVC.

So here's my first template. I have approximately three thousand entries in my test database, I haven't benchmarked the query and I'm sure it's something that would make most DBAs cringe. :)

<?php

// Gimme the URL helper
$this->EE->load->helper('url');
$this->EE->load->library('typography');

$base_url = $this->EE->config->item('base_url');

/**
  * Query
  */
$select = 'ct.site_id, ct.entry_id, ct.channel_id, ct.author_id, ct.title, ct.url_title, ' .
          'cd.field_id_1 as summary, cd.field_id_2 as body, cd.field_id_3 as extended, ' . 
          'm.member_id, m.group_id, m.username, m.screen_name, m.email, m.url, m.location, ' .
          'm.interests, m.occupation, m.avatar_filename, m.avatar_width, m.avatar_height';

$q = $this->EE->db->select($select)
                  ->from('channel_titles ct')
                  ->join('channel_data cd', 'cd.entry_id = ct.entry_id')
                  ->join('members m', 'm.member_id = ct.author_id')
                  ->where('ct.site_id', $this->EE->config->item('site_id'))
                  ->order_by('ct.entry_id', 'asc')
                  ->limit(100)
                  ->get();

/**
  * DO it, Do it!
  */
foreach ($q->result() as $row): ?>

<h2><a href="entry/<?php echo $row->url_title?>"><?php echo $row->title?></a></h2>
<?php if ($row->avatar_filename): ?>
    <a href="<?php echo $row->url?>">
    <img src="<?php echo $base_url?>/images/avatars/<?php echo $row->avatar_filename?>">
    </a>
<?php endif; ?>

This post is by <?php echo safe_mailto($row->email, $row->screen_name) ?>.  This person rules!

<?php echo $this->EE->typography->auto_typography($row->summary)?>
<?php echo $this->EE->typography->auto_typography($row->body)?>
<?php echo $this->EE->typography->auto_typography($row->extended)?>

<hr>
<?php endforeach; ?>

Okay, seriously. Ewww. How can we expect a designer we work with who doesn't know any PHP to deal with this? It's gross. Templates are supposed to be beautiful. This definitely isn't anywhere close to it.

I'm going to run some benchmarks on my iMac using Siege. My iMac runs Nginx as the web server, and php-cgi with three children spawned via spawn-fcgi. This first test is with APC disabled.

> siege -c 3 -b -t 10s http://10.0.0.5/ee/ee2/index.php?/test/ 
SIEGE 2.69
 Preparing 3 concurrent users for battle.
The server is now under siege...

Lifting the server siege...      done.
Transactions:             154 hits
Availability:             100.00 %
Elapsed time:             9.42 secs
Data transferred:         0.31 MB
Response time:            0.18 secs
Transaction rate:         16.35 trans/sec
Throughput:               0.03 MB/sec
Concurrency:              2.95
Successful transactions:  154
Failed transactions:      0
Longest transaction:      0.27
Shortest transaction:     0.12

Looks fairly reasonable. Now with APC enabled:

> siege -c 3 -b -t 10s http://10.0.0.5/ee/ee2/index.php?/test/ 
SIEGE 2.69 Preparing 3 concurrent users for battle.
The server is now under siege...

Transactions:             280 hits
Availability:             100.00 %
Elapsed time:             9.20 secs
Data transferred:         0.56 MB
Response time:            0.10 secs
Transaction rate:         30.43 trans/sec
Throughput:               0.06 MB/sec
Concurrency:              2.98
Successful transactions:  280
Failed transactions:      0
Longest transaction:      0.20
Shortest transaction:     0.06

Um, that's just a little improvement, eh? :) We do run into possible issues though. PHP in ExpressionEngine templates is run through eval(), so depending on what we do, the additional overhead can get considerable.

Let's drop all of this into a plugin and see what happens. My plugin is here and this is the template:

{exp:special_sauce}

<h2><a href="entry/{url_title}">{title}</a></h2>
<a href="{url}">
<img src="{avatar}">
</a>

This post is by {screen_name}. They totally Rule!

{summary}
{body}
{extended}

<hr>
{/exp:special_sauce}

Okay first. That looks like an awesome template. This is something a designer can work with, right? PHP syntax errors are going to be our fault! I've also disabled PHP in ExpressionEngine's template preferences. Let's battle the template with no APC installed.

> siege -c 3 -b -t 10s http://10.0.0.5/ee/ee2/index.php?/test/ 
SIEGE 2.69 Preparing 3 concurrent users for battle.
The server is now under siege...

Lifting the server siege...      done.
Transactions:             179 hits
Availability:             100.00 %
Elapsed time:             9.19 secs
Data transferred:         0.00 MB
Response time:            0.15 secs
Transaction rate:         19.48 trans/sec
Throughput:               0.00 MB/sec
Concurrency:              2.97
Successful transactions:  179
Failed transactions:      0
Longest transaction:      0.38
Shortest transaction:     0.09

With APC:

> siege -c 3 -b -t 10s http://10.0.0.5/ee/ee2/index.php?/test/ 
SIEGE 2.69 Preparing 3 concurrent users for battle.
The server is now under siege...

Lifting the server siege...      done.
Transactions:             446 hits
Availability:             100.00 %
Elapsed time:             9.84 secs
Data transferred:         0.01 MB
Response time:            0.07 secs
Transaction rate:         45.33 trans/sec
Throughput:               0.00 MB/sec
Concurrency:              2.98
Successful transactions:  446
Failed transactions:      0
Longest transaction:      0.11
Shortest transaction:     0.03

Um, wow, eh? So the point is: The ability to put PHP in templates rules, but I've been hounding people to put them in plugins. Now you see why. To reiterate, the choices you make in your templates can and will have a direct correlation to your site's performance. What experiences do you have with this? Anyone seen anything similar?