SlideShare a Scribd company logo
1 of 118
Download to read offline
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

The History of PHPersistence
The History of PHPersistenceThe History of PHPersistence
The History of PHPersistenceHugo Hamon
 
Database Design Patterns
Database Design PatternsDatabase Design Patterns
Database Design PatternsHugo Hamon
 
Silex meets SOAP & REST
Silex meets SOAP & RESTSilex meets SOAP & REST
Silex meets SOAP & RESTHugo Hamon
 
Doctrine MongoDB ODM (PDXPHP)
Doctrine MongoDB ODM (PDXPHP)Doctrine MongoDB ODM (PDXPHP)
Doctrine MongoDB ODM (PDXPHP)Kris Wallsmith
 
Dependency injection-zendcon-2010
Dependency injection-zendcon-2010Dependency injection-zendcon-2010
Dependency injection-zendcon-2010Fabien Potencier
 
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.5Leonardo Proietti
 
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.4Fabien Potencier
 
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 clienteLeonardo Proietti
 
Dependency injection - phpday 2010
Dependency injection - phpday 2010Dependency injection - phpday 2010
Dependency injection - phpday 2010Fabien Potencier
 
Advanced symfony Techniques
Advanced symfony TechniquesAdvanced symfony Techniques
Advanced symfony TechniquesKris Wallsmith
 
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 fixturesBill Chang
 
Unit and Functional Testing with Symfony2
Unit and Functional Testing with Symfony2Unit and Functional Testing with Symfony2
Unit and Functional Testing with Symfony2Fabien 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.3Fabien Potencier
 
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 HobgoblinsRoss Tuck
 
News of the Symfony2 World
News of the Symfony2 WorldNews of the Symfony2 World
News of the Symfony2 WorldFabien Potencier
 
Future of HTTP in CakePHP
Future of HTTP in CakePHPFuture of HTTP in CakePHP
Future of HTTP in CakePHPmarkstory
 

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

Virtual Madness @ Etsy
Virtual Madness @ EtsyVirtual Madness @ Etsy
Virtual Madness @ EtsyNishan Subedi
 
Doctrine For Beginners
Doctrine For BeginnersDoctrine For Beginners
Doctrine For BeginnersJonathan Wage
 
Bag Of Tricks From Iusethis
Bag Of Tricks From IusethisBag Of Tricks From Iusethis
Bag Of Tricks From IusethisMarcus Ramberg
 
Zend Framework Study@Tokyo #2
Zend Framework Study@Tokyo #2Zend Framework Study@Tokyo #2
Zend Framework Study@Tokyo #2Shinya Ohyanagi
 
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 2012D
 
Dependency Injection
Dependency InjectionDependency Injection
Dependency InjectionRifat Nabi
 
Symfony2 from the Trenches
Symfony2 from the TrenchesSymfony2 from the Trenches
Symfony2 from the TrenchesJonathan Wage
 
DrupalCamp Foz - Novas APIs Drupal 7
DrupalCamp Foz - Novas APIs Drupal 7DrupalCamp Foz - Novas APIs Drupal 7
DrupalCamp Foz - Novas APIs Drupal 7chuvainc
 
関西PHP勉強会 php5.4つまみぐい
関西PHP勉強会 php5.4つまみぐい関西PHP勉強会 php5.4つまみぐい
関西PHP勉強会 php5.4つまみぐいHisateru Tanaka
 
Unit testing with zend framework PHPBenelux
Unit testing with zend framework PHPBeneluxUnit testing with zend framework PHPBenelux
Unit testing with zend framework PHPBeneluxMichelangelo van Dam
 
Unit testing with zend framework tek11
Unit testing with zend framework tek11Unit testing with zend framework tek11
Unit testing with zend framework tek11Michelangelo van Dam
 
Introduction to Zend Framework web services
Introduction to Zend Framework web servicesIntroduction to Zend Framework web services
Introduction to Zend Framework web servicesMichelangelo van Dam
 
Facebook的缓存系统
Facebook的缓存系统Facebook的缓存系统
Facebook的缓存系统yiditushe
 
WordPress as an application framework
WordPress as an application frameworkWordPress as an application framework
WordPress as an application frameworkDustin Filippini
 
laravel tricks in 50minutes
laravel tricks in 50minuteslaravel tricks in 50minutes
laravel tricks in 50minutesBarang CK
 

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

Measuring Your Code 2.0
Measuring Your Code 2.0Measuring Your Code 2.0
Measuring Your Code 2.0Nate Abele
 
Measuring Your Code
Measuring Your CodeMeasuring Your Code
Measuring Your CodeNate Abele
 
Building Apps with MongoDB
Building Apps with MongoDBBuilding Apps with MongoDB
Building Apps with MongoDBNate Abele
 
