SlideShare a Scribd company logo
A Framework for People
Who Hate Frameworks.
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.
Lithium: The Framework for People Who Hate Frameworks, Tokyo Edition
Why?
Frameworks Suck

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

Martin Fowler.
His name is the
biggest, so it’s his
      fault.
We’re not saying design patterns are bad.
Quite the opposite.
Lithium implements many well known 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.”
Configuration.
Everyone does it differently.
Sometimes in the same framework.
Sometimes in the same class.
Dependency injection.
A great idea!
... but you need to understand what you’re
doing.
Some patterns were implemented in Java, to
solve language problems that PHP just doesn’t
have.
“Design Patterns In Dynamic Languages”
              Peter Norvig
   http://norvig.com/design-patterns/
Try this some time...
Lithium: The Framework for People Who Hate Frameworks, Tokyo Edition
The second part.
Everything
you know
is wrong.
The sun does not
revolve around OOP
                                            ...nevertheless,
                                                it moves.




        Galileo facing the Roman Inquistion
                     - Cristiano Banti (1857)
Dependency injection.
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
Dependency Injection

• Various attempts at making DI work better:
 • DI Container
 • Using dynamic nature of PHP to your
    advantage.
Coupling should be in
proportion to domain
relevance.
The problem of

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


  is evil,
$this->debug = 0;


  is the
Lithium: The Framework for People Who Hate Frameworks, Tokyo Edition
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
FA
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
• Separation of concerns
• Domain classes should not know or care about cross-
   cutting concerns

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

• Only possible when functions are first-class
  citizens

• Referential transparency
• Functional purity
Referential transparency is not...


 $this                date()


           $_*
Referential transparency is not...


 $this                date()


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

• PHP 5.3+ only
• Doesn’t assume you’re stupid
Ways we try to 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;   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

   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' => 'Whatever!'
    )
));
use lithiumstorageSession;

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



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?
• 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!
• 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 with
  XHProf and XDebug cachegrinds.
Example: Resource
Routing
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')
    ));
});
Platform
Rackspace Cloud
 •   256MB ram
 •   Ubuntu Karmic
 •   Apache 2.2.12
 •   PHP 5.3.1
 •   Xcache
 •   Siege 2.68




                     2009-11-26
Flexibility.
Lithium is a highly
flexible framework.
Most class dependencies
are dynamic.
(Our implementation of dependency injection)
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

    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                                                                                   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?

• Dependent on a centralized publish/
  subscribe system

• Extra layer of abstraction
• Fewer possibilities
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/Redis/Riak in the works, too
<?php

$post = Post::create(array(
    'title' => '         ',
     'body' => '               '
));
$post->save();

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

?>

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

• MongoDB
• CouchDB
• MySQL
• SQLite
MongoDB + CouchDB:
$post = Post::create(array(
    'title' => '         ',
      'body' => '                           ',
      'tags' => array('PHP', 'Japan'),
      'author' => array('name' => 'Nate')
));

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


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

• Adapter based, plugin aware
• Will ship with MySQL, SQLite & PostgreSQL
• 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)

                   http://lithify.me
            Sucks. But check it out anyway.

More Related Content

PDF
The State of Lithium
PDF
Building Lithium Apps
PDF
The Zen of Lithium
PDF
Lithium: The Framework for People Who Hate Frameworks
PDF
The Origin of Lithium
PDF
PHP 5.3 and Lithium: the most rad php framework
PDF
Dependency Injection IPC 201
PDF
Dependency injection-zendcon-2010
The State of Lithium
Building Lithium Apps
The Zen of Lithium
Lithium: The Framework for People Who Hate Frameworks
The Origin of Lithium
PHP 5.3 and Lithium: the most rad php framework
Dependency Injection IPC 201
Dependency injection-zendcon-2010

What's hot (20)

PDF
Design Patterns avec PHP 5.3, Symfony et Pimple
KEY
Lithium Best
PDF
Silex meets SOAP & REST
PDF
The History of PHPersistence
PDF
Dependency injection in PHP 5.3/5.4
PDF
Database Design Patterns
PDF
Dependency injection - phpday 2010
PDF
Symfony2 - WebExpo 2010
PDF
Decouple Your Code For Reusability (International PHP Conference / IPC 2008)
PDF
Unit and Functional Testing with Symfony2
PDF
Dependency Injection with PHP and PHP 5.3
ODP
Rich domain model with symfony 2.5 and doctrine 2.5
ODP
Symfony2, creare bundle e valore per il cliente
PDF
Be RESTful (Symfony Camp 2008)
PDF
Agile database access with CakePHP 3
PDF
Doctrine fixtures
PDF
Advanced Querying with CakePHP 3
PDF
New in cakephp3
PDF
Future of HTTP in CakePHP
PDF
PHP Data Objects
Design Patterns avec PHP 5.3, Symfony et Pimple
Lithium Best
Silex meets SOAP & REST
The History of PHPersistence
Dependency injection in PHP 5.3/5.4
Database Design Patterns
Dependency injection - phpday 2010
Symfony2 - WebExpo 2010
Decouple Your Code For Reusability (International PHP Conference / IPC 2008)
Unit and Functional Testing with Symfony2
Dependency Injection with PHP and PHP 5.3
Rich domain model with symfony 2.5 and doctrine 2.5
Symfony2, creare bundle e valore per il cliente
Be RESTful (Symfony Camp 2008)
Agile database access with CakePHP 3
Doctrine fixtures
Advanced Querying with CakePHP 3
New in cakephp3
Future of HTTP in CakePHP
PHP Data Objects
Ad

