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.
And yes, I mean:
Why?
(Besides the obvious attempt at being provocative)
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 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.”
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.
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.]
                [... not really. It’s Symfony.]
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....
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?
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  =
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 $logger;

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

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

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

$user = new User();
$user->logger = new Logger();
$user->create();
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));
        // ...
    }
}
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
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
• Function 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 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() {
         // ...
     }
}

?>
<?php

namespace applicationbar;

use lithiumutilString;
use lithiumutilCollection;

class Foo extends lithiumcoreObject {

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

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

     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 =
    lithiumutilString;                         array())
use lithiumutilCollection;
    public function __construct(array $config =   array())
class Foo extends lithiumcoreObject {
    public function __construct(array $config =   array())
    protected $_classes = array(
    public function'lithiumstorageCache', =
        'cache' => __construct(array $config      array())
        '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
     <?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;   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());
}    }
}
?>
?>
$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'      => 'http',
        'adapter' => 'CouchDb',
        '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!'
    )
));



 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)
        )
    )
));
Works identically for all adapters.
If you remember nothing
else about configuration
state...
Immutability.
Set it and forget it.
Speed.
Zoom!

• Use native extensions (PECL) whenever
  possible.

• Don’t like a class? Change it. At runtime.
• Profiled at every step of the way.
Flexibility.
Lithium is the most
flexible framework.
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

    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);
});
Post                                                                                   Logger
       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);
       });
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.
G11n (Globalization)
• CLDR
• Gettext
• PHP arrays
• Code
• Pecl/intl support on the way
Databases

• 1st-class support for document-oriented
  databases

• MongoDB & CouchDB: production ready
• Relational databases coming soon
<?php

$post = Post::create(array(
    'title' => 'Forget the words and sing along',
    'body' => 'Everything you know is wrong'
));
$post->save();

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

?>

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

• MongoDB
• CouchDB
• MySQL
• SQLite (almost)
Databases

• Adapter based. Plugin aware.
• Will ship with MySQL, SQLite, PostgreSQL &
  SQL Server.

• Query API
Integration with other
frameworks

• Easily load & use libraries from other
  frameworks:

  • Zend Framework, Solar, Symfony
/* add the trunk */
Libraries::add("Zend", array(
    "prefix" => "Zend_",
    "includePath" => '/htdocs/libraries/Zend/trunk/library',
    "bootstrap" => "Loader/Autoloader.php",
    "loader" => array("Zend_Loader_Autoloader", "autoload"),
    "transform" => function($class) { return str_replace("_", "/", $class) . ".php"; }
));
// Note that 'path' can be dropped if trunk/library/Zend is placed
// directly into your /libraries directory.

