In terms of a real-world example of what goes in these 4 different layers, assume we are writing a database-backed application that needs to fetch a user record containing various fields. This is a very common thing to do in a web app. Our template layer might look something like this:

php.ini
auto_prepend_file = "./helpers.inc"
include_path = "/usr/local/lib/php"
Template Layer
<?title('Example Page')?>
<?greeting
()?>
<h1>Heading 1</h1>
  <p>
    Page content
  </p>
<h1>Heading 2</h1>
  <p>
    Yada Yada
  </p>
<h1>Heading 3</h1>
  <p>
    Page content
  </p>
<?footer()?>
Template Helpers
<?php
  
include "logic.inc";
  echo 
'<?xml version="1.0" encoding="UTF-8"?>';
?>
<!DOCTYPE html 
  PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" 
      xml:lang="en" lang="en">
<?php
  $user 
get_user_record($_COOKIE['user_id']);
  function 
greeting() {
    global 
$user;
    echo 
"Hi " .$user['first_name'] ."!<br />\n";
    if(
$age=birthday($user)) {
      echo 
"Congratulations, today is your ";
      echo 
"$age birthday!<br />\n";
    }
  } 
  function 
title($title) {
    echo 
"<head><title>$title</title></head>\n";
    echo 
"<body>\n";
  }
  function 
footer() {
    echo 
"</body>\n</html>";
  }
?>
Business Logic
<?php
  
function get_user_record($id) {
    
mysql_connect('localhost');
    
mysql_select_db('users');
    
$res mysql_query("select * from users where id='$id'");
    if(!
$res) echo mysql_error();
    else 
$row mysql_fetch_array($res);
    return 
$row;
  }
  function 
birthday($user) {
    if(
strftime('%m %d')==strftime('%m %d',$user['bday'])) 
      
$age strftime('%Y') - strftime('%Y',$user['bday']);
      if((
$age%100)>10 && ($age%100)<20$ap='th';
      else switch(
$age%10) {
        case 
1$ap 'st'; break;
        case 
2$ap 'nd'; break;
        case 
3$ap 'rd'; break;
        default:  
$ap 'th'; break; 
      }
      return 
$age.$ap;
    else 
      return 
false;
  }
?>
In this case the final layer written in C contains the mysql_* functions, and the date() function. These happen to be standard PHP functions. If birthday() is called many times on every single request and since how you figure out if it is someone's birthday is unlikely to change very often, this may be a good candidate to translate into C. Although, in this example, the birthday function is probably too simple to see much of a performance improvement. On the other hand, other than a little bit of added parameter parsing, if you compare the C version of birthday() to the PHP version, it isn't that much harder to write it in C.

C Layer

PHP_FUNCTION(birthday)
{
  time_t timestamp, now;
  struct tm *ta1, tmbuf1, *ta2, tmbuf2;
  int age;
  char ret_age[8];

  if (zend_parse_parameters(1 TSRMLS_CC, "l", &timestamp) == FAILURE)
      return;

  ta1 = php_localtime_r(&timestamp, &tmbuf1);
  time(&now);
  ta2 = php_localtime_r(&now, &tmbuf2);

  if(tmbuf1.tm_mday==tmbuf2.tm_mday && tmbuf1.tm_mon==tmbuf2.tm_mon) {
      age = tmbuf2.tm_year - tmbuf1.tm_year;
      if((age%100)>10 && (age%100)<19) sprintf(ret_age,"%dth",age);
      else switch(age % 10) {
          case 1: sprintf(ret_age,"%dst",age); break;
          case 2: sprintf(ret_age,"%dnd",age); break;
          case 3: sprintf(ret_age,"%drd",age); break;
          default:sprintf(ret_age,"%dth",age); break;
      }
  } else {
      RETURN_FALSE;
  }
  RETURN_STRING(ret_age,1);
}