Viewers also liked (7)

PDF
Measuring Your Code
PDF
Measuring Your Code 2.0
PDF
Practical PHP 5.3
PDF
Building Apps with MongoDB
KEY
PHP, Lithium and MongoDB
KEY
Taking PHP To the next level
PPT
Lithium PHP Meetup 0210
Measuring Your Code
Measuring Your Code 2.0
Practical PHP 5.3
Building Apps with MongoDB
PHP, Lithium and MongoDB
Taking PHP To the next level
Lithium PHP Meetup 0210
Ad

Similar to Lithium: The Framework for People Who Hate Frameworks, Tokyo Edition (20)

PDF
Singletons in PHP - Why they are bad and how you can eliminate them from your...
PDF
20100730 phpstudy
PPTX
Php meetup 20130912 reflection
PPTX
Adding Dependency Injection to Legacy Applications
PDF
Fighting legacy with hexagonal architecture and frameworkless php
PDF
関西PHP勉強会 php5.4つまみぐい
PDF
Alexander Makarov "Let’s talk about code"
PPT
PDF
Beyond symfony 1.2 (Symfony Camp 2008)
PDF
The Solar Framework for PHP
PDF
Fluent Development with FLOW3 1.0
PDF
Object Oriented Programming in PHP
PDF
OOPs Concept
PDF
Effective PHP. Part 1
PDF
PHPCon 2016: PHP7 by Witek Adamus / XSolve
PDF
Ciconf 2012 - Better than Ad-hoc
PDF
Symfony2 from the Trenches
KEY
Keeping it small: Getting to know the Slim micro framework
KEY
Keeping It Small with Slim
PDF
Symfony War Stories
Singletons in PHP - Why they are bad and how you can eliminate them from your...
20100730 phpstudy
Php meetup 20130912 reflection
Adding Dependency Injection to Legacy Applications
Fighting legacy with hexagonal architecture and frameworkless php
関西PHP勉強会 php5.4つまみぐい
Alexander Makarov "Let’s talk about code"
Beyond symfony 1.2 (Symfony Camp 2008)
The Solar Framework for PHP
Fluent Development with FLOW3 1.0
Object Oriented Programming in PHP
OOPs Concept
Effective PHP. Part 1
PHPCon 2016: PHP7 by Witek Adamus / XSolve
Ciconf 2012 - Better than Ad-hoc
Symfony2 from the Trenches
Keeping it small: Getting to know the Slim micro framework
Keeping It Small with Slim
Symfony War Stories

Recently uploaded (20)