/* 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');
    }

}
Lithium: The Framework for People Who Hate Frameworks
This has been a presentation by:

Joël Perras (@jperras)
Nate Abele (@nateabele)


            Sucks. But check it out anyway.

More Related Content

What's hot (20)

PDF
The History of PHPersistence
Hugo Hamon
 
PDF
Database Design Patterns
Hugo Hamon
 
PDF
Dependency injection-zendcon-2010
Fabien Potencier
 
PDF
Symfony2 - WebExpo 2010
Fabien Potencier
 
PDF
Silex meets SOAP & REST
Hugo Hamon
 
PDF
Dependency injection in PHP 5.3/5.4
Fabien Potencier
 
PDF
Dependency injection - phpday 2010
Fabien Potencier
 
PDF
Dependency Injection with PHP and PHP 5.3
Fabien Potencier
 
PDF
Decouple Your Code For Reusability (International PHP Conference / IPC 2008)
Fabien Potencier
 
ODP
Rich domain model with symfony 2.5 and doctrine 2.5
Leonardo Proietti
 
ODP
Symfony2, creare bundle e valore per il cliente
Leonardo Proietti
 
PDF
Agile database access with CakePHP 3
José Lorenzo Rodríguez Urdaneta
 
PDF
Doctrine fixtures
Bill Chang
 
PDF
Unit and Functional Testing with Symfony2
Fabien Potencier
 
PDF
New in cakephp3
markstory
 
PDF
Advanced Querying with CakePHP 3
José Lorenzo Rodríguez Urdaneta
 
PDF
Future of HTTP in CakePHP
markstory
 
PDF
PHP Data Objects
Wez Furlong
 
PDF
CakeFest 2013 keynote
José Lorenzo Rodríguez Urdaneta
 
KEY
Lithium Best
Richard McIntyre
 
The History of PHPersistence
Hugo Hamon
 
Database Design Patterns
Hugo Hamon
 
Dependency injection-zendcon-2010
Fabien Potencier
 
Symfony2 - WebExpo 2010
Fabien Potencier
 
Silex meets SOAP & REST
Hugo Hamon
 
Dependency injection in PHP 5.3/5.4
Fabien Potencier
 
Dependency injection - phpday 2010
Fabien Potencier
 
Dependency Injection with PHP and PHP 5.3
Fabien Potencier
 
Decouple Your Code For Reusability (International PHP Conference / IPC 2008)
Fabien Potencier
 
Rich domain model with symfony 2.5 and doctrine 2.5
Leonardo Proietti
 
Symfony2, creare bundle e valore per il cliente
Leonardo Proietti
 
Agile database access with CakePHP 3
José Lorenzo Rodríguez Urdaneta
 
Doctrine fixtures
Bill Chang
 
Unit and Functional Testing with Symfony2
Fabien Potencier
 
New in cakephp3
markstory
 
Advanced Querying with CakePHP 3
José Lorenzo Rodríguez Urdaneta
 
Future of HTTP in CakePHP
markstory
 
PHP Data Objects
Wez Furlong
 
CakeFest 2013 keynote
José Lorenzo Rodríguez Urdaneta
 
Lithium Best
Richard McIntyre
 

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

KEY
Can't Miss Features of PHP 5.3 and 5.4
Jeff Carouth
 
PDF
Unittests für Dummies
Lars Jankowfsky
 
PDF
Dependency Injection
Rifat Nabi
 
PDF
Doctrine For Beginners
Jonathan Wage
 
PDF
All I Need to Know I Learned by Writing My Own Web Framework
Ben Scofield
 
KEY
Zend Framework Study@Tokyo #2
Shinya Ohyanagi
 
PDF
PhpUnit - The most unknown Parts
Bastian Feder
 
KEY
Symfony2 Building on Alpha / Beta technology
Daniel Knell
 
PDF
Separation of concerns - DPC12
Stephan Hochdörfer
 
PDF
Virtual Madness @ Etsy
Nishan Subedi
 
PDF
Why is crud a bad idea - focus on real scenarios
Divante
 
PDF
international PHP2011_Bastian Feder_The most unknown Parts of PHPUnit
smueller_sandsmedia
 
PDF
Php unit the-mostunknownparts
Bastian Feder
 
PDF
Singletons in PHP - Why they are bad and how you can eliminate them from your...
go_oh
 
PDF
Dependency injection in Drupal 8
Alexei Gorobets
 
KEY
Unit testing zend framework apps
Michelangelo van Dam
 
PDF
How Kris Writes Symfony Apps
Kris Wallsmith
 
PDF
SPL: The Missing Link in Development
jsmith92
 
PDF
Advanced symfony Techniques
Kris Wallsmith
 
PDF
Unit testing with zend framework tek11
Michelangelo van Dam
 
Can't Miss Features of PHP 5.3 and 5.4
Jeff Carouth
 
Unittests für Dummies
Lars Jankowfsky
 
Dependency Injection
Rifat Nabi
 
Doctrine For Beginners
Jonathan Wage
 
All I Need to Know I Learned by Writing My Own Web Framework
Ben Scofield
 
Zend Framework Study@Tokyo #2
Shinya Ohyanagi
 
PhpUnit - The most unknown Parts
Bastian Feder
 
Symfony2 Building on Alpha / Beta technology
Daniel Knell
 
Separation of concerns - DPC12
Stephan Hochdörfer
 
Virtual Madness @ Etsy
Nishan Subedi
 
Why is crud a bad idea - focus on real scenarios
Divante
 
international PHP2011_Bastian Feder_The most unknown Parts of PHPUnit
smueller_sandsmedia
 
Php unit the-mostunknownparts
Bastian Feder
 
Singletons in PHP - Why they are bad and how you can eliminate them from your...
go_oh
 
Dependency injection in Drupal 8
Alexei Gorobets
 
Unit testing zend framework apps
Michelangelo van Dam
 
How Kris Writes Symfony Apps
Kris Wallsmith
 
SPL: The Missing Link in Development
jsmith92
 
Advanced symfony Techniques
Kris Wallsmith
 
Unit testing with zend framework tek11
Michelangelo van Dam
 
Ad

More from Nate Abele (6)

PDF
Measuring Your Code 2.0
Nate Abele
 
PDF
Measuring Your Code
Nate Abele
 
PDF
Building Apps with MongoDB
Nate Abele
 
PDF
Practical PHP 5.3
Nate Abele
 
PDF
Measuring Your Code
Nate Abele
 
PDF
Doin' It Rong
Nate Abele
 
Measuring Your Code 2.0
Nate Abele
 
Measuring Your Code
Nate Abele
 
Building Apps with MongoDB
Nate Abele
 
Practical PHP 5.3
Nate Abele
 
Measuring Your Code
Nate Abele
 
Doin' It Rong
Nate Abele
 
Ad

Recently uploaded (20)

PDF
Trying to figure out MCP by actually building an app from scratch with open s...
Julien SIMON
 
PPTX
Simple and concise overview about Quantum computing..pptx
mughal641
 
PDF
Make GenAI investments go further with the Dell AI Factory
Principled Technologies
 
PDF
The Future of Mobile Is Context-Aware—Are You Ready?
iProgrammer Solutions Private Limited
 
PPTX
The Future of AI & Machine Learning.pptx
pritsen4700
 
PPTX
IT Runs Better with ThousandEyes AI-driven Assurance
ThousandEyes
 
PDF
State-Dependent Conformal Perception Bounds for Neuro-Symbolic Verification
Ivan Ruchkin
 
PPTX
cloud computing vai.pptx for the project
vaibhavdobariyal79
 
PPTX
Applied-Statistics-Mastering-Data-Driven-Decisions.pptx
parmaryashparmaryash
 
PDF
How ETL Control Logic Keeps Your Pipelines Safe and Reliable.pdf
Stryv Solutions Pvt. Ltd.
 
PDF
Data_Analytics_vs_Data_Science_vs_BI_by_CA_Suvidha_Chaplot.pdf
CA Suvidha Chaplot
 
PPTX
Dev Dives: Automate, test, and deploy in one place—with Unified Developer Exp...
AndreeaTom
 
PDF
TrustArc Webinar - Navigating Data Privacy in LATAM: Laws, Trends, and Compli...
TrustArc
 
PDF
Generative AI vs Predictive AI-The Ultimate Comparison Guide
Lily Clark
 
PDF
Researching The Best Chat SDK Providers in 2025
Ray Fields
 
PDF
Per Axbom: The spectacular lies of maps
Nexer Digital
 
PPTX
What-is-the-World-Wide-Web -- Introduction
tonifi9488
 
PDF
A Strategic Analysis of the MVNO Wave in Emerging Markets.pdf
IPLOOK Networks
 
PDF
MASTERDECK GRAPHSUMMIT SYDNEY (Public).pdf
Neo4j
 
PPTX
Agentic AI in Healthcare Driving the Next Wave of Digital Transformation
danielle hunter
 
Trying to figure out MCP by actually building an app from scratch with open s...
Julien SIMON
 
Simple and concise overview about Quantum computing..pptx
mughal641
 
Make GenAI investments go further with the Dell AI Factory
Principled Technologies
 
The Future of Mobile Is Context-Aware—Are You Ready?
iProgrammer Solutions Private Limited
 
The Future of AI & Machine Learning.pptx
pritsen4700
 
IT Runs Better with ThousandEyes AI-driven Assurance
ThousandEyes
 
State-Dependent Conformal Perception Bounds for Neuro-Symbolic Verification
Ivan Ruchkin
 
cloud computing vai.pptx for the project
vaibhavdobariyal79
 
Applied-Statistics-Mastering-Data-Driven-Decisions.pptx
parmaryashparmaryash
 
How ETL Control Logic Keeps Your Pipelines Safe and Reliable.pdf
Stryv Solutions Pvt. Ltd.
 
Data_Analytics_vs_Data_Science_vs_BI_by_CA_Suvidha_Chaplot.pdf
CA Suvidha Chaplot
 
Dev Dives: Automate, test, and deploy in one place—with Unified Developer Exp...
AndreeaTom
 
TrustArc Webinar - Navigating Data Privacy in LATAM: Laws, Trends, and Compli...
TrustArc
 
Generative AI vs Predictive AI-The Ultimate Comparison Guide
Lily Clark
 
Researching The Best Chat SDK Providers in 2025
Ray Fields
 
Per Axbom: The spectacular lies of maps
Nexer Digital
 
What-is-the-World-Wide-Web -- Introduction
tonifi9488
 
A Strategic Analysis of the MVNO Wave in Emerging Markets.pdf
IPLOOK Networks
 
MASTERDECK GRAPHSUMMIT SYDNEY (Public).pdf
Neo4j
 
Agentic AI in Healthcare Driving the Next Wave of Digital Transformation
danielle hunter
 

Lithium: The Framework for People Who Hate Frameworks

  • 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:
  • 8. And yes, I mean:
  • 9. Why? (Besides the obvious attempt at being provocative)
  • 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 design patterns.
  • 16. Some patterns only treat the symptoms, instead of the cause.
  • 18. Object dependencies. “The principle of separating configuration from use.”
  • 19. 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.
  • 21. Everyone does it differently.
  • 22. Sometimes in the same framework.
  • 23. Sometimes in the same class.
  • 24. Sucks function spam($emails) { $this->Email->replyTo = '[email protected]'; $this->Email->from = 'Desmond Etete <[email protected]>'; $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(); } }
  • 27. ... or is it?
  • 28. [variables changed to protect the innocent.] [... not really. It’s Symfony.]
  • 29. Show that object who’s boss. class User { function __construct($storage) { $this->storage = $storage; } } $storage = new SessionStorage('SESSION_ID'); $user = new User($storage);
  • 30. Of course, we want this to abstract this. Frameworks adore abstractions.
  • 31. DI Container to the rescue!
  • 32. 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; } }
  • 33. But now you want an abstraction to manage the DI container. Duh.
  • 34. So you create a “Service Container”.
  • 35. 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, )); } }
  • 36. Of course, we now want to abstract the crap out of the Service Container. Double duh.
  • 37. So lets use a Builder to configure the Services.
  • 38. 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'))) ;
  • 39. And for good measure, have our configurations for Service Containers in XML files.
  • 40. 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.
  • 41. Anyone see what’s wrong here?
  • 44. The sun does not revolve around OOP ...nevertheless, it moves. Galileo facing the Roman Inquistion - Cristiano Banti (1857)
  • 47. 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();
  • 48. class User { public $logger; public function create() { $this->logger->write('Creating a new user...'); $this->_doSomeInitialization(); $this->_databaseConnection->doATransaction($this)->create(); $this->logger->write('Finished creating user'); } } $user = new User(); $user->logger = new Logger(); $user->create();
  • 49. 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
  • 50. 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)); // ... } }
  • 51. Coupling should be in proportion to domain relevance.
  • 53. If... Configure::write('debug', 0); is evil, $this->debug = 0; is the
  • 56. 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 )); // ... } }
  • 57. OO doesn’t make you think (about state).
  • 59. ActiveRecord Data Access Object Unit of Work Dependency Injection Registry Front Controller MVC Value Object Data Mapper Service Layer
  • 61. 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
  • 62. Tools do not mean... ...you can build a house.
  • 64. Lithium tries to suck less.
  • 66. Aspect-Oriented Design • Separation of concerns • Domain classes should not know or care about cross- cutting concerns • Examples: • Caching • Logging • Access Control, etc.
  • 67. Functional Programming • Only possible when functions are first-class citizens • Referential transparency • Function purity
  • 68. Referential transparency is not... $this date() $_*
  • 69. Referential transparency is not... $this date() $_*
  • 70. These Are Not Design Patterns.
  • 71. Less Suck • Draws on years of experience building web frameworks • PHP 5.3+ only • Doesn’t assume you’re stupid
  • 72. Ways we suck less:
  • 74. <?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() { // ... } } ?>
  • 75. <?php namespace applicationbar; use lithiumutilString; use lithiumutilCollection; class Foo extends lithiumcoreObject { protected $_classes = array( 'cache' => 'lithiumstorageCache', 'logger' => 'lithiumanalysisLogger' ); public function __construct(array $config = array()) { // ... 1 } protected function _init() { // ... } } ?>
  • 76. 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 = lithiumutilString; array()) use lithiumutilCollection; public function __construct(array $config = array()) class Foo extends lithiumcoreObject { public function __construct(array $config = array()) protected $_classes = array( public function'lithiumstorageCache', = 'cache' => __construct(array $config array()) '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())
  • 77. <?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 } } ?>
  • 78. <?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)); ?>
  • 79. <?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() { // ... } } ?>
  • 80. <?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() { // ... } } ?>
  • 81. <?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()); } } } ?> ?>
  • 82. <?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()); } } } ?> ?>
  • 84. Keeps parameter lists short & Makes class APIs more extensible
  • 86. Same idea. But...! Modifies class / object state.
  • 87. Adaptable Auth Cache Catalog Connections Logger Session
  • 88. use lithiumsecurityAuth; Auth::config(array( 'customer' => array( 'adapter' => 'Form', 'model' => 'Customer', 'fields' => array('email', 'password') ) ));
  • 89. use lithiumstorageCache; Cache::config(array( 'local' => array('adapter' => 'Apc'), 'distributed' => array( 'adapter' => 'Memcached', 'servers' => array('127.0.0.1', 11211), ), 'default' => array('adapter' => 'File') ));
  • 90. use lithiumdataConnections; Connections::config(array( 'old' => array( 'type' => 'database', 'adapter' => 'MySql', 'user' => 'bobby_tables', 'password' => '******', 'database' => 'my_app' ), 'new' => array( 'type' => 'http', 'adapter' => 'CouchDb', 'database' => 'my_app' ) ));
  • 91. use lithiumstorageSession; Session::config(array( 'cookie' => array( 'adapter' => 'Cookie', 'expire' => '+2 days' ), 'default' => array('adapter' => 'Php') ));
  • 93. use lithiumstorageSession; Session::config(array( 'default' => array( 'adapter' => 'MyCustomAdapter', 'expires' => '+2 days', 'custom' => 'Whatevah!' ) )); public function __construct(array $config = array())
  • 95. use lithiumstorageCache; Cache::config(array( 'default' => array( 'development' => array( 'adapter' => 'Apc' ), 'production' => array( 'adapter' => 'Memcached', 'servers' => array('127.0.0.1', 11211) ) ) ));
  • 96. Works identically for all adapters.
  • 97. If you remember nothing else about configuration state...
  • 100. Zoom! • Use native extensions (PECL) whenever possible. • Don’t like a class? Change it. At runtime. • Profiled at every step of the way.
  • 102. Lithium is the most flexible framework.
  • 104. class Service extends lithiumcoreObject { protected $_classes = array( 'request' => 'lithiumnethttpRequest', 'response' => 'lithiumnethttpResponse', 'socket' => 'lithiumnetsocketContext' ); } $service = new Service(array('classes' => array( 'socket' => 'mycustomSocket' )));
  • 106. Example: Caching & Logging Caching Logging Post::find()
  • 107. Example: Caching & Logging Caching Logging Post::find()
  • 108. Example: Caching & Logging Caching Logging Post::find()
  • 109. 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); });
  • 110. Post Logger 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); });
  • 111. What about Observer? • Dependent on a centralized publish/ subscribe system • Extra layer of abstraction • Fewer possibilities
  • 112. What about Observer? • Filters are self-contained and attach directly to objects • Direct and intuitive
  • 113. Features: Everything is an adapter.
  • 114. G11n (Globalization) • CLDR • Gettext • PHP arrays • Code • Pecl/intl support on the way
  • 115. Databases • 1st-class support for document-oriented databases • MongoDB & CouchDB: production ready • Relational databases coming soon
  • 116. <?php $post = Post::create(array( 'title' => 'Forget the words and sing along', 'body' => 'Everything you know is wrong' )); $post->save(); $post = Post::find($id); ?> <h2><?=$post->title; ?></h2> <p><?=$post->body; ?></p>
  • 117. This works on... • MongoDB • CouchDB • MySQL • SQLite (almost)
  • 118. Databases • Adapter based. Plugin aware. • Will ship with MySQL, SQLite, PostgreSQL & SQL Server. • Query API
  • 119. Integration with other frameworks • Easily load & use libraries from other frameworks: • Zend Framework, Solar, Symfony
  • 120. /* add the trunk */ Libraries::add("Zend", array( "prefix" => "Zend_", "includePath" => '/htdocs/libraries/Zend/trunk/library', "bootstrap" => "Loader/Autoloader.php", "loader" => array("Zend_Loader_Autoloader", "autoload"), "transform" => function($class) { return str_replace("_", "/", $class) . ".php"; } )); // Note that 'path' can be dropped if trunk/library/Zend is placed // directly into your /libraries directory. /* add the incubator */ Libraries::add("Zend_Incubator", array( "prefix" => "Zend_", "includePath" => '/htdocs/libraries/Zend/incubator/library', "transform" => function($class) { return str_replace("_", "/", $class) . ".php"; } ));
  • 121. 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'); } }
  • 123. This has been a presentation by: Joël Perras (@jperras) Nate Abele (@nateabele) Sucks. But check it out anyway.