SlideShare a Scribd company logo
THE


OF
• Former lead developer, CakePHP
• Co-founder & lead developer of
  Lithium for ~2 years

• Original BostonPHP framework
  bake-off champ!

• Twitter: @nateabele
• Started as a series of test scripts on early dev builds of PHP 5.3
• Released as “Cake3” in July ‘09
• Spun off as Lithium in October ’09
• Based on 5 years’ experience developing a high-adoption web
   framework
ARCHITECTURE
Procedural   Object-Oriented
Procedural   Object-Oriented
Aspect-Oriented
Event-Driven                      Procedural

               PARADIGMS
 Declarative                      Functional

                Object-Oriented
The Fall of Rome




           PARADIGM HUBRIS
+   $
Z END F RAMEWORK 1.5
$transport =   new Zend_Mail_Transport_Smtp('smtp.gmail.com', array(
  'auth'       => 'login',
  'username'   => 'foo',
  'password'   => 'bar',
  'ssl'        => 'ssl',
  'port'       => 465,
));

$mailer = new Zend_Mail();
$mailer->setDefaultTransport($transport);
class Container {

    public function getMailTransport() {
      return new Zend_Mail_Transport_Smtp('smtp.gmail.com', array(
        'auth'     => 'login',
        'username' => 'root',
        'password' => 'sekr1t',
        'ssl'      => 'ssl',
        'port'     => 465,
      ));
    }

    public function getMailer() {
      $mailer = new Zend_Mail();
      $mailer->setDefaultTransport($this->getMailTransport());
      return $mailer;
    }
}
class Container {

    protected $parameters = array();

    public function __construct(array $parameters = array()) {
      $this->parameters = $parameters;
    }

    public function getMailTransport() {
      return new Zend_Mail_Transport_Smtp('smtp.gmail.com', array(
        'auth'     => 'login',
        'username' => $this->parameters['mailer.username'],
        'password' => $this->parameters['mailer.password'],
        'ssl'      => 'ssl',
        'port'     => 465,
      ));
    }

    public function getMailer() {
      $mailer = new Zend_Mail();
      $mailer->setDefaultTransport($this->getMailTransport());
      return $mailer;
    }
}
$container = new Container(array(
  'mailer.username' => 'root',
  'mailer.password' => 'sekr1t',
  'mailer.class'    => 'Zend_Mail',
));

$mailer = $container->getMailer();
class Container extends sfServiceContainer {

    static protected $shared = array();

    protected function getMailTransportService() {
      return new Zend_Mail_Transport_Smtp('smtp.gmail.com', array(
        'auth'     => 'login',
        'username' => $this['mailer.username'],
        'password' => $this['mailer.password'],
        'ssl'      => 'ssl',
        'port'     => 465,
      ));
    }

    protected function getMailerService() {
      if (isset(self::$shared['mailer'])) {
        return self::$shared['mailer'];
      }

        $class = $this['mailer.class'];

        $mailer = new $class();
        $mailer->setDefaultTransport($this->getMailTransportService());

        return self::$shared['mailer'] = $mailer;
    }
}
sfServiceContainerAutoloader::register();

$sc = new sfServiceContainerBuilder();

$sc->register('mail.transport', 'Zend_Mail_Transport_Smtp')->
  addArgument('smtp.gmail.com')->
  addArgument(array(
    'auth'     => 'login',
    'username' => '%mailer.username%',
    'password' => '%mailer.password%',
    'ssl'      => 'ssl',
    'port'     => 465,
  ))->setShared(false);

$sc->register('mailer', '%mailer.class%')->
  addMethodCall('setDefaultTransport', array(
    new sfServiceReference('mail.transport')
  ));
<?xml version="1.0" ?>

<container xmlns="http://symfony-project.org/2.0/container">
  <parameters>
    <parameter key="mailer.username">root</parameter>
    <parameter key="mailer.password">sekr1t</parameter>
    <parameter key="mailer.class">Zend_Mail</parameter>
  </parameters>
  <services>
    <service id="mail.transport" class="Zend_Mail_Transport_Smtp" shared="false">
      <argument>smtp.gmail.com</argument>
      <argument type="collection">
        <argument key="auth">login</argument>
        <argument key="username">%mailer.username%</argument>
        <argument key="password">%mailer.password%</argument>
        <argument key="ssl">ssl</argument>
        <argument key="port">465</argument>
      </argument>
    </service>
    <service id="mailer" class="%mailer.class%">
      <call method="setDefaultTransport">
        <argument type="service" id="mail.transport" />
      </call>
    </service>
  </services>
</container>
Dependency injection container

+ Service container

+ Service container builder

+ XML
==
mail()
THE MORAL



“
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
The Guggenheim      Fallingwater




    GREAT ARCHITECTURE
WHO WON?
Jet Li’s Fearless




        JET LI      AS   HOU YUANJIA
BRUCE LEE
J EET K UNE D O
The Way of the Intercepting Fist
GOALS


• Understand a variety of paradigms & their strengths
• Respect context when choosing paradigms / techniques
• Be simple as possible (but no simpler)
PROBLEMS

• Managing configuration
• Staying flexible
• Extending internals
• Easy things: easy; hard things: possible
CONFIGURATION
webroot/index.php




require dirname(__DIR__) . '/config/bootstrap.php';




echo lithiumactionDispatcher::run(
    new lithiumactionRequest()
);
config/bootstrap.php

require __DIR__ . '/bootstrap/libraries.php';

require __DIR__ . '/bootstrap/errors.php';

require __DIR__ . '/bootstrap/cache.php';

require __DIR__ . '/bootstrap/connections.php';

require __DIR__ . '/bootstrap/action.php';

require __DIR__ . '/bootstrap/session.php';

require __DIR__ . '/bootstrap/g11n.php';

require __DIR__ . '/bootstrap/media.php';

require __DIR__ . '/bootstrap/console.php';
config/bootstrap/libraries.php



use lithiumcoreLibraries;

Libraries::add('lithium');

Libraries::add('app', array('default' => true));

Libraries::add('li3_docs');
config/bootstrap/cache.php


use lithiumstorageCache;

Cache::config(array(
    'local' => array('adapter' => 'Apc'),
    'distributed' => array(
        'adapter' => 'Memcache',
        'host' => '127.0.0.1:11211'
    ),
    'default' => array('adapter' => 'File')
));
config/bootstrap/connections.php

use lithiumdataConnections;

Connections::config(array(
    'default' => array(
        'type' => 'MongoDb',
        'database' => 'my_mongo_db'
    ),
    'legacy' => array(
        'type' => 'database',
        'adapter' => 'MySql',
        'login' => 'bobbytables',
        'password' => 's3kr1t',
        'database' => 'my_mysql_db'
    )
));
config/bootstrap/session.php

use lithiumsecurityAuth;

Auth::config(array(
    'customer' => array(
        'adapter' => 'Form',
        'model'   => 'Customers',
        'fields' => array('email', 'password')
    ),
    'administrator' => array(
        'adapter' => 'Http',
        'method' => 'digest',
        'users' => array('nate' => 'li3')
    )
));
MULTIPLE ENVIRONMENTS?

use lithiumstorageCache;

Cache::config(array(
    'default' => array(
        'development' => array('adapter' => 'Apc'),
        'production' => array(
            'adapter' => 'Memcache',
            'host' => '127.0.0.1:1121'
        )
    )
));
MULTIPLE ENVIRONMENTS?

use lithiumstorageCache;

Cache::config(array(
    'default' => array(
        'development' => array('adapter' => 'Apc'),
        'production' => array(
            'adapter' => 'Memcache',
            'host' => '127.0.0.1:1121'
        )
    )
));
namespace lithiumnethttp;

use lithiumcoreLibraries;

class Service extends lithiumcoreObject {

    protected $_classes = array(
        'media'    => 'lithiumnethttpMedia',
        'request' => 'lithiumnethttpRequest',
        'response' => 'lithiumnethttpResponse',
    );

    public function __construct(array $config = array()) {
        $defaults = array(
            'scheme'     => 'http',
            'host'       => 'localhost',
            // ...
        );
        parent::__construct($config + $defaults);
    }

    protected function _init() {
        // ...
    }
}
$service = new   Service(array(
    'scheme'     => 'https',
    'host'       => 'web.service.com',
    'username'   => 'user',
    'password'   => 's3kr1t'
));
$service = new      Service(array(
    'scheme'        => 'https',
    'host'          => 'web.service.com',
    'username'      => 'user',
    'password'      => 's3kr1t'
));
{
    “lithiumnethttpService”: {
        “scheme”: “https”,
        “host”:     “web.service.com”,
        “username”: “user”,
        “password”: “s3kr1t”
    }
}
$service = new Service(array(
    'scheme'   => 'https',
    'host'     => 'web.service.com',
    'username' => 'user',
    'password' => 's3kr1t',
    'classes' => array(
        'request' => 'mycustomRequest'
    )
));
FLEXIBILITY
HELPERS


<?=$this->form->text('email'); ?>
HELPERS


<?=$this->form->text('email'); ?>

<input type="text" name="email" id="MemberEmail"
 value="nate.abele@gmail.com" />
HELPERS


<?=$this->form->field('name', array(
    'wrap' => array('class' => 'wrapper')
)); ?>
HELPERS


<?=$this->form->field('name', array(
    'wrap' => array('class' => 'wrapper')
)); ?>


<div class="wrapper">
    <label for="MemberName">Name</label>
    <input type="text" name="name" id="MemberName" />
    <div class="error">You don't have a name?</div>
</div>
HELPERS

<?=$this->form->field('name', array(
    'wrap' => array('class' => 'item'),
    'template' => '<li{:wrap}>{:error}{:label}{:input}</li>'
)); ?>
HELPERS

<?=$this->form->field('name', array(
    'wrap' => array('class' => 'item'),
    'template' => '<li{:wrap}>{:error}{:label}{:input}</li>'
)); ?>



<li class="item">
    <div class="error">You don't have a name?</div>
    <label for="MemberName">Name</label>
    <input type="text" name="name" id="MemberName" />
</div>
HELPERS



$this->form->config(array('templates' => array(
     'field' => "<li{:wrap}>{:error}{:label}{:input}</li>"
)));
HELPERS



<input type="text" name="email" id="MemberEmail"
        value="nate.abele@gmail.com" />
HELPERS

$form = $this->form;

$this->form->config(array('attributes' => array(
   'id' => function($method, $name, $options) use (&$form) {
      if ($method != 'text' && $method != 'select') {
         return;
      }
      $model = null;

       if ($binding = $form->binding()) {
          $model = basename(str_replace('', '/', $binding->model())) . '_';
       }
       return Inflector::underscore($model . $name);
   }
)));
HELPERS

$form = $this->form;

$this->form->config(array('attributes' => array(
   'id' => function($method, $name, $options) use (&$form) {
      if ($method != 'text' && $method != 'select') {
         return;
      }
      $model = null;

       if ($binding = $form->binding()) {
          $model = basename(str_replace('', '/', $binding->model())) . '_';
       }
       return Inflector::underscore($model . $name);
   }
)));
HELPERS



<input type="text" name="email" id="member_email"
        value="nate.abele@gmail.com" />
THE MEDIA CLASS

class WeblogController < ActionController::Base

  def index
    @posts = Post.find :all

    respond_to do |format|
      format.html
      format.xml { render :xml => @posts.to_xml }
      format.rss { render :action => "feed.rxml" }
    end
  end
end
THE MEDIA CLASS




         !
class WeblogController < ActionController::Base

  def index
    @posts = Post.find :all

    respond_to do |format|
      format.html
      format.xml { render :xml => @posts.to_xml }
      format.rss { render :action => "feed.rxml" }
    end
  end
end
THE MEDIA CLASS



<?php echo $javascript->object($data); ?>
THE MEDIA CLASS




       !
<?php echo $javascript->object($data); ?>
THE MEDIA CLASS

                lithiumnethttpMedia {

                     $formats = array(
array(                   'html' => array(...),
    'posts' => ...       'json' => array(...),
)                        'xml' => array(...),
                         '...'
                     );
                }
THE MEDIA CLASS

                lithiumnethttpMedia {

                     $formats = array(
array(                   'html' => array(...),
    'posts' => ...       'json' => array(...),
)                        'xml' => array(...),
                         '...'
                     );
                }
THE MEDIA CLASS
Media::type('mobile', array('text/html'), array(
    'view' => 'lithiumtemplateView',
    'paths' => array(
        'template' => array(
            '{:library}/views/{:controller}/{:template}.mobile.php',
            '{:library}/views/{:controller}/{:template}.html.php',
        ),
        'layout'   => array(
            '{:library}/views/layouts/{:layout}.mobile.php',
            '{:library}/views/layouts/{:layout}.html.php',
        ),
        'element' => array(
            '{:library}/views/elements/{:template}.mobile.php'
            '{:library}/views/elements/{:template}.html.php'
        )
    ),
    'conditions' => array('mobile' => true)
));
THE MEDIA CLASS
Media::type('mobile', array('text/html'), array(
    'view' => 'lithiumtemplateView',
    'paths' => array(
        'template' => array(
            '{:library}/views/{:controller}/{:template}.mobile.php',
            '{:library}/views/{:controller}/{:template}.html.php',
        ),
        'layout'   => array(
            '{:library}/views/layouts/{:layout}.mobile.php',
            '{:library}/views/layouts/{:layout}.html.php',
        ),
        'element' => array(
            '{:library}/views/elements/{:template}.mobile.php'
            '{:library}/views/elements/{:template}.html.php'
        )
    ),
    'conditions' => array('mobile' => true)
));
THE MEDIA CLASS

Media::type('ajax', array('text/html'), array(
    'view' => 'lithiumtemplateView',
    'paths' => array(
        'template' => array(
            '{:library}/views/{:controller}/{:template}.ajax.php',
            '{:library}/views/{:controller}/{:template}.html.php',
        ),
        'layout'   => false,
        'element' => array(
            '{:library}/views/elements/{:template}.ajax.php'
            '{:library}/views/elements/{:template}.html.php'
        )
    ),
    'conditions' => array('ajax' => true)
));
THE MEDIA CLASS

Media::type('ajax', array('text/html'), array(
    'view' => 'lithiumtemplateView',
    'paths' => array(
        'template' => array(
            '{:library}/views/{:controller}/{:template}.ajax.php',
            '{:library}/views/{:controller}/{:template}.html.php',
        ),
        'layout'   => false,
        'element' => array(
            '{:library}/views/elements/{:template}.ajax.php'
            '{:library}/views/elements/{:template}.html.php'
        )
    ),
    'conditions' => array('ajax' => true)
));
CONDITIONS?

        'conditions' => array('ajax' => true)


                         ==
               $request->is('ajax')


                         ==
  $_SERVER['HTTP_X_REQUESTED_WITH'] = 'XMLHttpRequest'
CONDITIONS?

$request->detect('iPhone', array('HTTP_USER_AGENT', '/iPhone/'));

$isiPhone = $request->is('iPhone');




$request->detect('custom', function($request) {
    if ($value = $request->env("HTTP_WHATEVER")) {
        // Do something with $value
    }
    return false;
});
ROUTING
Router::connect('/{:controller}/{:action}/{:id:[0-9]+}', array(
    'id' => null
));


new Route(array(
    'template' => '/{:controller}/{:action}/{:id:[0-9]+}',
    'pattern' => '@^(?:/(?P[^/]+))(?:/(?P[^/]+)?)?(?:/(?P[0-9]+)?)?$@',
    'params' => array('id' => null, 'action' => 'index'),
    // ...
    'subPatterns' => array('id' => '[0-9]+'),
    'persist' => array('controller')
));




Router::connect(new CustomRoute($params));
ROUTE HANDLERS

Router::connect('/{:user}/{:controller}/{:action}');
ROUTE HANDLERS

Router::connect('/{:user}/{:controller}/{:action}', array(), function($request) {
    if (!Users::count(array('conditions' => array('user' => $request->user)))) {
        return false;
    }
    return $request;
});
ROUTE HANDLERS

Router::connect('/{:user}/{:controller}/{:action}', array(), function($request) {
    if (!Users::count(array('conditions' => array('user' => $request->user)))) {
        return false;
    }
    return $request;
});



Router::connect('/', array(), function($request) {
    if (Session::read('user')) {
        $location = 'Accounts::index';
    } else {
        $location = 'Users::add';
    }
    return new Response(array('status' => 302, 'location' => $location));
});
ROUTE HANDLERS

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

Router::connect('/posts.json', array(), function($request) {
    return new Response(array(
        'headers' => array('Content-type' => 'application/json'),
        'body' => Posts::all()->to('json')
    ));
});


Router::connect('/posts/{:id}.json', array(), function($request) {
    return new Response(array(
        'headers' => array('Content-type' => 'application/json'),
        'body' => Posts::first($request->id)->to('json')
    ));
});
EXTENSIBILITY
HELPERS

     <?=$this->html->*() ?>


      lithiumtemplatehelperHtml
HELPERS

     <?=$this->html->*() ?>


      lithiumtemplatehelperHtml




       appextensionshelperHtml
MODELS


namespace appmodels;

class Posts extends lithiumdataModel {

}
MODELS
namespace appmodels;

class Posts extends lithiumdataModel {

    protected $_meta = array(
        'key' => 'custom_id',
        'source' => 'custom_posts_table'
        'connection' => 'legacy_mysql_db'
    );
}
MODELS
namespace appmodels;

class Posts extends lithiumdataModel {

    protected $_meta = array(
        'key' => array(
            'custom_id', 'other_custom_id'
        )
    );
}
MODELS


Posts::create(array(
    'title' => 'My first post ever',
    'body' => 'Wherein I extoll the virtues of Lithium'
));

// ...

$post->save();
MODELS

$post->tags = 'technology,PHP,news';
$post->save();

// ...

foreach ($post->tags as $tag) {
    #FALE
}
MODELS
namespace appmodels;

class Posts extends lithiumdataModel {

    public function tags($entity) {
        return explode(',', $entity->tags);
    }
}


foreach ($post->tags() as $tag) {
    // ...
}
MODELS
namespace appmodels;

class Posts extends lithiumdataModel {

    public function tags($entity) {
        return explode(',', $entity->tags);
    }
}


foreach ($post->tags() as $tag) {
    // ...
}
MODELS
namespace appmodels;

class Posts extends lithiumdataModel {

    public static function expire() {
        return static::update(
            array('expired' => true),
            array('updated' => array(
                '<=' => strtotime('3 months ago')
            ))
        );
    }
}

$didItWork = Posts::expire();
ENTITIES & COLLECTIONS
$posts = Posts::findAllBySomeCondition();
ENTITIES & COLLECTIONS
$posts = Posts::findAllBySomeCondition();

   $posts->first(function($post) {
       return $post->published == true;
   });

   $posts->each(function($post) {
       return $post->counter++;
   });

   $ids = $posts->map(function($post) {
       return $post->id;
   });
RELATIONSHIPS

namespace appmodels;

class Posts extends lithiumdataModel {

    public $belongsTo = array('Users');
}
RELATIONSHIPS

namespace appmodels;

class Posts extends lithiumdataModel {

    public $belongsTo = array('Author' => array(
        'class' => 'Users'
    ));
}
RELATIONSHIPS

namespace appmodels;

class Posts extends lithiumdataModel {

    public $belongsTo = array('Author' => array(
         'class' => 'Users',
         'conditions' => array('active' => true),
         'key' => 'author_id'
     ));
}
RELATIONSHIPS

namespace appmodels;

class Posts extends lithiumdataModel {

    public $belongsTo = array('Author' => array(
         'class' => 'Users',
         'conditions' => array('active' => true),
         'key' => array(
             'author_id' => 'id',
             'other_key' => 'other_id'
         )
     ));
}
NO H AS A ND B ELONGS T O M ANY !!
DOCUMENT DATABASES


$post = Posts::create(array(
    'title' => "New post",
    'body' => "Something worthwhile to read",
    'tags' => array('PHP', 'tech'),
    'author' => array('name' => 'Nate')
));
DOCUMENT DATABASES


$posts = Posts::all(array('conditions' => array(
     'tags' => array('PHP', 'tech'),
     'author.name' => 'Nate'
)));
DOCUMENT DATABASES


$ages = Users::all(array(
    'group'   => 'age',
    'reduce' => 'function(obj, prev) { prev.count++; }',
    'initial' => array('count' => 0)
));
THE QUERY API

$query = new Query(array(
    'type' => 'read',
    'model' => 'appmodelsPost',
    'fields' => array('Post.title', 'Post.body'),
    'conditions' => array('Post.id' => new Query(array(
        'type' => 'read',
        'fields' => array('post_id'),
        'model' => 'appmodelsTagging',
        'conditions' => array('Tag.name' => array('foo', 'bar', 'baz')),
    )))
));
FILTERS
$post = Posts::first($id);
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;
});
logging

caching

find()
logging

caching

find()
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;
});
THE TALK OF THE TOWN
CAN I USE IT IN PRODUCTION?
GIMMEBAR.COM


Sean Coates
MAPALONG.COM


 Chris Shiflett



Andrei Zmievski
TOTSY.COM


Mitch Pirtle
...AND MANY OTHERS
David Coallier
                                 • President, PEAR Group

                                 • CTO, Echolibre / Orchestra.io



“   After looking at Lithium I’ve come to realize how far ahead it is
    compared to other frameworks from a technologist's point of view.
                                                                        ”
Helgi Þormar Þorbjörnsson
                 • Developer, PEAR Installer

                 • PEAR Core Dev, 8 years




“
It’s the f*****g epiphany of modern!
                                            ”
Fahad Ibnay Heylaal
                       • Creator, Croogo CMS




“   I believe the future is in Lithium. give
    it time to grow, and the developers
    behind it are awesome.                     ”
1.0?
SO CLOSE!!
TOMORROW...
0.10
THANKS!!

Find me later :
  @nateabele
  nate.abele@gmail.com
  http://nateabele.com/
RESOURCES

Getting connected              Learning AOP
lithify.me                   bit.ly/aop-design

github.com/UnionOfRAD         bit.ly/aop-gwoo

#li3   on irc.freenode.net      bit.ly/aop-li3

@UnionOfRAD                     bit.ly/aop-oop


 Talks                        bit.ly/mwop-aop


slideshare.net/nateabele
PHOTO CREDITS
http://www.flickr.com/photos/mkebbe/28298461/
http://www.flickr.com/photos/josefeliciano/3849557951/
http://www.flickr.com/photos/cku/1386908692/
http://www.flickr.com/photos/macten/4611148426/
http://www.rustybrick.com/prototype-js-vs-jquery-comparison.html
http://www.flickr.com/photos/cadsonline/4321530819/
http://www.flickr.com/photos/maiptitfleur/4942829255/

More Related Content

What's hot

Dependency injection-zendcon-2010
Dependency injection-zendcon-2010Dependency injection-zendcon-2010
Dependency injection-zendcon-2010
Fabien Potencier
 
Dependency injection in PHP 5.3/5.4
Dependency injection in PHP 5.3/5.4Dependency injection in PHP 5.3/5.4
Dependency injection in PHP 5.3/5.4
Fabien Potencier
 
Dependency injection - phpday 2010
Dependency injection - phpday 2010Dependency injection - phpday 2010
Dependency injection - phpday 2010
Fabien Potencier
 
Decouple Your Code For Reusability (International PHP Conference / IPC 2008)
Decouple Your Code For Reusability (International PHP Conference / IPC 2008)Decouple Your Code For Reusability (International PHP Conference / IPC 2008)
Decouple Your Code For Reusability (International PHP Conference / IPC 2008)
Fabien Potencier
 
Doctrine fixtures
Doctrine fixturesDoctrine fixtures
Doctrine fixtures
Bill Chang
 
Unit and Functional Testing with Symfony2
Unit and Functional Testing with Symfony2Unit and Functional Testing with Symfony2
Unit and Functional Testing with Symfony2
Fabien Potencier
 
Dependency Injection with PHP and PHP 5.3
Dependency Injection with PHP and PHP 5.3Dependency Injection with PHP and PHP 5.3
Dependency Injection with PHP and PHP 5.3
Fabien Potencier
 
News of the Symfony2 World
News of the Symfony2 WorldNews of the Symfony2 World
News of the Symfony2 World
Fabien Potencier
 

What's hot (20)

The History of PHPersistence
The History of PHPersistenceThe History of PHPersistence
The History of PHPersistence
 
Database Design Patterns
Database Design PatternsDatabase Design Patterns
Database Design Patterns
 
Silex meets SOAP & REST
Silex meets SOAP & RESTSilex meets SOAP & REST
Silex meets SOAP & REST
 
Doctrine MongoDB ODM (PDXPHP)
Doctrine MongoDB ODM (PDXPHP)Doctrine MongoDB ODM (PDXPHP)
Doctrine MongoDB ODM (PDXPHP)
 
Dependency injection-zendcon-2010
Dependency injection-zendcon-2010Dependency injection-zendcon-2010
Dependency injection-zendcon-2010
 
Symfony2 - WebExpo 2010
Symfony2 - WebExpo 2010Symfony2 - WebExpo 2010
Symfony2 - WebExpo 2010
 
Rich domain model with symfony 2.5 and doctrine 2.5
Rich domain model with symfony 2.5 and doctrine 2.5Rich domain model with symfony 2.5 and doctrine 2.5
Rich domain model with symfony 2.5 and doctrine 2.5
 
Dependency injection in PHP 5.3/5.4
Dependency injection in PHP 5.3/5.4Dependency injection in PHP 5.3/5.4
Dependency injection in PHP 5.3/5.4
 
Symfony2, creare bundle e valore per il cliente
Symfony2, creare bundle e valore per il clienteSymfony2, creare bundle e valore per il cliente
Symfony2, creare bundle e valore per il cliente
 
Dependency injection - phpday 2010
Dependency injection - phpday 2010Dependency injection - phpday 2010
Dependency injection - phpday 2010
 
Advanced symfony Techniques
Advanced symfony TechniquesAdvanced symfony Techniques
Advanced symfony Techniques
 
Decouple Your Code For Reusability (International PHP Conference / IPC 2008)
Decouple Your Code For Reusability (International PHP Conference / IPC 2008)Decouple Your Code For Reusability (International PHP Conference / IPC 2008)
Decouple Your Code For Reusability (International PHP Conference / IPC 2008)
 
Doctrine fixtures
Doctrine fixturesDoctrine fixtures
Doctrine fixtures
 
Agile database access with CakePHP 3
Agile database access with CakePHP 3Agile database access with CakePHP 3
Agile database access with CakePHP 3
 
Lithium Best
Lithium Best Lithium Best
Lithium Best
 
Unit and Functional Testing with Symfony2
Unit and Functional Testing with Symfony2Unit and Functional Testing with Symfony2
Unit and Functional Testing with Symfony2
 
Dependency Injection with PHP and PHP 5.3
Dependency Injection with PHP and PHP 5.3Dependency Injection with PHP and PHP 5.3
Dependency Injection with PHP and PHP 5.3
 
Models and Service Layers, Hemoglobin and Hobgoblins
Models and Service Layers, Hemoglobin and HobgoblinsModels and Service Layers, Hemoglobin and Hobgoblins
Models and Service Layers, Hemoglobin and Hobgoblins
 
News of the Symfony2 World
News of the Symfony2 WorldNews of the Symfony2 World
News of the Symfony2 World
 
Future of HTTP in CakePHP
Future of HTTP in CakePHPFuture of HTTP in CakePHP
Future of HTTP in CakePHP
 

Similar to The Zen of Lithium

What mom never told you about bundle configurations - Symfony Live Paris 2012
What mom never told you about bundle configurations - Symfony Live Paris 2012What mom never told you about bundle configurations - Symfony Live Paris 2012
What mom never told you about bundle configurations - Symfony Live Paris 2012
D
 
DrupalCamp Foz - Novas APIs Drupal 7
DrupalCamp Foz - Novas APIs Drupal 7DrupalCamp Foz - Novas APIs Drupal 7
DrupalCamp Foz - Novas APIs Drupal 7
chuvainc
 
Facebook的缓存系统
Facebook的缓存系统Facebook的缓存系统
Facebook的缓存系统
yiditushe
 

Similar to The Zen of Lithium (20)

Virtual Madness @ Etsy
Virtual Madness @ EtsyVirtual Madness @ Etsy
Virtual Madness @ Etsy
 
Doctrine For Beginners
Doctrine For BeginnersDoctrine For Beginners
Doctrine For Beginners
 
Bag Of Tricks From Iusethis
Bag Of Tricks From IusethisBag Of Tricks From Iusethis
Bag Of Tricks From Iusethis
 
Zend Framework Study@Tokyo #2
Zend Framework Study@Tokyo #2Zend Framework Study@Tokyo #2
Zend Framework Study@Tokyo #2
 
Unit testing zend framework apps
Unit testing zend framework appsUnit testing zend framework apps
Unit testing zend framework apps
 
Add loop shortcode
Add loop shortcodeAdd loop shortcode
Add loop shortcode
 
CakePHP workshop
CakePHP workshopCakePHP workshop
CakePHP workshop
 
What mom never told you about bundle configurations - Symfony Live Paris 2012
What mom never told you about bundle configurations - Symfony Live Paris 2012What mom never told you about bundle configurations - Symfony Live Paris 2012
What mom never told you about bundle configurations - Symfony Live Paris 2012
 
Dependency Injection
Dependency InjectionDependency Injection
Dependency Injection
 
Symfony2 from the Trenches
Symfony2 from the TrenchesSymfony2 from the Trenches
Symfony2 from the Trenches
 
DrupalCamp Foz - Novas APIs Drupal 7
DrupalCamp Foz - Novas APIs Drupal 7DrupalCamp Foz - Novas APIs Drupal 7
DrupalCamp Foz - Novas APIs Drupal 7
 
関西PHP勉強会 php5.4つまみぐい
関西PHP勉強会 php5.4つまみぐい関西PHP勉強会 php5.4つまみぐい
関西PHP勉強会 php5.4つまみぐい
 
Unit testing with zend framework PHPBenelux
Unit testing with zend framework PHPBeneluxUnit testing with zend framework PHPBenelux
Unit testing with zend framework PHPBenelux
 
Unit testing with zend framework tek11
Unit testing with zend framework tek11Unit testing with zend framework tek11
Unit testing with zend framework tek11
 
Introduction to Zend Framework web services
Introduction to Zend Framework web servicesIntroduction to Zend Framework web services
Introduction to Zend Framework web services
 
Zend framework service
Zend framework serviceZend framework service
Zend framework service
 
Zend framework service
Zend framework serviceZend framework service
Zend framework service
 
Facebook的缓存系统
Facebook的缓存系统Facebook的缓存系统
Facebook的缓存系统
 
WordPress as an application framework
WordPress as an application frameworkWordPress as an application framework
WordPress as an application framework
 
laravel tricks in 50minutes
laravel tricks in 50minuteslaravel tricks in 50minutes
laravel tricks in 50minutes
 

More from Nate Abele

More from Nate Abele (6)

Measuring Your Code 2.0
Measuring Your Code 2.0Measuring Your Code 2.0
Measuring Your Code 2.0
 
Measuring Your Code
Measuring Your CodeMeasuring Your Code
Measuring Your Code
 
Building Apps with MongoDB
Building Apps with MongoDBBuilding Apps with MongoDB
Building Apps with MongoDB
 
Practical PHP 5.3
Practical PHP 5.3Practical PHP 5.3
Practical PHP 5.3
 
Measuring Your Code
Measuring Your CodeMeasuring Your Code
Measuring Your Code
 
Doin' It Rong
Doin' It RongDoin' It Rong
Doin' It Rong
 

Recently uploaded

Future Visions: Predictions to Guide and Time Tech Innovation, Peter Udo Diehl
Future Visions: Predictions to Guide and Time Tech Innovation, Peter Udo DiehlFuture Visions: Predictions to Guide and Time Tech Innovation, Peter Udo Diehl
Future Visions: Predictions to Guide and Time Tech Innovation, Peter Udo Diehl
Peter Udo Diehl
 
Search and Society: Reimagining Information Access for Radical Futures
Search and Society: Reimagining Information Access for Radical FuturesSearch and Society: Reimagining Information Access for Radical Futures
Search and Society: Reimagining Information Access for Radical Futures
Bhaskar Mitra
 
Essentials of Automations: Optimizing FME Workflows with Parameters
Essentials of Automations: Optimizing FME Workflows with ParametersEssentials of Automations: Optimizing FME Workflows with Parameters
Essentials of Automations: Optimizing FME Workflows with Parameters
Safe Software
 

Recently uploaded (20)

ODC, Data Fabric and Architecture User Group
ODC, Data Fabric and Architecture User GroupODC, Data Fabric and Architecture User Group
ODC, Data Fabric and Architecture User Group
 
Measures in SQL (a talk at SF Distributed Systems meetup, 2024-05-22)
Measures in SQL (a talk at SF Distributed Systems meetup, 2024-05-22)Measures in SQL (a talk at SF Distributed Systems meetup, 2024-05-22)
Measures in SQL (a talk at SF Distributed Systems meetup, 2024-05-22)
 
Knowledge engineering: from people to machines and back
Knowledge engineering: from people to machines and backKnowledge engineering: from people to machines and back
Knowledge engineering: from people to machines and back
 
Future Visions: Predictions to Guide and Time Tech Innovation, Peter Udo Diehl
Future Visions: Predictions to Guide and Time Tech Innovation, Peter Udo DiehlFuture Visions: Predictions to Guide and Time Tech Innovation, Peter Udo Diehl
Future Visions: Predictions to Guide and Time Tech Innovation, Peter Udo Diehl
 
IoT Analytics Company Presentation May 2024
IoT Analytics Company Presentation May 2024IoT Analytics Company Presentation May 2024
IoT Analytics Company Presentation May 2024
 
PLAI - Acceleration Program for Generative A.I. Startups
PLAI - Acceleration Program for Generative A.I. StartupsPLAI - Acceleration Program for Generative A.I. Startups
PLAI - Acceleration Program for Generative A.I. Startups
 
How world-class product teams are winning in the AI era by CEO and Founder, P...
How world-class product teams are winning in the AI era by CEO and Founder, P...How world-class product teams are winning in the AI era by CEO and Founder, P...
How world-class product teams are winning in the AI era by CEO and Founder, P...
 
IESVE for Early Stage Design and Planning
IESVE for Early Stage Design and PlanningIESVE for Early Stage Design and Planning
IESVE for Early Stage Design and Planning
 
Slack (or Teams) Automation for Bonterra Impact Management (fka Social Soluti...
Slack (or Teams) Automation for Bonterra Impact Management (fka Social Soluti...Slack (or Teams) Automation for Bonterra Impact Management (fka Social Soluti...
Slack (or Teams) Automation for Bonterra Impact Management (fka Social Soluti...
 
Search and Society: Reimagining Information Access for Radical Futures
Search and Society: Reimagining Information Access for Radical FuturesSearch and Society: Reimagining Information Access for Radical Futures
Search and Society: Reimagining Information Access for Radical Futures
 
Essentials of Automations: Optimizing FME Workflows with Parameters
Essentials of Automations: Optimizing FME Workflows with ParametersEssentials of Automations: Optimizing FME Workflows with Parameters
Essentials of Automations: Optimizing FME Workflows with Parameters
 
Connector Corner: Automate dynamic content and events by pushing a button
Connector Corner: Automate dynamic content and events by pushing a buttonConnector Corner: Automate dynamic content and events by pushing a button
Connector Corner: Automate dynamic content and events by pushing a button
 
Free and Effective: Making Flows Publicly Accessible, Yumi Ibrahimzade
Free and Effective: Making Flows Publicly Accessible, Yumi IbrahimzadeFree and Effective: Making Flows Publicly Accessible, Yumi Ibrahimzade
Free and Effective: Making Flows Publicly Accessible, Yumi Ibrahimzade
 
"Impact of front-end architecture on development cost", Viktor Turskyi
"Impact of front-end architecture on development cost", Viktor Turskyi"Impact of front-end architecture on development cost", Viktor Turskyi
"Impact of front-end architecture on development cost", Viktor Turskyi
 
The architecture of Generative AI for enterprises.pdf
The architecture of Generative AI for enterprises.pdfThe architecture of Generative AI for enterprises.pdf
The architecture of Generative AI for enterprises.pdf
 
Kubernetes & AI - Beauty and the Beast !?! @KCD Istanbul 2024
Kubernetes & AI - Beauty and the Beast !?! @KCD Istanbul 2024Kubernetes & AI - Beauty and the Beast !?! @KCD Istanbul 2024
Kubernetes & AI - Beauty and the Beast !?! @KCD Istanbul 2024
 
Software Delivery At the Speed of AI: Inflectra Invests In AI-Powered Quality
Software Delivery At the Speed of AI: Inflectra Invests In AI-Powered QualitySoftware Delivery At the Speed of AI: Inflectra Invests In AI-Powered Quality
Software Delivery At the Speed of AI: Inflectra Invests In AI-Powered Quality
 
Bits & Pixels using AI for Good.........
Bits & Pixels using AI for Good.........Bits & Pixels using AI for Good.........
Bits & Pixels using AI for Good.........
 
In-Depth Performance Testing Guide for IT Professionals
In-Depth Performance Testing Guide for IT ProfessionalsIn-Depth Performance Testing Guide for IT Professionals
In-Depth Performance Testing Guide for IT Professionals
 
Empowering NextGen Mobility via Large Action Model Infrastructure (LAMI): pav...
Empowering NextGen Mobility via Large Action Model Infrastructure (LAMI): pav...Empowering NextGen Mobility via Large Action Model Infrastructure (LAMI): pav...
Empowering NextGen Mobility via Large Action Model Infrastructure (LAMI): pav...
 

The Zen of Lithium

  • 2. • Former lead developer, CakePHP • Co-founder & lead developer of Lithium for ~2 years • Original BostonPHP framework bake-off champ! • Twitter: @nateabele
  • 3. • Started as a series of test scripts on early dev builds of PHP 5.3 • Released as “Cake3” in July ‘09 • Spun off as Lithium in October ’09 • Based on 5 years’ experience developing a high-adoption web framework
  • 5. Procedural Object-Oriented
  • 6. Procedural Object-Oriented
  • 7. Aspect-Oriented Event-Driven Procedural PARADIGMS Declarative Functional Object-Oriented
  • 8. The Fall of Rome PARADIGM HUBRIS
  • 9.
  • 10. + $
  • 11. Z END F RAMEWORK 1.5
  • 12. $transport = new Zend_Mail_Transport_Smtp('smtp.gmail.com', array( 'auth' => 'login', 'username' => 'foo', 'password' => 'bar', 'ssl' => 'ssl', 'port' => 465, )); $mailer = new Zend_Mail(); $mailer->setDefaultTransport($transport);
  • 13. class Container { public function getMailTransport() { return new Zend_Mail_Transport_Smtp('smtp.gmail.com', array( 'auth' => 'login', 'username' => 'root', 'password' => 'sekr1t', 'ssl' => 'ssl', 'port' => 465, )); } public function getMailer() { $mailer = new Zend_Mail(); $mailer->setDefaultTransport($this->getMailTransport()); return $mailer; } }
  • 14. class Container { protected $parameters = array(); public function __construct(array $parameters = array()) { $this->parameters = $parameters; } public function getMailTransport() { return new Zend_Mail_Transport_Smtp('smtp.gmail.com', array( 'auth' => 'login', 'username' => $this->parameters['mailer.username'], 'password' => $this->parameters['mailer.password'], 'ssl' => 'ssl', 'port' => 465, )); } public function getMailer() { $mailer = new Zend_Mail(); $mailer->setDefaultTransport($this->getMailTransport()); return $mailer; } }
  • 15. $container = new Container(array( 'mailer.username' => 'root', 'mailer.password' => 'sekr1t', 'mailer.class' => 'Zend_Mail', )); $mailer = $container->getMailer();
  • 16. class Container extends sfServiceContainer { static protected $shared = array(); protected function getMailTransportService() { return new Zend_Mail_Transport_Smtp('smtp.gmail.com', array( 'auth' => 'login', 'username' => $this['mailer.username'], 'password' => $this['mailer.password'], 'ssl' => 'ssl', 'port' => 465, )); } protected function getMailerService() { if (isset(self::$shared['mailer'])) { return self::$shared['mailer']; } $class = $this['mailer.class']; $mailer = new $class(); $mailer->setDefaultTransport($this->getMailTransportService()); return self::$shared['mailer'] = $mailer; } }
  • 17. sfServiceContainerAutoloader::register(); $sc = new sfServiceContainerBuilder(); $sc->register('mail.transport', 'Zend_Mail_Transport_Smtp')-> addArgument('smtp.gmail.com')-> addArgument(array( 'auth' => 'login', 'username' => '%mailer.username%', 'password' => '%mailer.password%', 'ssl' => 'ssl', 'port' => 465, ))->setShared(false); $sc->register('mailer', '%mailer.class%')-> addMethodCall('setDefaultTransport', array( new sfServiceReference('mail.transport') ));
  • 18. <?xml version="1.0" ?> <container xmlns="http://symfony-project.org/2.0/container"> <parameters> <parameter key="mailer.username">root</parameter> <parameter key="mailer.password">sekr1t</parameter> <parameter key="mailer.class">Zend_Mail</parameter> </parameters> <services> <service id="mail.transport" class="Zend_Mail_Transport_Smtp" shared="false"> <argument>smtp.gmail.com</argument> <argument type="collection"> <argument key="auth">login</argument> <argument key="username">%mailer.username%</argument> <argument key="password">%mailer.password%</argument> <argument key="ssl">ssl</argument> <argument key="port">465</argument> </argument> </service> <service id="mailer" class="%mailer.class%"> <call method="setDefaultTransport"> <argument type="service" id="mail.transport" /> </call> </service> </services> </container>
  • 19. Dependency injection container + Service container + Service container builder + XML
  • 20. ==
  • 22.
  • 23. THE MORAL “ 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
  • 24. The Guggenheim Fallingwater GREAT ARCHITECTURE
  • 25.
  • 27. Jet Li’s Fearless JET LI AS HOU YUANJIA
  • 29. J EET K UNE D O The Way of the Intercepting Fist
  • 30. GOALS • Understand a variety of paradigms & their strengths • Respect context when choosing paradigms / techniques • Be simple as possible (but no simpler)
  • 31. PROBLEMS • Managing configuration • Staying flexible • Extending internals • Easy things: easy; hard things: possible
  • 33. webroot/index.php require dirname(__DIR__) . '/config/bootstrap.php'; echo lithiumactionDispatcher::run( new lithiumactionRequest() );
  • 34. config/bootstrap.php require __DIR__ . '/bootstrap/libraries.php'; require __DIR__ . '/bootstrap/errors.php'; require __DIR__ . '/bootstrap/cache.php'; require __DIR__ . '/bootstrap/connections.php'; require __DIR__ . '/bootstrap/action.php'; require __DIR__ . '/bootstrap/session.php'; require __DIR__ . '/bootstrap/g11n.php'; require __DIR__ . '/bootstrap/media.php'; require __DIR__ . '/bootstrap/console.php';
  • 36. config/bootstrap/cache.php use lithiumstorageCache; Cache::config(array( 'local' => array('adapter' => 'Apc'), 'distributed' => array( 'adapter' => 'Memcache', 'host' => '127.0.0.1:11211' ), 'default' => array('adapter' => 'File') ));
  • 37. config/bootstrap/connections.php use lithiumdataConnections; Connections::config(array( 'default' => array( 'type' => 'MongoDb', 'database' => 'my_mongo_db' ), 'legacy' => array( 'type' => 'database', 'adapter' => 'MySql', 'login' => 'bobbytables', 'password' => 's3kr1t', 'database' => 'my_mysql_db' ) ));
  • 38. config/bootstrap/session.php use lithiumsecurityAuth; Auth::config(array( 'customer' => array( 'adapter' => 'Form', 'model' => 'Customers', 'fields' => array('email', 'password') ), 'administrator' => array( 'adapter' => 'Http', 'method' => 'digest', 'users' => array('nate' => 'li3') ) ));
  • 39. MULTIPLE ENVIRONMENTS? use lithiumstorageCache; Cache::config(array( 'default' => array( 'development' => array('adapter' => 'Apc'), 'production' => array( 'adapter' => 'Memcache', 'host' => '127.0.0.1:1121' ) ) ));
  • 40. MULTIPLE ENVIRONMENTS? use lithiumstorageCache; Cache::config(array( 'default' => array( 'development' => array('adapter' => 'Apc'), 'production' => array( 'adapter' => 'Memcache', 'host' => '127.0.0.1:1121' ) ) ));
  • 41. namespace lithiumnethttp; use lithiumcoreLibraries; class Service extends lithiumcoreObject { protected $_classes = array( 'media' => 'lithiumnethttpMedia', 'request' => 'lithiumnethttpRequest', 'response' => 'lithiumnethttpResponse', ); public function __construct(array $config = array()) { $defaults = array( 'scheme' => 'http', 'host' => 'localhost', // ... ); parent::__construct($config + $defaults); } protected function _init() { // ... } }
  • 42. $service = new Service(array( 'scheme' => 'https', 'host' => 'web.service.com', 'username' => 'user', 'password' => 's3kr1t' ));
  • 43. $service = new Service(array( 'scheme' => 'https', 'host' => 'web.service.com', 'username' => 'user', 'password' => 's3kr1t' )); { “lithiumnethttpService”: { “scheme”: “https”, “host”: “web.service.com”, “username”: “user”, “password”: “s3kr1t” } }
  • 44. $service = new Service(array( 'scheme' => 'https', 'host' => 'web.service.com', 'username' => 'user', 'password' => 's3kr1t', 'classes' => array( 'request' => 'mycustomRequest' ) ));
  • 47. HELPERS <?=$this->form->text('email'); ?> <input type="text" name="email" id="MemberEmail" value="nate.abele@gmail.com" />
  • 48. HELPERS <?=$this->form->field('name', array( 'wrap' => array('class' => 'wrapper') )); ?>
  • 49. HELPERS <?=$this->form->field('name', array( 'wrap' => array('class' => 'wrapper') )); ?> <div class="wrapper"> <label for="MemberName">Name</label> <input type="text" name="name" id="MemberName" /> <div class="error">You don't have a name?</div> </div>
  • 50. HELPERS <?=$this->form->field('name', array( 'wrap' => array('class' => 'item'), 'template' => '<li{:wrap}>{:error}{:label}{:input}</li>' )); ?>
  • 51. HELPERS <?=$this->form->field('name', array( 'wrap' => array('class' => 'item'), 'template' => '<li{:wrap}>{:error}{:label}{:input}</li>' )); ?> <li class="item"> <div class="error">You don't have a name?</div> <label for="MemberName">Name</label> <input type="text" name="name" id="MemberName" /> </div>
  • 52. HELPERS $this->form->config(array('templates' => array( 'field' => "<li{:wrap}>{:error}{:label}{:input}</li>" )));
  • 53. HELPERS <input type="text" name="email" id="MemberEmail" value="nate.abele@gmail.com" />
  • 54. HELPERS $form = $this->form; $this->form->config(array('attributes' => array( 'id' => function($method, $name, $options) use (&$form) { if ($method != 'text' && $method != 'select') { return; } $model = null; if ($binding = $form->binding()) { $model = basename(str_replace('', '/', $binding->model())) . '_'; } return Inflector::underscore($model . $name); } )));
  • 55. HELPERS $form = $this->form; $this->form->config(array('attributes' => array( 'id' => function($method, $name, $options) use (&$form) { if ($method != 'text' && $method != 'select') { return; } $model = null; if ($binding = $form->binding()) { $model = basename(str_replace('', '/', $binding->model())) . '_'; } return Inflector::underscore($model . $name); } )));
  • 56. HELPERS <input type="text" name="email" id="member_email" value="nate.abele@gmail.com" />
  • 57. THE MEDIA CLASS class WeblogController < ActionController::Base def index @posts = Post.find :all respond_to do |format| format.html format.xml { render :xml => @posts.to_xml } format.rss { render :action => "feed.rxml" } end end end
  • 58. THE MEDIA CLASS ! class WeblogController < ActionController::Base def index @posts = Post.find :all respond_to do |format| format.html format.xml { render :xml => @posts.to_xml } format.rss { render :action => "feed.rxml" } end end end
  • 59. THE MEDIA CLASS <?php echo $javascript->object($data); ?>
  • 60. THE MEDIA CLASS ! <?php echo $javascript->object($data); ?>
  • 61. THE MEDIA CLASS lithiumnethttpMedia { $formats = array( array( 'html' => array(...), 'posts' => ... 'json' => array(...), ) 'xml' => array(...), '...' ); }
  • 62. THE MEDIA CLASS lithiumnethttpMedia { $formats = array( array( 'html' => array(...), 'posts' => ... 'json' => array(...), ) 'xml' => array(...), '...' ); }
  • 63. THE MEDIA CLASS Media::type('mobile', array('text/html'), array( 'view' => 'lithiumtemplateView', 'paths' => array( 'template' => array( '{:library}/views/{:controller}/{:template}.mobile.php', '{:library}/views/{:controller}/{:template}.html.php', ), 'layout' => array( '{:library}/views/layouts/{:layout}.mobile.php', '{:library}/views/layouts/{:layout}.html.php', ), 'element' => array( '{:library}/views/elements/{:template}.mobile.php' '{:library}/views/elements/{:template}.html.php' ) ), 'conditions' => array('mobile' => true) ));
  • 64. THE MEDIA CLASS Media::type('mobile', array('text/html'), array( 'view' => 'lithiumtemplateView', 'paths' => array( 'template' => array( '{:library}/views/{:controller}/{:template}.mobile.php', '{:library}/views/{:controller}/{:template}.html.php', ), 'layout' => array( '{:library}/views/layouts/{:layout}.mobile.php', '{:library}/views/layouts/{:layout}.html.php', ), 'element' => array( '{:library}/views/elements/{:template}.mobile.php' '{:library}/views/elements/{:template}.html.php' ) ), 'conditions' => array('mobile' => true) ));
  • 65. THE MEDIA CLASS Media::type('ajax', array('text/html'), array( 'view' => 'lithiumtemplateView', 'paths' => array( 'template' => array( '{:library}/views/{:controller}/{:template}.ajax.php', '{:library}/views/{:controller}/{:template}.html.php', ), 'layout' => false, 'element' => array( '{:library}/views/elements/{:template}.ajax.php' '{:library}/views/elements/{:template}.html.php' ) ), 'conditions' => array('ajax' => true) ));
  • 66. THE MEDIA CLASS Media::type('ajax', array('text/html'), array( 'view' => 'lithiumtemplateView', 'paths' => array( 'template' => array( '{:library}/views/{:controller}/{:template}.ajax.php', '{:library}/views/{:controller}/{:template}.html.php', ), 'layout' => false, 'element' => array( '{:library}/views/elements/{:template}.ajax.php' '{:library}/views/elements/{:template}.html.php' ) ), 'conditions' => array('ajax' => true) ));
  • 67. CONDITIONS? 'conditions' => array('ajax' => true) == $request->is('ajax') == $_SERVER['HTTP_X_REQUESTED_WITH'] = 'XMLHttpRequest'
  • 68. CONDITIONS? $request->detect('iPhone', array('HTTP_USER_AGENT', '/iPhone/')); $isiPhone = $request->is('iPhone'); $request->detect('custom', function($request) { if ($value = $request->env("HTTP_WHATEVER")) { // Do something with $value } return false; });
  • 69. ROUTING Router::connect('/{:controller}/{:action}/{:id:[0-9]+}', array( 'id' => null )); new Route(array( 'template' => '/{:controller}/{:action}/{:id:[0-9]+}', 'pattern' => '@^(?:/(?P[^/]+))(?:/(?P[^/]+)?)?(?:/(?P[0-9]+)?)?$@', 'params' => array('id' => null, 'action' => 'index'), // ... 'subPatterns' => array('id' => '[0-9]+'), 'persist' => array('controller') )); Router::connect(new CustomRoute($params));
  • 71. ROUTE HANDLERS Router::connect('/{:user}/{:controller}/{:action}', array(), function($request) { if (!Users::count(array('conditions' => array('user' => $request->user)))) { return false; } return $request; });
  • 72. ROUTE HANDLERS Router::connect('/{:user}/{:controller}/{:action}', array(), function($request) { if (!Users::count(array('conditions' => array('user' => $request->user)))) { return false; } return $request; }); Router::connect('/', array(), function($request) { if (Session::read('user')) { $location = 'Accounts::index'; } else { $location = 'Users::add'; } return new Response(array('status' => 302, 'location' => $location)); });
  • 73. ROUTE HANDLERS Router::connect( '/photos/view/{:id:[0-9a-f]{24}}.jpg', array(), function($request) { return new Response(array( 'headers' => array('Content-type' => 'image/jpeg'), 'body' => Photos::first($request->id)->file->getBytes() )); } );
  • 74. MICRO-APPS Router::connect('/posts.json', array(), function($request) { return new Response(array( 'headers' => array('Content-type' => 'application/json'), 'body' => Posts::all()->to('json') )); }); Router::connect('/posts/{:id}.json', array(), function($request) { return new Response(array( 'headers' => array('Content-type' => 'application/json'), 'body' => Posts::first($request->id)->to('json') )); });
  • 76. HELPERS <?=$this->html->*() ?> lithiumtemplatehelperHtml
  • 77. HELPERS <?=$this->html->*() ?> lithiumtemplatehelperHtml appextensionshelperHtml
  • 78. MODELS namespace appmodels; class Posts extends lithiumdataModel { }
  • 79. MODELS namespace appmodels; class Posts extends lithiumdataModel { protected $_meta = array( 'key' => 'custom_id', 'source' => 'custom_posts_table' 'connection' => 'legacy_mysql_db' ); }
  • 80. MODELS namespace appmodels; class Posts extends lithiumdataModel { protected $_meta = array( 'key' => array( 'custom_id', 'other_custom_id' ) ); }
  • 81. MODELS Posts::create(array( 'title' => 'My first post ever', 'body' => 'Wherein I extoll the virtues of Lithium' )); // ... $post->save();
  • 82. MODELS $post->tags = 'technology,PHP,news'; $post->save(); // ... foreach ($post->tags as $tag) { #FALE }
  • 83. MODELS namespace appmodels; class Posts extends lithiumdataModel { public function tags($entity) { return explode(',', $entity->tags); } } foreach ($post->tags() as $tag) { // ... }
  • 84. MODELS namespace appmodels; class Posts extends lithiumdataModel { public function tags($entity) { return explode(',', $entity->tags); } } foreach ($post->tags() as $tag) { // ... }
  • 85. MODELS namespace appmodels; class Posts extends lithiumdataModel { public static function expire() { return static::update( array('expired' => true), array('updated' => array( '<=' => strtotime('3 months ago') )) ); } } $didItWork = Posts::expire();
  • 86. ENTITIES & COLLECTIONS $posts = Posts::findAllBySomeCondition();
  • 87. ENTITIES & COLLECTIONS $posts = Posts::findAllBySomeCondition(); $posts->first(function($post) { return $post->published == true; }); $posts->each(function($post) { return $post->counter++; }); $ids = $posts->map(function($post) { return $post->id; });
  • 88. RELATIONSHIPS namespace appmodels; class Posts extends lithiumdataModel { public $belongsTo = array('Users'); }
  • 89. RELATIONSHIPS namespace appmodels; class Posts extends lithiumdataModel { public $belongsTo = array('Author' => array( 'class' => 'Users' )); }
  • 90. RELATIONSHIPS namespace appmodels; class Posts extends lithiumdataModel { public $belongsTo = array('Author' => array( 'class' => 'Users', 'conditions' => array('active' => true), 'key' => 'author_id' )); }
  • 91. RELATIONSHIPS namespace appmodels; class Posts extends lithiumdataModel { public $belongsTo = array('Author' => array( 'class' => 'Users', 'conditions' => array('active' => true), 'key' => array( 'author_id' => 'id', 'other_key' => 'other_id' ) )); }
  • 92. NO H AS A ND B ELONGS T O M ANY !!
  • 93. DOCUMENT DATABASES $post = Posts::create(array( 'title' => "New post", 'body' => "Something worthwhile to read", 'tags' => array('PHP', 'tech'), 'author' => array('name' => 'Nate') ));
  • 94. DOCUMENT DATABASES $posts = Posts::all(array('conditions' => array( 'tags' => array('PHP', 'tech'), 'author.name' => 'Nate' )));
  • 95. DOCUMENT DATABASES $ages = Users::all(array( 'group' => 'age', 'reduce' => 'function(obj, prev) { prev.count++; }', 'initial' => array('count' => 0) ));
  • 96. THE QUERY API $query = new Query(array( 'type' => 'read', 'model' => 'appmodelsPost', 'fields' => array('Post.title', 'Post.body'), 'conditions' => array('Post.id' => new Query(array( 'type' => 'read', 'fields' => array('post_id'), 'model' => 'appmodelsTagging', 'conditions' => array('Tag.name' => array('foo', 'bar', 'baz')), ))) ));
  • 99. 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; });
  • 102. 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; });
  • 103. THE TALK OF THE TOWN
  • 104. CAN I USE IT IN PRODUCTION?
  • 109. David Coallier • President, PEAR Group • CTO, Echolibre / Orchestra.io “ After looking at Lithium I’ve come to realize how far ahead it is compared to other frameworks from a technologist's point of view. ”
  • 110. Helgi Þormar Þorbjörnsson • Developer, PEAR Installer • PEAR Core Dev, 8 years “ It’s the f*****g epiphany of modern! ”
  • 111. Fahad Ibnay Heylaal • Creator, Croogo CMS “ I believe the future is in Lithium. give it time to grow, and the developers behind it are awesome. ”
  • 112. 1.0?
  • 115. 0.10
  • 116. THANKS!! Find me later : @nateabele nate.abele@gmail.com http://nateabele.com/
  • 117. RESOURCES Getting connected Learning AOP lithify.me bit.ly/aop-design github.com/UnionOfRAD bit.ly/aop-gwoo #li3 on irc.freenode.net bit.ly/aop-li3 @UnionOfRAD bit.ly/aop-oop Talks bit.ly/mwop-aop slideshare.net/nateabele