Lithium (#li3)
The framework with the best of both worlds
Richard McIntyre: @mackstar

★ Lithium core team

★ Freelancer - Leeds/Manchester

★ Most recently at BBC, Mamas & Papas, UOR

★ Engineer on BBC Olympics App

★ Lived in Japan 15 years

★ Done PHP for donkeys
Frameworks
I’ve seen it
✦ Symfony 2        ✦ Merb

✦ Zend Framework   ✦ Sinatra

✦ CakePHP          ✦ Spring

✦ Code Igniter     ✦ Stripes (Java)

✦ Drupal 6&7       ✦ Django
                       Wordpress
✦ Silex
                   ✦




✦ Rails
many
frameworks
are
like
this
Each software solution should be in proportion to
the problem being solved.
Each software solution should be in proportion to
the problem being solved.

                            -some lazy bugger
Each software solution should be in proportion to
the problem being solved.

                            -some lazy bugger
                                           me
<?php

use BehatMinkExtensionContextMinkContext;

class FeatureContext extends MinkContext
{
   /**
    * @Then /^I wait for the suggestion box to appear$/
    */
   public function myNameHasAMeaninglessExistance()
   {
      $this->getSession()->wait(5000, "$('.suggestions-results').children().length >
0");
   }
}
All problems in computer science can be solved
by another level of indirection. Except for the
problem of too many layers of indirection.

                 Butler Lampson, David Wheeler
Code Sexy-ness
There’s nothing like the look of beautiful Ruby code in the morning.
Gracefully colored by TextMate and rendered in Bitstream Vera pt 12.
@dhh
Developer Happy-ness
Any fool can write code that a computer can understand. Good
programmers write code that humans can understand.
                                                        –Martin Fowler
Light Footprint
Light Footprint
Looking for a hackers Framework?




Light Footprint
Fast, Flexible and Rad
World 1




Full Stack Framework
   Convention Over Configuration
github.com/UnionOfRad/framework
The Photo Blog Tutorial
   github.com/nateabele/photoblog
Model
namespace photoblogmodels;

class Photos extends lithiumdataModel {

    public $validates = array();

    protected $_meta = array('source' => 'fs.files');

    public function save($entity, $data = null, array $options = array()) {
      if ($data) {
        $entity->set($data);
      }
      if ($entity->tags && !is_array($entity->tags)) {
        $entity->tags = array_map('trim', explode(',', $entity->tags));
      }
      return parent::save($entity, null, $options);
    }
}
Model
namespace photoblogmodels;

class Photos extends lithiumdataModel {

    public $validates = array();

    protected $_meta = array('source' => 'fs.files');

    public function save($entity, $data = null, array $options = array()) {
      if ($data) {
        $entity->set($data);
      }
      if ($entity->tags && !is_array($entity->tags)) {
        $entity->tags = array_map('trim', explode(',', $entity->tags));
      }
      return parent::save($entity, null, $options);
    }
}
Model
namespace photoblogmodels;

class Photos extends lithiumdataModel {

    public $validates = array();

    protected $_meta = array('source' => 'fs.files');

    public function save($entity, $data = null, array $options = array()) {
      if ($data) {
        $entity->set($data);
      }
      if ($entity->tags && !is_array($entity->tags)) {
        $entity->tags = array_map('trim', explode(',', $entity->tags));
      }
      return parent::save($entity, null, $options);
    }
}
Views

<?=$this->form->create($photo, array('type' => 'file')); ?>
 <?=$this->form->field('title'); ?>
 <?=$this->form->field('description'); ?>
 <?php if (!$photo->exists()): ?>
  <?=$this->form->field('file', array('type' => 'file')); ?>
 <?php endif; ?>
 <?=$this->form->field('tags', array('label' =>
                             'Add tags separated by commas')); ?>
 <?=$this->form->submit('Save'); ?>
<?=$this->form->end(); ?>
Views

<?=$this->form->create($photo, array('type' => 'file')); ?>
 <?=$this->form->field('title'); ?>
 <?=$this->form->field('description'); ?>
 <?php if (!$photo->exists()): ?>
  <?=$this->form->field('file', array('type' => 'file')); ?>
 <?php endif; ?>
 <?=$this->form->field('tags', array('label' =>
                             'Add tags separated by commas')); ?>
 <?=$this->form->submit('Save'); ?>
<?=$this->form->end(); ?>
Views

<?=$this->form->create($photo, array('type' => 'file')); ?>
 <?=$this->form->field('title'); ?>
 <?=$this->form->field('description'); ?>
 <?php if (!$photo->exists()): ?>
  <?=$this->form->field('file', array('type' => 'file')); ?>
 <?php endif; ?>
 <?=$this->form->field('tags', array('label' =>
                             'Add tags separated by commas')); ?>
 <?=$this->form->submit('Save'); ?>
<?=$this->form->end(); ?>
<h1>
 <?=$this->title($photo->title); ?>
 <em>[
   <?=$this->html->link(
               'edit', array('Photos::edit', 'id' => $photo->_id)
          ); ?> ]</em>
</h1>

<p><?=$photo->description; ?></p>

<?php if ($photo->tags): ?>
 Tags:
 <?php foreach ($photo->tags as $tag): ?>
  <?=$this->html->link(
      $tag, array('Photos::index', 'args' => array($tag))
   ); ?>
 <?php endforeach; ?>
<?php endif; ?>

<?=$this->html->image(
      "/photos/view/{$photo->_id}.jpeg", array(
         'alt' => $photo->title, 'width' => "100%"
      )
); ?>
<h1>
 <?=$this->title($photo->title); ?>
 <em>[
   <?=$this->html->link(
               'edit', array('Photos::edit', 'id' => $photo->_id)
          ); ?> ]</em>
</h1>

<p><?=$photo->description; ?></p>

<?php if ($photo->tags): ?>
 Tags:
 <?php foreach ($photo->tags as $tag): ?>
  <?=$this->html->link(
      $tag, array('Photos::index', 'args' => array($tag))
   ); ?>
 <?php endforeach; ?>
<?php endif; ?>

<?=$this->html->image(
      "/photos/view/{$photo->_id}.jpeg", array(
         'alt' => $photo->title, 'width' => "100%"
      )
); ?>
<h1>
 <?=$this->title($photo->title); ?>
 <em>[
   <?=$this->html->link(
               'edit', array('Photos::edit', 'id' => $photo->_id)
          ); ?> ]</em>
</h1>

<p><?=$photo->description; ?></p>

<?php if ($photo->tags): ?>
 Tags:
 <?php foreach ($photo->tags as $tag): ?>
  <?=$this->html->link(
      $tag, array('Photos::index', 'args' => array($tag))
   ); ?>
 <?php endforeach; ?>
<?php endif; ?>

<?=$this->html->image(
      "/photos/view/{$photo->_id}.jpeg", array(
         'alt' => $photo->title, 'width' => "100%"
      )
); ?>
Controller

namespace photoblogcontrollers;

use photoblogmodelsPhotos;
use li3_geoextensionsGeocoder;

class PhotosController extends lithiumactionController {

 public function index($tags = null) {
   $conditions = $tags ? compact('tags') : array();
   $photos = Photos::all(compact('conditions'));
   return compact('photos');
 }

 public function view() {
   $photo = Photos::first($this->request->id);
   return compact('photo');
 }
Controller

namespace photoblogcontrollers;

use photoblogmodelsPhotos;
use li3_geoextensionsGeocoder;

class PhotosController extends lithiumactionController {

 public function index($tags = null) {
   $conditions = $tags ? compact('tags') : array();
   $photos = Photos::all(compact('conditions'));
   return compact('photos');
 }

 public function view() {
   $photo = Photos::first($this->request->id);
   return compact('photo');
 }
Controller

namespace photoblogcontrollers;

use photoblogmodelsPhotos;
use li3_geoextensionsGeocoder;

class PhotosController extends lithiumactionController {

 public function index($tags = null) {
   $conditions = $tags ? compact('tags') : array();
   $photos = Photos::all(compact('conditions'));
   return compact('photos');
 }

 public function view() {
   $photo = Photos::first($this->request->id);
   return compact('photo');
 }
public function near($place = null) {
 $this->_render['template'] = 'index';
 $coords = Geocoder::find('google', $place);

    $photos = Photos::within(array($coords, $coords), array('limit' => 1));
    return compact('photos');
}

public function add() {
  $photo = Photos::create();
  if (($this->request->data) && $photo->save($this->request->data)) {
    $this->redirect(array('Photos::view', 'id' => $photo->_id));
  }
  $this->_render['template'] = 'edit';
  return compact('photo');
}

public function edit() {
  $photo = Photos::find($this->request->id);
  if (!$photo) {
    $this->redirect('Photos::index');
  }
  if (($this->request->data) && $photo->save($this->request->data)) {
    $this->redirect(array('Photos::view', 'id' => $photo->_id));
  }
  return compact('photo');
}
public function near($place = null) {
 $this->_render['template'] = 'index';
 $coords = Geocoder::find('google', $place);

    $photos = Photos::within(array($coords, $coords), array('limit' => 1));
    return compact('photos');
}

public function add() {
  $photo = Photos::create();
                                              Geo Location
  if (($this->request->data) && $photo->save($this->request->data)) {
    $this->redirect(array('Photos::view', 'id' => $photo->_id));
  }
  $this->_render['template'] = 'edit';
  return compact('photo');
}

public function edit() {
  $photo = Photos::find($this->request->id);
  if (!$photo) {
    $this->redirect('Photos::index');
  }
  if (($this->request->data) && $photo->save($this->request->data)) {
    $this->redirect(array('Photos::view', 'id' => $photo->_id));
  }
  return compact('photo');
}
Routes
use   lithiumnethttpRouter;
use   lithiumcoreEnvironment;
use   lithiumactionResponse;
use   photoblogmodelsPhotos;

Router::connect('/photos/view/{:id:[0-9a-f]{24}}.jpeg', array(), function($request) {
  return new Response(array(
    'headers' => array('Content-type' => 'image/jpeg'),
    'body' => Photos::first($request->id)->file->getBytes()
  ));
});

/**
 * Connect the testing routes.
 */
if (!Environment::is('production')) {
  Router::connect('/test/{:args}', array('controller' => 'lithiumtestController'));
  Router::connect('/test', array('controller' => 'lithiumtestController'));
}
Routes
use   lithiumnethttpRouter;
use   lithiumcoreEnvironment;
use   lithiumactionResponse;
use   photoblogmodelsPhotos;

Router::connect('/photos/view/{:id:[0-9a-f]{24}}.jpeg', array(), function($request) {
  return new Response(array(
    'headers' => array('Content-type' => 'image/jpeg'),
    'body' => Photos::first($request->id)->file->getBytes()
  ));
});

/**
 * Connect the testing routes.
                                                                               By-Passes
 */
if (!Environment::is('production')) {                                       the framework
  Router::connect('/test/{:args}', array('controller' => 'lithiumtestController'));
  Router::connect('/test', array('controller' => 'lithiumtestController'));
}
Routes
use   lithiumnethttpRouter;
use   lithiumcoreEnvironment;
use   lithiumactionResponse;
use
        It’s PHP you can
      photoblogmodelsPhotos;

Router::connect('/photos/view/{:id:[0-9a-f]{24}}.jpeg', array(), function($request) {
 return new Response(array( you
            doarray('Content-type' => 'image/jpeg'),
                  what
   'headers' =>
                   want
   'body' => Photos::first($request->id)->file->getBytes()
 ));
});

/**
 * Connect the testing routes.
 */
if (!Environment::is('production')) {
  Router::connect('/test/{:args}', array('controller' => 'lithiumtestController'));
  Router::connect('/test', array('controller' => 'lithiumtestController'));
}
Structure
Features
✦ Full stack MVC          ✦ Object based record-
                            sets
✦ Logger
                          ✦ Command Line
✦ Caching                   Framework

✦ Sessions/Cookies        ✦ Authentication

✦ Full templating suite   ✦ Validator

✦ Integrated TDD suite    ✦ Http Services
Boring
Now for some fun!
World 2




Kick-ass de-coupled hacker happy framework
Are you a
slave to your
framework?
Flexible, transparent boostrap process
Database

namespace models;

class Posts extends lithiumdataModel {

}
define('LITHIUM_LIBRARY_PATH', dirname(__DIR__) . '/libraries');
require LITHIUM_LIBRARY_PATH . '/lithium/core/Libraries.php';

use lithiumcoreLibraries;
Libraries::add('lithium');

use lithiumdataConnections;
Connections::add('default', array(
  'type' => 'MongoDb',
  'database' => 'phpnw',
  'host' => 'localhost'
));

Libraries::add('models', array('path' => __DIR__ . '/models'));

use modelsPosts;
$posts = Posts::create(array('title' => 'Hi Guys'));
$posts->save();
define('LITHIUM_LIBRARY_PATH', dirname(__DIR__) . '/libraries');
require LITHIUM_LIBRARY_PATH . '/lithium/core/Libraries.php';

use lithiumcoreLibraries;
Libraries::add('lithium');                  Simple lithium setup
use lithiumdataConnections;
Connections::add('default', array(
  'type' => 'MongoDb',
  'database' => 'phpnw',
  'host' => 'localhost'
));

Libraries::add('models', array('path' => __DIR__ . '/models'));

use modelsPosts;
$posts = Posts::create(array('title' => 'Hi Guys'));
$posts->save();
define('LITHIUM_LIBRARY_PATH', dirname(__DIR__) . '/libraries');
require LITHIUM_LIBRARY_PATH . '/lithium/core/Libraries.php';

use lithiumcoreLibraries;
Libraries::add('lithium');

use lithiumdataConnections;
Connections::add('default', array(
  'type' => 'MongoDb',
  'database' => 'phpnw',                       DB Config
  'host' => 'localhost'
));

Libraries::add('models', array('path' => __DIR__ . '/models'));

use modelsPosts;
$posts = Posts::create(array('title' => 'Hi Guys'));
$posts->save();
define('LITHIUM_LIBRARY_PATH', dirname(__DIR__) . '/libraries');
require LITHIUM_LIBRARY_PATH . '/lithium/core/Libraries.php';

use lithiumcoreLibraries;
Libraries::add('lithium');

use lithiumdataConnections;
Connections::add('default', array(
  'type' => 'MongoDb',
  'database' => 'phpnw',
  'host' => 'localhost'
));                                         Add models ‘app’
Libraries::add('models', array('path' => __DIR__ . '/models'));

use modelsPosts;
$posts = Posts::create(array('title' => 'Hi Guys'));
$posts->save();
define('LITHIUM_LIBRARY_PATH', dirname(__DIR__) . '/libraries');
require LITHIUM_LIBRARY_PATH . '/lithium/core/Libraries.php';

use lithiumcoreLibraries;
Libraries::add('lithium');

use lithiumdataConnections;
Connections::add('default', array(
  'type' => 'MongoDb',
  'database' => 'phpnw',
  'host' => 'localhost'
));

        DB Save
Libraries::add('models', array('path' => __DIR__ . '/models'));

use modelsPosts;
$posts = Posts::create(array('title' => 'Hi Guys'));
$posts->save();
Routes

define('LITHIUM_LIBRARY_PATH', dirname(__DIR__) . '/libraries');
require LITHIUM_LIBRARY_PATH . '/lithium/core/Libraries.php';

use lithiumcoreLibraries;
Libraries::add('lithium');

use lithiumnethttpRouter;
use lithiumactionRequest;

$request = new Request();
$router = new Router();

$router->connect('/cool-root', array('controller' => 'Yea'));
$router->connect('/cool-root/{:application_id:[0-9]{1}}', array('controller' => 'Yea'));
$router->parse($request);

echo Router::match('Yea::index');
echo Router::match(array('Yea::index', 'application_id' => 1));
Routes

define('LITHIUM_LIBRARY_PATH', dirname(__DIR__) . '/libraries');
require LITHIUM_LIBRARY_PATH . '/lithium/core/Libraries.php';

use lithiumcoreLibraries;
Libraries::add('lithium');

use lithiumnethttpRouter;
use lithiumactionRequest;

$request = new Request();
$router = new Router();

$router->connect('/cool-root', array('controller' => 'Yea'));
$router->connect('/cool-root/{:application_id:[0-9]{1}}', array('controller' => 'Yea'));
$router->parse($request);

echo Router::match('Yea::index');
echo Router::match(array('Yea::index', 'application_id' => 1));
Routes

define('LITHIUM_LIBRARY_PATH', dirname(__DIR__) . '/libraries');
require LITHIUM_LIBRARY_PATH . '/lithium/core/Libraries.php';

use lithiumcoreLibraries;
Libraries::add('lithium');

use lithiumnethttpRouter;
use lithiumactionRequest;

$request = new Request();
$router = new Router();

$router->connect('/cool-root', array('controller' => 'Yea'));
$router->connect('/cool-root/{:application_id:[0-9]{1}}', array('controller' => 'Yea'));
$router->parse($request);

echo Router::match('Yea::index');
echo Router::match(array('Yea::index', 'application_id' => 1));
Routes

define('LITHIUM_LIBRARY_PATH', dirname(__DIR__) . '/libraries');
require LITHIUM_LIBRARY_PATH . '/lithium/core/Libraries.php';

use lithiumcoreLibraries;
Libraries::add('lithium');

use lithiumnethttpRouter;
use lithiumactionRequest;

$request = new Request();
$router = new Router();

$router->connect('/cool-root', array('controller' => 'Yea'));
$router->connect('/cool-root/{:application_id:[0-9]{1}}', array('controller' => 'Yea'));
$router->parse($request);

echo Router::match('Yea::index');
echo Router::match(array('Yea::index', 'application_id' => 1));
/cool-root/4
/cool-root/4

$router->connect('/cool-root/{:application_id:[0-9]{1}}',
                    array('controller' => 'Yea'));
/cool-root/4

$router->connect('/cool-root/{:application_id:[0-9]{1}}',
                    array('controller' => 'Yea'));



  object(lithiumactionRequest)[1]
   public 'url' => string '/cool-root/4' (length=12)
   public 'params' =>
    array (size=3)
      'application_id' => string '4' (length=1)
      'controller' => string 'Yea' (length=3)
      'action' => string 'index' (length=5)
Adaptable Classes

define('LITHIUM_LIBRARY_PATH', dirname(__DIR__) . '/libraries');
define('LITHIUM_APP_PATH', __DIR__);

require   LITHIUM_LIBRARY_PATH     .   '/lithium/core/Libraries.php';
require   LITHIUM_LIBRARY_PATH     .   '/lithium/core/Object.php';
require   LITHIUM_LIBRARY_PATH     .   '/lithium/core/StaticObject.php';
require   LITHIUM_LIBRARY_PATH     .   '/lithium/core/Environment.php';
require   LITHIUM_LIBRARY_PATH     .   '/lithium/core/Adaptable.php';

use lithiumcoreAdaptable;

class Email extends Adaptable{

    protected static $_configurations = array();
    protected static $_adapters = 'emails';

}
Adaptable Classes

define('LITHIUM_LIBRARY_PATH', dirname(__DIR__) . '/libraries');
define('LITHIUM_APP_PATH', __DIR__);

require   LITHIUM_LIBRARY_PATH     .   '/lithium/core/Libraries.php';
require   LITHIUM_LIBRARY_PATH     .   '/lithium/core/Object.php';
require   LITHIUM_LIBRARY_PATH     .   '/lithium/core/StaticObject.php';
require   LITHIUM_LIBRARY_PATH     .   '/lithium/core/Environment.php';
require   LITHIUM_LIBRARY_PATH     .   '/lithium/core/Adaptable.php';

use lithiumcoreAdaptable;

class Email extends Adaptable{

    protected static $_configurations = array();
    protected static $_adapters = 'emails';

}
namespace adapters;
use lithiumcoreLibraries;
Libraries::add('lithium');                      class EmailTest
Libraries::add('adapters',                      {
     array('path' => __DIR__ . '/adapters'));     public function send() {
Libraries::paths(                                   echo 'email test send';
     array('emails' => array(                     }
        '{:library}{:name}')));                }
Email::config(array(
  'development' => array(
    'adapter' => 'EmailReal'
  ),                                            namespace adapters;
  'test' => array(
    'adapter' => 'EmailTest'                    class EmailReal
  ),                                            {
));                                               public function send() {
                                                    echo 'email real send';
use lithiumcoreEnvironment;                     }
                                                }
Environment::set('test');
$env = Environment::get();

Email::adapter($env)->send();
namespace adapters;
use lithiumcoreLibraries;
Libraries::add('lithium');                      class EmailTest
Libraries::add('adapters',                      {
     array('path' => __DIR__ . '/adapters'));     public function send() {
Libraries::paths(                                   echo 'email test send';
     array('emails' => array(                     }
        '{:library}{:name}')));                }
Email::config(array(
  'development' => array(
    'adapter' => 'EmailReal'
  ),                                            namespace adapters;
  'test' => array(
    'adapter' => 'EmailTest'                    class EmailReal
  ),                                            {
));                                               public function send() {
                                                    echo 'email real send';
use lithiumcoreEnvironment;                     }
                                                }
Environment::set('test');
$env = Environment::get();

Email::adapter($env)->send();
namespace adapters;
use lithiumcoreLibraries;
Libraries::add('lithium');                      class EmailTest
Libraries::add('adapters',                      {
     array('path' => __DIR__ . '/adapters'));     public function send() {
Libraries::paths(                                   echo 'email test send';
     array('emails' => array(                     }
        '{:library}{:name}')));                }
Email::config(array(
  'development' => array(
    'adapter' => 'EmailReal'
  ),                                            namespace adapters;
  'test' => array(
    'adapter' => 'EmailTest'                    class EmailReal
  ),                                            {
));                                               public function send() {
                                                    echo 'email real send';
use lithiumcoreEnvironment;                     }
                                                }
Environment::set('test');
$env = Environment::get();

Email::adapter($env)->send();
namespace adapters;
use lithiumcoreLibraries;
Libraries::add('lithium');                      class EmailTest
Libraries::add('adapters',                      {
     array('path' => __DIR__ . '/adapters'));     public function send() {
Libraries::paths(                                   echo 'email test send';
     array('emails' => array(                     }
        '{:library}{:name}')));                }
Email::config(array(
  'development' => array(
    'adapter' => 'EmailReal'
  ),                                            namespace adapters;
  'test' => array(
    'adapter' => 'EmailTest'                    class EmailReal
  ),                                            {
));                                               public function send() {
                                                    echo 'email real send';
use lithiumcoreEnvironment;                     }
                                                }
Environment::set('test');
$env = Environment::get();

Email::adapter($env)->send();
Environment::set('development');

class EmailAnother extends Adaptable{             Already
    protected static $_configurations = array();
    protected static $_adapters = 'emails';
                                                  Environment
    public static function send() {
      static::adapter('default')->send();
                                                  Aware
    }

}

EmailAnother::config(array(
  'default' => array(
    'test' => array (
      'adapter' => 'EmailTest'
    ),
    'development' => array(
      'adapter' => 'EmailReal'
    ),
  )
));

EmailAnother::send();
Environment::set('development');

class EmailAnother extends Adaptable{             Already
    protected static $_configurations = array();
    protected static $_adapters = 'emails';
                                                  Environment
    public static function send() {
      static::adapter('default')->send();
                                                  Aware
    }

}

EmailAnother::config(array(
  'default' => array(
    'test' => array (
      'adapter' => 'EmailTest'
    ),
    'development' => array(
      'adapter' => 'EmailReal'
    ),
  )
));

EmailAnother::send();
Environment::set('development');

class EmailAnother extends Adaptable{             Already
    protected static $_configurations = array();
    protected static $_adapters = 'emails';
                                                  Environment
    public static function send() {
      static::adapter('default')->send();
                                                  Aware
    }

}

EmailAnother::config(array(
  'default' => array(
    'test' => array (
      'adapter' => 'EmailTest'
    ),
    'development' => array(
      'adapter' => 'EmailReal'
    ),
  )
));

EmailAnother::send();
Environment::set('development');

class EmailAnother extends Adaptable{             Already
    protected static $_configurations = array();
    protected static $_adapters = 'emails';
                                                  Environment
    public static function send() {
      static::adapter('default')->send();
                                                  Aware
    }

}

EmailAnother::config(array(
  'default' => array(
    'test' => array (
      'adapter' => 'EmailTest'
    ),
    'development' => array(
      'adapter' => 'EmailReal'
    ),
  )
));

EmailAnother::send();
Filters
define('LITHIUM_LIBRARY_PATH', dirname(__DIR__) . '/libraries');

require LITHIUM_LIBRARY_PATH . '/lithium/core/Object.php';
require LITHIUM_LIBRARY_PATH . '/lithium/util/Collection.php';
require LITHIUM_LIBRARY_PATH . '/lithium/util/collection/Filters.php';

class Action extends lithiumcoreObject {

    public function doSomethingStupid() {
      return $this->_filter(__METHOD__, $params, function($self, $params) {
          $result = ‘Your girlfriend is angry at you’;
          return $result;
      });
    }
}

$action = new Action;
$action->applyFilter('doSomethingStupid', function($self, $params, $chain) {
  echo 'About to do something stupid,';
  $result = $chain->next($self, $params, $chain);
  echo 'You IDIOT!!!!!';
  return $result;
});

$action->doSomthingStupid();
define('LITHIUM_LIBRARY_PATH', dirname(__DIR__) . '/libraries');

require LITHIUM_LIBRARY_PATH . '/lithium/core/Object.php';
require LITHIUM_LIBRARY_PATH . '/lithium/util/Collection.php';
require LITHIUM_LIBRARY_PATH . '/lithium/util/collection/Filters.php';

class Action extends lithiumcoreObject {

    public function doSomethingStupid() {
      return $this->_filter(__METHOD__, $params, function($self, $params) {
          $result = ‘Your girlfriend is angry at you’;
          return $result;

    }
      });
                                                                                 Make
}
                                                                               Filterable
$action = new Action;
$action->applyFilter('doSomethingStupid', function($self, $params, $chain) {
  echo 'About to do something stupid,';
  $result = $chain->next($self, $params, $chain);
  echo 'You IDIOT!!!!!';
  return $result;
});

$action->doSomthingStupid();
define('LITHIUM_LIBRARY_PATH', dirname(__DIR__) . '/libraries');

require LITHIUM_LIBRARY_PATH . '/lithium/core/Object.php';
require LITHIUM_LIBRARY_PATH . '/lithium/util/Collection.php';
require LITHIUM_LIBRARY_PATH . '/lithium/util/collection/Filters.php';

class Action extends lithiumcoreObject {

    public function doSomethingStupid() {
      return $this->_filter(__METHOD__, $params, function($self, $params) {
          $result = ‘Your girlfriend is angry at you’;
          return $result;
      });
    }
}

$action = new Action;
$action->applyFilter('doSomethingStupid', function($self, $params, $chain) {
  echo 'About to do something stupid,';
  $result = $chain->next($self, $params, $chain);
  echo 'You IDIOT!!!!!';
  return $result;
});

$action->doSomthingStupid();
use lithiumanalysisLogger;

Logger::config(array(
  'default' => array(
    'adapter' => 'File',
    'path' => __DIR__ . '/crap/logs/'
  )
));


$action = new Action;
$action->applyFilter('doSomethingStupid', function($self, $params, $chain) {

 Logger::debug(date("D M j G:i:s") . " " . 'About to do something stupid,');

 $result = $chain->next($self, $params, $chain);

 Logger::debug(date("D M j G:i:s") . " " . 'You total moron!');

 return $result;

});

$action->doSomthingStupid();
use lithiumanalysisLogger;

Logger::config(array(
  'default' => array(
    'adapter' => 'File',
    'path' => __DIR__ . '/crap/logs/'
  )
));


$action = new Action;
$action->applyFilter('doSomethingStupid', function($self, $params, $chain) {

 Logger::debug(date("D M j G:i:s") . " " . 'About to do something stupid,');

 $result = $chain->next($self, $params, $chain);

 Logger::debug(date("D M j G:i:s") . " " . 'You total moron!');

 return $result;

});

$action->doSomthingStupid();
use lithiumanalysisLogger;

Logger::config(array(
  'default' => array(
    'adapter' => 'File',
    'path' => __DIR__ . '/crap/logs/'
  )
));


$action = new Action;
$action->applyFilter('doSomethingStupid', function($self, $params, $chain) {

 Logger::debug(date("D M j G:i:s") . " " . 'About to do something stupid,');

 $result = $chain->next($self, $params, $chain);

 Logger::debug(date("D M j G:i:s") . " " . 'You total moron!');

 return $result;

});

$action->doSomthingStupid();
use lithiumanalysisLogger;

Logger::config(array(
  'default' => array(
    'adapter' => 'File',
    'path' => __DIR__ . '/crap/logs/'
  )
));


$action = new Action;
$action->applyFilter('doSomethingStupid', function($self, $params, $chain) {

 Logger::debug(date("D M j G:i:s") . " " . 'About to do something stupid,');

 $result = $chain->next($self, $params, $chain);

 Logger::debug(date("D M j G:i:s") . " " . 'You total moron!');

 return $result;

});

$action->doSomthingStupid();
Find
Cache

 Find
Log

Cache

 Find
Log

Cache

 Find
Log

Cache

 Find
Log

Cache

 Find
Posts::applyFilter('find', function($self, $params, $chain) {

 $result = $chain->next($self, $params, $chain);
 $search = http_build_query($params['options']);
 Logger::debug(
   'Search for: ' . $search . 'returned' . $result->to('json')
 );
 return $result;

});

Posts::applyFilter('find', function($self, $params, $chain) {

 $key = // Make a cache key from params['options']
 if ($result = Cache::read('default', $key)) {
   return $result;
 }
 $result = $chain->next($self, $params, $chain);
 Cache::write('default', $key, $result));
 return $result;

});
Posts::applyFilter('find', function($self, $params, $chain) {

 $result = $chain->next($self, $params, $chain);
 $search = http_build_query($params['options']);
 Logger::debug(
   'Search for: ' . $search . 'returned' . $result->to('json')
 );
 return $result;

});
                     Logging Filter
Posts::applyFilter('find', function($self, $params, $chain) {

 $key = // Make a cache key from params['options']
 if ($result = Cache::read('default', $key)) {
   return $result;
 }
 $result = $chain->next($self, $params, $chain);
 Cache::write('default', $key, $result));
 return $result;

});
Posts::applyFilter('find', function($self, $params, $chain) {

 $result = $chain->next($self, $params, $chain);
 $search = http_build_query($params['options']);
 Logger::debug(

 );                                                     Caching Filter
   'Search for: ' . $search . 'returned' . $result->to('json')

 return $result;

});

Posts::applyFilter('find', function($self, $params, $chain) {

 $key = // Make a cache key from params['options']
 if ($result = Cache::read('default', $key)) {
   return $result;
 }
 $result = $chain->next($self, $params, $chain);
 Cache::write('default', $key, $result));
 return $result;

});
Full Page Caching
Cache::config(array(
  'default' => array(
    'adapter' =>
      'lithiumstoragecacheadapter' . ($apcEnabled ? 'Apc' : 'File')
  )
));

Dispatcher::applyFilter('run', function($self, $params, $chain) {
 $key = md5(LITHIUM_APP_PATH) . '.app.cache.'.md5($params['request']->url);
 if($cache = Cache::read('default', $key)) {
   return $cache;
 }

 $result = $chain->next($self, $params, $chain);

  Cache::write('default', $key, $result, '+1 day');
  return $result;
});
Full Page Caching
                                                 Set Cache Configuration
Cache::config(array(
  'default' => array(
    'adapter' =>
      'lithiumstoragecacheadapter' . ($apcEnabled ? 'Apc' : 'File')
  )
));

Dispatcher::applyFilter('run', function($self, $params, $chain) {
 $key = md5(LITHIUM_APP_PATH) . '.app.cache.'.md5($params['request']->url);
 if($cache = Cache::read('default', $key)) {
   return $cache;
 }

 $result = $chain->next($self, $params, $chain);

  Cache::write('default', $key, $result, '+1 day');
  return $result;
});
Full Page Caching
Cache::config(array(
  'default' => array(
    'adapter' =>
      'lithiumstoragecacheadapter' . ($apcEnabled ? 'Apc' : 'File')
  )
));

Dispatcher::applyFilter('run', function($self, $params, $chain) {
 $key = md5(LITHIUM_APP_PATH) . '.app.cache.'.md5($params['request']->url);
 if($cache = Cache::read('default', $key)) {
   return $cache;
 }

 $result = $chain->next($self, $params, $chain);
                                                      Filter on Dispatcher::run
  Cache::write('default', $key, $result, '+1 day');   Checks for cache w/key
  return $result;
});
Full Page Caching
Cache::config(array(
  'default' => array(
    'adapter' =>
      'lithiumstoragecacheadapter' . ($apcEnabled ? 'Apc' : 'File')
  )
));


      w/out cache content carry on
Dispatcher::applyFilter('run', function($self, $params, $chain) {
 $key = md5(LITHIUM_APP_PATH) . '.app.cache.'.md5($params['request']->url);

        running - saving to cache
 if($cache = Cache::read('default', $key)) {
   return $cache;
 }

 $result = $chain->next($self, $params, $chain);

  Cache::write('default', $key, $result, '+1 day');
  return $result;
});
Content Rendering
namespace dispatcher_appcontrollers;

class YeaController extends lithiumactionController{
 public function index() {
   $yeah = true;
   return compact('yeah');
 }

    public function render(array $options = array()) {
      return 'The response of Yeah is: ' . $this->_render['data']['yeah'];
    }
}
Content Rendering
namespace dispatcher_appcontrollers;

class YeaController extends lithiumactionController{
 public function index() {
   $yeah = true;
   return compact('yeah');
 }

    public function render(array $options = array()) {
      return 'The response of Yeah is: ' . $this->_render['data']['yeah'];
    }
}
Content Rendering
namespace dispatcher_appcontrollers;

class YeaController extends lithiumactionController{
 public function index() {
   $yeah = true;
                      Overwrite ‘render’ method
   return compact('yeah');
 }

    public function render(array $options = array()) {
      return 'The response of Yeah is: ' . $this->_render['data']['yeah'];
    }
}
define('LITHIUM_LIBRARY_PATH', dirname(__DIR__) . '/libraries');

require LITHIUM_LIBRARY_PATH . '/lithium/core/Libraries.php';

use lithiumcoreLibraries;

Libraries::add('lithium');
Libraries::add('dispatcher_app', array('path' => __DIR__ . '/dispatcher_app'));

use lithiumnethttpRouter;
$router = new Router();

Router::connect('/cool-root', array('controller' => 'Yea', 'library' => 'dispatcher_app'));

echo lithiumactionDispatcher::run(
  new lithiumactionRequest()
);
define('LITHIUM_LIBRARY_PATH', dirname(__DIR__) . '/libraries');

require LITHIUM_LIBRARY_PATH . '/lithium/core/Libraries.php';

use lithiumcoreLibraries;

Libraries::add('lithium');
                                 Create Routing
Libraries::add('dispatcher_app', array('path' => __DIR__ . '/dispatcher_app'));

use lithiumnethttpRouter;
$router = new Router();

Router::connect('/cool-root', array('controller' => 'Yea', 'library' => 'dispatcher_app'));

echo lithiumactionDispatcher::run(
  new lithiumactionRequest()
);
define('LITHIUM_LIBRARY_PATH', dirname(__DIR__) . '/libraries');

require LITHIUM_LIBRARY_PATH . '/lithium/core/Libraries.php';

use lithiumcoreLibraries;

Libraries::add('lithium');
Libraries::add('dispatcher_app', array('path' => __DIR__ . '/dispatcher_app'));

use lithiumnethttpRouter;
$router = new Router();

                                                 Run Dispatcher
Router::connect('/cool-root', array('controller' => 'Yea', 'library' => 'dispatcher_app'));

echo lithiumactionDispatcher::run(
  new lithiumactionRequest()
);
Media Class
Media::type('ajax', array('application/xhtml+xml', 'text/html'), array(
  'view' => 'lithiumtemplateView',
  'paths' => array(
    'template' => array(
      '{:library}/views/{:controller}/{:template}.ajax.php',
      '{:library}/views/{:controller}/{:template}.html.php'
    ),
    'layout' => '{:library}/views/layouts/default.ajax.php'
  ),
  'conditions' => array('ajax' => true)
));




                                        Easily setup
                                      template paths
Media::type('jpg', 'image/jpeg', array('cast' => false, 'encode' => function($data) {
  return $data['photo']->file->getBytes();
}));

Media::type('json', 'application/json', array('cast' => false, 'encode' => function($data) {
  return json_encode($data);
}));

Media::type('json', 'application/json', array(
   'cast' => false, 'encode' => 'json_encode')
);
Media::type('jpg', 'image/jpeg', array('cast' => false, 'encode' => function($data) {
  return $data['photo']->file->getBytes();
}));

                                                            Handle Images
Media::type('json', 'application/json', array('cast' => false, 'encode' => function($data) {
  return json_encode($data);
}));

Media::type('json', 'application/json', array(
   'cast' => false, 'encode' => 'json_encode')
);
Media::type('jpg', 'image/jpeg', array('cast' => false, 'encode' => function($data) {
  return $data['photo']->file->getBytes();
}));

Media::type('json', 'application/json', array('cast' => false, 'encode' => function($data) {
  return json_encode($data);
}));

Media::type('json', 'application/json', array(
   'cast' => false, 'encode' => 'json_encode')
);
                                                        Handle JSON
Error Handling
Error Handling
use lithiumcoreErrorHandler;
use lithiumcoreStaticObject;

class IThrowAnException
{
  public function method() {
    throw new Exception('Yo');
  }
}

class IDontThrow extends StaticObject
{
  public static function method($bar) {
    static::_filter(__FUNCTION__, $params, function($self, $params) {
      $object = new IThrowAnException;
      $object->method();
    });
  }
}

ErrorHandler::apply('IDontThrow::method', array(), function($info, $params) {
  echo 'Exception raised with message - ' . $info['exception']->getMessage();
});

IDontThrowAnException::method();
Error Handling
use lithiumcoreErrorHandler;
use lithiumcoreStaticObject;

class IThrowAnException
{
  public function method() {
    throw new Exception('Yo');
  }
}

class IDontThrow extends StaticObject
{
  public static function method($bar) {
    static::_filter(__FUNCTION__, $params, function($self, $params) {
      $object = new IThrowAnException;
      $object->method();
    });
  }
}

ErrorHandler::apply('IDontThrow::method', array(), function($info, $params) {
  echo 'Exception raised with message - ' . $info['exception']->getMessage();
});

IDontThrowAnException::method();
Error Handling
use lithiumcoreErrorHandler;
use lithiumcoreStaticObject;

class IThrowAnException
{
  public function method() {
    throw new Exception('Yo');
  }                                                                             Filterable
}

class IDontThrow extends StaticObject
{
  public static function method($bar) {
    static::_filter(__FUNCTION__, $params, function($self, $params) {
      $object = new IThrowAnException;
      $object->method();
    });
  }
}

ErrorHandler::apply('IDontThrow::method', array(), function($info, $params) {
  echo 'Exception raised with message - ' . $info['exception']->getMessage();
});

IDontThrowAnException::method();
Error Handling
use lithiumcoreErrorHandler;
use lithiumcoreStaticObject;

class IThrowAnException
{
  public function method() {
    throw new Exception('Yo');
  }
}

class IDontThrow extends StaticObject
{
  public static function method($bar) {
    static::_filter(__FUNCTION__, $params, function($self, $params) {
      $object = new IThrowAnException;
      $object->method();
    });
  }
}

ErrorHandler::apply('IDontThrow::method', array(), function($info, $params) {
  echo 'Exception raised with message - ' . $info['exception']->getMessage();
});

IDontThrowAnException::method();
ErrorHandler::apply('lithiumactionDispatcher::run', array(),
 function($info, $params) {
 $response = new Response(array(
   'request' => $params['request'],
   'status' => $info['exception']->getCode(),
   'trace' => $info['exception']->getTrace(),
 ));

 Media::render($response, compact('info', 'params'), array(
  'controller' => 'errors',
  'template' => 'default',
  'layout' => 'default',
  'library' => 'golf',
  'request' => $params['request'],

  ));
  return $response;
});




                                                        Example
Create
ErrorHandler::apply('lithiumactionDispatcher::run', array(),
 function($info, $params) {
                                                                  Response
 $response = new Response(array(
   'request' => $params['request'],
   'status' => $info['exception']->getCode(),
   'trace' => $info['exception']->getTrace(),
 ));

 Media::render($response, compact('info', 'params'), array(
  'controller' => 'errors',
  'template' => 'default',
  'layout' => 'default',
  'library' => 'golf',
  'request' => $params['request'],

  ));
  return $response;
});




                                                        Example
ErrorHandler::apply('lithiumactionDispatcher::run', array(),
 function($info, $params) {
 $response = new Response(array(
   'request' => $params['request'],
   'status' => $info['exception']->getCode(),
   'trace' => $info['exception']->getTrace(),
 ));

 Media::render($response, compact('info', 'params'), array(
  'controller' => 'errors',
  'template' => 'default',
  'layout' => 'default',
  'library' => 'golf',                                            Media
  'request' => $params['request'],
                                                                  Render
  ));
  return $response;
});




                                                        Example
Zendy Love

Libraryies::add(
  'prefix' => 'Zend_',
  'includePath' => '/htdocs/libraries/Zend/trunk/library',
  'bootstrap' => 'Loader/Autoloader.php',
  'loader' => array('Zend_Loader_Autoloader', 'autoload'),
  'transform' => function($class) { return str_replace('_', '/', $class) . '.php'; }
);
define('LITHIUM_LIBRARY_PATH', dirname(__DIR__) . '/libraries');
define('LITHIUM_APP_PATH', __DIR__ . '/templates');

require LITHIUM_LIBRARY_PATH . '/lithium/core/Libraries.php';

use   lithiumcoreLibraries;
use   lithiumcoreEnvironment;
use   lithiumactionDispatcher;
use   lithiumg11nMessage;
use   lithiumnethttpMedia;

Libraries::add('lithium');
Libraries::add('li3_docs');
Libraries::add('templates', array('path' => __DIR__ . '/templates',
                               'default' => true));
Libraries::add('adapters', array('path' => __DIR__ . '/adapters'));

Libraries::add('li3_docs', array(
    'index' => array('lithium', 'adapters')
));




                            Libraries Example
define('LITHIUM_LIBRARY_PATH', dirname(__DIR__) . '/libraries');
define('LITHIUM_APP_PATH', __DIR__ . '/templates');

require LITHIUM_LIBRARY_PATH . '/lithium/core/Libraries.php';

use   lithiumcoreLibraries;                                          Sets up
use   lithiumcoreEnvironment;
use
use
      lithiumactionDispatcher;
      lithiumg11nMessage;
                                                                      Libraries
use   lithiumnethttpMedia;

Libraries::add('lithium');
Libraries::add('li3_docs');
Libraries::add('templates', array('path' => __DIR__ . '/templates',
                               'default' => true));
Libraries::add('adapters', array('path' => __DIR__ . '/adapters'));

Libraries::add('li3_docs', array(
    'index' => array('lithium', 'adapters')
));




                            Libraries Example
$locale = 'en';
$locales = array('en' => 'English');

Environment::set('development', compact('locale', 'locales'));
Environment::set('development'); }
    $file = "{$config['path']}/config/routes.php";
    file_exists($file) ? call_user_func(function() use ($file) { include $file; }) : null;
  }
  return $chain->next($self, $params, $chain);
});

