SlideShare una empresa de Scribd logo
1 de 118
Descargar para leer sin conexión
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/

Más contenido relacionado

La actualidad más candente

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
 

La actualidad más candente (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 a 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 a 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
 

Más de 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
 

Más de 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
 

Último

TrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
TrustArc Webinar - Stay Ahead of US State Data Privacy Law DevelopmentsTrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
TrustArc Webinar - Stay Ahead of US State Data Privacy Law DevelopmentsTrustArc
 
Driving Behavioral Change for Information Management through Data-Driven Gree...
Driving Behavioral Change for Information Management through Data-Driven Gree...Driving Behavioral Change for Information Management through Data-Driven Gree...
Driving Behavioral Change for Information Management through Data-Driven Gree...Enterprise Knowledge
 
08448380779 Call Girls In Civil Lines Women Seeking Men
08448380779 Call Girls In Civil Lines Women Seeking Men08448380779 Call Girls In Civil Lines Women Seeking Men
08448380779 Call Girls In Civil Lines Women Seeking MenDelhi Call girls
 
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdf
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdfThe Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdf
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdfEnterprise Knowledge
 
Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024The Digital Insurer
 
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...Drew Madelung
 
Unblocking The Main Thread Solving ANRs and Frozen Frames
Unblocking The Main Thread Solving ANRs and Frozen FramesUnblocking The Main Thread Solving ANRs and Frozen Frames
Unblocking The Main Thread Solving ANRs and Frozen FramesSinan KOZAK
 
[2024]Digital Global Overview Report 2024 Meltwater.pdf
[2024]Digital Global Overview Report 2024 Meltwater.pdf[2024]Digital Global Overview Report 2024 Meltwater.pdf
[2024]Digital Global Overview Report 2024 Meltwater.pdfhans926745
 
Slack Application Development 101 Slides
Slack Application Development 101 SlidesSlack Application Development 101 Slides
Slack Application Development 101 Slidespraypatel2
 
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...Miguel Araújo
 
Developing An App To Navigate The Roads of Brazil
Developing An App To Navigate The Roads of BrazilDeveloping An App To Navigate The Roads of Brazil
Developing An App To Navigate The Roads of BrazilV3cube
 
🐬 The future of MySQL is Postgres 🐘
🐬  The future of MySQL is Postgres   🐘🐬  The future of MySQL is Postgres   🐘
🐬 The future of MySQL is Postgres 🐘RTylerCroy
 
08448380779 Call Girls In Greater Kailash - I Women Seeking Men
08448380779 Call Girls In Greater Kailash - I Women Seeking Men08448380779 Call Girls In Greater Kailash - I Women Seeking Men
08448380779 Call Girls In Greater Kailash - I Women Seeking MenDelhi Call girls
 
Factors to Consider When Choosing Accounts Payable Services Providers.pptx
Factors to Consider When Choosing Accounts Payable Services Providers.pptxFactors to Consider When Choosing Accounts Payable Services Providers.pptx
Factors to Consider When Choosing Accounts Payable Services Providers.pptxKatpro Technologies
 
Presentation on how to chat with PDF using ChatGPT code interpreter
Presentation on how to chat with PDF using ChatGPT code interpreterPresentation on how to chat with PDF using ChatGPT code interpreter
Presentation on how to chat with PDF using ChatGPT code interpreternaman860154
 
IAC 2024 - IA Fast Track to Search Focused AI Solutions
IAC 2024 - IA Fast Track to Search Focused AI SolutionsIAC 2024 - IA Fast Track to Search Focused AI Solutions
IAC 2024 - IA Fast Track to Search Focused AI SolutionsEnterprise Knowledge
 
Boost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivityBoost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivityPrincipled Technologies
 
WhatsApp 9892124323 ✓Call Girls In Kalyan ( Mumbai ) secure service
WhatsApp 9892124323 ✓Call Girls In Kalyan ( Mumbai ) secure serviceWhatsApp 9892124323 ✓Call Girls In Kalyan ( Mumbai ) secure service
WhatsApp 9892124323 ✓Call Girls In Kalyan ( Mumbai ) secure servicePooja Nehwal
 
Scaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organizationScaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organizationRadu Cotescu
 
Salesforce Community Group Quito, Salesforce 101
Salesforce Community Group Quito, Salesforce 101Salesforce Community Group Quito, Salesforce 101
Salesforce Community Group Quito, Salesforce 101Paola De la Torre
 

Último (20)

TrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
TrustArc Webinar - Stay Ahead of US State Data Privacy Law DevelopmentsTrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
TrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
 
Driving Behavioral Change for Information Management through Data-Driven Gree...
Driving Behavioral Change for Information Management through Data-Driven Gree...Driving Behavioral Change for Information Management through Data-Driven Gree...
Driving Behavioral Change for Information Management through Data-Driven Gree...
 
08448380779 Call Girls In Civil Lines Women Seeking Men
08448380779 Call Girls In Civil Lines Women Seeking Men08448380779 Call Girls In Civil Lines Women Seeking Men
08448380779 Call Girls In Civil Lines Women Seeking Men
 
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdf
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdfThe Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdf
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdf
 
Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024
 
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
 
Unblocking The Main Thread Solving ANRs and Frozen Frames
Unblocking The Main Thread Solving ANRs and Frozen FramesUnblocking The Main Thread Solving ANRs and Frozen Frames
Unblocking The Main Thread Solving ANRs and Frozen Frames
 
[2024]Digital Global Overview Report 2024 Meltwater.pdf
[2024]Digital Global Overview Report 2024 Meltwater.pdf[2024]Digital Global Overview Report 2024 Meltwater.pdf
[2024]Digital Global Overview Report 2024 Meltwater.pdf
 
Slack Application Development 101 Slides
Slack Application Development 101 SlidesSlack Application Development 101 Slides
Slack Application Development 101 Slides
 
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
 
Developing An App To Navigate The Roads of Brazil
Developing An App To Navigate The Roads of BrazilDeveloping An App To Navigate The Roads of Brazil
Developing An App To Navigate The Roads of Brazil
 
🐬 The future of MySQL is Postgres 🐘
🐬  The future of MySQL is Postgres   🐘🐬  The future of MySQL is Postgres   🐘
🐬 The future of MySQL is Postgres 🐘
 
08448380779 Call Girls In Greater Kailash - I Women Seeking Men
08448380779 Call Girls In Greater Kailash - I Women Seeking Men08448380779 Call Girls In Greater Kailash - I Women Seeking Men
08448380779 Call Girls In Greater Kailash - I Women Seeking Men
 
Factors to Consider When Choosing Accounts Payable Services Providers.pptx
Factors to Consider When Choosing Accounts Payable Services Providers.pptxFactors to Consider When Choosing Accounts Payable Services Providers.pptx
Factors to Consider When Choosing Accounts Payable Services Providers.pptx
 
Presentation on how to chat with PDF using ChatGPT code interpreter
Presentation on how to chat with PDF using ChatGPT code interpreterPresentation on how to chat with PDF using ChatGPT code interpreter
Presentation on how to chat with PDF using ChatGPT code interpreter
 
IAC 2024 - IA Fast Track to Search Focused AI Solutions
IAC 2024 - IA Fast Track to Search Focused AI SolutionsIAC 2024 - IA Fast Track to Search Focused AI Solutions
IAC 2024 - IA Fast Track to Search Focused AI Solutions
 
Boost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivityBoost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivity
 
WhatsApp 9892124323 ✓Call Girls In Kalyan ( Mumbai ) secure service
WhatsApp 9892124323 ✓Call Girls In Kalyan ( Mumbai ) secure serviceWhatsApp 9892124323 ✓Call Girls In Kalyan ( Mumbai ) secure service
WhatsApp 9892124323 ✓Call Girls In Kalyan ( Mumbai ) secure service
 
Scaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organizationScaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organization
 
Salesforce Community Group Quito, Salesforce 101
Salesforce Community Group Quito, Salesforce 101Salesforce Community Group Quito, Salesforce 101
Salesforce Community Group Quito, Salesforce 101
 

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