SlideShare una empresa de Scribd logo
1 de 223
A Framework for People
Who Hate Frameworks.
Find us on          :
http://joind.in/1592
A movement in 3 parts

• Frameworks suck
• Everything you know is wrong
• Lithium tries to suck less
The first part.
Let’s get one thing out
of the way:
Lithium sucks.
But that’s okay.
Because your
framework sucks, too.
And yes, I mean:
And yes, I mean:
Why?
(Besides the obvious attempt at being provocative)
Frameworks Suck
Frameworks Suck

• Code you will never use.
Frameworks Suck

• Code you will never use.
• Complexity overhead.
Frameworks Suck

• Code you will never use.
• Complexity overhead.
• You didn’t write it.
Also,
Also,

Martin Fowler.
His name is the
biggest, so it’s his
      fault.
His name is the
biggest, so it’s his
      fault.
We’re not saying design patterns are bad.
Quite the opposite.
Lithium implements many design patterns.
The Problem™
Some patterns only treat the symptoms,
instead of the cause.
Some examples:
Object dependencies.
“The principle of separating configuration from use.”
[CakePHP code]

function initialize(&$controller, $settings = array()) {
    /* ...[snip]... */

    $prefixes = Router::prefixes();
    if (!empty($prefixes)) {
        foreach ($prefixes as $prefix) {
            /* ...[do something nasty to the global state]... */
        }
    }
    if (Configure::read() > 0) {
        App::import('Debugger');
        Debugger::checkSecurityKeys();
    }
}
Sucks.
function initialize(&$controller, $settings = array()) {
    /* ...[snip]... */

    $prefixes = Router::prefixes();
    if (!empty($prefixes)) {
        foreach ($prefixes as $prefix) {
            /* ...[do something nasty to the global state]... */
        }
    }
    if (Configure::read() > 0) {
        App::import('Debugger');
        Debugger::checkSecurityKeys();
    }
}
Sucks.
function initialize(&$controller, $settings = array()) {
    /* ...[snip]... */

    $prefixes = Router::prefixes();
    if (!empty($prefixes)) {
        foreach ($prefixes as $prefix) {
            /* ...[do something nasty to the global state]... */
        }
    }
    if (Configure::read() > 0) {
        App::import('Debugger');
        Debugger::checkSecurityKeys();
    }
}

                                        Sucks hard.
Configuration.
Everyone does it differently.
Sometimes in the same framework.
Sometimes in the same class.
function spam($emails) {

    $this->Email->replyTo = 'nigerian.prince@example.com';
    $this->Email->from = 'Desmond Etete <nigerian.prince@example.com>';
    $this->Email->template = 'mo_monies';
    $this->Email->sendAs = 'annoying_html';

    foreach ($emails as $email) {
        $this->Email->to = $email['address'];
        $this->Email->subject = "Good to you news I have {$email['name']}";
        $this->Email->send();
    }
}
Sucks
function spam($emails) {

    $this->Email->replyTo = 'nigerian.prince@example.com';
    $this->Email->from = 'Desmond Etete <nigerian.prince@example.com>';
    $this->Email->template = 'mo_monies';
    $this->Email->sendAs = 'annoying_html';

    foreach ($emails as $email) {
        $this->Email->to = $email['address'];
        $this->Email->subject = "Good to you news I have {$email['name']}";
        $this->Email->send();
    }
}
Dependency injection.
A good idea!
... or is it?
[variables changed to protect the innocent.]
[variables changed to protect the innocent.]
                [... not really. It’s Symfony.]
class User {
    function __construct($storage) {
        $this->storage = $storage;
    }
}

$storage = new SessionStorage('SESSION_ID');
$user = new User($storage);
Show that object who’s boss.
class User {
    function __construct($storage) {
        $this->storage = $storage;
    }
}

$storage = new SessionStorage('SESSION_ID');
$user = new User($storage);
Of course, we want this to abstract this.

Frameworks adore abstractions.
DI Container to the rescue!
class Container {
    static protected $shared = array();
    // ...

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

        $class = $this->parameters['mailer.class'];
        $mailer = new $class();
        $mailer->setDefaultTransport($this->getMailTransport());

        return self::$shared['mailer'] = $mailer;
    }
}
But now you want an abstraction to manage
the DI container.


Duh.
So you create a “Service Container”.
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,
        ));
    }
}
Of course, we now want to abstract the
crap out of the Service Container.


Double duh.
So lets use a Builder to configure the
Services.
Yeah!
require_once '/PATH/TO/sfServiceContainerAutoloader.php';
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')))
;
And for good measure, have our configurations
for Service Containers in XML files.
So....
So....
We have Dependency Injection.
So....
We have Dependency Injection.
Managed by a Service Container.
So....
We have Dependency Injection.
Managed by a Service Container.
Parametrized with XML data.
So....
We have Dependency Injection.
Managed by a Service Container.
Parametrized with XML data.
And the whole thing configured by a Builder.
So....
We have Dependency Injection.
Managed by a Service Container.
Parametrized with XML data.
And the whole thing configured by a Builder.

...to fix one problem.
Anyone see what’s wrong here?
Many of these patterns were implemented in
Java, to solve language problems that PHP just
doesn’t have.
Try this some time...
The second part.
Everything
you know
is wrong.
The sun does not
revolve around OOP




        Galileo facing the Roman Inquistion
                     - Cristiano Banti (1857)
The sun does not
revolve around OOP
                                            ...nevertheless,
                                                it moves.




        Galileo facing the Roman Inquistion
                     - Cristiano Banti (1857)
Dependency injection.
Dependency
Injection  =
class User {

    public function create() {
        $logger = new Logger();
        $logger->write('Creating a new user...');

        $this->_doSomeInitialization();
        $this->_databaseConnection->doATransaction($this)->create();

        $logger->write('Finished creating user');
    }
}

$user = new User();
$user->create();
class User {

    public function create() {
        $logger = new Logger();
        $logger->write('Creating a new user...');

        $this->_doSomeInitialization();
        $this->_databaseConnection->doATransaction($this)->create();

        $logger->write('Finished creating user');
    }
}

$user = new User();
$user->create();
Dependency Injection
Dependency Injection
• Fixes the problem of static dependencies
Dependency Injection
• Fixes the problem of static dependencies
• Ignores the problem of static relationships
Dependency Injection
• Fixes the problem of static dependencies
• Ignores the problem of static relationships
 • Same methods called on injected classes
Dependency Injection
• Fixes the problem of static dependencies
• Ignores the problem of static relationships
 • Same methods called on injected classes
 • No way to introduce new relationships
Dependency Injection
• Fixes the problem of static dependencies
• Ignores the problem of static relationships
 • Same methods called on injected classes
 • No way to introduce new relationships
• Higher overhead, more boilerplate code
class Service extends lithiumcoreObject {

    protected $_classes = array(
        'request' => 'lithiumnethttpRequest',
        'response' => 'lithiumnethttpResponse',
        'socket'   => 'lithiumnetsocketContext'
    );

    protected function _init() {
        $class = Libraries::locate('socket.util', $this->_classes['socket']);
        $this->_connection = new $class($this->_config);
        // ...
    }

    /* ...[snip]... */

    public function send($method, $path = null, $data = null, array $options = array()) {
        /* ...[snip]... */
        $response = $this->_connection->send($request, array('classes' => $this->_classes));
        // ...
    }
}
class Service extends lithiumcoreObject {

    protected $_classes = array(
        'request' => 'lithiumnethttpRequest',
        'response' => 'lithiumnethttpResponse',
        'socket'   => 'lithiumnetsocketContext'
    );

    protected function _init() {
        $class = Libraries::locate('socket.util', $this->_classes['socket']);
        $this->_connection = new $class($this->_config);
        // ...
    }

    /* ...[snip]... */

    public function send($method, $path = null, $data = null, array $options = array()) {
        /* ...[snip]... */
        $response = $this->_connection->send($request, array('classes' => $this->_classes));
        // ...
    }
}
Coupling should be in
proportion to domain
relevance.
The problem of

state.
If...
If...
Configure::write('debug', 0);
If...
Configure::write('debug', 0);


  is evil,
If...
Configure::write('debug', 0);


  is evil,
$this->debug = 0;
If...
Configure::write('debug', 0);


  is evil,
$this->debug = 0;


  is the
of evil.
class Service {

    protected $_timeout = 30;

    public function send($method, $data = null, array $options = array()) {

        // WTF does this do?
        $this->_prepare();

        $response = $this->_connection->send($request, array(
            'timeout' => $this->_timeout
        ));
        // ...
    }
}
OO doesn’t make you
think (about state).
Design patterns.
ActiveRecord
                        Data Access Object

 Unit of Work
                              Dependency Injection
                  Registry

   Front Controller               MVC

                   Value Object
Data Mapper                         Service Layer
L E
F A
Design patterns
Design patterns
• Each pattern is only useful in a limited
  context
Design patterns
• Each pattern is only useful in a limited
  context

• Layering many design patterns on top of
  each other often indicates poor design
  choices