PDF
Blue Purple Modern Animated Computer Science Presentation.pdf.pdf
PDF
CIFDAQ's Market Insight: SEC Turns Pro Crypto
PPTX
Understanding_Digital_Forensics_Presentation.pptx
PDF
solutions_manual_-_materials___processing_in_manufacturing__demargo_.pdf
PDF
Per capita expenditure prediction using model stacking based on satellite ima...
PDF
Advanced Soft Computing BINUS July 2025.pdf
PDF
Bridging biosciences and deep learning for revolutionary discoveries: a compr...
PDF
Reach Out and Touch Someone: Haptics and Empathic Computing
PDF
Peak of Data & AI Encore- AI for Metadata and Smarter Workflows
PDF
Advanced IT Governance
PPTX
Effective Security Operations Center (SOC) A Modern, Strategic, and Threat-In...
PDF
Chapter 3 Spatial Domain Image Processing.pdf
PDF
HCSP-Presales-Campus Network Planning and Design V1.0 Training Material-Witho...
PDF
Sensors and Actuators in IoT Systems using pdf
PPTX
Cloud computing and distributed systems.
PDF
Diabetes mellitus diagnosis method based random forest with bat algorithm
PDF
TokAI - TikTok AI Agent : The First AI Application That Analyzes 10,000+ Vira...
PPTX
MYSQL Presentation for SQL database connectivity
PDF
How Onsite IT Support Drives Business Efficiency, Security, and Growth.pdf
PDF
Chapter 2 Digital Image Fundamentals.pdf
Blue Purple Modern Animated Computer Science Presentation.pdf.pdf
CIFDAQ's Market Insight: SEC Turns Pro Crypto
Understanding_Digital_Forensics_Presentation.pptx
solutions_manual_-_materials___processing_in_manufacturing__demargo_.pdf
Per capita expenditure prediction using model stacking based on satellite ima...
Advanced Soft Computing BINUS July 2025.pdf
Bridging biosciences and deep learning for revolutionary discoveries: a compr...
Reach Out and Touch Someone: Haptics and Empathic Computing
Peak of Data & AI Encore- AI for Metadata and Smarter Workflows
Advanced IT Governance
Effective Security Operations Center (SOC) A Modern, Strategic, and Threat-In...
Chapter 3 Spatial Domain Image Processing.pdf
HCSP-Presales-Campus Network Planning and Design V1.0 Training Material-Witho...
Sensors and Actuators in IoT Systems using pdf
Cloud computing and distributed systems.
Diabetes mellitus diagnosis method based random forest with bat algorithm
TokAI - TikTok AI Agent : The First AI Application That Analyzes 10,000+ Vira...
MYSQL Presentation for SQL database connectivity
How Onsite IT Support Drives Business Efficiency, Security, and Growth.pdf
Chapter 2 Digital Image Fundamentals.pdf

