• Share
  • Email
  • Embed
  • Like
  • Save
  • Private Content
Lithium: The Framework for People Who Hate Frameworks
 

Lithium: The Framework for People Who Hate Frameworks

on

  • 34,869 views

This is the presentation was given at ConFoo on March 11th by Nate Abele and Joël Perras, and is an introduction to the architectural problems with other frameworks that Lithium was designed to ...

This is the presentation was given at ConFoo on March 11th by Nate Abele and Joël Perras, and is an introduction to the architectural problems with other frameworks that Lithium was designed to address, and how it addresses them. It also introduces programming paradigms like functional and aspect-oriented programming which address issues that OOP doesn't account for.

Finally, the talk provides a quick overview of the innovative and unparalleled features that Lithium provides, including the data layer, which supports both relational and non-relational databases.

Statistics

Views

Total Views
34,869
Views on SlideShare
29,201
Embed Views
5,668

Actions

Likes
55
Downloads
0
Comments
2

29 Embeds 5,668

http://localhost 1832
http://francescoagati.wordpress.com 1645
http://1-byte.jp 1027
http://www.devhands.com 615
http://www.veselin.bg 321
http://www.slideshare.net 103
http://blog.gianbasagre.com 28
http://wilfried-fox.de 17
http://www.lithium101.com 14
http://webcache.googleusercontent.com 11
https://twitter.com 6
http://www.techgig.com 6
http://www.php.rk.edu.pl 6
http://dev.lithium101.com 5
http://www.phpzend.com.ar 5
https://si0.twimg.com 4
http://feeds.feedburner.com 3
http://www.mostofreddy.com.ar 3
http://paper.li 2
http://a0.twimg.com 2
http://test.idoc.vn 2
http://translate.googleusercontent.com 2
http://www.m.techgig.com 2
http://pinterest.com 2
http://static.slidesharecdn.com 1
http://www.rkblog.rk.edu.pl 1
http://localhost:8000 1
url_unknown 1
http://www.pinterest.com 1
More...

Accessibility

Categories

Upload Details

Uploaded via as Adobe PDF

Usage Rights

CC Attribution-NoDerivs LicenseCC Attribution-NoDerivs License

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel

12 of 2 previous next

  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

    Lithium: The Framework for People Who Hate Frameworks Lithium: The Framework for People Who Hate Frameworks Presentation Transcript

    • 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
    • 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'); } }
    • This has been a presentation by: Joël Perras (@jperras) Nate Abele (@nateabele) Sucks. But check it out anyway.