Design patterns
• Each pattern is only useful in a limited
  context

• Layering many design patterns on top of
  each other often indicates poor design
  choices

• Mis-application arises from trying to run
  before you can walk
Tools do not mean...




...you can build a house.
The third part.
Lithium tries to suck less.
Un-broken solutions.
Aspect-Oriented Design
Aspect-Oriented Design
• Separation of concerns
Aspect-Oriented Design
• Separation of concerns
• Domain classes should not know or care about cross-
   cutting concerns
Aspect-Oriented Design
• Separation of concerns
• Domain classes should not know or care about cross-
   cutting concerns

• Examples:
Aspect-Oriented Design
• Separation of concerns
• Domain classes should not know or care about cross-
   cutting concerns

• Examples:
 • Caching
Aspect-Oriented Design
• Separation of concerns
• Domain classes should not know or care about cross-
   cutting concerns

• Examples:
 • Caching
 • Logging
Aspect-Oriented Design
• Separation of concerns
• Domain classes should not know or care about cross-
   cutting concerns

• Examples:
 • Caching
 • Logging
 • Access Control, etc.
Functional Programming
Functional Programming

• Only possible when functions are first-class
  citizens
Functional Programming

• Only possible when functions are first-class
  citizens

• Referential transparency
Functional Programming

• Only possible when functions are first-class
  citizens

• Referential transparency
• Function purity
Referential transparency is not...
Referential transparency is not...


 $this                date()


           $_*
Referential transparency is not...


 $this                date()


           $_*
These Are Not
Design Patterns.
Less Suck
Less Suck
• Draws on years of experience building web
  frameworks
Less Suck
• Draws on years of experience building web
  frameworks

• PHP 5.3+ only
Less Suck
• Draws on years of experience building web
  frameworks

• PHP 5.3+ only
• Doesn’t assume you’re stupid
Ways we suck less:
Consistency.
<?php

namespace applicationbar;

use lithiumutilString;
use lithiumutilCollection;

class Foo extends lithiumcoreObject {

     protected $_classes = array(
         'cache' => 'lithiumstorageCache',
         'logger' => 'lithiumanalysisLogger'
     );

     public function __construct(array $config = array()) {
         // ...
     }

     protected function _init() {
         // ...
     }
}

?>
public function __construct(array $config = array())
<?php
    public function __construct(array $config = array())

namespace applicationbar;
    public function __construct(array $config = array())

use public function __construct(array $config = array())
    lithiumutilString;
use lithiumutilCollection;
    public function __construct(array $config = array())
class Foo extends lithiumcoreObject {
    public function __construct(array $config = array())
     protected $_classes = array(
     public function'lithiumstorageCache', = array())
         'cache' => __construct(array $config
         'logger' => 'lithiumanalysisLogger'
     public function __construct(array $config = array())
     );

     public function __construct(array $config = array()) {
         // ...
     }
     public function __construct(array $config = array())

     protected function _init() {
     public function __construct(array $config = array())
         // ...
     }
     public function __construct(array $config = array())
}
     public function __construct(array $config = array())
?>
     public function __construct(array $config = array())
<?php

namespace applicationbar;

use lithiumutilString;
use lithiumutilCollection;

class Foo extends lithiumcoreObject {

     protected $_classes = array(
         'cache' => 'lithiumstorageCache',
         'logger' => 'lithiumanalysisLogger'
     );

     public function __construct(array $config = array()) {
         // ...
     }

     protected function _init() {
         // ...
     }
}

?>
<?php
        <?php
namespace applicationbar;

     class Foo extends lithiumcoreObject
use lithiumutilString;                               {
use lithiumutilCollection;
          protected function _init() {
class Foo extends lithiumcoreObject {
                $or = $some->highOverHead($operation);
                $or()->otherwise(HARD_TO_TEST)->code();
    protected $_classes = array(
        'cache' => 'lithiumstorageCache',
          }
        'logger' => 'lithiumanalysisLogger'
     }
    );

       ?>
     public function __construct(array $config = array()) {
          // ...
     }

     protected function _init() {   2
         // ...
     }
}

?>
<?php
        <?php
namespace applicationbar;

     class Foo extends lithiumcoreObject
use lithiumutilString;                               {
use lithiumutilCollection;
          protected function _init() {
class Foo extends lithiumcoreObject {
                $or = $some->highOverHead($operation);
                $or()->otherwise(HARD_TO_TEST)->code();
    protected $_classes = array(
        'cache' => 'lithiumstorageCache',
          }
        'logger' => 'lithiumanalysisLogger'
     }
    );

       ?>
     public function __construct(array $config = array()) {
          // ...
     }

     protected function _init() {   2
         // ...
     }
}
     $foo = new Foo(array('init' => false));
?>
<?php

namespace applicationbar;

use lithiumutilString;
use lithiumutilCollection;

class Foo extends lithiumcoreObject {

     protected $_classes = array(
         'cache' => 'lithiumstorageCache',
         'logger' => 'lithiumanalysisLogger'
     );

     public function __construct(array $config = array()) {
         // ...
     }

     protected function _init() {
         // ...
     }
}

?>
<?php

namespace applicationbar;

use lithiumutilString;
use lithiumutilCollection;

class Foo extends lithiumcoreObject {

     protected $_classes = array(
         'cache' => 'lithiumstorageCache',
         'logger' => 'lithiumanalysisLogger'
     );

     public function __construct(array $config = array()) {
         // ...
     }

     protected function _init() {
         // ...
     }
}

?>
<?php

namespace applicationbar;
                             3
use lithiumutilString;
use lithiumutilCollection;
                                    new applicationbarFoo();
                                    // loads app/bar/Foo.php
class Foo extends lithiumcoreObject {

     protected $_classes = array(
         'cache' => 'lithiumstorageCache',
         'logger' => 'lithiumanalysisLogger'
     );

     public function __construct(array $config = array()) {
         // ...
     }

     protected function _init() {
         // ...
     }
}

?>
<?php

namespace applicationbar;



                                    4
use lithiumutilString;
use lithiumutilCollection;

class Foo extends lithiumcoreObject {

     protected $_classes = array(
         'cache' => 'lithiumstorageCache',
         'logger' => 'lithiumanalysisLogger'
     );

     public function __construct(array $config = array()) {
         // ...
     }

     protected function _init() {
         // ...
     }
}

?>
<?php

namespace applicationbar;

use lithiumutilString;
use lithiumutilCollection;

class Foo extends lithiumcoreObject {

     protected $_classes = array(
         'cache' => 'lithiumstorageCache',
         'logger' => 'lithiumanalysisLogger'
                                                  5
     );

     public function __construct(array $config = array()) {
         // ...
     }

     protected function _init() {
         // ... = $this->_classes['cache'];
         $cache
     }   $cache::write(__CLASS__, $this->_someGeneratedValue());
}    }
}
?>
?>
<?php

namespace applicationbar;

use lithiumutilString;
use lithiumutilCollection;

class Foo extends lithiumcoreObject {

     protected $_classes = array(
         'cache' => 'lithiumstorageCache',
         'logger' => 'lithiumanalysisLogger'
                                                  5
     );

     $foo = new Foo(array('classes' => array(
     public function __construct(array $config = array()) {
          'cache' => 'applicationextensionsCache'
         // ...
     )));
     }

     protected function _init() {
         // ... = $this->_classes['cache'];
         $cache
     }   $cache::write(__CLASS__, $this->_someGeneratedValue());
}    }
}
?>
?>
<?php

namespace applicationbar;

use lithiumutilString;
use lithiumutilCollection;

class Foo extends lithiumcoreObject {

     protected $_classes = array(
         'cache' => 'lithiumstorageCache',
         'logger' => 'lithiumanalysisLogger'
     );

     public function __construct(array $config = array()) {
         // ...
     }

     protected function _init() {
         // ... = $this->_classes['cache'];
         $cache
     }   $cache::write(__CLASS__, $this->_someGeneratedValue());
}    }
}
?>
?>
$options = array()
Keeps parameter lists short
             &
Makes class APIs more extensible
$config = array()
Same idea. But...!
Modifies class / object state.
Adaptable
Adaptable

   Auth

  Cache

  Catalog

Connections

  Logger

  Session
use lithiumsecurityAuth;

Auth::config(array(
    'customer' => array(
        'adapter' => 'Form',
        'model'   => 'Customer',
        'fields' => array('email', 'password')
    )
));
use lithiumstorageCache;

Cache::config(array(
    'local' => array('adapter' => 'Apc'),
    'distributed' => array(
        'adapter' => 'Memcached',
        'servers' => array('127.0.0.1', 11211),
    ),
    'default' => array('adapter' => 'File')
));
use lithiumdataConnections;

Connections::config(array(
    'old' => array(
        'type'      => 'database',
        'adapter' => 'MySql',
        'user'      => 'bobby_tables',
        'password' => '******',
        'database' => 'my_app'
    ),
    'new' => array(
        'type'      => 'MongoDb',
        'database' => 'my_app'
    )
));
use lithiumstorageSession;

Session::config(array(
    'cookie' => array(
        'adapter' => 'Cookie',
        'expire' => '+2 days'
    ),
    'default' => array('adapter' => 'Php')
));
Also fun:
use lithiumstorageSession;

Session::config(array(
    'default' => array(
        'adapter' => 'MyCustomAdapter',
        'expires' => '+2 days',
        'custom' => 'Whatevah!'
    )
));
use lithiumstorageSession;

Session::config(array(
    'default' => array(
        'adapter' => 'MyCustomAdapter',
        'expires' => '+2 days',
        'custom' => 'Whatevah!'
    )
));
use lithiumstorageSession;

Session::config(array(
    'default' => array(
        'adapter' => 'MyCustomAdapter',
        'expires' => '+2 days',
        'custom' => 'Whatevah!'
    )
));



public function __construct(array $config = array())
Multiple environments?
use lithiumstorageCache;

Cache::config(array(
    'default' => array(
        'development' => array(
            'adapter' => 'Apc'
        ),
        'production' => array(
            'adapter' => 'Memcached',
            'servers' => array('127.0.0.1', 11211)
        )
    )
));
use lithiumstorageCache;

Cache::config(array(
    'default' => array(
        'development' => array(
            'adapter' => 'Apc'
        ),
        'production' => array(
            'adapter' => 'Memcached',
            'servers' => array('127.0.0.1', 11211)
        )
    )
));
Works identically for all adapters.
If you remember nothing
else about configuration
state...
Immutability.
Set it and forget it.
Performance.
Zoom?
Zoom?
• Performance vs. speed of development is a
  series of trade-offs
Zoom?
• Performance vs. speed of development is a
  series of trade-offs

• Large-scale apps don’t use stock framework
  infrastructure, and that’s a good thing
Zoom?
• Performance vs. speed of development is a
  series of trade-offs

• Large-scale apps don’t use stock framework
  infrastructure, and that’s a good thing

• A generalized framework will never be as
  fast as hand-tuned code
Zoom!
Zoom!
• Choice is good
Zoom!
• Choice is good
• Use native extensions (PECL) whenever
  possible.
Zoom!
• Choice is good
• Use native extensions (PECL) whenever
  possible.

• Don’t like a class? Change it. At runtime.
Zoom!
• Choice is good
• Use native extensions (PECL) whenever
  possible.

• Don’t like a class? Change it. At runtime.
• Profiled at every step of the way.
Example
use appmodelsPost;
use lithiumactionResponse;
use lithiumnethttpRouter;



Router::connect('/frequent_api_call.json', array(), function($request) {
    return new Response(array(
        'type' => 'application/json',
        'body' => Post::recent()->to('json')
    ));
});
Flexibility.
Lithium is the most
flexible framework.
         (yeah, we said it)
Most class dependencies
are dynamic.
class Service extends lithiumcoreObject {

    protected $_classes = array(
        'request' => 'lithiumnethttpRequest',
        'response' => 'lithiumnethttpResponse',
        'socket'   => 'lithiumnetsocketContext'
    );
}

$service = new Service(array('classes' => array(
     'socket' => 'mycustomSocket'
)));
The Filter System:
Aspect-Oriented Design
for PHP.
Example: Caching & Logging
Example: Caching & Logging




          Post::find()
Example: Caching & Logging


      Logging


           Post::find()
Example: Caching & Logging

    Caching

       Logging


              Post::find()
Example: Caching & Logging

    Caching

       Logging


              Post::find()
Example: Caching & Logging

    Caching

       Logging


              Post::find()
Example: Caching & Logging

    Caching

       Logging


              Post::find()
Example: Caching & Logging

    Caching

       Logging


              Post::find()
Example: Caching & Logging

    Caching

       Logging


              Post::find()
Example: Caching & Logging

    Caching

       Logging


              Post::find()
Example: Caching & Logging

    Caching

       Logging


              Post::find()
Example: Caching & Logging

    Caching

       Logging


              Post::find()
use lithiumanalysisLogger;

Post::applyFilter('find', function($self, $params, $chain) {
    // Generate the log message
    $conditions = $params['options']['conditions'];
    $message = 'Post query with constraint ' . var_export($conditions, true);

      Logger::write('info', $message);
      return $chain->next($self, $params, $chain);
});
use lithiumanalysisLogger;

Post::applyFilter('find', function($self, $params, $chain) {
    // Generate the log message
    $conditions = $params['options']['conditions'];
    $message = 'Post query with constraint ' . var_export($conditions, true);

      Logger::write('info', $message);
      return $chain->next($self, $params, $chain);
});
use lithiumanalysisLogger;

Post::applyFilter('find', function($self, $params, $chain) {
    // Generate the log message
    $conditions = $params['options']['conditions'];
    $message = 'Post query with constraint ' . var_export($conditions, true);

      Logger::write('info', $message);
      return $chain->next($self, $params, $chain);
});
use lithiumanalysisLogger;

Post::applyFilter('find', function($self, $params, $chain) {
    // Generate the log message
    $conditions = $params['options']['conditions'];
    $message = 'Post query with constraint ' . var_export($conditions, true);

      Logger::write('info', $message);
      return $chain->next($self, $params, $chain);
});
use lithiumanalysisLogger;

Post::applyFilter('find', function($self, $params, $chain) {
    // Generate the log message
    $conditions = $params['options']['conditions'];
    $message = 'Post query with constraint ' . var_export($conditions, true);

      Logger::write('info', $message);
      return $chain->next($self, $params, $chain);
});
use lithiumanalysisLogger;




Post                                                                                   Logger
       Post::applyFilter('find', function($self, $params, $chain) {
           // Generate the log message
           $conditions = $params['options']['conditions'];
           $message = 'Post query with constraint ' . var_export($conditions, true);

             Logger::write('info', $message);
             return $chain->next($self, $params, $chain);
       });
What about Observer?
What about Observer?

• Dependent on a centralized publish/
  subscribe system
What about Observer?

• Dependent on a centralized publish/
  subscribe system

• Extra layer of abstraction
What about Observer?

• Dependent on a centralized publish/
  subscribe system

• Extra layer of abstraction
• Fewer possibilities
What about Observer?
What about Observer?


• Filters are self-contained and attach
  directly to objects
What about Observer?


• Filters are self-contained and attach
  directly to objects

• Direct and intuitive
Features: Everything is an adapter.

                       (well, almost)
Databases
• 1st-class support for document-oriented
  databases

• MongoDB & CouchDB: production ready
• Relational databases in beta
• Cassandra in the works, too
<?php

$post = Post::create(array(
    'title' => 'Ein bier, bitte',
    'body' => 'Was ist los mit dir?'
));
$post->save();

$post = Post::find($id);

?>

<h2><?=$post->title; ?></h2>
<p><?=$post->body; ?></p>
This works on...
This works on...

• MongoDB
This works on...

• MongoDB
• CouchDB
This works on...

• MongoDB
• CouchDB
• MySQL
This works on...

• MongoDB
• CouchDB
• MySQL
• SQLite (almost)
MongoDB + CouchDB:
$post = Post::create(array(
    'title' => 'Ein bier, bitte',
    'body' => 'Was ist los mit dir?',
    'tags' => array('PHP', 'Bier'),
    'author' => array('name' => 'Nate')
));

$post->save();
MongoDB:
$posts = Post::all(array('conditions' => array(
     'tags' => array('PHP', 'Bier'),
     'author.name' => 'Nate'
)));


// Translates to...
db.posts.find({
    tags : { $in : ['PHP', 'Bier'] },
    'author.name' : 'Nate'
})
Databases

• Adapter based, plugin aware
• Will ship with MySQL, SQLite
• SQL Server support via plugin
• Query API
The Query API

• Flexible data container
• Allows each backend data store to only worry
  about features it implements

• Keeps model API separate from backend data
  sources
The Query API

$ages = User::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')),
    )))
));
The Query API

• Run simple queries via the Model API
• Build your own complex queries with the
  Query API

• Create your own adapter, or drop in a
  custom query optimizer
Btw, li3_doctrine
Plays nice with others

• Easily load & use libraries from other
  frameworks:

  • Zend Framework, Solar, Symfony, PEAR,
    etc.

  • PSR-0 Class-loading standard
/* add the trunk */
Libraries::add("Zend", array(
    "prefix" => "Zend_",
    "includePath" => true,
    "bootstrap" => "Loader/Autoloader.php",
    "loader" => array("Zend_Loader_Autoloader", "autoload"),
    "transform" => function($class) { return str_replace("_", "/", $class) . ".php"; }
));



/* add the incubator */
Libraries::add("Zend_Incubator", array(
    "prefix" => "Zend_",
    "includePath" => '/htdocs/libraries/Zend/incubator/library',
    "transform" => function($class) { return str_replace("_", "/", $class) . ".php"; }
));
namespace appcontrollers;

use Zend_Mail_Storage_Pop3;

class EmailController extends lithiumactionController {

    public function index() {
        $mail = new Zend_Mail_Storage_Pop3(array(
            'host' => 'localhost', 'user' => 'test', 'password' => 'test'
        ));
        return compact('mail');
    }

}
This has been a presentation by:

Nate Abele (@nateabele)

Joël Perras (@jperras)

            Sucks. But check it out anyway.

Más contenido relacionado

Último

How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerThousandEyes
 
Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024
Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024
Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024The Digital Insurer
 
Connector Corner: Accelerate revenue generation using UiPath API-centric busi...
Connector Corner: Accelerate revenue generation using UiPath API-centric busi...Connector Corner: Accelerate revenue generation using UiPath API-centric busi...
Connector Corner: Accelerate revenue generation using UiPath API-centric busi...DianaGray10
 
Boost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdfBoost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdfsudhanshuwaghmare1
 
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
 
Handwritten Text Recognition for manuscripts and early printed texts
Handwritten Text Recognition for manuscripts and early printed textsHandwritten Text Recognition for manuscripts and early printed texts
Handwritten Text Recognition for manuscripts and early printed textsMaria Levchenko
 
From Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time AutomationFrom Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time AutomationSafe Software
 
Workshop - Best of Both Worlds_ Combine KG and Vector search for enhanced R...
Workshop - Best of Both Worlds_ Combine  KG and Vector search for  enhanced R...Workshop - Best of Both Worlds_ Combine  KG and Vector search for  enhanced R...
Workshop - Best of Both Worlds_ Combine KG and Vector search for enhanced R...Neo4j
 
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobe
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, AdobeApidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobe
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobeapidays
 
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
 
Histor y of HAM Radio presentation slide
Histor y of HAM Radio presentation slideHistor y of HAM Radio presentation slide
Histor y of HAM Radio presentation slidevu2urc
 
How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerThousandEyes
 
Understanding Discord NSFW Servers A Guide for Responsible Users.pdf
Understanding Discord NSFW Servers A Guide for Responsible Users.pdfUnderstanding Discord NSFW Servers A Guide for Responsible Users.pdf
Understanding Discord NSFW Servers A Guide for Responsible Users.pdfUK Journal
 
Advantages of Hiring UIUX Design Service Providers for Your Business
Advantages of Hiring UIUX Design Service Providers for Your BusinessAdvantages of Hiring UIUX Design Service Providers for Your Business
Advantages of Hiring UIUX Design Service Providers for Your BusinessPixlogix Infotech
 
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
 
Real Time Object Detection Using Open CV
Real Time Object Detection Using Open CVReal Time Object Detection Using Open CV
Real Time Object Detection Using Open CVKhem
 
TrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
TrustArc Webinar - Unlock the Power of AI-Driven Data DiscoveryTrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
TrustArc Webinar - Unlock the Power of AI-Driven Data DiscoveryTrustArc
 
Strategies for Landing an Oracle DBA Job as a Fresher
Strategies for Landing an Oracle DBA Job as a FresherStrategies for Landing an Oracle DBA Job as a Fresher
Strategies for Landing an Oracle DBA Job as a FresherRemote DBA Services
 
Strategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
Strategize a Smooth Tenant-to-tenant Migration and Copilot TakeoffStrategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
Strategize a Smooth Tenant-to-tenant Migration and Copilot Takeoffsammart93
 

Último (20)

How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected Worker
 
Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024
Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024
Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024
 
Connector Corner: Accelerate revenue generation using UiPath API-centric busi...
Connector Corner: Accelerate revenue generation using UiPath API-centric busi...Connector Corner: Accelerate revenue generation using UiPath API-centric busi...
Connector Corner: Accelerate revenue generation using UiPath API-centric busi...
 
Boost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdfBoost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdf
 
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
 
Handwritten Text Recognition for manuscripts and early printed texts
Handwritten Text Recognition for manuscripts and early printed textsHandwritten Text Recognition for manuscripts and early printed texts
Handwritten Text Recognition for manuscripts and early printed texts
 
From Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time AutomationFrom Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time Automation
 
Workshop - Best of Both Worlds_ Combine KG and Vector search for enhanced R...
Workshop - Best of Both Worlds_ Combine  KG and Vector search for  enhanced R...Workshop - Best of Both Worlds_ Combine  KG and Vector search for  enhanced R...
Workshop - Best of Both Worlds_ Combine KG and Vector search for enhanced R...
 
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
 
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobe
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, AdobeApidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobe
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobe
 
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...
 
Histor y of HAM Radio presentation slide
Histor y of HAM Radio presentation slideHistor y of HAM Radio presentation slide
Histor y of HAM Radio presentation slide
 
How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected Worker
 
Understanding Discord NSFW Servers A Guide for Responsible Users.pdf
Understanding Discord NSFW Servers A Guide for Responsible Users.pdfUnderstanding Discord NSFW Servers A Guide for Responsible Users.pdf
Understanding Discord NSFW Servers A Guide for Responsible Users.pdf
 
Advantages of Hiring UIUX Design Service Providers for Your Business
Advantages of Hiring UIUX Design Service Providers for Your BusinessAdvantages of Hiring UIUX Design Service Providers for Your Business
Advantages of Hiring UIUX Design Service Providers for Your Business
 
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...
 
Real Time Object Detection Using Open CV
Real Time Object Detection Using Open CVReal Time Object Detection Using Open CV
Real Time Object Detection Using Open CV
 
TrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
TrustArc Webinar - Unlock the Power of AI-Driven Data DiscoveryTrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
TrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
 
Strategies for Landing an Oracle DBA Job as a Fresher
Strategies for Landing an Oracle DBA Job as a FresherStrategies for Landing an Oracle DBA Job as a Fresher
Strategies for Landing an Oracle DBA Job as a Fresher
 
Strategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
Strategize a Smooth Tenant-to-tenant Migration and Copilot TakeoffStrategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
Strategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
 

Destacado

How Race, Age and Gender Shape Attitudes Towards Mental Health
How Race, Age and Gender Shape Attitudes Towards Mental HealthHow Race, Age and Gender Shape Attitudes Towards Mental Health
How Race, Age and Gender Shape Attitudes Towards Mental HealthThinkNow
 
AI Trends in Creative Operations 2024 by Artwork Flow.pdf
AI Trends in Creative Operations 2024 by Artwork Flow.pdfAI Trends in Creative Operations 2024 by Artwork Flow.pdf
AI Trends in Creative Operations 2024 by Artwork Flow.pdfmarketingartwork
 
PEPSICO Presentation to CAGNY Conference Feb 2024
PEPSICO Presentation to CAGNY Conference Feb 2024PEPSICO Presentation to CAGNY Conference Feb 2024
PEPSICO Presentation to CAGNY Conference Feb 2024Neil Kimberley
 
Content Methodology: A Best Practices Report (Webinar)
Content Methodology: A Best Practices Report (Webinar)Content Methodology: A Best Practices Report (Webinar)
Content Methodology: A Best Practices Report (Webinar)contently
 
How to Prepare For a Successful Job Search for 2024
How to Prepare For a Successful Job Search for 2024How to Prepare For a Successful Job Search for 2024
How to Prepare For a Successful Job Search for 2024Albert Qian
 
Social Media Marketing Trends 2024 // The Global Indie Insights
Social Media Marketing Trends 2024 // The Global Indie InsightsSocial Media Marketing Trends 2024 // The Global Indie Insights
Social Media Marketing Trends 2024 // The Global Indie InsightsKurio // The Social Media Age(ncy)
 
Trends In Paid Search: Navigating The Digital Landscape In 2024
Trends In Paid Search: Navigating The Digital Landscape In 2024Trends In Paid Search: Navigating The Digital Landscape In 2024
Trends In Paid Search: Navigating The Digital Landscape In 2024Search Engine Journal
 
5 Public speaking tips from TED - Visualized summary
5 Public speaking tips from TED - Visualized summary5 Public speaking tips from TED - Visualized summary
5 Public speaking tips from TED - Visualized summarySpeakerHub
 
ChatGPT and the Future of Work - Clark Boyd
ChatGPT and the Future of Work - Clark Boyd ChatGPT and the Future of Work - Clark Boyd
ChatGPT and the Future of Work - Clark Boyd Clark Boyd
 
Getting into the tech field. what next
Getting into the tech field. what next Getting into the tech field. what next
Getting into the tech field. what next Tessa Mero
 
Google's Just Not That Into You: Understanding Core Updates & Search Intent
Google's Just Not That Into You: Understanding Core Updates & Search IntentGoogle's Just Not That Into You: Understanding Core Updates & Search Intent
Google's Just Not That Into You: Understanding Core Updates & Search IntentLily Ray
 
Time Management & Productivity - Best Practices
Time Management & Productivity -  Best PracticesTime Management & Productivity -  Best Practices
Time Management & Productivity - Best PracticesVit Horky
 
The six step guide to practical project management
The six step guide to practical project managementThe six step guide to practical project management
The six step guide to practical project managementMindGenius
 
Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...
Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...
Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...RachelPearson36
 
Unlocking the Power of ChatGPT and AI in Testing - A Real-World Look, present...
Unlocking the Power of ChatGPT and AI in Testing - A Real-World Look, present...Unlocking the Power of ChatGPT and AI in Testing - A Real-World Look, present...
Unlocking the Power of ChatGPT and AI in Testing - A Real-World Look, present...Applitools
 
12 Ways to Increase Your Influence at Work
12 Ways to Increase Your Influence at Work12 Ways to Increase Your Influence at Work
12 Ways to Increase Your Influence at WorkGetSmarter
 

Destacado (20)

How Race, Age and Gender Shape Attitudes Towards Mental Health
How Race, Age and Gender Shape Attitudes Towards Mental HealthHow Race, Age and Gender Shape Attitudes Towards Mental Health
How Race, Age and Gender Shape Attitudes Towards Mental Health
 
AI Trends in Creative Operations 2024 by Artwork Flow.pdf
AI Trends in Creative Operations 2024 by Artwork Flow.pdfAI Trends in Creative Operations 2024 by Artwork Flow.pdf
AI Trends in Creative Operations 2024 by Artwork Flow.pdf
 
Skeleton Culture Code
Skeleton Culture CodeSkeleton Culture Code
Skeleton Culture Code
 
PEPSICO Presentation to CAGNY Conference Feb 2024
PEPSICO Presentation to CAGNY Conference Feb 2024PEPSICO Presentation to CAGNY Conference Feb 2024
PEPSICO Presentation to CAGNY Conference Feb 2024
 
Content Methodology: A Best Practices Report (Webinar)
Content Methodology: A Best Practices Report (Webinar)Content Methodology: A Best Practices Report (Webinar)
Content Methodology: A Best Practices Report (Webinar)
 
How to Prepare For a Successful Job Search for 2024
How to Prepare For a Successful Job Search for 2024How to Prepare For a Successful Job Search for 2024
How to Prepare For a Successful Job Search for 2024
 
Social Media Marketing Trends 2024 // The Global Indie Insights
Social Media Marketing Trends 2024 // The Global Indie InsightsSocial Media Marketing Trends 2024 // The Global Indie Insights
Social Media Marketing Trends 2024 // The Global Indie Insights
 
Trends In Paid Search: Navigating The Digital Landscape In 2024
Trends In Paid Search: Navigating The Digital Landscape In 2024Trends In Paid Search: Navigating The Digital Landscape In 2024
Trends In Paid Search: Navigating The Digital Landscape In 2024
 
5 Public speaking tips from TED - Visualized summary
5 Public speaking tips from TED - Visualized summary5 Public speaking tips from TED - Visualized summary
5 Public speaking tips from TED - Visualized summary
 
ChatGPT and the Future of Work - Clark Boyd
ChatGPT and the Future of Work - Clark Boyd ChatGPT and the Future of Work - Clark Boyd
ChatGPT and the Future of Work - Clark Boyd
 
Getting into the tech field. what next
Getting into the tech field. what next Getting into the tech field. what next
Getting into the tech field. what next
 
Google's Just Not That Into You: Understanding Core Updates & Search Intent
Google's Just Not That Into You: Understanding Core Updates & Search IntentGoogle's Just Not That Into You: Understanding Core Updates & Search Intent
Google's Just Not That Into You: Understanding Core Updates & Search Intent
 
How to have difficult conversations
How to have difficult conversations How to have difficult conversations
How to have difficult conversations
 
Introduction to Data Science
Introduction to Data ScienceIntroduction to Data Science
Introduction to Data Science
 
Time Management & Productivity - Best Practices
Time Management & Productivity -  Best PracticesTime Management & Productivity -  Best Practices
Time Management & Productivity - Best Practices
 
The six step guide to practical project management
The six step guide to practical project managementThe six step guide to practical project management
The six step guide to practical project management
 
Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...
Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...
Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...
 
Unlocking the Power of ChatGPT and AI in Testing - A Real-World Look, present...
Unlocking the Power of ChatGPT and AI in Testing - A Real-World Look, present...Unlocking the Power of ChatGPT and AI in Testing - A Real-World Look, present...
Unlocking the Power of ChatGPT and AI in Testing - A Real-World Look, present...
 
12 Ways to Increase Your Influence at Work
12 Ways to Increase Your Influence at Work12 Ways to Increase Your Influence at Work
12 Ways to Increase Your Influence at Work
 
ChatGPT webinar slides
ChatGPT webinar slidesChatGPT webinar slides
ChatGPT webinar slides
 

Tek-X: A Framework for People who Hate Frameworks - Lithium

  • 1. A Framework for People Who Hate Frameworks.
  • 2. Find us on : http://joind.in/1592
  • 3. A movement in 3 parts • Frameworks suck • Everything you know is wrong • Lithium tries to suck less
  • 5. Let’s get one thing out of the way:
  • 9. And yes, I mean:
  • 10. And yes, I mean:
  • 11. Why? (Besides the obvious attempt at being provocative)
  • 13. Frameworks Suck • Code you will never use.
  • 14. Frameworks Suck • Code you will never use. • Complexity overhead.
  • 15. Frameworks Suck • Code you will never use. • Complexity overhead. • You didn’t write it.
  • 16. Also,
  • 18. His name is the biggest, so it’s his fault.
  • 19. His name is the biggest, so it’s his fault.
  • 20. We’re not saying design patterns are bad. Quite the opposite.
  • 21. Lithium implements many design patterns.
  • 23. Some patterns only treat the symptoms, instead of the cause.
  • 25. Object dependencies. “The principle of separating configuration from use.”
  • 26. [CakePHP code] function initialize(&$controller, $settings = array()) { /* ...[snip]... */ $prefixes = Router::prefixes(); if (!empty($prefixes)) { foreach ($prefixes as $prefix) { /* ...[do something nasty to the global state]... */ } } if (Configure::read() > 0) { App::import('Debugger'); Debugger::checkSecurityKeys(); } }
  • 27. Sucks. function initialize(&$controller, $settings = array()) { /* ...[snip]... */ $prefixes = Router::prefixes(); if (!empty($prefixes)) { foreach ($prefixes as $prefix) { /* ...[do something nasty to the global state]... */ } } if (Configure::read() > 0) { App::import('Debugger'); Debugger::checkSecurityKeys(); } }
  • 28. Sucks. function initialize(&$controller, $settings = array()) { /* ...[snip]... */ $prefixes = Router::prefixes(); if (!empty($prefixes)) { foreach ($prefixes as $prefix) { /* ...[do something nasty to the global state]... */ } } if (Configure::read() > 0) { App::import('Debugger'); Debugger::checkSecurityKeys(); } } Sucks hard.
  • 30. Everyone does it differently.
  • 31. Sometimes in the same framework.
  • 32. Sometimes in the same class.
  • 33. function spam($emails) { $this->Email->replyTo = 'nigerian.prince@example.com'; $this->Email->from = 'Desmond Etete <nigerian.prince@example.com>'; $this->Email->template = 'mo_monies'; $this->Email->sendAs = 'annoying_html'; foreach ($emails as $email) { $this->Email->to = $email['address']; $this->Email->subject = "Good to you news I have {$email['name']}"; $this->Email->send(); } }
  • 34. Sucks function spam($emails) { $this->Email->replyTo = 'nigerian.prince@example.com'; $this->Email->from = 'Desmond Etete <nigerian.prince@example.com>'; $this->Email->template = 'mo_monies'; $this->Email->sendAs = 'annoying_html'; foreach ($emails as $email) { $this->Email->to = $email['address']; $this->Email->subject = "Good to you news I have {$email['name']}"; $this->Email->send(); } }
  • 37. ... or is it?
  • 38. [variables changed to protect the innocent.]
  • 39. [variables changed to protect the innocent.] [... not really. It’s Symfony.]
  • 40. class User { function __construct($storage) { $this->storage = $storage; } } $storage = new SessionStorage('SESSION_ID'); $user = new User($storage);
  • 41. Show that object who’s boss. class User { function __construct($storage) { $this->storage = $storage; } } $storage = new SessionStorage('SESSION_ID'); $user = new User($storage);
  • 42. Of course, we want this to abstract this. Frameworks adore abstractions.
  • 43. DI Container to the rescue!
  • 44. class Container { static protected $shared = array(); // ... public function getMailer() { if (isset(self::$shared['mailer'])) { return self::$shared['mailer']; } $class = $this->parameters['mailer.class']; $mailer = new $class(); $mailer->setDefaultTransport($this->getMailTransport()); return self::$shared['mailer'] = $mailer; } }
  • 45. But now you want an abstraction to manage the DI container. Duh.
  • 46. So you create a “Service Container”.
  • 47. 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, )); } }
  • 48. Of course, we now want to abstract the crap out of the Service Container. Double duh.
  • 49. So lets use a Builder to configure the Services.
  • 50. Yeah! require_once '/PATH/TO/sfServiceContainerAutoloader.php'; 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'))) ;
  • 51. And for good measure, have our configurations for Service Containers in XML files.
  • 52.
  • 55. So.... We have Dependency Injection. Managed by a Service Container.
  • 56. So.... We have Dependency Injection. Managed by a Service Container. Parametrized with XML data.
  • 57. So.... We have Dependency Injection. Managed by a Service Container. Parametrized with XML data. And the whole thing configured by a Builder.
  • 58. So.... We have Dependency Injection. Managed by a Service Container. Parametrized with XML data. And the whole thing configured by a Builder. ...to fix one problem.
  • 59. Anyone see what’s wrong here?
  • 60. Many of these patterns were implemented in Java, to solve language problems that PHP just doesn’t have.
  • 61. Try this some time...
  • 62.
  • 65. The sun does not revolve around OOP Galileo facing the Roman Inquistion - Cristiano Banti (1857)
  • 66. The sun does not revolve around OOP ...nevertheless, it moves. Galileo facing the Roman Inquistion - Cristiano Banti (1857)
  • 69. class User { public function create() { $logger = new Logger(); $logger->write('Creating a new user...'); $this->_doSomeInitialization(); $this->_databaseConnection->doATransaction($this)->create(); $logger->write('Finished creating user'); } } $user = new User(); $user->create();
  • 70. class User { public function create() { $logger = new Logger(); $logger->write('Creating a new user...'); $this->_doSomeInitialization(); $this->_databaseConnection->doATransaction($this)->create(); $logger->write('Finished creating user'); } } $user = new User(); $user->create();
  • 72. Dependency Injection • Fixes the problem of static dependencies
  • 73. Dependency Injection • Fixes the problem of static dependencies • Ignores the problem of static relationships
  • 74. Dependency Injection • Fixes the problem of static dependencies • Ignores the problem of static relationships • Same methods called on injected classes
  • 75. Dependency Injection • Fixes the problem of static dependencies • Ignores the problem of static relationships • Same methods called on injected classes • No way to introduce new relationships
  • 76. Dependency Injection • Fixes the problem of static dependencies • Ignores the problem of static relationships • Same methods called on injected classes • No way to introduce new relationships • Higher overhead, more boilerplate code
  • 77. class Service extends lithiumcoreObject { protected $_classes = array( 'request' => 'lithiumnethttpRequest', 'response' => 'lithiumnethttpResponse', 'socket' => 'lithiumnetsocketContext' ); protected function _init() { $class = Libraries::locate('socket.util', $this->_classes['socket']); $this->_connection = new $class($this->_config); // ... } /* ...[snip]... */ public function send($method, $path = null, $data = null, array $options = array()) { /* ...[snip]... */ $response = $this->_connection->send($request, array('classes' => $this->_classes)); // ... } }
  • 78. class Service extends lithiumcoreObject { protected $_classes = array( 'request' => 'lithiumnethttpRequest', 'response' => 'lithiumnethttpResponse', 'socket' => 'lithiumnetsocketContext' ); protected function _init() { $class = Libraries::locate('socket.util', $this->_classes['socket']); $this->_connection = new $class($this->_config); // ... } /* ...[snip]... */ public function send($method, $path = null, $data = null, array $options = array()) { /* ...[snip]... */ $response = $this->_connection->send($request, array('classes' => $this->_classes)); // ... } }
  • 79. Coupling should be in proportion to domain relevance.
  • 81. If...
  • 84. If... Configure::write('debug', 0); is evil, $this->debug = 0;
  • 85. If... Configure::write('debug', 0); is evil, $this->debug = 0; is the
  • 86.
  • 87.
  • 89. class Service { protected $_timeout = 30; public function send($method, $data = null, array $options = array()) { // WTF does this do? $this->_prepare(); $response = $this->_connection->send($request, array( 'timeout' => $this->_timeout )); // ... } }
  • 90. OO doesn’t make you think (about state).
  • 92.
  • 93. ActiveRecord Data Access Object Unit of Work Dependency Injection Registry Front Controller MVC Value Object Data Mapper Service Layer
  • 94.
  • 97. Design patterns • Each pattern is only useful in a limited context
  • 98. Design patterns • Each pattern is only useful in a limited context • Layering many design patterns on top of each other often indicates poor design choices
  • 99. Design patterns • Each pattern is only useful in a limited context • Layering many design patterns on top of each other often indicates poor design choices • Mis-application arises from trying to run before you can walk
  • 100.
  • 101. Tools do not mean... ...you can build a house.
  • 103. Lithium tries to suck less.
  • 107. Aspect-Oriented Design • Separation of concerns • Domain classes should not know or care about cross- cutting concerns
  • 108. Aspect-Oriented Design • Separation of concerns • Domain classes should not know or care about cross- cutting concerns • Examples:
  • 109. Aspect-Oriented Design • Separation of concerns • Domain classes should not know or care about cross- cutting concerns • Examples: • Caching
  • 110. Aspect-Oriented Design • Separation of concerns • Domain classes should not know or care about cross- cutting concerns • Examples: • Caching • Logging
  • 111. Aspect-Oriented Design • Separation of concerns • Domain classes should not know or care about cross- cutting concerns • Examples: • Caching • Logging • Access Control, etc.
  • 113. Functional Programming • Only possible when functions are first-class citizens
  • 114. Functional Programming • Only possible when functions are first-class citizens • Referential transparency
  • 115. Functional Programming • Only possible when functions are first-class citizens • Referential transparency • Function purity
  • 117. Referential transparency is not... $this date() $_*
  • 118. Referential transparency is not... $this date() $_*
  • 119. These Are Not Design Patterns.
  • 121. Less Suck • Draws on years of experience building web frameworks
  • 122. Less Suck • Draws on years of experience building web frameworks • PHP 5.3+ only
  • 123. Less Suck • Draws on years of experience building web frameworks • PHP 5.3+ only • Doesn’t assume you’re stupid
  • 124. Ways we suck less:
  • 126. <?php namespace applicationbar; use lithiumutilString; use lithiumutilCollection; class Foo extends lithiumcoreObject { protected $_classes = array( 'cache' => 'lithiumstorageCache', 'logger' => 'lithiumanalysisLogger' ); public function __construct(array $config = array()) { // ... } protected function _init() { // ... } } ?>
  • 127. public function __construct(array $config = array()) <?php public function __construct(array $config = array()) namespace applicationbar; public function __construct(array $config = array()) use public function __construct(array $config = array()) lithiumutilString; use lithiumutilCollection; public function __construct(array $config = array()) class Foo extends lithiumcoreObject { public function __construct(array $config = array()) protected $_classes = array( public function'lithiumstorageCache', = array()) 'cache' => __construct(array $config 'logger' => 'lithiumanalysisLogger' public function __construct(array $config = array()) ); public function __construct(array $config = array()) { // ... } public function __construct(array $config = array()) protected function _init() { public function __construct(array $config = array()) // ... } public function __construct(array $config = array()) } public function __construct(array $config = array()) ?> public function __construct(array $config = array())
  • 128. <?php namespace applicationbar; use lithiumutilString; use lithiumutilCollection; class Foo extends lithiumcoreObject { protected $_classes = array( 'cache' => 'lithiumstorageCache', 'logger' => 'lithiumanalysisLogger' ); public function __construct(array $config = array()) { // ... } protected function _init() { // ... } } ?>
  • 129. <?php <?php namespace applicationbar; class Foo extends lithiumcoreObject use lithiumutilString; { use lithiumutilCollection; protected function _init() { class Foo extends lithiumcoreObject { $or = $some->highOverHead($operation); $or()->otherwise(HARD_TO_TEST)->code(); protected $_classes = array( 'cache' => 'lithiumstorageCache', } 'logger' => 'lithiumanalysisLogger' } ); ?> public function __construct(array $config = array()) { // ... } protected function _init() { 2 // ... } } ?>
  • 130. <?php <?php namespace applicationbar; class Foo extends lithiumcoreObject use lithiumutilString; { use lithiumutilCollection; protected function _init() { class Foo extends lithiumcoreObject { $or = $some->highOverHead($operation); $or()->otherwise(HARD_TO_TEST)->code(); protected $_classes = array( 'cache' => 'lithiumstorageCache', } 'logger' => 'lithiumanalysisLogger' } ); ?> public function __construct(array $config = array()) { // ... } protected function _init() { 2 // ... } } $foo = new Foo(array('init' => false)); ?>
  • 131. <?php namespace applicationbar; use lithiumutilString; use lithiumutilCollection; class Foo extends lithiumcoreObject { protected $_classes = array( 'cache' => 'lithiumstorageCache', 'logger' => 'lithiumanalysisLogger' ); public function __construct(array $config = array()) { // ... } protected function _init() { // ... } } ?>
  • 132. <?php namespace applicationbar; use lithiumutilString; use lithiumutilCollection; class Foo extends lithiumcoreObject { protected $_classes = array( 'cache' => 'lithiumstorageCache', 'logger' => 'lithiumanalysisLogger' ); public function __construct(array $config = array()) { // ... } protected function _init() { // ... } } ?>
  • 133. <?php namespace applicationbar; 3 use lithiumutilString; use lithiumutilCollection; new applicationbarFoo(); // loads app/bar/Foo.php class Foo extends lithiumcoreObject { protected $_classes = array( 'cache' => 'lithiumstorageCache', 'logger' => 'lithiumanalysisLogger' ); public function __construct(array $config = array()) { // ... } protected function _init() { // ... } } ?>
  • 134. <?php namespace applicationbar; 4 use lithiumutilString; use lithiumutilCollection; class Foo extends lithiumcoreObject { protected $_classes = array( 'cache' => 'lithiumstorageCache', 'logger' => 'lithiumanalysisLogger' ); public function __construct(array $config = array()) { // ... } protected function _init() { // ... } } ?>
  • 135. <?php namespace applicationbar; use lithiumutilString; use lithiumutilCollection; class Foo extends lithiumcoreObject { protected $_classes = array( 'cache' => 'lithiumstorageCache', 'logger' => 'lithiumanalysisLogger' 5 ); public function __construct(array $config = array()) { // ... } protected function _init() { // ... = $this->_classes['cache']; $cache } $cache::write(__CLASS__, $this->_someGeneratedValue()); } } } ?> ?>
  • 136. <?php namespace applicationbar; use lithiumutilString; use lithiumutilCollection; class Foo extends lithiumcoreObject { protected $_classes = array( 'cache' => 'lithiumstorageCache', 'logger' => 'lithiumanalysisLogger' 5 ); $foo = new Foo(array('classes' => array( public function __construct(array $config = array()) { 'cache' => 'applicationextensionsCache' // ... ))); } protected function _init() { // ... = $this->_classes['cache']; $cache } $cache::write(__CLASS__, $this->_someGeneratedValue()); } } } ?> ?>
  • 137. <?php namespace applicationbar; use lithiumutilString; use lithiumutilCollection; class Foo extends lithiumcoreObject { protected $_classes = array( 'cache' => 'lithiumstorageCache', 'logger' => 'lithiumanalysisLogger' ); public function __construct(array $config = array()) { // ... } protected function _init() { // ... = $this->_classes['cache']; $cache } $cache::write(__CLASS__, $this->_someGeneratedValue()); } } } ?> ?>
  • 139.
  • 140. Keeps parameter lists short & Makes class APIs more extensible
  • 142.
  • 143. Same idea. But...! Modifies class / object state.
  • 144.
  • 146. Adaptable Auth Cache Catalog Connections Logger Session
  • 147. use lithiumsecurityAuth; Auth::config(array( 'customer' => array( 'adapter' => 'Form', 'model' => 'Customer', 'fields' => array('email', 'password') ) ));
  • 148. use lithiumstorageCache; Cache::config(array( 'local' => array('adapter' => 'Apc'), 'distributed' => array( 'adapter' => 'Memcached', 'servers' => array('127.0.0.1', 11211), ), 'default' => array('adapter' => 'File') ));
  • 149. use lithiumdataConnections; Connections::config(array( 'old' => array( 'type' => 'database', 'adapter' => 'MySql', 'user' => 'bobby_tables', 'password' => '******', 'database' => 'my_app' ), 'new' => array( 'type' => 'MongoDb', 'database' => 'my_app' ) ));
  • 150. use lithiumstorageSession; Session::config(array( 'cookie' => array( 'adapter' => 'Cookie', 'expire' => '+2 days' ), 'default' => array('adapter' => 'Php') ));
  • 152. use lithiumstorageSession; Session::config(array( 'default' => array( 'adapter' => 'MyCustomAdapter', 'expires' => '+2 days', 'custom' => 'Whatevah!' ) ));
  • 153. use lithiumstorageSession; Session::config(array( 'default' => array( 'adapter' => 'MyCustomAdapter', 'expires' => '+2 days', 'custom' => 'Whatevah!' ) ));
  • 154. use lithiumstorageSession; Session::config(array( 'default' => array( 'adapter' => 'MyCustomAdapter', 'expires' => '+2 days', 'custom' => 'Whatevah!' ) )); public function __construct(array $config = array())
  • 156. use lithiumstorageCache; Cache::config(array( 'default' => array( 'development' => array( 'adapter' => 'Apc' ), 'production' => array( 'adapter' => 'Memcached', 'servers' => array('127.0.0.1', 11211) ) ) ));
  • 157. use lithiumstorageCache; Cache::config(array( 'default' => array( 'development' => array( 'adapter' => 'Apc' ), 'production' => array( 'adapter' => 'Memcached', 'servers' => array('127.0.0.1', 11211) ) ) ));
  • 158. Works identically for all adapters.
  • 159. If you remember nothing else about configuration state...
  • 162. Zoom?
  • 163. Zoom? • Performance vs. speed of development is a series of trade-offs
  • 164. Zoom? • Performance vs. speed of development is a series of trade-offs • Large-scale apps don’t use stock framework infrastructure, and that’s a good thing
  • 165. Zoom? • Performance vs. speed of development is a series of trade-offs • Large-scale apps don’t use stock framework infrastructure, and that’s a good thing • A generalized framework will never be as fast as hand-tuned code
  • 166. Zoom!
  • 168. Zoom! • Choice is good • Use native extensions (PECL) whenever possible.
  • 169. Zoom! • Choice is good • Use native extensions (PECL) whenever possible. • Don’t like a class? Change it. At runtime.
  • 170. Zoom! • Choice is good • Use native extensions (PECL) whenever possible. • Don’t like a class? Change it. At runtime. • Profiled at every step of the way.
  • 172. use appmodelsPost; use lithiumactionResponse; use lithiumnethttpRouter; Router::connect('/frequent_api_call.json', array(), function($request) { return new Response(array( 'type' => 'application/json', 'body' => Post::recent()->to('json') )); });
  • 174. Lithium is the most flexible framework. (yeah, we said it)
  • 176. class Service extends lithiumcoreObject { protected $_classes = array( 'request' => 'lithiumnethttpRequest', 'response' => 'lithiumnethttpResponse', 'socket' => 'lithiumnetsocketContext' ); } $service = new Service(array('classes' => array( 'socket' => 'mycustomSocket' )));
  • 178. Example: Caching & Logging
  • 179. Example: Caching & Logging Post::find()
  • 180. Example: Caching & Logging Logging Post::find()
  • 181. Example: Caching & Logging Caching Logging Post::find()
  • 182. Example: Caching & Logging Caching Logging Post::find()
  • 183. Example: Caching & Logging Caching Logging Post::find()
  • 184. Example: Caching & Logging Caching Logging Post::find()
  • 185. Example: Caching & Logging Caching Logging Post::find()
  • 186. Example: Caching & Logging Caching Logging Post::find()
  • 187. Example: Caching & Logging Caching Logging Post::find()
  • 188. Example: Caching & Logging Caching Logging Post::find()
  • 189. Example: Caching & Logging Caching Logging Post::find()
  • 190. use lithiumanalysisLogger; Post::applyFilter('find', function($self, $params, $chain) { // Generate the log message $conditions = $params['options']['conditions']; $message = 'Post query with constraint ' . var_export($conditions, true); Logger::write('info', $message); return $chain->next($self, $params, $chain); });
  • 191. use lithiumanalysisLogger; Post::applyFilter('find', function($self, $params, $chain) { // Generate the log message $conditions = $params['options']['conditions']; $message = 'Post query with constraint ' . var_export($conditions, true); Logger::write('info', $message); return $chain->next($self, $params, $chain); });
  • 192. use lithiumanalysisLogger; Post::applyFilter('find', function($self, $params, $chain) { // Generate the log message $conditions = $params['options']['conditions']; $message = 'Post query with constraint ' . var_export($conditions, true); Logger::write('info', $message); return $chain->next($self, $params, $chain); });
  • 193. use lithiumanalysisLogger; Post::applyFilter('find', function($self, $params, $chain) { // Generate the log message $conditions = $params['options']['conditions']; $message = 'Post query with constraint ' . var_export($conditions, true); Logger::write('info', $message); return $chain->next($self, $params, $chain); });
  • 194. use lithiumanalysisLogger; Post::applyFilter('find', function($self, $params, $chain) { // Generate the log message $conditions = $params['options']['conditions']; $message = 'Post query with constraint ' . var_export($conditions, true); Logger::write('info', $message); return $chain->next($self, $params, $chain); });
  • 195. use lithiumanalysisLogger; Post Logger Post::applyFilter('find', function($self, $params, $chain) { // Generate the log message $conditions = $params['options']['conditions']; $message = 'Post query with constraint ' . var_export($conditions, true); Logger::write('info', $message); return $chain->next($self, $params, $chain); });
  • 197. What about Observer? • Dependent on a centralized publish/ subscribe system
  • 198. What about Observer? • Dependent on a centralized publish/ subscribe system • Extra layer of abstraction
  • 199. What about Observer? • Dependent on a centralized publish/ subscribe system • Extra layer of abstraction • Fewer possibilities
  • 201. What about Observer? • Filters are self-contained and attach directly to objects
  • 202. What about Observer? • Filters are self-contained and attach directly to objects • Direct and intuitive
  • 203. Features: Everything is an adapter. (well, almost)
  • 204. Databases • 1st-class support for document-oriented databases • MongoDB & CouchDB: production ready • Relational databases in beta • Cassandra in the works, too
  • 205. <?php $post = Post::create(array( 'title' => 'Ein bier, bitte', 'body' => 'Was ist los mit dir?' )); $post->save(); $post = Post::find($id); ?> <h2><?=$post->title; ?></h2> <p><?=$post->body; ?></p>
  • 208. This works on... • MongoDB • CouchDB
  • 209. This works on... • MongoDB • CouchDB • MySQL
  • 210. This works on... • MongoDB • CouchDB • MySQL • SQLite (almost)
  • 211. MongoDB + CouchDB: $post = Post::create(array( 'title' => 'Ein bier, bitte', 'body' => 'Was ist los mit dir?', 'tags' => array('PHP', 'Bier'), 'author' => array('name' => 'Nate') )); $post->save();
  • 212. MongoDB: $posts = Post::all(array('conditions' => array( 'tags' => array('PHP', 'Bier'), 'author.name' => 'Nate' ))); // Translates to... db.posts.find({ tags : { $in : ['PHP', 'Bier'] }, 'author.name' : 'Nate' })
  • 213. Databases • Adapter based, plugin aware • Will ship with MySQL, SQLite • SQL Server support via plugin • Query API
  • 214. The Query API • Flexible data container • Allows each backend data store to only worry about features it implements • Keeps model API separate from backend data sources
  • 215. The Query API $ages = User::all(array( 'group' => 'age', 'reduce' => 'function(obj, prev) { prev.count++; }', 'initial' => array('count' => 0) ));
  • 216. 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')), ))) ));
  • 217. The Query API • Run simple queries via the Model API • Build your own complex queries with the Query API • Create your own adapter, or drop in a custom query optimizer
  • 219. Plays nice with others • Easily load & use libraries from other frameworks: • Zend Framework, Solar, Symfony, PEAR, etc. • PSR-0 Class-loading standard
  • 220. /* add the trunk */ Libraries::add("Zend", array( "prefix" => "Zend_", "includePath" => true, "bootstrap" => "Loader/Autoloader.php", "loader" => array("Zend_Loader_Autoloader", "autoload"), "transform" => function($class) { return str_replace("_", "/", $class) . ".php"; } )); /* add the incubator */ Libraries::add("Zend_Incubator", array( "prefix" => "Zend_", "includePath" => '/htdocs/libraries/Zend/incubator/library', "transform" => function($class) { return str_replace("_", "/", $class) . ".php"; } ));
  • 221. namespace appcontrollers; use Zend_Mail_Storage_Pop3; class EmailController extends lithiumactionController { public function index() { $mail = new Zend_Mail_Storage_Pop3(array( 'host' => 'localhost', 'user' => 'test', 'password' => 'test' )); return compact('mail'); } }
  • 222.
  • 223. This has been a presentation by: Nate Abele (@nateabele) Joël Perras (@jperras) Sucks. But check it out anyway.

Notas del editor

  1. Introductions: team
  2. Handoff: Nate
  3. Intro / overview: Nate
  4. Handoff: Jo&amp;#xEB;l
  5. Handoff: Nate - They&amp;#x2019;re all about knowing when and how to apply them - Often, patterns derived from other languages make little to no sense in PHP
  6. Handoff: Jo&amp;#xEB;l
  7. Handoff: Nate - Many GOF patterns were invented for Java to overcome deficiencies in the language itself - You don&amp;#x2019;t need a dependency injection container in PHP, it&amp;#x2019;s pointless - In languages with first-class functions (PHP as of 5.3), patterns like Command and Visitor are irrelevant
  8. - Method calls and direction of invocation remain static - Each individual cross-cutting concern adds more boilerplate code
  9. - Method calls and direction of invocation remain static - Each individual cross-cutting concern adds more boilerplate code
  10. - Method calls and direction of invocation remain static - Each individual cross-cutting concern adds more boilerplate code
  11. - Method calls and direction of invocation remain static - Each individual cross-cutting concern adds more boilerplate code
  12. - Method calls and direction of invocation remain static - Each individual cross-cutting concern adds more boilerplate code
  13. - Encapsulation notwithstanding, OO does not address the problem of state.
  14. - No way to know the value of _timeout. - Mutable state is the source of most software defects - Computers are good at dynamic process flows. Humans aren&amp;#x2019;t. Handoff: Jo&amp;#xEB;l, talking about mutable state
  15. - Easy != right.
  16. Handoff: Nate - But it&amp;#x2019;s okay... because &amp;#x201C;Design Patterns&amp;#x201D; make everything okay
  17. - Everyone has their favorites...
  18. - Everyone has their favorites...
  19. - Everyone has their favorites...
  20. - Everyone has their favorites...
  21. - Everyone has their favorites...
  22. - Everyone has their favorites...
  23. - Everyone has their favorites...
  24. - Everyone has their favorites...
  25. - Everyone has their favorites...
  26. - Everyone has their favorites...
  27. Design patterns are not a golden hammer
  28. Handoff: Jo&amp;#xEB;l
  29. Handoff: Nate - A referentially transparent function is one whose return value is only dependent on input parameters
  30. Handoff: Nate - A referentially transparent function is one whose return value is only dependent on input parameters
  31. Handoff: Nate - A referentially transparent function is one whose return value is only dependent on input parameters
  32. Handoff: Nate - A referentially transparent function is one whose return value is only dependent on input parameters
  33. Handoff: Nate - A referentially transparent function is one whose return value is only dependent on input parameters
  34. Handoff: Nate - A referentially transparent function is one whose return value is only dependent on input parameters
  35. - Learning other paradigms helps you in the same way as learning other languages. Handoff: Jo&amp;#xEB;l - Rant about programmers vs. typists
  36. - This is different from assuming you won&amp;#x2019;t make mistakes - We don&amp;#x2019;t dumb down APIs or hide things. We just make it clear what you&amp;#x2019;re doing.
  37. - This is different from assuming you won&amp;#x2019;t make mistakes - We don&amp;#x2019;t dumb down APIs or hide things. We just make it clear what you&amp;#x2019;re doing.
  38. - This is different from assuming you won&amp;#x2019;t make mistakes - We don&amp;#x2019;t dumb down APIs or hide things. We just make it clear what you&amp;#x2019;re doing.
  39. Handoff: Nate
  40. - Supported by a majority of methods in the framework - *Always* the last parameter - Any settings contained only modify behavior for the duration of the call
  41. - Managing state is a very important idea which Lithium puts a lot of emphasis on. - Delineation between configuration state and request state
  42. - Managing state is a very important idea which Lithium puts a lot of emphasis on. - Delineation between configuration state and request state
  43. - Managing state is a very important idea which Lithium puts a lot of emphasis on. - Delineation between configuration state and request state
  44. - This works because of the universal constructor
  45. - This works because of the universal constructor
  46. Handoff: Jo&amp;#xEB;l
  47. - Use components from other libraries or frameworks, or hand-written classes - Filter system allows class interdependencies to be pushed to the surface, instead of buried within the framework
  48. - Use components from other libraries or frameworks, or hand-written classes - Filter system allows class interdependencies to be pushed to the surface, instead of buried within the framework
  49. - Use components from other libraries or frameworks, or hand-written classes - Filter system allows class interdependencies to be pushed to the surface, instead of buried within the framework
  50. - Use components from other libraries or frameworks, or hand-written classes - Filter system allows class interdependencies to be pushed to the surface, instead of buried within the framework
  51. - This is for when you have a frequently-used API call, or other request that you need to make extra fast - Bypasses the entire framework request cycle
  52. Handoff: Nate
  53. Point out parts that are `before` method execution, and `after` method execution.
  54. Point out parts that are `before` method execution, and `after` method execution.
  55. Point out parts that are `before` method execution, and `after` method execution.
  56. Point out parts that are `before` method execution, and `after` method execution.
  57. - either pass it around or make it global
  58. - either pass it around or make it global
  59. - either pass it around or make it global
  60. - You can rip it out and things will still work - Relies on native language constructs, no micro-syntax
  61. - You can rip it out and things will still work - Relies on native language constructs, no micro-syntax
  62. Handoff: Jo&amp;#xEB;l
  63. Handoff: Nate - Explain about SQL operators being translated to Mongo commands - Helps transitioning from relational DBs to Mongo - Aids in developing cross-functional plugins
  64. Handoff: Jo&amp;#xEB;l Everything works because of the magical Query API
  65. Handoff: Nate
  66. Allows you to do &amp;#x201C;non-standard&amp;#x201D; stuff
  67. Generates a SELECT statement with a subselect that finds all posts tagged foo, bar or baz
  68. - For all your &amp;#x201C;enterprise&amp;#x201D; needs, we have an officially-maintained plugin for fully integrating with Doctrine 2 - PDO abstraction layer with support for many RDBMS systems - Accessed through the same Model API as standard Lithium backends
  69. Handoff: Jo&amp;#xEB;l