Successfully reported this slideshow.

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

1

Share

Upcoming SlideShare
The State of Lithium
The State of Lithium
Loading in …3
×
1 of 121
1 of 121

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

1

Share

Download to read offline

This is the updated version of Joël and my standard Lithium talk, with a few tweaks and updates since the last time we gave it.

This is the updated version of Joël and my standard Lithium talk, with a few tweaks and updates since the last time we gave it.

More Related Content

Related Books

Free with a 14 day trial from Scribd

See all

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

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

×