Practical PHP 5.3
Practical PHP 5.3Practical PHP 5.3
Practical PHP 5.3Nate Abele
 
Measuring Your Code
Measuring Your CodeMeasuring Your Code
Measuring Your CodeNate 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

Developer Data Modeling Mistakes: From Postgres to NoSQL
Developer Data Modeling Mistakes: From Postgres to NoSQLDeveloper Data Modeling Mistakes: From Postgres to NoSQL
Developer Data Modeling Mistakes: From Postgres to NoSQLScyllaDB
 
The Role of FIDO in a Cyber Secure Netherlands: FIDO Paris Seminar.pptx
The Role of FIDO in a Cyber Secure Netherlands: FIDO Paris Seminar.pptxThe Role of FIDO in a Cyber Secure Netherlands: FIDO Paris Seminar.pptx
The Role of FIDO in a Cyber Secure Netherlands: FIDO Paris Seminar.pptxLoriGlavin3
 
WordPress Websites for Engineers: Elevate Your Brand
WordPress Websites for Engineers: Elevate Your BrandWordPress Websites for Engineers: Elevate Your Brand
WordPress Websites for Engineers: Elevate Your Brandgvaughan
 
Time Series Foundation Models - current state and future directions
Time Series Foundation Models - current state and future directionsTime Series Foundation Models - current state and future directions
Time Series Foundation Models - current state and future directionsNathaniel Shimoni
 
Dev Dives: Streamline document processing with UiPath Studio Web
Dev Dives: Streamline document processing with UiPath Studio WebDev Dives: Streamline document processing with UiPath Studio Web
Dev Dives: Streamline document processing with UiPath Studio WebUiPathCommunity
 
Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024BookNet Canada
 
Scale your database traffic with Read & Write split using MySQL Router
Scale your database traffic with Read & Write split using MySQL RouterScale your database traffic with Read & Write split using MySQL Router
Scale your database traffic with Read & Write split using MySQL RouterMydbops
 
What is Artificial Intelligence?????????
What is Artificial Intelligence?????????What is Artificial Intelligence?????????
What is Artificial Intelligence?????????blackmambaettijean
 
DevoxxFR 2024 Reproducible Builds with Apache Maven
DevoxxFR 2024 Reproducible Builds with Apache MavenDevoxxFR 2024 Reproducible Builds with Apache Maven
DevoxxFR 2024 Reproducible Builds with Apache MavenHervé Boutemy
 
Digital Identity is Under Attack: FIDO Paris Seminar.pptx
Digital Identity is Under Attack: FIDO Paris Seminar.pptxDigital Identity is Under Attack: FIDO Paris Seminar.pptx
Digital Identity is Under Attack: FIDO Paris Seminar.pptxLoriGlavin3
 
Ensuring Technical Readiness For Copilot in Microsoft 365
Ensuring Technical Readiness For Copilot in Microsoft 365Ensuring Technical Readiness For Copilot in Microsoft 365
Ensuring Technical Readiness For Copilot in Microsoft 3652toLead Limited
 
What's New in Teams Calling, Meetings and Devices March 2024
What's New in Teams Calling, Meetings and Devices March 2024What's New in Teams Calling, Meetings and Devices March 2024
What's New in Teams Calling, Meetings and Devices March 2024Stephanie Beckett
 
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024BookNet Canada
 
Gen AI in Business - Global Trends Report 2024.pdf
Gen AI in Business - Global Trends Report 2024.pdfGen AI in Business - Global Trends Report 2024.pdf
Gen AI in Business - Global Trends Report 2024.pdfAddepto
 
How AI, OpenAI, and ChatGPT impact business and software.
How AI, OpenAI, and ChatGPT impact business and software.How AI, OpenAI, and ChatGPT impact business and software.
How AI, OpenAI, and ChatGPT impact business and software.Curtis Poe
 
From Family Reminiscence to Scholarly Archive .
From Family Reminiscence to Scholarly Archive .From Family Reminiscence to Scholarly Archive .
From Family Reminiscence to Scholarly Archive .Alan Dix
 
Nell’iperspazio con Rocket: il Framework Web di Rust!
Nell’iperspazio con Rocket: il Framework Web di Rust!Nell’iperspazio con Rocket: il Framework Web di Rust!
Nell’iperspazio con Rocket: il Framework Web di Rust!Commit University
 
Passkey Providers and Enabling Portability: FIDO Paris Seminar.pptx
Passkey Providers and Enabling Portability: FIDO Paris Seminar.pptxPasskey Providers and Enabling Portability: FIDO Paris Seminar.pptx
Passkey Providers and Enabling Portability: FIDO Paris Seminar.pptxLoriGlavin3
 
Transcript: New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024Transcript: New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024BookNet Canada
 
The Ultimate Guide to Choosing WordPress Pros and Cons
The Ultimate Guide to Choosing WordPress Pros and ConsThe Ultimate Guide to Choosing WordPress Pros and Cons
The Ultimate Guide to Choosing WordPress Pros and ConsPixlogix Infotech
 

Recently uploaded (20)

Developer Data Modeling Mistakes: From Postgres to NoSQL
Developer Data Modeling Mistakes: From Postgres to NoSQLDeveloper Data Modeling Mistakes: From Postgres to NoSQL
Developer Data Modeling Mistakes: From Postgres to NoSQL
 
The Role of FIDO in a Cyber Secure Netherlands: FIDO Paris Seminar.pptx
The Role of FIDO in a Cyber Secure Netherlands: FIDO Paris Seminar.pptxThe Role of FIDO in a Cyber Secure Netherlands: FIDO Paris Seminar.pptx
The Role of FIDO in a Cyber Secure Netherlands: FIDO Paris Seminar.pptx
 
WordPress Websites for Engineers: Elevate Your Brand
WordPress Websites for Engineers: Elevate Your BrandWordPress Websites for Engineers: Elevate Your Brand
WordPress Websites for Engineers: Elevate Your Brand
 
Time Series Foundation Models - current state and future directions
Time Series Foundation Models - current state and future directionsTime Series Foundation Models - current state and future directions
Time Series Foundation Models - current state and future directions
 
Dev Dives: Streamline document processing with UiPath Studio Web
Dev Dives: Streamline document processing with UiPath Studio WebDev Dives: Streamline document processing with UiPath Studio Web
Dev Dives: Streamline document processing with UiPath Studio Web
 
Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
 
Scale your database traffic with Read & Write split using MySQL Router
Scale your database traffic with Read & Write split using MySQL RouterScale your database traffic with Read & Write split using MySQL Router
Scale your database traffic with Read & Write split using MySQL Router
 
What is Artificial Intelligence?????????
What is Artificial Intelligence?????????What is Artificial Intelligence?????????
What is Artificial Intelligence?????????
 
DevoxxFR 2024 Reproducible Builds with Apache Maven
DevoxxFR 2024 Reproducible Builds with Apache MavenDevoxxFR 2024 Reproducible Builds with Apache Maven
DevoxxFR 2024 Reproducible Builds with Apache Maven
 
Digital Identity is Under Attack: FIDO Paris Seminar.pptx
Digital Identity is Under Attack: FIDO Paris Seminar.pptxDigital Identity is Under Attack: FIDO Paris Seminar.pptx
Digital Identity is Under Attack: FIDO Paris Seminar.pptx
 
Ensuring Technical Readiness For Copilot in Microsoft 365
Ensuring Technical Readiness For Copilot in Microsoft 365Ensuring Technical Readiness For Copilot in Microsoft 365
Ensuring Technical Readiness For Copilot in Microsoft 365
 
What's New in Teams Calling, Meetings and Devices March 2024
What's New in Teams Calling, Meetings and Devices March 2024What's New in Teams Calling, Meetings and Devices March 2024
What's New in Teams Calling, Meetings and Devices March 2024
 
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
 
Gen AI in Business - Global Trends Report 2024.pdf
Gen AI in Business - Global Trends Report 2024.pdfGen AI in Business - Global Trends Report 2024.pdf
Gen AI in Business - Global Trends Report 2024.pdf
 
How AI, OpenAI, and ChatGPT impact business and software.
How AI, OpenAI, and ChatGPT impact business and software.How AI, OpenAI, and ChatGPT impact business and software.
How AI, OpenAI, and ChatGPT impact business and software.
 
From Family Reminiscence to Scholarly Archive .
From Family Reminiscence to Scholarly Archive .From Family Reminiscence to Scholarly Archive .
From Family Reminiscence to Scholarly Archive .
 
Nell’iperspazio con Rocket: il Framework Web di Rust!
Nell’iperspazio con Rocket: il Framework Web di Rust!Nell’iperspazio con Rocket: il Framework Web di Rust!
Nell’iperspazio con Rocket: il Framework Web di Rust!
 
Passkey Providers and Enabling Portability: FIDO Paris Seminar.pptx
Passkey Providers and Enabling Portability: FIDO Paris Seminar.pptxPasskey Providers and Enabling Portability: FIDO Paris Seminar.pptx
Passkey Providers and Enabling Portability: FIDO Paris Seminar.pptx
 
Transcript: New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024Transcript: New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
 
The Ultimate Guide to Choosing WordPress Pros and Cons
The Ultimate Guide to Choosing WordPress Pros and ConsThe Ultimate Guide to Choosing WordPress Pros and Cons
The Ultimate Guide to Choosing WordPress Pros and Cons
 

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