Lithium: The Framework for People Who Hate Frameworks, Tokyo Edition

  • 1. A Framework for People Who Hate Frameworks.
  • 2. A movement in 3 parts • Frameworks suck • Everything you know is wrong • Lithium tries to suck less
  • 4. Let’s get one thing out of the way:
  • 10. Frameworks Suck • Code you will never use. • Complexity overhead. • You didn’t write it.
  • 12. His name is the biggest, so it’s his fault.
  • 13. We’re not saying design patterns are bad. Quite the opposite.
  • 14. Lithium implements many well known design patterns.
  • 16. Some patterns only treat the symptoms, instead of the cause.
  • 18. Object dependencies. “The principle of separating configuration from use.”
  • 20. Everyone does it differently.
  • 21. Sometimes in the same framework.
  • 22. Sometimes in the same class.
  • 25. ... but you need to understand what you’re doing.
  • 26. Some patterns were implemented in Java, to solve language problems that PHP just doesn’t have.
  • 27. “Design Patterns In Dynamic Languages” Peter Norvig http://norvig.com/design-patterns/
  • 28. Try this some time...
  • 32. The sun does not revolve around OOP ...nevertheless, it moves. Galileo facing the Roman Inquistion - Cristiano Banti (1857)
  • 34. 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
  • 35. Dependency Injection • Various attempts at making DI work better: • DI Container • Using dynamic nature of PHP to your advantage.
  • 36. Coupling should be in proportion to domain relevance.
  • 38. If... Configure::write('debug', 0); is evil, $this->debug = 0; is the
  • 41. 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 )); // ... } }
  • 42. OO doesn’t make you think (about state).
  • 44. ActiveRecord Data Access Object Unit of Work Dependency Injection Registry Front Controller MVC Value Object Data Mapper Service Layer
  • 46. 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
  • 47. Tools do not mean... ...you can build a house.
  • 49. Lithium tries to suck less.
  • 51. Aspect-Oriented Design • Separation of concerns • Domain classes should not know or care about cross- cutting concerns • Examples: • Caching • Logging • Access Control, etc.
  • 52. Functional Programming • Only possible when functions are first-class citizens • Referential transparency • Functional purity
  • 53. Referential transparency is not... $this date() $_*
  • 54. Referential transparency is not... $this date() $_*
  • 55. These Are Not Design Patterns.
  • 56. Less Suck • Draws on years of experience building web frameworks • PHP 5.3+ only • Doesn’t assume you’re stupid
  • 57. Ways we try to suck less:
  • 59. <?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() { // ... } } ?>
  • 60. 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())
  • 61. <?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() { // ... } } ?>
  • 62. <?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 } } ?>
  • 63. <?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)); ?>
  • 64. <?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() { // ... } } ?>
  • 65. <?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() { // ... } } ?>
  • 66. <?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() { // ... } } ?>
  • 67. <?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()); } } } ?> ?>
  • 68. <?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()); } } } ?> ?>
  • 69. <?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()); } } } ?> ?>
  • 71. Keeps parameter lists short & Makes class APIs more extensible
  • 73. Same idea. But...! Modifies class / object state.
  • 74. Adaptable Auth Cache Catalog Connections Logger Session
  • 75. use lithiumsecurityAuth; Auth::config(array( 'customer' => array( 'adapter' => 'Form', 'model' => 'Customer', 'fields' => array('email', 'password') ) ));
  • 76. use lithiumstorageCache; Cache::config(array( 'local' => array('adapter' => 'Apc'), 'distributed' => array( 'adapter' => 'Memcached', 'servers' => array('127.0.0.1', 11211), ), 'default' => array('adapter' => 'File') ));
  • 77. 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' ) ));
  • 78. use lithiumstorageSession; Session::config(array( 'cookie' => array( 'adapter' => 'Cookie', 'expire' => '+2 days' ), 'default' => array('adapter' => 'Php') ));
  • 80. use lithiumstorageSession; Session::config(array( 'default' => array( 'adapter' => 'MyCustomAdapter', 'expires' => '+2 days', 'custom' => 'Whatever!' ) ));
  • 81. use lithiumstorageSession; Session::config(array( 'default' => array( 'adapter' => 'MyCustomAdapter', 'expires' => '+2 days', 'custom' => 'Whatever!' ) )); public function __construct(array $config = array())
  • 83. use lithiumstorageCache; Cache::config(array( 'default' => array( 'development' => array( 'adapter' => 'Apc' ), 'production' => array( 'adapter' => 'Memcached', 'servers' => array('127.0.0.1', 11211) ) ) ));
  • 84. use lithiumstorageCache; Cache::config(array( 'default' => array( 'development' => array( 'adapter' => 'Apc' ), 'production' => array( 'adapter' => 'Memcached', 'servers' => array('127.0.0.1', 11211) ) ) ));
  • 85. Works identically for all adapters.
  • 86. If you remember nothing else about configuration state...
  • 89. 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
  • 90. 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 with XHProf and XDebug cachegrinds.
  • 92. 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') )); });
  • 93. Platform Rackspace Cloud • 256MB ram • Ubuntu Karmic • Apache 2.2.12 • PHP 5.3.1 • Xcache • Siege 2.68 2009-11-26
  • 95. Lithium is a highly flexible framework.
  • 96. Most class dependencies are dynamic. (Our implementation of dependency injection)
  • 97. class Service extends lithiumcoreObject { protected $_classes = array( 'request' => 'lithiumnethttpRequest', 'response' => 'lithiumnethttpResponse', 'socket' => 'lithiumnetsocketContext' ); } $service = new Service(array('classes' => array( 'socket' => 'mycustomSocket' )));
  • 99. Example: Caching & Logging Caching Logging Post::find()
  • 100. Example: Caching & Logging Caching Logging Post::find()
  • 101. 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); });
  • 102. 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); });
  • 103. What about Observer? • Dependent on a centralized publish/ subscribe system • Extra layer of abstraction • Fewer possibilities
  • 104. What about Observer? • Filters are self-contained and attach directly to objects • Direct and intuitive
  • 105. Features: Everything is an adapter. (well, almost)
  • 106. Databases • 1st-class support for document-oriented databases • MongoDB & CouchDB: production ready • Relational databases in beta • Cassandra/Redis/Riak in the works, too
  • 107. <?php $post = Post::create(array( 'title' => ' ', 'body' => ' ' )); $post->save(); $post = Post::find($id); ?> <h2><?=$post->title; ?></h2> <p><?=$post->body; ?></p>
  • 109. This works on... • MongoDB • CouchDB • MySQL • SQLite
  • 110. MongoDB + CouchDB: $post = Post::create(array( 'title' => ' ', 'body' => ' ', 'tags' => array('PHP', 'Japan'), 'author' => array('name' => 'Nate') )); $post->save();
  • 111. MongoDB: $posts = Post::all(array('conditions' => array( 'tags' => array('PHP', 'Japan'), 'author.name' => 'Nate' ))); // Translates to... db.posts.find({ tags : { $in : ['PHP', 'Japan'] }, 'author.name' : 'Nate' })
  • 112. Databases • Adapter based, plugin aware • Will ship with MySQL, SQLite & PostgreSQL • SQL Server support via plugin • Query API
  • 113. 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
  • 114. The Query API $ages = User::all(array( 'group' => 'age', 'reduce' => 'function(obj, prev) { prev.count++; }', 'initial' => array('count' => 0) ));
  • 115. 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')), ))) ));
  • 116. 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
  • 118. Plays nice with others • Easily load & use libraries from other frameworks: • Zend Framework, Solar, Symfony, PEAR, etc. • PSR-0 Class-loading standard
  • 119. /* 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"; } ));
  • 120. 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'); } }
  • 121. This has been a presentation by: Nate Abele (@nateabele) Joël Perras (@jperras) http://lithify.me Sucks. But check it out anyway.