Skip to content

Instantly share code, notes, and snippets.

@eric1234
Last active May 20, 2023 15:25
Show Gist options
  • Star 7 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save eric1234/2151128 to your computer and use it in GitHub Desktop.
Save eric1234/2151128 to your computer and use it in GitHub Desktop.
A better way of doing layouts in PHP

Usage

Creating a Layout File

Create a file called layout.php. Where you want the content to appear add <?php echo $content ?>. Here is an example:

<!DOCTYPE html>
<html>

<head>
  <title><?php echo $title ?></title>
</head>

<body>
  <div id="page">
    <?php echo $content ?>
  </div>
</body>
</html>

This should be placed in the same directory a your scripts.

Using the Layout File

Now just add the following to the top of every script (or some include the scripts pull in):

<?php require_once 'lib/layout.php'; ?>
My page content. This will be placed where $content is.

Any content outputted in your script will be placed where $content is outputted. Note I call the default layout file layout.php but also the file you are including is called layout.php. This is why I put it in the lib directory. To differentiate it. The one in your root directory is your layout content. The one in the lib directory is the one attached to this gist that does the magic.

Configuration

The $title variable is automatically pulled into the layout scope. To set the title simply place the following in your script:

<?php $title = 'Home page' ?>

You can place this before or after the layout require.

If you want to change where the layout file is located just set the $layout variable before calling layout. For example:

<?php
  $layout = __DIR__.'/path/to/layout.php';
  require_once 'lib/layout.php';
?>

Content Filters

This script provides an excellent opportunity to apply a filter to the entire output. Similar to an after_filter in Rails. Simply add a callback function to the global variable $content_filters. You can do this before the call to layout.php (so you are defining $content_filters or after by simply appending. Example:

<?php
  require_once 'lib/layout.php';
  $content_filter[] = 'adjust_markup';
?>

The callback should take the HTML as input and return the final HTML. Content filters only apply to the $content and not the layout. Also if the layout is not used then the content filters are not called.

Handling Redirects

Redirects are automatically handled. If you set the Location header and exit the layout script will automatically avoid rendering the layout and instead just exit.

<?php
# https://gist.github.com/2151128
if( !array_key_exists('layout', get_defined_vars()) )
$layout = realpath('./layout.php');
if( $_SERVER['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest' ) $layout = null;
$use_layout = true;
if(is_null($layout)) $use_layout = false;
function apply_content_filters($in) {
global $content_filters;
foreach($content_filters as $filter) {
$in = call_user_func($filter, $in);
}
return $in;
}
if( !isset($content_filters) ) $content_filters = array();
if( $use_layout ) ob_start();
function layout_shutdown() {
global $layout;
global $use_layout;
global $error_triggered;
if( !$use_layout ) return;
$content = apply_content_filters(ob_get_contents());
ob_end_clean();
if( $error_triggered || is_null($layout) ) {
echo $content;
return;
}
foreach(headers_list() as $header)
if( preg_match('/^Location/', $header) ) return;
global $title;
require $layout;
}
register_shutdown_function('layout_shutdown');
@ccucalon
Copy link

How do you handle the actual writing of say HTML within the $content section area? is there a way to group multiple lines of HTML?

@pedro-m-g
Copy link

You may use ob_start() and ob_get_clean() to write HTML.

@MasterRomantic
Copy link

You may

How do you handle the actual writing of say HTML within the $content section area? is there a way to group multiple lines of HTML?

You can just write it in a normal html script, make sure you use single quotes inside the string if relevant, then after that you can make the html script into strings and put it into the $content variable using the concatenation assignment .=.

$content="<h1 class='header1'>This is heading 1</h1>";
$content.="<p>This is some text.</p>";
$content.="<hr>";
$content.="<h2 class='header2'>This is heading 2</h2>";
$content.="<p>This is some other text.</p>";
$content.="<hr>";

ensure that you use single quotes when adding code inside of the string, and represent a string with double quotes, like this, $content="<h1 class='header1'>This is heading 1</h1>"; because the class quotes will not interfere with the string itself.

The concatenation assignment .= basically adds on to the string without the fuss of adding all the html at once, it helps clean up your code but you can choose to put the html as one string in the $content variable

@eric1234
Copy link
Author

This script will set $content for you. There is no need for any sort of concatenation mess or using ob_start (this is used internally by this gist for you).

Let's say your PHP script is called index.php that has your content. It would look like this:

<?php require_once 'lib/layout.php'; ?>

My content page content goes here. Just plain HTML. No string or concatenation.
I can have dynamic content like this: <?php echo 1+1 ?>

The layout.php in this gist you would place in the lib directory (since that is where we require it from). Then your layout file we can just put at the root of the directory and also call it layout.php. See the section above titled "Creating a Layout File" for what this file should look like.

End result is that you can easily wrap any script with a "layout" by doing one require. Your page content is automatically assigned to $content.

@joaquinrfs
Copy link

This script is quite powerful yet flexible and simple enough to copy and modify for your specific needs.

I am fairly new to PHP though and am having trouble understanding how to use the content filters, could you expand your example a little bit?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment