O slideshow foi denunciado.
Utilizamos seu perfil e dados de atividades no LinkedIn para personalizar e exibir anúncios mais relevantes. Altere suas preferências de anúncios quando desejar.
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::p...
Sucks.
function initialize(&$controller, $settings = array()) {
    /* ...[snip]... */

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

    $prefixes = Router::prefixes()...
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 Ete...
Sucks
function spam($emails) {

    $this->Email->replyTo = 'nigerian.prince@example.com';
    $this->Email->from = 'Desmo...
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 SessionStorag...
Show that object who’s boss.
class User {
    function __construct($storage) {
        $this->storage = $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(...
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 getMailTransp...
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 sfSer...
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 confi...
So....
We have Dependency Injection.
Managed by a Service Container.
Parametrized with XML data.
And the whole thing confi...
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...
The sun does not
revolve around OOP
                                            ...nevertheless,
                         ...
Dependency injection.
Dependency
Injection  =
class User {

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

    public function create() {
        $logger = new Logger();
        $logger->write('Creating a new user.....
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 meth...
Dependency Injection
• Fixes the problem of static dependencies
• Ignores the problem of static relationships
 • Same meth...
Dependency Injection
• Fixes the problem of static dependencies
• Ignores the problem of static relationships
 • Same meth...
class Service extends lithiumcoreObject {

    protected $_classes = array(
        'request' => 'lithiumnethttpRequest',
...
class Service extends lithiumcoreObject {

    protected $_classes = array(
        'request' => 'lithiumnethttpRequest',
...
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()) ...
OO doesn’t make you
think (about state).
Design patterns.
ActiveRecord
                        Data Access Object

 Unit of Work
                              Dependency Injection
...
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 oth...
Design patterns
• Each pattern is only useful in a limited
  context

• Layering many design patterns on top of
  each oth...
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
...
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
...
Aspect-Oriented Design
• Separation of concerns
• Domain classes should not know or care about cross-
   cutting concerns
...
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 p...
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 {...
public function __construct(array $config = array())
<?php
    public function __construct(array $config = array())

names...
<?php

namespace applicationbar;

use lithiumutilString;
use lithiumutilCollection;

class Foo extends lithiumcoreObject {...
<?php
        <?php
namespace applicationbar;

     class Foo extends lithiumcoreObject
use lithiumutilString;            ...
<?php
        <?php
namespace applicationbar;

     class Foo extends lithiumcoreObject
use lithiumutilString;            ...
<?php

namespace applicationbar;

use lithiumutilString;
use lithiumutilCollection;

class Foo extends lithiumcoreObject {...
<?php

namespace applicationbar;

use lithiumutilString;
use lithiumutilCollection;

class Foo extends lithiumcoreObject {...
<?php

namespace applicationbar;
                             3
use lithiumutilString;
use lithiumutilCollection;
        ...
<?php

namespace applicationbar;



                                    4
use lithiumutilString;
use lithiumutilCollection...
<?php

namespace applicationbar;

use lithiumutilString;
use lithiumutilCollection;

class Foo extends lithiumcoreObject {...
<?php

namespace applicationbar;

use lithiumutilString;
use lithiumutilCollection;

class Foo extends lithiumcoreObject {...
<?php

namespace applicationbar;

use lithiumutilString;
use lithiumutilCollection;

class Foo extends lithiumcoreObject {...
$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'   => '...
use lithiumstorageCache;

Cache::config(array(
    'local' => array('adapter' => 'Apc'),
    'distributed' => array(
     ...
use lithiumdataConnections;

Connections::config(array(
    'old' => array(
        'type'      => 'database',
        'ad...
use lithiumstorageSession;

Session::config(array(
    'cookie' => array(
        'adapter' => 'Cookie',
        'expire' ...
Also fun:
use lithiumstorageSession;

Session::config(array(
    'default' => array(
        'adapter' => 'MyCustomAdapter',
       ...
use lithiumstorageSession;

Session::config(array(
    'default' => array(
        'adapter' => 'MyCustomAdapter',
       ...
use lithiumstorageSession;

Session::config(array(
    'default' => array(
        'adapter' => 'MyCustomAdapter',
       ...
Multiple environments?
use lithiumstorageCache;

Cache::config(array(
    'default' => array(
        'development' => array(
            'adapte...
use lithiumstorageCache;

Cache::config(array(
    'default' => array(
        'development' => array(
            'adapte...
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
  i...
Zoom?
• Performance vs. speed of development is a
  series of trade-offs

• Large-scale apps don’t use stock framework
  i...
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.
•...
Example
use appmodelsPost;
use lithiumactionResponse;
use lithiumnethttpRouter;



Router::connect('/frequent_api_call.json', arra...
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',
...
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
 ...
use lithiumanalysisLogger;

Post::applyFilter('find', function($self, $params, $chain) {
    // Generate the log message
 ...
use lithiumanalysisLogger;

Post::applyFilter('find', function($self, $params, $chain) {
    // Generate the log message
 ...
use lithiumanalysisLogger;

Post::applyFilter('find', function($self, $params, $chain) {
    // Generate the log message
 ...
use lithiumanalysisLogger;

Post::applyFilter('find', function($self, $params, $chain) {
    // Generate the log message
 ...
use lithiumanalysisLogger;




Post                                                                                   Logg...
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 possi...
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 databa...
<?php

$post = Post::create(array(
    'title' => 'Ein bier, bitte',
    'body' => 'Was ist los mit dir?'
));
$post->save(...
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?',
   ...
MongoDB:
$posts = Post::all(array('conditions' => array(
     'tags' => array('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

• ...
The Query API

$ages = User::all(array(
      'group'   => 'age',
      'reduce' => 'function(obj, prev) { prev.count++; }...
The Query API
$query = new Query(array(
    'type' => 'read',
    'model' => 'appmodelsPost',
    'fields' => array('Post....
The Query API

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

• Create your...
Btw, li3_doctrine
Plays nice with others

• Easily load & use libraries from other
  frameworks:

  • Zend Framework, Solar, Symfony, PEAR,
...
/* add the trunk */
Libraries::add("Zend", array(
    "prefix" => "Zend_",
    "includePath" => true,
    "bootstrap" => "...
namespace appcontrollers;

use Zend_Mail_Storage_Pop3;

class EmailController extends lithiumactionController {

    publi...
This has been a presentation by:

Nate Abele (@nateabele)

Joël Perras (@jperras)

            Sucks. But check it out any...
Tek-X: A Framework for People who Hate Frameworks - Lithium
Tek-X: A Framework for People who Hate Frameworks - Lithium
Tek-X: A Framework for People who Hate Frameworks - Lithium
Tek-X: A Framework for People who Hate Frameworks - Lithium
Tek-X: A Framework for People who Hate Frameworks - Lithium
Tek-X: A Framework for People who Hate Frameworks - Lithium
Tek-X: A Framework for People who Hate Frameworks - Lithium
Tek-X: A Framework for People who Hate Frameworks - Lithium
Tek-X: A Framework for People who Hate Frameworks - Lithium
Tek-X: A Framework for People who Hate Frameworks - Lithium
Tek-X: A Framework for People who Hate Frameworks - Lithium
Próximos SlideShares
Carregando em…5
×

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

6.747 visualizações

Publicada em

All web application frameworks suck.

Some are too complex for the task at hand, and others don’t offer enough flexibility when your application steps outside of the confines of the ubiquitous blog tutorial. As stated by the venerable Sean Coates: “the #1 reason to avoid frameworks: you’ll spend all your time working around edge cases.”

Lithium, a new PHP 5.3+ rapid application development framework started by several CakePHP core alumnus tired of the status quo, is designed to help you get the job done, and get out of your way. Built from the ground-up to cater to people who hate frameworks, it attempts to reduce edge cases, and expose an intelligent public interface that sucks less.

We’ll take a jolly jaunt through the internals of Lithium and examine how we’re leveraging closures, late static binding and anonymous functions made available in PHP 5.3 to write a framework that Sucks Less, including our one-of-a-kind aspect-oriented inspired filter architecture, adapter-based architecture, and first-class support for non-relational datastores such as MongoDB and CouchDB.

Publicada em: Tecnologia
  • Follow the link, new dating source: ❶❶❶ http://bit.ly/39mQKz3 ❶❶❶
       Responder 
    Tem certeza que deseja  Sim  Não
    Insira sua mensagem aqui
  • Dating for everyone is here: ❶❶❶ http://bit.ly/39mQKz3 ❶❶❶
       Responder 
    Tem certeza que deseja  Sim  Não
    Insira sua mensagem aqui

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

  1. 1. A Framework for People Who Hate Frameworks.
  2. 2. Find us on : http://joind.in/1592
  3. 3. A movement in 3 parts • Frameworks suck • Everything you know is wrong • Lithium tries to suck less
  4. 4. The first part.
  5. 5. Let’s get one thing out of the way:
  6. 6. Lithium sucks.
  7. 7. But that’s okay.
  8. 8. Because your framework sucks, too.
  9. 9. And yes, I mean:
  10. 10. And yes, I mean:
  11. 11. Why? (Besides the obvious attempt at being provocative)
  12. 12. Frameworks Suck
  13. 13. Frameworks Suck • Code you will never use.
  14. 14. Frameworks Suck • Code you will never use. • Complexity overhead.
  15. 15. Frameworks Suck • Code you will never use. • Complexity overhead. • You didn’t write it.
  16. 16. Also,
  17. 17. Also, Martin Fowler.
  18. 18. His name is the biggest, so it’s his fault.
  19. 19. His name is the biggest, so it’s his fault.
  20. 20. We’re not saying design patterns are bad. Quite the opposite.
  21. 21. Lithium implements many design patterns.
  22. 22. The Problem™
  23. 23. Some patterns only treat the symptoms, instead of the cause.
  24. 24. Some examples:
  25. 25. Object dependencies. “The principle of separating configuration from use.”
  26. 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. 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. 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.
  29. 29. Configuration.
  30. 30. Everyone does it differently.
  31. 31. Sometimes in the same framework.
  32. 32. Sometimes in the same class.
  33. 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. 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(); } }
  35. 35. Dependency injection.
  36. 36. A good idea!
  37. 37. ... or is it?
  38. 38. [variables changed to protect the innocent.]
  39. 39. [variables changed to protect the innocent.] [... not really. It’s Symfony.]
  40. 40. class User { function __construct($storage) { $this->storage = $storage; } } $storage = new SessionStorage('SESSION_ID'); $user = new User($storage);
  41. 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. 42. Of course, we want this to abstract this. Frameworks adore abstractions.
  43. 43. DI Container to the rescue!
  44. 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. 45. But now you want an abstraction to manage the DI container. Duh.
  46. 46. So you create a “Service Container”.
  47. 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. 48. Of course, we now want to abstract the crap out of the Service Container. Double duh.
  49. 49. So lets use a Builder to configure the Services.
  50. 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. 51. And for good measure, have our configurations for Service Containers in XML files.
  52. 52. So....
  53. 53. So.... We have Dependency Injection.
  54. 54. So.... We have Dependency Injection. Managed by a Service Container.
  55. 55. So.... We have Dependency Injection. Managed by a Service Container. Parametrized with XML data.
  56. 56. So.... We have Dependency Injection. Managed by a Service Container. Parametrized with XML data. And the whole thing configured by a Builder.
  57. 57. 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.
  58. 58. Anyone see what’s wrong here?
  59. 59. Many of these patterns were implemented in Java, to solve language problems that PHP just doesn’t have.
  60. 60. Try this some time...
  61. 61. The second part.
  62. 62. Everything you know is wrong.
  63. 63. The sun does not revolve around OOP Galileo facing the Roman Inquistion - Cristiano Banti (1857)
  64. 64. The sun does not revolve around OOP ...nevertheless, it moves. Galileo facing the Roman Inquistion - Cristiano Banti (1857)
  65. 65. Dependency injection.
  66. 66. Dependency Injection =
  67. 67. 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();
  68. 68. 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();
  69. 69. Dependency Injection
  70. 70. Dependency Injection • Fixes the problem of static dependencies
  71. 71. Dependency Injection • Fixes the problem of static dependencies • Ignores the problem of static relationships
  72. 72. Dependency Injection • Fixes the problem of static dependencies • Ignores the problem of static relationships • Same methods called on injected classes
  73. 73. 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
  74. 74. 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
  75. 75. 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)); // ... } }
  76. 76. 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)); // ... } }
  77. 77. Coupling should be in proportion to domain relevance.
  78. 78. The problem of state.
  79. 79. If...
  80. 80. If... Configure::write('debug', 0);
  81. 81. If... Configure::write('debug', 0); is evil,
  82. 82. If... Configure::write('debug', 0); is evil, $this->debug = 0;
  83. 83. If... Configure::write('debug', 0); is evil, $this->debug = 0; is the
  84. 84. of evil.
  85. 85. 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 )); // ... } }
  86. 86. OO doesn’t make you think (about state).
  87. 87. Design patterns.
  88. 88. ActiveRecord Data Access Object Unit of Work Dependency Injection Registry Front Controller MVC Value Object Data Mapper Service Layer
  89. 89. L E F A
  90. 90. Design patterns
  91. 91. Design patterns • Each pattern is only useful in a limited context
  92. 92. 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
  93. 93. 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
  94. 94. Tools do not mean... ...you can build a house.
  95. 95. The third part.
  96. 96. Lithium tries to suck less.
  97. 97. Un-broken solutions.
  98. 98. Aspect-Oriented Design
  99. 99. Aspect-Oriented Design • Separation of concerns
  100. 100. Aspect-Oriented Design • Separation of concerns • Domain classes should not know or care about cross- cutting concerns
  101. 101. Aspect-Oriented Design • Separation of concerns • Domain classes should not know or care about cross- cutting concerns • Examples:
  102. 102. Aspect-Oriented Design • Separation of concerns • Domain classes should not know or care about cross- cutting concerns • Examples: • Caching
  103. 103. Aspect-Oriented Design • Separation of concerns • Domain classes should not know or care about cross- cutting concerns • Examples: • Caching • Logging
  104. 104. Aspect-Oriented Design • Separation of concerns • Domain classes should not know or care about cross- cutting concerns • Examples: • Caching • Logging • Access Control, etc.
  105. 105. Functional Programming
  106. 106. Functional Programming • Only possible when functions are first-class citizens
  107. 107. Functional Programming • Only possible when functions are first-class citizens • Referential transparency
  108. 108. Functional Programming • Only possible when functions are first-class citizens • Referential transparency • Function purity
  109. 109. Referential transparency is not...
  110. 110. Referential transparency is not... $this date() $_*
  111. 111. Referential transparency is not... $this date() $_*
  112. 112. These Are Not Design Patterns.
  113. 113. Less Suck
  114. 114. Less Suck • Draws on years of experience building web frameworks
  115. 115. Less Suck • Draws on years of experience building web frameworks • PHP 5.3+ only
  116. 116. Less Suck • Draws on years of experience building web frameworks • PHP 5.3+ only • Doesn’t assume you’re stupid
  117. 117. Ways we suck less:
  118. 118. Consistency.
  119. 119. <?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() { // ... } } ?>
  120. 120. 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())
  121. 121. <?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() { // ... } } ?>
  122. 122. <?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 // ... } } ?>
  123. 123. <?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)); ?>
  124. 124. <?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() { // ... } } ?>
  125. 125. <?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() { // ... } } ?>
  126. 126. <?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() { // ... } } ?>
  127. 127. <?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() { // ... } } ?>
  128. 128. <?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()); } } } ?> ?>
  129. 129. <?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()); } } } ?> ?>
  130. 130. <?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()); } } } ?> ?>
  131. 131. $options = array()
  132. 132. Keeps parameter lists short & Makes class APIs more extensible
  133. 133. $config = array()
  134. 134. Same idea. But...! Modifies class / object state.
  135. 135. Adaptable
  136. 136. Adaptable Auth Cache Catalog Connections Logger Session
  137. 137. use lithiumsecurityAuth; Auth::config(array( 'customer' => array( 'adapter' => 'Form', 'model' => 'Customer', 'fields' => array('email', 'password') ) ));
  138. 138. use lithiumstorageCache; Cache::config(array( 'local' => array('adapter' => 'Apc'), 'distributed' => array( 'adapter' => 'Memcached', 'servers' => array('127.0.0.1', 11211), ), 'default' => array('adapter' => 'File') ));
  139. 139. 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' ) ));
  140. 140. use lithiumstorageSession; Session::config(array( 'cookie' => array( 'adapter' => 'Cookie', 'expire' => '+2 days' ), 'default' => array('adapter' => 'Php') ));
  141. 141. Also fun:
  142. 142. use lithiumstorageSession; Session::config(array( 'default' => array( 'adapter' => 'MyCustomAdapter', 'expires' => '+2 days', 'custom' => 'Whatevah!' ) ));
  143. 143. use lithiumstorageSession; Session::config(array( 'default' => array( 'adapter' => 'MyCustomAdapter', 'expires' => '+2 days', 'custom' => 'Whatevah!' ) ));
  144. 144. use lithiumstorageSession; Session::config(array( 'default' => array( 'adapter' => 'MyCustomAdapter', 'expires' => '+2 days', 'custom' => 'Whatevah!' ) )); public function __construct(array $config = array())
  145. 145. Multiple environments?
  146. 146. use lithiumstorageCache; Cache::config(array( 'default' => array( 'development' => array( 'adapter' => 'Apc' ), 'production' => array( 'adapter' => 'Memcached', 'servers' => array('127.0.0.1', 11211) ) ) ));
  147. 147. use lithiumstorageCache; Cache::config(array( 'default' => array( 'development' => array( 'adapter' => 'Apc' ), 'production' => array( 'adapter' => 'Memcached', 'servers' => array('127.0.0.1', 11211) ) ) ));
  148. 148. Works identically for all adapters.
  149. 149. If you remember nothing else about configuration state...
  150. 150. Immutability. Set it and forget it.
  151. 151. Performance.
  152. 152. Zoom?
  153. 153. Zoom? • Performance vs. speed of development is a series of trade-offs
  154. 154. 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
  155. 155. 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
  156. 156. Zoom!
  157. 157. Zoom! • Choice is good
  158. 158. Zoom! • Choice is good • Use native extensions (PECL) whenever possible.
  159. 159. Zoom! • Choice is good • Use native extensions (PECL) whenever possible. • Don’t like a class? Change it. At runtime.
  160. 160. 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.
  161. 161. Example
  162. 162. 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') )); });
  163. 163. Flexibility.
  164. 164. Lithium is the most flexible framework. (yeah, we said it)
  165. 165. Most class dependencies are dynamic.
  166. 166. class Service extends lithiumcoreObject { protected $_classes = array( 'request' => 'lithiumnethttpRequest', 'response' => 'lithiumnethttpResponse', 'socket' => 'lithiumnetsocketContext' ); } $service = new Service(array('classes' => array( 'socket' => 'mycustomSocket' )));
  167. 167. The Filter System: Aspect-Oriented Design for PHP.
  168. 168. Example: Caching & Logging
  169. 169. Example: Caching & Logging Post::find()
  170. 170. Example: Caching & Logging Logging Post::find()
  171. 171. Example: Caching & Logging Caching Logging Post::find()
  172. 172. Example: Caching & Logging Caching Logging Post::find()
  173. 173. Example: Caching & Logging Caching Logging Post::find()
  174. 174. Example: Caching & Logging Caching Logging Post::find()
  175. 175. Example: Caching & Logging Caching Logging Post::find()
  176. 176. Example: Caching & Logging Caching Logging Post::find()
  177. 177. Example: Caching & Logging Caching Logging Post::find()
  178. 178. Example: Caching & Logging Caching Logging Post::find()
  179. 179. Example: Caching & Logging Caching Logging Post::find()
  180. 180. 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); });
  181. 181. 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); });
  182. 182. 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); });
  183. 183. 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); });
  184. 184. 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); });
  185. 185. 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); });
  186. 186. What about Observer?
  187. 187. What about Observer? • Dependent on a centralized publish/ subscribe system
  188. 188. What about Observer? • Dependent on a centralized publish/ subscribe system • Extra layer of abstraction
  189. 189. What about Observer? • Dependent on a centralized publish/ subscribe system • Extra layer of abstraction • Fewer possibilities
  190. 190. What about Observer?
  191. 191. What about Observer? • Filters are self-contained and attach directly to objects
  192. 192. What about Observer? • Filters are self-contained and attach directly to objects • Direct and intuitive
  193. 193. Features: Everything is an adapter. (well, almost)
  194. 194. Databases • 1st-class support for document-oriented databases • MongoDB & CouchDB: production ready • Relational databases in beta • Cassandra in the works, too
  195. 195. <?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>
  196. 196. This works on...
  197. 197. This works on... • MongoDB
  198. 198. This works on... • MongoDB • CouchDB
  199. 199. This works on... • MongoDB • CouchDB • MySQL
  200. 200. This works on... • MongoDB • CouchDB • MySQL • SQLite (almost)
  201. 201. 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();
  202. 202. 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' })
  203. 203. Databases • Adapter based, plugin aware • Will ship with MySQL, SQLite • SQL Server support via plugin • Query API
  204. 204. 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
  205. 205. The Query API $ages = User::all(array( 'group' => 'age', 'reduce' => 'function(obj, prev) { prev.count++; }', 'initial' => array('count' => 0) ));
  206. 206. 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')), ))) ));
  207. 207. 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
  208. 208. Btw, li3_doctrine
  209. 209. Plays nice with others • Easily load & use libraries from other frameworks: • Zend Framework, Solar, Symfony, PEAR, etc. • PSR-0 Class-loading standard
  210. 210. /* 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"; } ));
  211. 211. 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'); } }
  212. 212. This has been a presentation by: Nate Abele (@nateabele) Joël Perras (@jperras) Sucks. But check it out anyway.

×