Media::applyFilter('_handle', function($self, $params, $chain) {
  $params['handler'] += array('outputFilters' => array());
  $params['handler']['outputFilters'] += Message::aliases();
  return $chain->next($self, $params, $chain);
});

echo lithiumactionDispatcher::run(new lithiumactionRequest());
Locale &
$locale = 'en';
                                                                                          Routes
$locales = array('en' => 'English');

Environment::set('development', compact('locale', 'locales'));
Environment::set('development'); }
    $file = "{$config['path']}/config/routes.php";
    file_exists($file) ? call_user_func(function() use ($file) { include $file; }) : null;
  }
  return $chain->next($self, $params, $chain);
});

Media::applyFilter('_handle', function($self, $params, $chain) {
  $params['handler'] += array('outputFilters' => array());
  $params['handler']['outputFilters'] += Message::aliases();
  return $chain->next($self, $params, $chain);
});

echo lithiumactionDispatcher::run(new lithiumactionRequest());
$locale = 'en';
$locales = array('en' => 'English');

Environment::set('development', compact('locale', 'locales'));
Environment::set('development'); }
    $file = "{$config['path']}/config/routes.php";
    file_exists($file) ? call_user_func(function() use ($file) { include $file; }) : null;
  }
  return $chain->next($self, $params, $chain);
});

Media::applyFilter('_handle', function($self, $params, $chain) {
  $params['handler'] += array('outputFilters' => array());
  $params['handler']['outputFilters'] += Message::aliases();
  return $chain->next($self, $params, $chain);
                                                                                             Gets
});                                                                                      translations
echo lithiumactionDispatcher::run(new lithiumactionRequest());
$locale = 'en';
$locales = array('en' => 'English');

Environment::set('development', compact('locale', 'locales'));
Environment::set('development'); }
    $file = "{$config['path']}/config/routes.php";
    file_exists($file) ? call_user_func(function() use ($file) { include $file; }) : null;
  }
  return $chain->next($self, $params, $chain);
});

Media::applyFilter('_handle', function($self, $params, $chain) {
  $params['handler'] += array('outputFilters' => array());
  $params['handler']['outputFilters'] += Message::aliases();
  return $chain->next($self, $params, $chain);
});

echo lithiumactionDispatcher::run(new lithiumactionRequest());




                               Go
Recap
★ Hackers framework
                      Recap
★ Hackers framework
                      Recap
★ Light, fast & fun
★ Hackers framework
                            Recap
★ Light, fast & fun

★ Services have adaptable base
★ Hackers framework
                            Recap
★ Light, fast & fun

★ Services have adaptable base

★ Filters are awesome/powerful
★ Hackers framework
                            Recap
★ Light, fast & fun

★ Services have adaptable base

★ Filters are awesome/powerful

★ Everything is a library
★ Hackers framework
                            Recap
★ Light, fast & fun

★ Services have adaptable base

★ Filters are awesome/powerful

★ Everything is a library

★ As much or as little framework as you
  need
★ Hackers framework
                            Recap
★ Light, fast & fun

★ Services have adaptable base

★ Filters are awesome/powerful

★ Everything is a library

★ As much or as little framework as you
  need

★ Great balance weight/power/simplicity
Thanks
github.com/mackstar/phpnw-li3




   @mackstar | #mackstar

Lithium Best

  • 1.
    Lithium (#li3) The frameworkwith the best of both worlds
  • 2.
    Richard McIntyre: @mackstar ★Lithium core team ★ Freelancer - Leeds/Manchester ★ Most recently at BBC, Mamas & Papas, UOR ★ Engineer on BBC Olympics App ★ Lived in Japan 15 years ★ Done PHP for donkeys
  • 4.
  • 6.
    I’ve seen it ✦Symfony 2 ✦ Merb ✦ Zend Framework ✦ Sinatra ✦ CakePHP ✦ Spring ✦ Code Igniter ✦ Stripes (Java) ✦ Drupal 6&7 ✦ Django Wordpress ✦ Silex ✦ ✦ Rails
  • 8.
  • 9.
    Each software solutionshould be in proportion to the problem being solved.
  • 10.
    Each software solutionshould be in proportion to the problem being solved. -some lazy bugger
  • 11.
    Each software solutionshould be in proportion to the problem being solved. -some lazy bugger me
  • 12.
    <?php use BehatMinkExtensionContextMinkContext; class FeatureContextextends MinkContext { /** * @Then /^I wait for the suggestion box to appear$/ */ public function myNameHasAMeaninglessExistance() { $this->getSession()->wait(5000, "$('.suggestions-results').children().length > 0"); } }
  • 13.
    All problems incomputer science can be solved by another level of indirection. Except for the problem of too many layers of indirection. Butler Lampson, David Wheeler
  • 14.
    Code Sexy-ness There’s nothinglike the look of beautiful Ruby code in the morning. Gracefully colored by TextMate and rendered in Bitstream Vera pt 12. @dhh
  • 15.
    Developer Happy-ness Any foolcan write code that a computer can understand. Good programmers write code that humans can understand. –Martin Fowler
  • 16.
  • 17.
  • 18.
    Looking for ahackers Framework? Light Footprint
  • 19.
  • 20.
    World 1 Full StackFramework Convention Over Configuration
  • 21.
  • 22.
    The Photo BlogTutorial github.com/nateabele/photoblog
  • 23.
    Model namespace photoblogmodels; class Photosextends lithiumdataModel { public $validates = array(); protected $_meta = array('source' => 'fs.files'); public function save($entity, $data = null, array $options = array()) { if ($data) { $entity->set($data); } if ($entity->tags && !is_array($entity->tags)) { $entity->tags = array_map('trim', explode(',', $entity->tags)); } return parent::save($entity, null, $options); } }
  • 24.
    Model namespace photoblogmodels; class Photosextends lithiumdataModel { public $validates = array(); protected $_meta = array('source' => 'fs.files'); public function save($entity, $data = null, array $options = array()) { if ($data) { $entity->set($data); } if ($entity->tags && !is_array($entity->tags)) { $entity->tags = array_map('trim', explode(',', $entity->tags)); } return parent::save($entity, null, $options); } }
  • 25.
    Model namespace photoblogmodels; class Photosextends lithiumdataModel { public $validates = array(); protected $_meta = array('source' => 'fs.files'); public function save($entity, $data = null, array $options = array()) { if ($data) { $entity->set($data); } if ($entity->tags && !is_array($entity->tags)) { $entity->tags = array_map('trim', explode(',', $entity->tags)); } return parent::save($entity, null, $options); } }
  • 26.
    Views <?=$this->form->create($photo, array('type' =>'file')); ?> <?=$this->form->field('title'); ?> <?=$this->form->field('description'); ?> <?php if (!$photo->exists()): ?> <?=$this->form->field('file', array('type' => 'file')); ?> <?php endif; ?> <?=$this->form->field('tags', array('label' => 'Add tags separated by commas')); ?> <?=$this->form->submit('Save'); ?> <?=$this->form->end(); ?>
  • 27.
    Views <?=$this->form->create($photo, array('type' =>'file')); ?> <?=$this->form->field('title'); ?> <?=$this->form->field('description'); ?> <?php if (!$photo->exists()): ?> <?=$this->form->field('file', array('type' => 'file')); ?> <?php endif; ?> <?=$this->form->field('tags', array('label' => 'Add tags separated by commas')); ?> <?=$this->form->submit('Save'); ?> <?=$this->form->end(); ?>
  • 28.
    Views <?=$this->form->create($photo, array('type' =>'file')); ?> <?=$this->form->field('title'); ?> <?=$this->form->field('description'); ?> <?php if (!$photo->exists()): ?> <?=$this->form->field('file', array('type' => 'file')); ?> <?php endif; ?> <?=$this->form->field('tags', array('label' => 'Add tags separated by commas')); ?> <?=$this->form->submit('Save'); ?> <?=$this->form->end(); ?>
  • 29.
    <h1> <?=$this->title($photo->title); ?> <em>[ <?=$this->html->link( 'edit', array('Photos::edit', 'id' => $photo->_id) ); ?> ]</em> </h1> <p><?=$photo->description; ?></p> <?php if ($photo->tags): ?> Tags: <?php foreach ($photo->tags as $tag): ?> <?=$this->html->link( $tag, array('Photos::index', 'args' => array($tag)) ); ?> <?php endforeach; ?> <?php endif; ?> <?=$this->html->image( "/photos/view/{$photo->_id}.jpeg", array( 'alt' => $photo->title, 'width' => "100%" ) ); ?>
  • 30.
    <h1> <?=$this->title($photo->title); ?> <em>[ <?=$this->html->link( 'edit', array('Photos::edit', 'id' => $photo->_id) ); ?> ]</em> </h1> <p><?=$photo->description; ?></p> <?php if ($photo->tags): ?> Tags: <?php foreach ($photo->tags as $tag): ?> <?=$this->html->link( $tag, array('Photos::index', 'args' => array($tag)) ); ?> <?php endforeach; ?> <?php endif; ?> <?=$this->html->image( "/photos/view/{$photo->_id}.jpeg", array( 'alt' => $photo->title, 'width' => "100%" ) ); ?>
  • 31.
    <h1> <?=$this->title($photo->title); ?> <em>[ <?=$this->html->link( 'edit', array('Photos::edit', 'id' => $photo->_id) ); ?> ]</em> </h1> <p><?=$photo->description; ?></p> <?php if ($photo->tags): ?> Tags: <?php foreach ($photo->tags as $tag): ?> <?=$this->html->link( $tag, array('Photos::index', 'args' => array($tag)) ); ?> <?php endforeach; ?> <?php endif; ?> <?=$this->html->image( "/photos/view/{$photo->_id}.jpeg", array( 'alt' => $photo->title, 'width' => "100%" ) ); ?>
  • 32.
    Controller namespace photoblogcontrollers; use photoblogmodelsPhotos; useli3_geoextensionsGeocoder; class PhotosController extends lithiumactionController { public function index($tags = null) { $conditions = $tags ? compact('tags') : array(); $photos = Photos::all(compact('conditions')); return compact('photos'); } public function view() { $photo = Photos::first($this->request->id); return compact('photo'); }
  • 33.
    Controller namespace photoblogcontrollers; use photoblogmodelsPhotos; useli3_geoextensionsGeocoder; class PhotosController extends lithiumactionController { public function index($tags = null) { $conditions = $tags ? compact('tags') : array(); $photos = Photos::all(compact('conditions')); return compact('photos'); } public function view() { $photo = Photos::first($this->request->id); return compact('photo'); }
  • 34.
    Controller namespace photoblogcontrollers; use photoblogmodelsPhotos; useli3_geoextensionsGeocoder; class PhotosController extends lithiumactionController { public function index($tags = null) { $conditions = $tags ? compact('tags') : array(); $photos = Photos::all(compact('conditions')); return compact('photos'); } public function view() { $photo = Photos::first($this->request->id); return compact('photo'); }
  • 35.
    public function near($place= null) { $this->_render['template'] = 'index'; $coords = Geocoder::find('google', $place); $photos = Photos::within(array($coords, $coords), array('limit' => 1)); return compact('photos'); } public function add() { $photo = Photos::create(); if (($this->request->data) && $photo->save($this->request->data)) { $this->redirect(array('Photos::view', 'id' => $photo->_id)); } $this->_render['template'] = 'edit'; return compact('photo'); } public function edit() { $photo = Photos::find($this->request->id); if (!$photo) { $this->redirect('Photos::index'); } if (($this->request->data) && $photo->save($this->request->data)) { $this->redirect(array('Photos::view', 'id' => $photo->_id)); } return compact('photo'); }
  • 36.
    public function near($place= null) { $this->_render['template'] = 'index'; $coords = Geocoder::find('google', $place); $photos = Photos::within(array($coords, $coords), array('limit' => 1)); return compact('photos'); } public function add() { $photo = Photos::create(); Geo Location if (($this->request->data) && $photo->save($this->request->data)) { $this->redirect(array('Photos::view', 'id' => $photo->_id)); } $this->_render['template'] = 'edit'; return compact('photo'); } public function edit() { $photo = Photos::find($this->request->id); if (!$photo) { $this->redirect('Photos::index'); } if (($this->request->data) && $photo->save($this->request->data)) { $this->redirect(array('Photos::view', 'id' => $photo->_id)); } return compact('photo'); }
  • 37.
    Routes use lithiumnethttpRouter; use lithiumcoreEnvironment; use lithiumactionResponse; use photoblogmodelsPhotos; Router::connect('/photos/view/{:id:[0-9a-f]{24}}.jpeg', array(), function($request) { return new Response(array( 'headers' => array('Content-type' => 'image/jpeg'), 'body' => Photos::first($request->id)->file->getBytes() )); }); /** * Connect the testing routes. */ if (!Environment::is('production')) { Router::connect('/test/{:args}', array('controller' => 'lithiumtestController')); Router::connect('/test', array('controller' => 'lithiumtestController')); }
  • 38.
    Routes use lithiumnethttpRouter; use lithiumcoreEnvironment; use lithiumactionResponse; use photoblogmodelsPhotos; Router::connect('/photos/view/{:id:[0-9a-f]{24}}.jpeg', array(), function($request) { return new Response(array( 'headers' => array('Content-type' => 'image/jpeg'), 'body' => Photos::first($request->id)->file->getBytes() )); }); /** * Connect the testing routes. By-Passes */ if (!Environment::is('production')) { the framework Router::connect('/test/{:args}', array('controller' => 'lithiumtestController')); Router::connect('/test', array('controller' => 'lithiumtestController')); }
  • 39.
    Routes use lithiumnethttpRouter; use lithiumcoreEnvironment; use lithiumactionResponse; use It’s PHP you can photoblogmodelsPhotos; Router::connect('/photos/view/{:id:[0-9a-f]{24}}.jpeg', array(), function($request) { return new Response(array( you doarray('Content-type' => 'image/jpeg'), what 'headers' => want 'body' => Photos::first($request->id)->file->getBytes() )); }); /** * Connect the testing routes. */ if (!Environment::is('production')) { Router::connect('/test/{:args}', array('controller' => 'lithiumtestController')); Router::connect('/test', array('controller' => 'lithiumtestController')); }
  • 40.
  • 41.
    Features ✦ Full stackMVC ✦ Object based record- sets ✦ Logger ✦ Command Line ✦ Caching Framework ✦ Sessions/Cookies ✦ Authentication ✦ Full templating suite ✦ Validator ✦ Integrated TDD suite ✦ Http Services
  • 42.
  • 43.
  • 44.
    World 2 Kick-ass de-coupledhacker happy framework
  • 45.
    Are you a slaveto your framework?
  • 48.
  • 49.
    Database namespace models; class Postsextends lithiumdataModel { }
  • 50.
    define('LITHIUM_LIBRARY_PATH', dirname(__DIR__) .'/libraries'); require LITHIUM_LIBRARY_PATH . '/lithium/core/Libraries.php'; use lithiumcoreLibraries; Libraries::add('lithium'); use lithiumdataConnections; Connections::add('default', array( 'type' => 'MongoDb', 'database' => 'phpnw', 'host' => 'localhost' )); Libraries::add('models', array('path' => __DIR__ . '/models')); use modelsPosts; $posts = Posts::create(array('title' => 'Hi Guys')); $posts->save();
  • 51.
    define('LITHIUM_LIBRARY_PATH', dirname(__DIR__) .'/libraries'); require LITHIUM_LIBRARY_PATH . '/lithium/core/Libraries.php'; use lithiumcoreLibraries; Libraries::add('lithium'); Simple lithium setup use lithiumdataConnections; Connections::add('default', array( 'type' => 'MongoDb', 'database' => 'phpnw', 'host' => 'localhost' )); Libraries::add('models', array('path' => __DIR__ . '/models')); use modelsPosts; $posts = Posts::create(array('title' => 'Hi Guys')); $posts->save();
  • 52.
    define('LITHIUM_LIBRARY_PATH', dirname(__DIR__) .'/libraries'); require LITHIUM_LIBRARY_PATH . '/lithium/core/Libraries.php'; use lithiumcoreLibraries; Libraries::add('lithium'); use lithiumdataConnections; Connections::add('default', array( 'type' => 'MongoDb', 'database' => 'phpnw', DB Config 'host' => 'localhost' )); Libraries::add('models', array('path' => __DIR__ . '/models')); use modelsPosts; $posts = Posts::create(array('title' => 'Hi Guys')); $posts->save();
  • 53.
    define('LITHIUM_LIBRARY_PATH', dirname(__DIR__) .'/libraries'); require LITHIUM_LIBRARY_PATH . '/lithium/core/Libraries.php'; use lithiumcoreLibraries; Libraries::add('lithium'); use lithiumdataConnections; Connections::add('default', array( 'type' => 'MongoDb', 'database' => 'phpnw', 'host' => 'localhost' )); Add models ‘app’ Libraries::add('models', array('path' => __DIR__ . '/models')); use modelsPosts; $posts = Posts::create(array('title' => 'Hi Guys')); $posts->save();
  • 54.
    define('LITHIUM_LIBRARY_PATH', dirname(__DIR__) .'/libraries'); require LITHIUM_LIBRARY_PATH . '/lithium/core/Libraries.php'; use lithiumcoreLibraries; Libraries::add('lithium'); use lithiumdataConnections; Connections::add('default', array( 'type' => 'MongoDb', 'database' => 'phpnw', 'host' => 'localhost' )); DB Save Libraries::add('models', array('path' => __DIR__ . '/models')); use modelsPosts; $posts = Posts::create(array('title' => 'Hi Guys')); $posts->save();
  • 55.
    Routes define('LITHIUM_LIBRARY_PATH', dirname(__DIR__) .'/libraries'); require LITHIUM_LIBRARY_PATH . '/lithium/core/Libraries.php'; use lithiumcoreLibraries; Libraries::add('lithium'); use lithiumnethttpRouter; use lithiumactionRequest; $request = new Request(); $router = new Router(); $router->connect('/cool-root', array('controller' => 'Yea')); $router->connect('/cool-root/{:application_id:[0-9]{1}}', array('controller' => 'Yea')); $router->parse($request); echo Router::match('Yea::index'); echo Router::match(array('Yea::index', 'application_id' => 1));
  • 56.
    Routes define('LITHIUM_LIBRARY_PATH', dirname(__DIR__) .'/libraries'); require LITHIUM_LIBRARY_PATH . '/lithium/core/Libraries.php'; use lithiumcoreLibraries; Libraries::add('lithium'); use lithiumnethttpRouter; use lithiumactionRequest; $request = new Request(); $router = new Router(); $router->connect('/cool-root', array('controller' => 'Yea')); $router->connect('/cool-root/{:application_id:[0-9]{1}}', array('controller' => 'Yea')); $router->parse($request); echo Router::match('Yea::index'); echo Router::match(array('Yea::index', 'application_id' => 1));
  • 57.
    Routes define('LITHIUM_LIBRARY_PATH', dirname(__DIR__) .'/libraries'); require LITHIUM_LIBRARY_PATH . '/lithium/core/Libraries.php'; use lithiumcoreLibraries; Libraries::add('lithium'); use lithiumnethttpRouter; use lithiumactionRequest; $request = new Request(); $router = new Router(); $router->connect('/cool-root', array('controller' => 'Yea')); $router->connect('/cool-root/{:application_id:[0-9]{1}}', array('controller' => 'Yea')); $router->parse($request); echo Router::match('Yea::index'); echo Router::match(array('Yea::index', 'application_id' => 1));
  • 58.
    Routes define('LITHIUM_LIBRARY_PATH', dirname(__DIR__) .'/libraries'); require LITHIUM_LIBRARY_PATH . '/lithium/core/Libraries.php'; use lithiumcoreLibraries; Libraries::add('lithium'); use lithiumnethttpRouter; use lithiumactionRequest; $request = new Request(); $router = new Router(); $router->connect('/cool-root', array('controller' => 'Yea')); $router->connect('/cool-root/{:application_id:[0-9]{1}}', array('controller' => 'Yea')); $router->parse($request); echo Router::match('Yea::index'); echo Router::match(array('Yea::index', 'application_id' => 1));
  • 60.
  • 61.
  • 62.
    /cool-root/4 $router->connect('/cool-root/{:application_id:[0-9]{1}}', array('controller' => 'Yea')); object(lithiumactionRequest)[1] public 'url' => string '/cool-root/4' (length=12) public 'params' => array (size=3) 'application_id' => string '4' (length=1) 'controller' => string 'Yea' (length=3) 'action' => string 'index' (length=5)
  • 63.
    Adaptable Classes define('LITHIUM_LIBRARY_PATH', dirname(__DIR__). '/libraries'); define('LITHIUM_APP_PATH', __DIR__); require LITHIUM_LIBRARY_PATH . '/lithium/core/Libraries.php'; require LITHIUM_LIBRARY_PATH . '/lithium/core/Object.php'; require LITHIUM_LIBRARY_PATH . '/lithium/core/StaticObject.php'; require LITHIUM_LIBRARY_PATH . '/lithium/core/Environment.php'; require LITHIUM_LIBRARY_PATH . '/lithium/core/Adaptable.php'; use lithiumcoreAdaptable; class Email extends Adaptable{ protected static $_configurations = array(); protected static $_adapters = 'emails'; }
  • 64.
    Adaptable Classes define('LITHIUM_LIBRARY_PATH', dirname(__DIR__). '/libraries'); define('LITHIUM_APP_PATH', __DIR__); require LITHIUM_LIBRARY_PATH . '/lithium/core/Libraries.php'; require LITHIUM_LIBRARY_PATH . '/lithium/core/Object.php'; require LITHIUM_LIBRARY_PATH . '/lithium/core/StaticObject.php'; require LITHIUM_LIBRARY_PATH . '/lithium/core/Environment.php'; require LITHIUM_LIBRARY_PATH . '/lithium/core/Adaptable.php'; use lithiumcoreAdaptable; class Email extends Adaptable{ protected static $_configurations = array(); protected static $_adapters = 'emails'; }
  • 65.
    namespace adapters; use lithiumcoreLibraries; Libraries::add('lithium'); class EmailTest Libraries::add('adapters', { array('path' => __DIR__ . '/adapters')); public function send() { Libraries::paths( echo 'email test send'; array('emails' => array( } '{:library}{:name}'))); } Email::config(array( 'development' => array( 'adapter' => 'EmailReal' ), namespace adapters; 'test' => array( 'adapter' => 'EmailTest' class EmailReal ), { )); public function send() { echo 'email real send'; use lithiumcoreEnvironment; } } Environment::set('test'); $env = Environment::get(); Email::adapter($env)->send();
  • 66.
    namespace adapters; use lithiumcoreLibraries; Libraries::add('lithium'); class EmailTest Libraries::add('adapters', { array('path' => __DIR__ . '/adapters')); public function send() { Libraries::paths( echo 'email test send'; array('emails' => array( } '{:library}{:name}'))); } Email::config(array( 'development' => array( 'adapter' => 'EmailReal' ), namespace adapters; 'test' => array( 'adapter' => 'EmailTest' class EmailReal ), { )); public function send() { echo 'email real send'; use lithiumcoreEnvironment; } } Environment::set('test'); $env = Environment::get(); Email::adapter($env)->send();
  • 67.
    namespace adapters; use lithiumcoreLibraries; Libraries::add('lithium'); class EmailTest Libraries::add('adapters', { array('path' => __DIR__ . '/adapters')); public function send() { Libraries::paths( echo 'email test send'; array('emails' => array( } '{:library}{:name}'))); } Email::config(array( 'development' => array( 'adapter' => 'EmailReal' ), namespace adapters; 'test' => array( 'adapter' => 'EmailTest' class EmailReal ), { )); public function send() { echo 'email real send'; use lithiumcoreEnvironment; } } Environment::set('test'); $env = Environment::get(); Email::adapter($env)->send();
  • 68.
    namespace adapters; use lithiumcoreLibraries; Libraries::add('lithium'); class EmailTest Libraries::add('adapters', { array('path' => __DIR__ . '/adapters')); public function send() { Libraries::paths( echo 'email test send'; array('emails' => array( } '{:library}{:name}'))); } Email::config(array( 'development' => array( 'adapter' => 'EmailReal' ), namespace adapters; 'test' => array( 'adapter' => 'EmailTest' class EmailReal ), { )); public function send() { echo 'email real send'; use lithiumcoreEnvironment; } } Environment::set('test'); $env = Environment::get(); Email::adapter($env)->send();
  • 69.
    Environment::set('development'); class EmailAnother extendsAdaptable{ Already protected static $_configurations = array(); protected static $_adapters = 'emails'; Environment public static function send() { static::adapter('default')->send(); Aware } } EmailAnother::config(array( 'default' => array( 'test' => array ( 'adapter' => 'EmailTest' ), 'development' => array( 'adapter' => 'EmailReal' ), ) )); EmailAnother::send();
  • 70.
    Environment::set('development'); class EmailAnother extendsAdaptable{ Already protected static $_configurations = array(); protected static $_adapters = 'emails'; Environment public static function send() { static::adapter('default')->send(); Aware } } EmailAnother::config(array( 'default' => array( 'test' => array ( 'adapter' => 'EmailTest' ), 'development' => array( 'adapter' => 'EmailReal' ), ) )); EmailAnother::send();
  • 71.
    Environment::set('development'); class EmailAnother extendsAdaptable{ Already protected static $_configurations = array(); protected static $_adapters = 'emails'; Environment public static function send() { static::adapter('default')->send(); Aware } } EmailAnother::config(array( 'default' => array( 'test' => array ( 'adapter' => 'EmailTest' ), 'development' => array( 'adapter' => 'EmailReal' ), ) )); EmailAnother::send();
  • 72.
    Environment::set('development'); class EmailAnother extendsAdaptable{ Already protected static $_configurations = array(); protected static $_adapters = 'emails'; Environment public static function send() { static::adapter('default')->send(); Aware } } EmailAnother::config(array( 'default' => array( 'test' => array ( 'adapter' => 'EmailTest' ), 'development' => array( 'adapter' => 'EmailReal' ), ) )); EmailAnother::send();
  • 73.
  • 74.
    define('LITHIUM_LIBRARY_PATH', dirname(__DIR__) .'/libraries'); require LITHIUM_LIBRARY_PATH . '/lithium/core/Object.php'; require LITHIUM_LIBRARY_PATH . '/lithium/util/Collection.php'; require LITHIUM_LIBRARY_PATH . '/lithium/util/collection/Filters.php'; class Action extends lithiumcoreObject { public function doSomethingStupid() { return $this->_filter(__METHOD__, $params, function($self, $params) { $result = ‘Your girlfriend is angry at you’; return $result; }); } } $action = new Action; $action->applyFilter('doSomethingStupid', function($self, $params, $chain) { echo 'About to do something stupid,'; $result = $chain->next($self, $params, $chain); echo 'You IDIOT!!!!!'; return $result; }); $action->doSomthingStupid();
  • 75.
    define('LITHIUM_LIBRARY_PATH', dirname(__DIR__) .'/libraries'); require LITHIUM_LIBRARY_PATH . '/lithium/core/Object.php'; require LITHIUM_LIBRARY_PATH . '/lithium/util/Collection.php'; require LITHIUM_LIBRARY_PATH . '/lithium/util/collection/Filters.php'; class Action extends lithiumcoreObject { public function doSomethingStupid() { return $this->_filter(__METHOD__, $params, function($self, $params) { $result = ‘Your girlfriend is angry at you’; return $result; } }); Make } Filterable $action = new Action; $action->applyFilter('doSomethingStupid', function($self, $params, $chain) { echo 'About to do something stupid,'; $result = $chain->next($self, $params, $chain); echo 'You IDIOT!!!!!'; return $result; }); $action->doSomthingStupid();
  • 76.
    define('LITHIUM_LIBRARY_PATH', dirname(__DIR__) .'/libraries'); require LITHIUM_LIBRARY_PATH . '/lithium/core/Object.php'; require LITHIUM_LIBRARY_PATH . '/lithium/util/Collection.php'; require LITHIUM_LIBRARY_PATH . '/lithium/util/collection/Filters.php'; class Action extends lithiumcoreObject { public function doSomethingStupid() { return $this->_filter(__METHOD__, $params, function($self, $params) { $result = ‘Your girlfriend is angry at you’; return $result; }); } } $action = new Action; $action->applyFilter('doSomethingStupid', function($self, $params, $chain) { echo 'About to do something stupid,'; $result = $chain->next($self, $params, $chain); echo 'You IDIOT!!!!!'; return $result; }); $action->doSomthingStupid();
  • 77.
    use lithiumanalysisLogger; Logger::config(array( 'default' => array( 'adapter' => 'File', 'path' => __DIR__ . '/crap/logs/' ) )); $action = new Action; $action->applyFilter('doSomethingStupid', function($self, $params, $chain) { Logger::debug(date("D M j G:i:s") . " " . 'About to do something stupid,'); $result = $chain->next($self, $params, $chain); Logger::debug(date("D M j G:i:s") . " " . 'You total moron!'); return $result; }); $action->doSomthingStupid();
  • 78.
    use lithiumanalysisLogger; Logger::config(array( 'default' => array( 'adapter' => 'File', 'path' => __DIR__ . '/crap/logs/' ) )); $action = new Action; $action->applyFilter('doSomethingStupid', function($self, $params, $chain) { Logger::debug(date("D M j G:i:s") . " " . 'About to do something stupid,'); $result = $chain->next($self, $params, $chain); Logger::debug(date("D M j G:i:s") . " " . 'You total moron!'); return $result; }); $action->doSomthingStupid();
  • 79.
    use lithiumanalysisLogger; Logger::config(array( 'default' => array( 'adapter' => 'File', 'path' => __DIR__ . '/crap/logs/' ) )); $action = new Action; $action->applyFilter('doSomethingStupid', function($self, $params, $chain) { Logger::debug(date("D M j G:i:s") . " " . 'About to do something stupid,'); $result = $chain->next($self, $params, $chain); Logger::debug(date("D M j G:i:s") . " " . 'You total moron!'); return $result; }); $action->doSomthingStupid();
  • 80.
    use lithiumanalysisLogger; Logger::config(array( 'default' => array( 'adapter' => 'File', 'path' => __DIR__ . '/crap/logs/' ) )); $action = new Action; $action->applyFilter('doSomethingStupid', function($self, $params, $chain) { Logger::debug(date("D M j G:i:s") . " " . 'About to do something stupid,'); $result = $chain->next($self, $params, $chain); Logger::debug(date("D M j G:i:s") . " " . 'You total moron!'); return $result; }); $action->doSomthingStupid();
  • 82.
  • 83.
  • 84.
  • 85.
  • 86.
  • 87.
  • 88.
    Posts::applyFilter('find', function($self, $params,$chain) { $result = $chain->next($self, $params, $chain); $search = http_build_query($params['options']); Logger::debug( 'Search for: ' . $search . 'returned' . $result->to('json') ); return $result; }); Posts::applyFilter('find', function($self, $params, $chain) { $key = // Make a cache key from params['options'] if ($result = Cache::read('default', $key)) { return $result; } $result = $chain->next($self, $params, $chain); Cache::write('default', $key, $result)); return $result; });
  • 89.
    Posts::applyFilter('find', function($self, $params,$chain) { $result = $chain->next($self, $params, $chain); $search = http_build_query($params['options']); Logger::debug( 'Search for: ' . $search . 'returned' . $result->to('json') ); return $result; }); Logging Filter Posts::applyFilter('find', function($self, $params, $chain) { $key = // Make a cache key from params['options'] if ($result = Cache::read('default', $key)) { return $result; } $result = $chain->next($self, $params, $chain); Cache::write('default', $key, $result)); return $result; });
  • 90.
    Posts::applyFilter('find', function($self, $params,$chain) { $result = $chain->next($self, $params, $chain); $search = http_build_query($params['options']); Logger::debug( ); Caching Filter 'Search for: ' . $search . 'returned' . $result->to('json') return $result; }); Posts::applyFilter('find', function($self, $params, $chain) { $key = // Make a cache key from params['options'] if ($result = Cache::read('default', $key)) { return $result; } $result = $chain->next($self, $params, $chain); Cache::write('default', $key, $result)); return $result; });
  • 91.
    Full Page Caching Cache::config(array( 'default' => array( 'adapter' => 'lithiumstoragecacheadapter' . ($apcEnabled ? 'Apc' : 'File') ) )); Dispatcher::applyFilter('run', function($self, $params, $chain) { $key = md5(LITHIUM_APP_PATH) . '.app.cache.'.md5($params['request']->url); if($cache = Cache::read('default', $key)) { return $cache; } $result = $chain->next($self, $params, $chain); Cache::write('default', $key, $result, '+1 day'); return $result; });
  • 92.
    Full Page Caching Set Cache Configuration Cache::config(array( 'default' => array( 'adapter' => 'lithiumstoragecacheadapter' . ($apcEnabled ? 'Apc' : 'File') ) )); Dispatcher::applyFilter('run', function($self, $params, $chain) { $key = md5(LITHIUM_APP_PATH) . '.app.cache.'.md5($params['request']->url); if($cache = Cache::read('default', $key)) { return $cache; } $result = $chain->next($self, $params, $chain); Cache::write('default', $key, $result, '+1 day'); return $result; });
  • 93.
    Full Page Caching Cache::config(array( 'default' => array( 'adapter' => 'lithiumstoragecacheadapter' . ($apcEnabled ? 'Apc' : 'File') ) )); Dispatcher::applyFilter('run', function($self, $params, $chain) { $key = md5(LITHIUM_APP_PATH) . '.app.cache.'.md5($params['request']->url); if($cache = Cache::read('default', $key)) { return $cache; } $result = $chain->next($self, $params, $chain); Filter on Dispatcher::run Cache::write('default', $key, $result, '+1 day'); Checks for cache w/key return $result; });
  • 94.
    Full Page Caching Cache::config(array( 'default' => array( 'adapter' => 'lithiumstoragecacheadapter' . ($apcEnabled ? 'Apc' : 'File') ) )); w/out cache content carry on Dispatcher::applyFilter('run', function($self, $params, $chain) { $key = md5(LITHIUM_APP_PATH) . '.app.cache.'.md5($params['request']->url); running - saving to cache if($cache = Cache::read('default', $key)) { return $cache; } $result = $chain->next($self, $params, $chain); Cache::write('default', $key, $result, '+1 day'); return $result; });
  • 95.
    Content Rendering namespace dispatcher_appcontrollers; classYeaController extends lithiumactionController{ public function index() { $yeah = true; return compact('yeah'); } public function render(array $options = array()) { return 'The response of Yeah is: ' . $this->_render['data']['yeah']; } }
  • 96.
    Content Rendering namespace dispatcher_appcontrollers; classYeaController extends lithiumactionController{ public function index() { $yeah = true; return compact('yeah'); } public function render(array $options = array()) { return 'The response of Yeah is: ' . $this->_render['data']['yeah']; } }
  • 97.
    Content Rendering namespace dispatcher_appcontrollers; classYeaController extends lithiumactionController{ public function index() { $yeah = true; Overwrite ‘render’ method return compact('yeah'); } public function render(array $options = array()) { return 'The response of Yeah is: ' . $this->_render['data']['yeah']; } }
  • 98.
    define('LITHIUM_LIBRARY_PATH', dirname(__DIR__) .'/libraries'); require LITHIUM_LIBRARY_PATH . '/lithium/core/Libraries.php'; use lithiumcoreLibraries; Libraries::add('lithium'); Libraries::add('dispatcher_app', array('path' => __DIR__ . '/dispatcher_app')); use lithiumnethttpRouter; $router = new Router(); Router::connect('/cool-root', array('controller' => 'Yea', 'library' => 'dispatcher_app')); echo lithiumactionDispatcher::run( new lithiumactionRequest() );
  • 99.
    define('LITHIUM_LIBRARY_PATH', dirname(__DIR__) .'/libraries'); require LITHIUM_LIBRARY_PATH . '/lithium/core/Libraries.php'; use lithiumcoreLibraries; Libraries::add('lithium'); Create Routing Libraries::add('dispatcher_app', array('path' => __DIR__ . '/dispatcher_app')); use lithiumnethttpRouter; $router = new Router(); Router::connect('/cool-root', array('controller' => 'Yea', 'library' => 'dispatcher_app')); echo lithiumactionDispatcher::run( new lithiumactionRequest() );
  • 100.
    define('LITHIUM_LIBRARY_PATH', dirname(__DIR__) .'/libraries'); require LITHIUM_LIBRARY_PATH . '/lithium/core/Libraries.php'; use lithiumcoreLibraries; Libraries::add('lithium'); Libraries::add('dispatcher_app', array('path' => __DIR__ . '/dispatcher_app')); use lithiumnethttpRouter; $router = new Router(); Run Dispatcher Router::connect('/cool-root', array('controller' => 'Yea', 'library' => 'dispatcher_app')); echo lithiumactionDispatcher::run( new lithiumactionRequest() );
  • 101.
  • 102.
    Media::type('ajax', array('application/xhtml+xml', 'text/html'),array( 'view' => 'lithiumtemplateView', 'paths' => array( 'template' => array( '{:library}/views/{:controller}/{:template}.ajax.php', '{:library}/views/{:controller}/{:template}.html.php' ), 'layout' => '{:library}/views/layouts/default.ajax.php' ), 'conditions' => array('ajax' => true) )); Easily setup template paths
  • 103.
    Media::type('jpg', 'image/jpeg', array('cast'=> false, 'encode' => function($data) { return $data['photo']->file->getBytes(); })); Media::type('json', 'application/json', array('cast' => false, 'encode' => function($data) { return json_encode($data); })); Media::type('json', 'application/json', array( 'cast' => false, 'encode' => 'json_encode') );
  • 104.
    Media::type('jpg', 'image/jpeg', array('cast'=> false, 'encode' => function($data) { return $data['photo']->file->getBytes(); })); Handle Images Media::type('json', 'application/json', array('cast' => false, 'encode' => function($data) { return json_encode($data); })); Media::type('json', 'application/json', array( 'cast' => false, 'encode' => 'json_encode') );
  • 105.
    Media::type('jpg', 'image/jpeg', array('cast'=> false, 'encode' => function($data) { return $data['photo']->file->getBytes(); })); Media::type('json', 'application/json', array('cast' => false, 'encode' => function($data) { return json_encode($data); })); Media::type('json', 'application/json', array( 'cast' => false, 'encode' => 'json_encode') ); Handle JSON
  • 106.
  • 107.
    Error Handling use lithiumcoreErrorHandler; uselithiumcoreStaticObject; class IThrowAnException { public function method() { throw new Exception('Yo'); } } class IDontThrow extends StaticObject { public static function method($bar) { static::_filter(__FUNCTION__, $params, function($self, $params) { $object = new IThrowAnException; $object->method(); }); } } ErrorHandler::apply('IDontThrow::method', array(), function($info, $params) { echo 'Exception raised with message - ' . $info['exception']->getMessage(); }); IDontThrowAnException::method();
  • 108.
    Error Handling use lithiumcoreErrorHandler; uselithiumcoreStaticObject; class IThrowAnException { public function method() { throw new Exception('Yo'); } } class IDontThrow extends StaticObject { public static function method($bar) { static::_filter(__FUNCTION__, $params, function($self, $params) { $object = new IThrowAnException; $object->method(); }); } } ErrorHandler::apply('IDontThrow::method', array(), function($info, $params) { echo 'Exception raised with message - ' . $info['exception']->getMessage(); }); IDontThrowAnException::method();
  • 109.
    Error Handling use lithiumcoreErrorHandler; uselithiumcoreStaticObject; class IThrowAnException { public function method() { throw new Exception('Yo'); } Filterable } class IDontThrow extends StaticObject { public static function method($bar) { static::_filter(__FUNCTION__, $params, function($self, $params) { $object = new IThrowAnException; $object->method(); }); } } ErrorHandler::apply('IDontThrow::method', array(), function($info, $params) { echo 'Exception raised with message - ' . $info['exception']->getMessage(); }); IDontThrowAnException::method();
  • 110.
    Error Handling use lithiumcoreErrorHandler; uselithiumcoreStaticObject; class IThrowAnException { public function method() { throw new Exception('Yo'); } } class IDontThrow extends StaticObject { public static function method($bar) { static::_filter(__FUNCTION__, $params, function($self, $params) { $object = new IThrowAnException; $object->method(); }); } } ErrorHandler::apply('IDontThrow::method', array(), function($info, $params) { echo 'Exception raised with message - ' . $info['exception']->getMessage(); }); IDontThrowAnException::method();
  • 111.
    ErrorHandler::apply('lithiumactionDispatcher::run', array(), function($info,$params) { $response = new Response(array( 'request' => $params['request'], 'status' => $info['exception']->getCode(), 'trace' => $info['exception']->getTrace(), )); Media::render($response, compact('info', 'params'), array( 'controller' => 'errors', 'template' => 'default', 'layout' => 'default', 'library' => 'golf', 'request' => $params['request'], )); return $response; }); Example
  • 112.
    Create ErrorHandler::apply('lithiumactionDispatcher::run', array(), function($info,$params) { Response $response = new Response(array( 'request' => $params['request'], 'status' => $info['exception']->getCode(), 'trace' => $info['exception']->getTrace(), )); Media::render($response, compact('info', 'params'), array( 'controller' => 'errors', 'template' => 'default', 'layout' => 'default', 'library' => 'golf', 'request' => $params['request'], )); return $response; }); Example
  • 113.
    ErrorHandler::apply('lithiumactionDispatcher::run', array(), function($info,$params) { $response = new Response(array( 'request' => $params['request'], 'status' => $info['exception']->getCode(), 'trace' => $info['exception']->getTrace(), )); Media::render($response, compact('info', 'params'), array( 'controller' => 'errors', 'template' => 'default', 'layout' => 'default', 'library' => 'golf', Media 'request' => $params['request'], Render )); return $response; }); Example
  • 114.
    Zendy Love Libraryies::add( 'prefix' => 'Zend_', 'includePath' => '/htdocs/libraries/Zend/trunk/library', 'bootstrap' => 'Loader/Autoloader.php', 'loader' => array('Zend_Loader_Autoloader', 'autoload'), 'transform' => function($class) { return str_replace('_', '/', $class) . '.php'; } );
  • 115.
    define('LITHIUM_LIBRARY_PATH', dirname(__DIR__) .'/libraries'); define('LITHIUM_APP_PATH', __DIR__ . '/templates'); require LITHIUM_LIBRARY_PATH . '/lithium/core/Libraries.php'; use lithiumcoreLibraries; use lithiumcoreEnvironment; use lithiumactionDispatcher; use lithiumg11nMessage; use lithiumnethttpMedia; Libraries::add('lithium'); Libraries::add('li3_docs'); Libraries::add('templates', array('path' => __DIR__ . '/templates', 'default' => true)); Libraries::add('adapters', array('path' => __DIR__ . '/adapters')); Libraries::add('li3_docs', array( 'index' => array('lithium', 'adapters') )); Libraries Example
  • 116.
    define('LITHIUM_LIBRARY_PATH', dirname(__DIR__) .'/libraries'); define('LITHIUM_APP_PATH', __DIR__ . '/templates'); require LITHIUM_LIBRARY_PATH . '/lithium/core/Libraries.php'; use lithiumcoreLibraries; Sets up use lithiumcoreEnvironment; use use lithiumactionDispatcher; lithiumg11nMessage; Libraries use lithiumnethttpMedia; Libraries::add('lithium'); Libraries::add('li3_docs'); Libraries::add('templates', array('path' => __DIR__ . '/templates', 'default' => true)); Libraries::add('adapters', array('path' => __DIR__ . '/adapters')); Libraries::add('li3_docs', array( 'index' => array('lithium', 'adapters') )); Libraries Example
  • 117.
    $locale = 'en'; $locales= array('en' => 'English'); Environment::set('development', compact('locale', 'locales')); Environment::set('development'); } $file = "{$config['path']}/config/routes.php"; file_exists($file) ? call_user_func(function() use ($file) { include $file; }) : null; } return $chain->next($self, $params, $chain); }); Media::applyFilter('_handle', function($self, $params, $chain) { $params['handler'] += array('outputFilters' => array()); $params['handler']['outputFilters'] += Message::aliases(); return $chain->next($self, $params, $chain); }); echo lithiumactionDispatcher::run(new lithiumactionRequest());
  • 118.
    Locale & $locale ='en'; Routes $locales = array('en' => 'English'); Environment::set('development', compact('locale', 'locales')); Environment::set('development'); } $file = "{$config['path']}/config/routes.php"; file_exists($file) ? call_user_func(function() use ($file) { include $file; }) : null; } return $chain->next($self, $params, $chain); }); Media::applyFilter('_handle', function($self, $params, $chain) { $params['handler'] += array('outputFilters' => array()); $params['handler']['outputFilters'] += Message::aliases(); return $chain->next($self, $params, $chain); }); echo lithiumactionDispatcher::run(new lithiumactionRequest());
  • 119.
    $locale = 'en'; $locales= array('en' => 'English'); Environment::set('development', compact('locale', 'locales')); Environment::set('development'); } $file = "{$config['path']}/config/routes.php"; file_exists($file) ? call_user_func(function() use ($file) { include $file; }) : null; } return $chain->next($self, $params, $chain); }); Media::applyFilter('_handle', function($self, $params, $chain) { $params['handler'] += array('outputFilters' => array()); $params['handler']['outputFilters'] += Message::aliases(); return $chain->next($self, $params, $chain); Gets }); translations echo lithiumactionDispatcher::run(new lithiumactionRequest());
  • 120.
    $locale = 'en'; $locales= array('en' => 'English'); Environment::set('development', compact('locale', 'locales')); Environment::set('development'); } $file = "{$config['path']}/config/routes.php"; file_exists($file) ? call_user_func(function() use ($file) { include $file; }) : null; } return $chain->next($self, $params, $chain); }); Media::applyFilter('_handle', function($self, $params, $chain) { $params['handler'] += array('outputFilters' => array()); $params['handler']['outputFilters'] += Message::aliases(); return $chain->next($self, $params, $chain); }); echo lithiumactionDispatcher::run(new lithiumactionRequest()); Go
  • 121.
  • 122.
  • 123.
    ★ Hackers framework Recap ★ Light, fast & fun
  • 124.
    ★ Hackers framework Recap ★ Light, fast & fun ★ Services have adaptable base
  • 125.
    ★ Hackers framework Recap ★ Light, fast & fun ★ Services have adaptable base ★ Filters are awesome/powerful
  • 126.
    ★ Hackers framework Recap ★ Light, fast & fun ★ Services have adaptable base ★ Filters are awesome/powerful ★ Everything is a library
  • 127.
    ★ Hackers framework Recap ★ Light, fast & fun ★ Services have adaptable base ★ Filters are awesome/powerful ★ Everything is a library ★ As much or as little framework as you need
  • 128.
    ★ Hackers framework Recap ★ Light, fast & fun ★ Services have adaptable base ★ Filters are awesome/powerful ★ Everything is a library ★ As much or as little framework as you need ★ Great balance weight/power/simplicity
  • 129.

Editor's Notes

  • #2 \n
  • #3 Union of Rad - worked with Nate\nTalk to me about japan\n
  • #4 Thank you - engine yard sponsors Lithium - php 5.4\n
  • #5 Lets talk about frameworks - there are too many\n
  • #6 People like to fight about their framework choice\n
  • #7 My favorites\nMy Least favorites\n
  • #8 Bloated\nCarry too much bulk - Symfony/Doctrine - VHeavy\nJava programmers\n
  • #9 \n
  • #10 \n
  • #11 Any body else find this odd?\n
  • #12 \n
  • #13 What drives us -\nIt is important to love the code you write - rails has had a big influence\n
  • #14 Our feelings count\nI don&amp;#x2019;t think we should need a high powered ide\n
  • #15 Lithium is really light 1mb tests 1mb - no bulk\n
  • #16 Lithium is really light 1mb tests 1mb - no bulk\n
  • #17 It is fast to develop in, it will speed up your development time. \nCompare Symfony, ZF, Drupal\n
  • #18 World one - every one familiar with this phrase?\n
  • #19 The repo - skeleton\n
  • #20 Great way to familiarize yourself - next few slides are taken from this project\n
  • #21 Example model - Save override to save tags\n
  • #22 Example model - Save override to save tags\n
  • #23 Example model - Save override to save tags\n
  • #24 View helpers\nCan override these\n
  • #25 View helpers\nCan override these\n
  • #26 View helpers\nCan override these\n
  • #27 &lt;?= escaping\nLink routes\nimage url helper\n
  • #28 &lt;?= escaping\nLink routes\nimage url helper\n
  • #29 &lt;?= escaping\nLink routes\nimage url helper\n
  • #30 Easy to read controllers - simple methods\n
  • #31 Easy to read controllers - simple methods\n
  • #32 Easy to read controllers - simple methods\n
  • #33 Geo-code is here - still really simple\n
  • #34 1.Completely avoids the framework stack\nCan also use with JSON end points\n2. Its PHP, you can do what you want \n
  • #35 1.Completely avoids the framework stack\nCan also use with JSON end points\n2. Its PHP, you can do what you want \n
  • #36 1.Completely avoids the framework stack\nCan also use with JSON end points\n2. Its PHP, you can do what you want \n
  • #37 testing important\neverything is a library\n
  • #38 command line\n
  • #39 But this alone is not massively different to what is out there - like fuel\n
  • #40 It gets more interesting from here\n
  • #41 world 2\n
  • #42 Sky - no framework - create it from scratch - framework constraints\n
  • #43 maybe we feel a bit like this\n
  • #44 Not just 1 programming paradigm - php is very flexible\n
  • #45 \n
  • #46 Make a simple model\n
  • #47 Decoupled example\njust add lithium library - low overhead - all the code you need\nEasily added to DB - These are working examples\n
  • #48 Decoupled example\njust add lithium library - low overhead - all the code you need\nEasily added to DB - These are working examples\n
  • #49 Decoupled example\njust add lithium library - low overhead - all the code you need\nEasily added to DB - These are working examples\n
  • #50 Decoupled example\njust add lithium library - low overhead - all the code you need\nEasily added to DB - These are working examples\n
  • #51 Decoupled example\njust add lithium library - low overhead - all the code you need\nEasily added to DB - These are working examples\n
  • #52 Decoupled example\njust add lithium library - low overhead - all the code you need\nEasily added to DB - These are working examples\n
  • #53 Decoupled example\njust add lithium library - low overhead - all the code you need\nEasily added to DB - These are working examples\n
  • #54 Instantiate request and router\nSet routes - regex - now works (request contains correct controller)\nFull path matching\n\n\n
  • #55 Instantiate request and router\nSet routes - regex - now works (request contains correct controller)\nFull path matching\n\n\n
  • #56 Instantiate request and router\nSet routes - regex - now works (request contains correct controller)\nFull path matching\n\n\n
  • #57 Instantiate request and router\nSet routes - regex - now works (request contains correct controller)\nFull path matching\n\n\n
  • #58 Instantiate request and router\nSet routes - regex - now works (request contains correct controller)\nFull path matching\n\n\n
  • #59 We have an end point, \nfollowing route, \nparses the request object to contain\n
  • #60 We have an end point, \nfollowing route, \nparses the request object to contain\n
  • #61 We have an end point, \nfollowing route, \nparses the request object to contain\n
  • #62 Make simplest adaptable class\n
  • #63 We tell the Lithium where the app and adapters live -normally not needed\n-note emails same as where highlighted in the class \nSet the environment and use the adapter\n
  • #64 We tell the Lithium where the app and adapters live -normally not needed\n-note emails same as where highlighted in the class \nSet the environment and use the adapter\n
  • #65 We tell the Lithium where the app and adapters live -normally not needed\n-note emails same as where highlighted in the class \nSet the environment and use the adapter\n
  • #66 We tell the Lithium where the app and adapters live -normally not needed\n-note emails same as where highlighted in the class \nSet the environment and use the adapter\n
  • #67 We tell the Lithium where the app and adapters live -normally not needed\n-note emails same as where highlighted in the class \nSet the environment and use the adapter\n
  • #68 This time we write it a little more differently\nset up environment aware config\nSending is easy. Base of lithium services. Strategy implementation, very testable. DI in proportion\n
  • #69 This time we write it a little more differently\nset up environment aware config\nSending is easy. Base of lithium services. Strategy implementation, very testable. DI in proportion\n
  • #70 This time we write it a little more differently\nset up environment aware config\nSending is easy. Base of lithium services. Strategy implementation, very testable. DI in proportion\n
  • #71 This time we write it a little more differently\nset up environment aware config\nSending is easy. Base of lithium services. Strategy implementation, very testable. DI in proportion\n
  • #72 This time we write it a little more differently\nset up environment aware config\nSending is easy. Base of lithium services. Strategy implementation, very testable. DI in proportion\n
  • #73 The real fun part, this is a real high point of lithiums architecture.\n
  • #74 Write a method that doesn&amp;#x2019;t really do anything, but this is a filterable class\nEcho-ing is non-sense\n
  • #75 Write a method that doesn&amp;#x2019;t really do anything, but this is a filterable class\nEcho-ing is non-sense\n
  • #76 Write a method that doesn&amp;#x2019;t really do anything, but this is a filterable class\nEcho-ing is non-sense\n
  • #77 We deal with dependencies that have nothing to do with role\nLogger\n
  • #78 We deal with dependencies that have nothing to do with role\nLogger\n
  • #79 We deal with dependencies that have nothing to do with role\nLogger\n
  • #80 We deal with dependencies that have nothing to do with role\nLogger\n
  • #81 We deal with dependencies that have nothing to do with role\nLogger\n
  • #82 Find Example\n
  • #83 Find Example\n
  • #84 Find Example\n
  • #85 Find Example\n
  • #86 Find Example\n
  • #87 Find Example\n
  • #88 It would look like this\n
  • #89 It would look like this\n
  • #90 It would look like this\n
  • #91 Cache is an adaptable class\nBAD - filter classes that relates to itself\n
  • #92 Cache is an adaptable class\nBAD - filter classes that relates to itself\n
  • #93 Cache is an adaptable class\nBAD - filter classes that relates to itself\n
  • #94 Cache is an adaptable class\nBAD - filter classes that relates to itself\n
  • #95 Cache is an adaptable class\nBAD - filter classes that relates to itself\n
  • #96 Override content render - I am doing this\n
  • #97 Override content render - I am doing this\n
  • #98 Override content render - I am doing this\n
  • #99 \n
  • #100 \n
  • #101 \n
  • #102 How controller render works\n
  • #103 Ajax paths - types \n
  • #104 Handle media returned from the controller - images, json, encoding handlers\nHappens in controllers render method\n
  • #105 Handle media returned from the controller - images, json, encoding handlers\nHappens in controllers render method\n
  • #106 Handle media returned from the controller - images, json, encoding handlers\nHappens in controllers render method\n
  • #107 Granular control of exception handling\n
  • #108 Granular control of exception handling\n
  • #109 Granular control of exception handling\n
  • #110 Granular control of exception handling\n
  • #111 Granular control of exception handling\n
  • #112 Granular control of exception handling\n
  • #113 Real example - setting up error handling\n
  • #114 Real example - setting up error handling\n
  • #115 Real example - setting up error handling\n
  • #116 \n
  • #117 \n
  • #118 Set up stuff required by library - do demo\n
  • #119 Set up stuff required by library - do demo\n
  • #120 Set up stuff required by library - do demo\n
  • #121 Set up stuff required by library - do demo\n
  • #122 Set up stuff required by library - do demo\n
  • #123 Recap -really fast development\n
  • #124 Recap -really fast development\n
  • #125 Recap -really fast development\n
  • #126 Recap -really fast development\n
  • #127 Recap -really fast development\n
  • #128 Recap -really fast development\n
  • #129 Recap -really fast development\n
  • #130 \n