Make great applications with PHP
 

simply put: Output - Covers slots, views, HTML, themes, css, and javascript.


Types of requests

A typical request to a web server results in some output being produced. Typing the URL into a webpage produces a GET request, submitting a form produces a POST request, while certain Javascript may cause an asynchronous "AJAX" request.

PIE has sensible conventions for handling all these requests, and more. You can, of course, override these conventions (if you need to), but it's much more convenient to just use what PIE provides.

Slots and Layouts

Typically, PIE apps generate output in a two-step process:

  1. First, they fill some slots (such as dashboard, content, title)
  2. Then, they render a layout, where the contents of those slots are variables (such as $dashboard, $content and $title)

Internally, slots are filled by a call to Pie_Response::fillSlot($slot_name). If the slot has already been filled — e.g. with a call to Pie_Response::setSlot($slot_name, $content) — then fillSlot just returns its contents. Otherwise, it invokes the slot's handler, to get the slot's content.

This is done as follows: let's say the URI "foo/bar" was requested. To fill a slot named "content", you would implement a handler for the event "foo/bar/response/content". Its return value is used to fill the slot. That is what we did when were writing our first app.

When a URL is requested by a browser directly (not through javascript), the default set of slots to fill is ["content", "dashboard", "title", "errors"]. You can replace this array by setting the config field $module/response/slotNames (where $module is the module the request has been routed to).

Slots are a really useful feature in PIE. They let your app fill multiple content areas in a single request. This will become very useful when we talk about AJAX. You can define additional, custom slots with arbitrary names. For example, an AJAX call might want the server to fill and return the "votes" slot, or "updated_profile_html" slot. These custom slots can return strings, arrays, or anything else.

As you know, PIE splits the various stages of handling a request into separate events. For example, "pie/post" would be used for making changes to the server state. All the output generation should happen in the "pie/response" handler, and this is when you fill the slots named in Pie_Request::slotNames().

Views

PIE supports a Model-View-Controller (MVC) architecture. As a result, you will typically keep separate files (in the APP_DIR/views folder) which are easy for web designers to work with.

By convention, if a view is meant to be rendered in a certain slot, it should be placed in a folder named after that slot. Thus, for the content slot, the views are in the APP_DIR/views/$app/content/ folder.

The layout is actually just a view, and the default "pie/response" handler selects the layout based on Pie_Request::isAjax() and Pie_Request::accepts($mime_type). For example, if an HTML document is requested by a browser, the APP_DIR/views/$app/layout/html.php layout is used.

The Pie_Html class

When rendering output, you would do well to make use of Pie_Html functions as much as possible. They do a lot of things on behalf of your app, that not only let you interoperate with the rest of PIE (such as doing automatic un-routing), but also help you write standards-compliant code more easily.

Here is a partial list of useful functions from Pie_Html. In the list below, note that $href, $action, etc. don't have to be URLs, but can also be URI strings, such as "youMixer/welcome".

  • Pie_Html::text($str) is used to escape literal text in HTML
  • Pie_Html::a($href, $attributes = array()) outputs a link
  • Pie_Html::form($action, $method = 'post', $attributes = array()) outputs a form
  • Pie_Html::img($src, $alt='image', $attributes = array()) outputs an image
  • Pie_Html::div($id, $class, $attributes = array()) outputs a div
  • Pie_Html::tag($tagname, $attributes = array()) outputs an arbitrary HTML tag
  • Pie_Html::script($contents) outputs an inline javascript inside HTML

Most of the tags above output an opening tag, which you would then follow by some HTML and then a closing tag. For example:

<?php echo Pie_Html::a('youMixer/welcome') ?>cool</a>
However, you can usually pass the content of the tag as its final parameter
<?php echo Pie_Html::a('youMixer/welcome', null, 'cool') ?>
You can also output a self-closing tag like this:
<?php echo Pie_Html::a('script', null, true)

Also, a tip: use json_encode($something) to prepare variables for output in javascript. Doing this will be effective even for escaping strings. You will then have to run the result through Pie_Html::text($result) to output it in an HTML document.

Themes

PIE has support for dynamic theming. For example, your app can give each user the ability to customize the appearance of the site. The way this is achieved is through a "theme cascade".

To add a theme folder to the cascade at runtime, you call Pie_Html::pushThemeUrl($theme_url). From then on, the function Pie_Html::themedUrl($src) checks this folder before trying previous folders. If the specified $src exists relative to a particular theme folder, the resulting URL is used for the src attribute.

Let's illustrate with some examples:

Pie_Html::pushThemeUrl(Pie_Request::baseUrl());
Pie_Html::pushThemeUrl(Pie_Request::baseUrl().'/themes/red'); // red theme
Pie_Html::img('img/foo.jpg'); // if themes/red/img/foo.jpg exists, we use that
                              // otherwise, img/foo.jpg
Pie_Response::addScript('js/foo.js'); // if themes/red/js/foo.js exists, use that
                              // otherwise, use js/foo/js

Scripts and stylesheets

When filling a slot, you will often want to reference javascripts and stylesheets. You can easily do this by using calls like the following:

Pie_Response::addStylesheet('css/foo.js');
Pie_Response::addScript('js/foo.js');
Pie_Response::addScript('http://yahoo.com/yui/CoolBar.js');
PIE translates strings like "js/foo.js" into absolute urls using Pie_Html::getThemedUrl($local).

You can also add javascript lines and stylesheet rules to be rendered directly inside the document, as follows:

Pie_Response::setStyle('p, div.foo', array('background' => 'red'));
Pie_Response::addScriptLine("document.location = $new_location_json;");

When to print output

The Pie_Dispatcher::dispatch() function, which dispatches requests, fires the following events in order:

  • pie/validate - validate the input for correctness
  • pie/objects - time to create any objects from the input
  • pie/reroute - may change URI, e.g. to login page
  • pie/post - called if the $_POST array is not empty; you can change server state here
  • pie/analytics - this is a good time to record something in the logs
  • pie/response - finally, the event handler for this should do

Out of all of these events, the only one where you should print output is pie/response (just use echo or print). That is what gets returned to the browser. If an uncaught exception ever escapes the pie/response handler, you will see an exception dump on the front page, that you need to fix. If an exception is thrown in some other event (such as pie/post), PIE attempts to render a regular page (by calling pie/response anyway) but with the errors reported in the errors slot.

Redirecting

To redirect to another URL or URI, call Pie_Response::redirect($uri). For example, you can do

Pie_Response::redirect('youMixer/welcome')
You should typically do this before the "pie/response" event -- which is expected to start printing output -- since headers can't be set after that.

A typical time to redirect is when a POST request succeeded. (That way, refreshing the page will not cause the browser to attempt the POST request again.) However, PIE does this automatically through a convention: the $_REQUEST['_']['onSuccess'] field is automatically checked by the default "pie/response" handler -- and if it not empty, PIE redirects there (if the Pie_Response::getErrors() array was empty). This is explained further in the "Forms and Tools" article.

Buffering output

By default, PIE buffers your output. You can buffer handler to gzip the output by calling Pie_Response::isBuffered('gzip'). Or you can turn it off altogether calling Pie_Response::isBuffered(false). Turning output buffering off disables the use of layouts, but may be useful for pages that are designed to load slowly over time. For example, "Comet" is a technique that uses long-polling for pushing information over HTTP, and can benefit from this.

Complete reference to PHP ON PIE

TODO: include an iframe with PHPDoc-generated reference