Skip navigation

I love PHP frameworks like CakePHP, but sometimes they seem a little bloated. I’ve seen some “lightweight” frameworks like MicroMVC (which brags about being “less than 150kB”) and TinyMVC (which is still over 30kB zipped). For those of you who want a lighter-than-air, no-nonsense, zero-learning-curve, lightning-fast MVC framework in less than 4/5 of a kilobyte (uncompressed), I present PunyMVC:

<?function v($c,$a,$vars){if(is_file("views/$c/$a.php")){ob_start();include"views/$c/$a.php";$v=ob_get_contents();ob_end_clean();}return isset($v)?$v:”;}function p($content,$l){if(isset($l)){include"layouts/$l.php";}else{echo$content;}}includeconfig/config.php‘;$b=strlen(dirname($_SERVER['PHP_SELF']));$u=explode(/‘,substr($_SERVER['REDIRECT_URL'],$b>1?$b+1:1));if(!($c=$u[0])){includepages/index.php‘;die();}elseif(!preg_match(/\W/‘,$c.isset($u[1])?isset($u[1]):)&&is_file("controllers/$c.php")){include"controllers/$c.php";$d=$c.Controller‘;$a=isset($u[1])&&!empty($u[1])?$u[1]:index‘;if(class_exists($d)&&in_array($a,get_class_methods($d))){$o=new$d;p(v($c,$a,$o->$a(array_slice($u,2))),isset($o->layout)?$o->layout:null);die();}}header(HTTP/1.1 404 Not Found);includepages/404.php‘;

Yep, that’s it. Of course, I had to minify the code in order to squash it down to under a kilobyte; if you scroll to the bottom of this post, you can download a copy of the developer-friendly unminified version (which includes all of the following sample code as well). Or you can grab it off of my github.

Anyway, here’s how to use it:

  • Step 1: Save the code blob above as index.php and place it in your webroot.

  • Step 2: Make an .htaccess file that will tell Apache to filter all URLs through PunyMVC (unless a file exists):

RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule . index.php [L]


  • Step 3: Make some subdirectories:

$ mkdir config controllers layouts models pages views


  • Step 4: Place an .htaccess file in each of the subdirectories for security. The files can just contain the line “deny from all”.

  • Step 5: Make a default index page:

$ echo “Welcome to my awesome website.” > pages/index.php


  • Step 6: Make a “404 not found” page:

$ echo “Page not found.” > pages/404.php


  • Step 7: Make a controller. Here’s a sample controller called Posts:

<?php // controllers/posts.php
class PostsController {
  public $layout = 'simple';
  private $Posts;

  public function __construct() {
    require_once 'models/posts.php';
    $this->Posts = new PostsModel();

  public function index() {
    $posts = $this->Posts->find();
    return array('posts' => $posts);

  public function view($args) {
    if (ctype_digit($args[0])) {
      $post = $this->Posts->find(array('_id' => intval($args[0])));

    if (!isset($post) || empty($post)) {
      include 'pages/404.php';

    return array('post' => $post[$args[0]]);


  • Step 8: Make a model. Here’s a sample model (also called Posts). Just for fun, we’ll use MongoDB instead of a traditional RDBMS like MySQL:

<?php // models/posts.php
class PostsModel {
  private $mongo;
  private $mongoDb;
  private $posts;

  public function __construct() {
    $this->mongo = new Mongo(MONGO_SERVER);
    $this->mongoDb = $this->mongo->{MONGO_DATABASE};
    $this->posts = $this->mongoDb->posts;

  public function find($query = array(), $fields = array()) {
    # Very simple example, we just pass the query straight to Mongo
    return iterator_to_array($this->posts->find($query, $fields));


  • Step 9: Make some views. Here are two sample views for the actions in our Posts controller:

<?php // views/posts/index.php ?>
Here are the posts:<br /><br />
<?php foreach ($vars['posts'] as $post) : ?>
  <?php $post_uri = sprintf('%s/posts/view/%u', dirname($_SERVER['PHP_SELF']), $post['_id']) ?>
  <a href="<?php echo $post_uri ?>"><?php echo $post['title'] ?></a> by <?php echo $post['author'] ?><br />
<?php endforeach ?>


<?php // views/posts/view.php ?>
This is a single post:<br />
<br />
Title: <?php echo $vars['post']['title'] ?><br />
Author: <?php echo $vars['post']['author'] ?><br />
Content:<br />
<?php echo $vars['post']['content'] ?><br /><br /><br />


  • Step 10: Make a layout. Layouts are optional (but a good idea). We specified one in the controller, so we’ll make it:

<?php // layouts/simple.php ?>
  <?php echo $content ?>


  • Step 11: Make a configuration file. You can store anything you want here (I use it for database settings in the demo app):

<?php // config/config.php
define('MONGO_SERVER', 'mongodb://localhost:27017');
define('MONGO_DATABASE', 'punymvc_demo');


  • Step 12: We’re done with the app, but it won’t do much unless we have data. Set up a MongoDB server and dump some posts into a database:

> use punymvc_demo
> db.posts.insert({“_id”:1,”title”:”This is an awesome post”, “author”:”Dave Hensley”, “content”:”This is the first post on my PunyMVC demo blog.”})
> db.posts.insert({“_id”:2,”title”:”This is an even awesomer post”, “author”:”John Smith”, “content”:”Just kidding. Dave’s post was way better than mine.”})

You can download the full demo app here. (Note: The download version is not minified, and includes the license and phpDoc comments — but even with all that bloat, it’s still well under 4K compressed.)


    • StXh
    • Posted 2011/12/31 at 2:25 am
    • Permalink

    How to use this? I can just get “Index Page”

    • swathi
    • Posted 2013/01/29 at 1:08 am
    • Permalink

    I need the code

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>