Autonomous Machine

Posts tagged with PHP

A simple content_for in PHP

I'd been meaning to check out PHP 5.3's closures. When someone asked me a question about Fitzgerald today, I had to look at the source to find the answer. I then spent five minutes sketching the following, which is a very minimal implementation of Rails' content_for view helper.

<?php

$contents = array();

function content_for($name, $method) {
  global $contents;
  $contents[$name] = $method;
}

function yield($name) {
  global $contents;
  if (array_key_exists($name, $contents)) {
      return $contents[$name]();
  }
}

$variable = "something great";

?>

<?php content_for('header', function() use($variable) { ?>
  <h1>'<?php echo $variable ?>' should be in the header</h1>
<?php })?>

<?php content_for('footer', function() { ?>
  <h1>should be in the footer</h1>
<?php })?>

<div class="header">
  <?php yield('header')?>
</div>

<p>Here is the content for a page.</p>

<div class="footer">
  <?php yield('footer')?>
</div>

The rendered result looks like this:

<div class="header"> 
    <h1>'something great' should be in the header</h1> 
</div> 
 
<p>Here is the content for a page.</p> 
 
<div class="footer"> 
    <h1>should be in the footer</h1> 
</div> 

Having to declare inherited variables with use is a little annoying, but otherwise, I'm impressed with this addition to PHP.

  • June 04, 2010
  • Article
  • Code, PHP

Fitzgerald update: before filters and sendDownload

Fitzgerald has been running at least two websites for a little while now, but I've had no time to do much with it for the last month. There were a few feature additions as a result of a client site I was working one, and now's as good a time as any to mention them. Especially seeing as the official docs are a couple blogs posts for now.

Before Filters

One feature of Fitzgerald I added to the repo a while ago but failed to document is the ability to add before filters to actions. Anyone that's used an MVC web framework will probably be familiar with this concept; for those who aren't, the idea is to assign one or more methods to be called before a given action. The most common use case for this is probably ensuring that a use is logged in before viewing a given page.

It should be noted that adding after filter would be trivial; however, I'm not sure there really is a need for them in a framework of this size. So I've left them out for now.

Fitzgerald's implementation of filters is a little different than most in order allow a filter to return text to the browser or redirect. Any time a filter returns anything, the filter chain is halted and the result is sent to the browser.

Filters are defined using a similar syntax to binding actions to routes, and are executed in the order they are defined:

<?php
class ApplicationWithAFilter extends Fitzgerald {

    protected function verify_user() {
        // redirect unauthorized users to the home page
        if(!$this->isAllowed()) {
            return $this->redirect('/');
        }
    }

    protected function get_protected() {
        // display some secrets
    }

    protected function post_protected() {
        // make some secrets
    }

}

$app = new ApplicationWithAFilter();

// Define a before filter to be executed before one or more actions
$app->before('get_protected|post_protected', 'verify_user');

?>

Just add a method with the correct name to your Application definition, and you are all set. I've updated example.php in the repo to include usage of a before filter.

sendDownload

sendDownload is really just a brute force version of sendFile that does everything it can to persuade a browser to download a file instead of opening it inline. There are no guarantees this technique will work all of the time, but in my testing, sendDownload contained the correct black magic to force Internet Explorer to behave itself.

Since I haven't documented either of these methods yet, here's how to use sendFile and sendDownload:

<?php
class ApplicationWithDownloads extends Fitzgerald {
    public function get_pdf() {
        $this->sendFile('filename_to_send_as.pdf', 'application/pdf', $this->path('pdf/awesome.pdf'));
    }
    public function get_pdf_for_dowload() {
        $this->sendDownload('filename_to_send_as.pdf', $this->path('pdf/awesome.pdf'));
    }
}
?>

sendFile takes three arguments: the filename to send the file as, the content type to set in the headers, and the path to the local file to send. Note the call to path, which simply appends its argument to the app root (which is defined as one level up from fitzgerald.php). Usage of sendDownload is the same, except it doesn't take a content type argument.

  • February 03, 2009
  • Article
  • PHP

Fitzgerald: a Sinatra clone in PHP

Two weeks ago I had a tiny client website that suddenly needed a login section to provide some downloads to approved users. Initially I reached for Sinatra, which I have grown to love in recent months, but sadly the client's server had no opportunity to run Ruby. A sad day for sure, but the world moves on and I decided to revisit my old friend PHP and see what could be done.

I spent about ten minutes looking through various PHP frameworks, but quickly decided that it would be faster to write the code the tiny site would need than learn the ins and outs of a full framework. I wanted something tiny anyway (preferably a single file), and most of what I found online were various fully fledged MVC Rails clones. Not going to work.

I knew I wanted a solution that would get me as close to possible to Sinatra's syntax, while making the best of the confines of PHP. Here's the final syntax I came up with:

<?php
    include('lib/fitzgerald.php');
    
    class Application extends Fitzgerald {
        
        // Basic get request
        public function get_index() {
            return $this->render('index');
        }
    }
    
    $app = new Application();
    
    $app->get('/', 'get_index');
    
    $app->run();
?>

The basic concept is to subclass Fitzgerald with controller methods, and then map them to urls using get() and post() calls on an instantiated application object.

I added some Merb-style argument passing to the mix using PHP5's Reflection API:

<?php
    include('lib/fitzgerald.php');
    
    class Application extends Fitzgerald {
        
        // $page will be one of 'about', 'contact', or 'faq' thanks to our URL mapping below
        public function get_page($page) {
            return $this->render($page);
        }
    }
    
    $app = new Application();

    // :page is a placeholder- it will match anything by default, or a regex to match can
    // be passed in using an optional third argument to get() or post()
    $app->get('/:page', 'get_page', array('page' => 'about|contact|faq'));
    
    $app->run();
?>

Like Sinatra, the return value of a method is what is returned to the browser. Fitzgerald has built in methods to render a template or redirect the browser:

<?php
    class Application extends Fitzgerald {
        
        // Renders a php file in '../views/', making variables provided to compact
        // available as local variables in the template
        public function get_page($page) {
            $var1 = 'Value to use in template';
            $var2 = 'Another value to use in template';
            return $this->render($page, compact('var1', 'var2'));
        }

        // You can also send redirect headers
        public function get_redirect {
            return $this->redirect('/url/to/redirect/to');
        }
    }
?>

You can also specify a layout with an options array passed to the application initialization:

<?php
  // This will capture the output of any render calls into a local variable $content,
  // and then render the layout provided here
  $app = new ApplicationWithLogin(array('layout' => 'shell'));
?>

And that's Fitzgerald. There is a more advanced example that shows how to build a simple authentication system in the repo on Github- look at example.php.

  • November 21, 2008
  • Article
  • PHP