Your SlideShare is downloading. ×
0
A Framework for People
Who Hate Frameworks.
Find us on          :
http://joind.in/1592
A movement in 3 parts

• Frameworks suck
• Everything you know is wrong
• Lithium tries to suck less
The first part.
Let’s get one thing out
of the way:
Lithium sucks.
But that’s okay.
Because your
framework sucks, too.
And yes, I mean:
And yes, I mean:
Why?
(Besides the obvious attempt at being provocative)
Frameworks Suck
Frameworks Suck

• Code you will never use.
Frameworks Suck

• Code you will never use.
• Complexity overhead.
Frameworks Suck

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

Martin Fowler.
His name is the
biggest, so it’s his
      fault.
His name is the
biggest, so it’s his
      fault.
We’re not saying design patterns are bad.
Quite the opposite.
Lithium implements many design patterns.
The Problem™
Some patterns only treat the symptoms,
instead of the cause.
Some examples:
Object dependencies.
“The principle of separating configuration from use.”
[CakePHP code]

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

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

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

    $prefixes = Router::prefixes()...
Configuration.
Everyone does it differently.
Sometimes in the same framework.
Sometimes in the same class.
function spam($emails) {

    $this->Email->replyTo = 'nigerian.prince@example.com';
    $this->Email->from = 'Desmond Ete...
Sucks
function spam($emails) {

    $this->Email->replyTo = 'nigerian.prince@example.com';
    $this->Email->from = 'Desmo...
Dependency injection.
A good idea!
... or is it?
[variables changed to protect the innocent.]
[variables changed to protect the innocent.]
                [... not really. It’s Symfony.]
class User {
    function __construct($storage) {
        $this->storage = $storage;
    }
}

$storage = new SessionStorag...
Show that object who’s boss.
class User {
    function __construct($storage) {
        $this->storage = $storage;
    }
}
...
Of course, we want this to abstract this.

Frameworks adore abstractions.
DI Container to the rescue!
class Container {
    static protected $shared = array();
    // ...

    public function getMailer() {
        if (isset(...
But now you want an abstraction to manage
the DI container.


Duh.
So you create a “Service Container”.
class Container extends sfServiceContainer {
    static protected $shared = array();

    protected function getMailTransp...
Of course, we now want to abstract the
crap out of the Service Container.


Double duh.
So lets use a Builder to configure the
Services.
Yeah!
require_once '/PATH/TO/sfServiceContainerAutoloader.php';
sfServiceContainerAutoloader::register();

$sc = new sfSer...
And for good measure, have our configurations
for Service Containers in XML files.
So....
So....
We have Dependency Injection.
So....
We have Dependency Injection.
Managed by a Service Container.
So....
We have Dependency Injection.
Managed by a Service Container.
Parametrized with XML data.
So....
We have Dependency Injection.
Managed by a Service Container.
Parametrized with XML data.
And the whole thing confi...
So....
We have Dependency Injection.
Managed by a Service Container.
Parametrized with XML data.
And the whole thing confi...
Anyone see what’s wrong here?
Many of these patterns were implemented in
Java, to solve language problems that PHP just
doesn’t have.
Try this some time...
The second part.
Everything
you know
is wrong.
The sun does not
revolve around OOP




        Galileo facing the Roman Inquistion
                     - Cristiano Banti...
The sun does not
revolve around OOP
                                            ...nevertheless,
                         ...
Dependency injection.
Dependency
Injection  =
class User {

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

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

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

    protected $_classes = array(
        'request' => 'lithiumnethttpRequest',
...
Coupling should be in
proportion to domain
relevance.
The problem of

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


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


  is evil,
$this->debug = 0;
If...
Configure::write('debug', 0);


  is evil,
$this->debug = 0;


  is the
of evil.
class Service {

    protected $_timeout = 30;

    public function send($method, $data = null, array $options = array()) ...
OO doesn’t make you
think (about state).
Design patterns.
ActiveRecord
                        Data Access Object

 Unit of Work
                              Dependency Injection
...
L E
F A
Design patterns
Design patterns
• Each pattern is only useful in a limited
  context
Design patterns
• Each pattern is only useful in a limited
  context

• Layering many design patterns on top of
  each oth...
Design patterns
• Each pattern is only useful in a limited
  context

• Layering many design patterns on top of
  each oth...
Tools do not mean...




...you can build a house.
The third part.
Lithium tries to suck less.
Un-broken solutions.
Aspect-Oriented Design
Aspect-Oriented Design
• Separation of concerns
Aspect-Oriented Design
• Separation of concerns
• Domain classes should not know or care about cross-
   cutting concerns
Aspect-Oriented Design
• Separation of concerns
• Domain classes should not know or care about cross-
   cutting concerns
...
Aspect-Oriented Design
• Separation of concerns
• Domain classes should not know or care about cross-
   cutting concerns
...
Aspect-Oriented Design
• Separation of concerns
• Domain classes should not know or care about cross-
   cutting concerns
...
Aspect-Oriented Design
• Separation of concerns
• Domain classes should not know or care about cross-
   cutting concerns
...
Functional Programming
Functional Programming

• Only possible when functions are first-class
  citizens
Functional Programming

• Only possible when functions are first-class
  citizens

• Referential transparency
Functional Programming

• Only possible when functions are first-class
  citizens

• Referential transparency
• Function p...
Referential transparency is not...
Referential transparency is not...


 $this                date()


           $_*
Referential transparency is not...


 $this                date()


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

• PHP 5.3+ only
Less Suck
• Draws on years of experience building web
  frameworks

• PHP 5.3+ only
• Doesn’t assume you’re stupid
Ways we suck less:
Consistency.
<?php

namespace applicationbar;

use lithiumutilString;
use lithiumutilCollection;

class Foo extends lithiumcoreObject {...
public function __construct(array $config = array())
<?php
    public function __construct(array $config = array())

names...
<?php

namespace applicationbar;

use lithiumutilString;
use lithiumutilCollection;

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

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

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

namespace applicationbar;

use lithiumutilString;
use lithiumutilCollection;

class Foo extends lithiumcoreObject {...
<?php

namespace applicationbar;

use lithiumutilString;
use lithiumutilCollection;

class Foo extends lithiumcoreObject {...
<?php

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

namespace applicationbar;



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

namespace applicationbar;

use lithiumutilString;
use lithiumutilCollection;

class Foo extends lithiumcoreObject {...
<?php

namespace applicationbar;

use lithiumutilString;
use lithiumutilCollection;

class Foo extends lithiumcoreObject {...
<?php

namespace applicationbar;

use lithiumutilString;
use lithiumutilCollection;

class Foo extends lithiumcoreObject {...
$options = array()
Keeps parameter lists short
             &
Makes class APIs more extensible
$config = array()
Same idea. But...!
Modifies class / object state.
Adaptable
Adaptable

   Auth

  Cache

  Catalog

Connections

  Logger

  Session
use lithiumsecurityAuth;

Auth::config(array(
    'customer' => array(
        'adapter' => 'Form',
        'model'   => '...
use lithiumstorageCache;

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

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

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

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

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

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

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

Cache::config(array(
    'default' => array(
        'development' => array(
            'adapte...
Works identically for all adapters.
If you remember nothing
else about configuration
state...
Immutability.
Set it and forget it.
Performance.
Zoom?
Zoom?
• Performance vs. speed of development is a
  series of trade-offs
Zoom?
• Performance vs. speed of development is a
  series of trade-offs

• Large-scale apps don’t use stock framework
  i...
Zoom?
• Performance vs. speed of development is a
  series of trade-offs

• Large-scale apps don’t use stock framework
  i...
Zoom!
Zoom!
• Choice is good
Zoom!
• Choice is good
• Use native extensions (PECL) whenever
  possible.
Zoom!
• Choice is good
• Use native extensions (PECL) whenever
  possible.

• Don’t like a class? Change it. At runtime.
Zoom!
• Choice is good
• Use native extensions (PECL) whenever
  possible.

• Don’t like a class? Change it. At runtime.
•...
Example
use appmodelsPost;
use lithiumactionResponse;
use lithiumnethttpRouter;



Router::connect('/frequent_api_call.json', arra...
Flexibility.
Lithium is the most
flexible framework.
         (yeah, we said it)
Most class dependencies
are dynamic.
class Service extends lithiumcoreObject {

    protected $_classes = array(
        'request' => 'lithiumnethttpRequest',
...
The Filter System:
Aspect-Oriented Design
for PHP.
Example: Caching & Logging
Example: Caching & Logging




          Post::find()
Example: Caching & Logging


      Logging


           Post::find()
Example: Caching & Logging

    Caching

       Logging


              Post::find()
Example: Caching & Logging

    Caching

       Logging


              Post::find()
Example: Caching & Logging

    Caching

       Logging


              Post::find()
Example: Caching & Logging

    Caching

       Logging


              Post::find()
Example: Caching & Logging

    Caching

       Logging


              Post::find()
Example: Caching & Logging

    Caching

       Logging


              Post::find()
Example: Caching & Logging

    Caching

       Logging


              Post::find()
Example: Caching & Logging

    Caching

       Logging


              Post::find()
Example: Caching & Logging

    Caching

       Logging


              Post::find()
use lithiumanalysisLogger;

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

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

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

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

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




Post                                                                                   Logg...
What about Observer?
What about Observer?

• Dependent on a centralized publish/
  subscribe system
What about Observer?

• Dependent on a centralized publish/
  subscribe system

• Extra layer of abstraction
What about Observer?

• Dependent on a centralized publish/
  subscribe system

• Extra layer of abstraction
• Fewer possi...
What about Observer?
What about Observer?


• Filters are self-contained and attach
  directly to objects
What about Observer?


• Filters are self-contained and attach
  directly to objects

• Direct and intuitive
Features: Everything is an adapter.

                       (well, almost)
Databases
• 1st-class support for document-oriented
  databases

• MongoDB & CouchDB: production ready
• Relational databa...
<?php

$post = Post::create(array(
    'title' => 'Ein bier, bitte',
    'body' => 'Was ist los mit dir?'
));
$post->save(...
This works on...
This works on...

• MongoDB
This works on...

• MongoDB
• CouchDB
This works on...

• MongoDB
• CouchDB
• MySQL
This works on...

• MongoDB
• CouchDB
• MySQL
• SQLite (almost)
MongoDB + CouchDB:
$post = Post::create(array(
    'title' => 'Ein bier, bitte',
    'body' => 'Was ist los mit dir?',
   ...
MongoDB:
$posts = Post::all(array('conditions' => array(
     'tags' => array('PHP', 'Bier'),
     'author.name' => 'Nate'...
Databases

• Adapter based, plugin aware
• Will ship with MySQL, SQLite
• SQL Server support via plugin
• Query API
The Query API

• Flexible data container
• Allows each backend data store to only worry
  about features it implements

• ...
The Query API

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

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

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

• Easily load & use libraries from other
  frameworks:

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

use Zend_Mail_Storage_Pop3;

class EmailController extends lithiumactionController {

    publi...
This has been a presentation by:

Nate Abele (@nateabele)

Joël Perras (@jperras)

            Sucks. But check it out any...
Tek-X: A Framework for People who Hate Frameworks - Lithium
Tek-X: A Framework for People who Hate Frameworks - Lithium
Tek-X: A Framework for People who Hate Frameworks - Lithium
Tek-X: A Framework for People who Hate Frameworks - Lithium
Tek-X: A Framework for People who Hate Frameworks - Lithium
Tek-X: A Framework for People who Hate Frameworks - Lithium
Tek-X: A Framework for People who Hate Frameworks - Lithium
Tek-X: A Framework for People who Hate Frameworks - Lithium
Tek-X: A Framework for People who Hate Frameworks - Lithium
Tek-X: A Framework for People who Hate Frameworks - Lithium
Tek-X: A Framework for People who Hate Frameworks - Lithium
Upcoming SlideShare
Loading in...5
×

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

5,827

Published on

All web application frameworks suck.

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

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

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

Published in: Technology
0 Comments
15 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total Views
5,827
On Slideshare
0
From Embeds
0
Number of Embeds
3
Actions
Shares
0
Downloads
101
Comments
0
Likes
15
Embeds 0
No embeds

No notes for slide
  • Introductions: team
  • Handoff: Nate
  • Intro / overview: Nate
  • Handoff: Jo&amp;#xEB;l














  • Handoff: Nate
    - They&amp;#x2019;re all about knowing when and how to apply them
    - Often, patterns derived from other languages make little to no sense in PHP


  • Handoff: Jo&amp;#xEB;l


































  • Handoff: Nate
    - Many GOF patterns were invented for Java to overcome deficiencies in the language itself
    - You don&amp;#x2019;t need a dependency injection container in PHP, it&amp;#x2019;s pointless
    - In languages with first-class functions (PHP as of 5.3), patterns like Command and Visitor are irrelevant









  • - Method calls and direction of invocation remain static
    - Each individual cross-cutting concern adds more boilerplate code
  • - Method calls and direction of invocation remain static
    - Each individual cross-cutting concern adds more boilerplate code
  • - Method calls and direction of invocation remain static
    - Each individual cross-cutting concern adds more boilerplate code
  • - Method calls and direction of invocation remain static
    - Each individual cross-cutting concern adds more boilerplate code
  • - Method calls and direction of invocation remain static
    - Each individual cross-cutting concern adds more boilerplate code









  • - Encapsulation notwithstanding, OO does not address the problem of state.
  • - No way to know the value of _timeout.
    - Mutable state is the source of most software defects
    - Computers are good at dynamic process flows. Humans aren&amp;#x2019;t.

    Handoff: Jo&amp;#xEB;l, talking about mutable state
  • - Easy != right.
  • Handoff: Nate

    - But it&amp;#x2019;s okay... because &amp;#x201C;Design Patterns&amp;#x201D; make everything okay
  • - Everyone has their favorites...
  • - Everyone has their favorites...
  • - Everyone has their favorites...
  • - Everyone has their favorites...
  • - Everyone has their favorites...
  • - Everyone has their favorites...
  • - Everyone has their favorites...
  • - Everyone has their favorites...
  • - Everyone has their favorites...
  • - Everyone has their favorites...
  • Design patterns are not a golden hammer







  • Handoff: Jo&amp;#xEB;l










  • Handoff: Nate
    - A referentially transparent function is one whose return value is only dependent on input parameters
  • Handoff: Nate
    - A referentially transparent function is one whose return value is only dependent on input parameters
  • Handoff: Nate
    - A referentially transparent function is one whose return value is only dependent on input parameters
  • Handoff: Nate
    - A referentially transparent function is one whose return value is only dependent on input parameters
  • Handoff: Nate
    - A referentially transparent function is one whose return value is only dependent on input parameters
  • Handoff: Nate
    - A referentially transparent function is one whose return value is only dependent on input parameters
  • - Learning other paradigms helps you in the same way as learning other languages.

    Handoff: Jo&amp;#xEB;l
    - Rant about programmers vs. typists

  • - This is different from assuming you won&amp;#x2019;t make mistakes
    - We don&amp;#x2019;t dumb down APIs or hide things. We just make it clear what you&amp;#x2019;re doing.

  • - This is different from assuming you won&amp;#x2019;t make mistakes
    - We don&amp;#x2019;t dumb down APIs or hide things. We just make it clear what you&amp;#x2019;re doing.

  • - This is different from assuming you won&amp;#x2019;t make mistakes
    - We don&amp;#x2019;t dumb down APIs or hide things. We just make it clear what you&amp;#x2019;re doing.


  • Handoff: Nate













































  • - Supported by a majority of methods in the framework
    - *Always* the last parameter
    - Any settings contained only modify behavior for the duration of the call



  • - Managing state is a very important idea which Lithium puts a lot of emphasis on.
    - Delineation between configuration state and request state
  • - Managing state is a very important idea which Lithium puts a lot of emphasis on.
    - Delineation between configuration state and request state
  • - Managing state is a very important idea which Lithium puts a lot of emphasis on.
    - Delineation between configuration state and request state












  • - This works because of the universal constructor
  • - This works because of the universal constructor






  • Handoff: Jo&amp;#xEB;l



  • - Use components from other libraries or frameworks, or hand-written classes
    - Filter system allows class interdependencies to be pushed to the surface, instead of buried within the framework
  • - Use components from other libraries or frameworks, or hand-written classes
    - Filter system allows class interdependencies to be pushed to the surface, instead of buried within the framework
  • - Use components from other libraries or frameworks, or hand-written classes
    - Filter system allows class interdependencies to be pushed to the surface, instead of buried within the framework
  • - Use components from other libraries or frameworks, or hand-written classes
    - Filter system allows class interdependencies to be pushed to the surface, instead of buried within the framework

  • - This is for when you have a frequently-used API call, or other request that you need to make extra fast
    - Bypasses the entire framework request cycle
  • Handoff: Nate














  • Point out parts that are `before` method execution, and `after` method execution.
  • Point out parts that are `before` method execution, and `after` method execution.
  • Point out parts that are `before` method execution, and `after` method execution.
  • Point out parts that are `before` method execution, and `after` method execution.





  • - either pass it around or make it global
  • - either pass it around or make it global
  • - either pass it around or make it global
  • - You can rip it out and things will still work
    - Relies on native language constructs, no micro-syntax
  • - You can rip it out and things will still work
    - Relies on native language constructs, no micro-syntax
  • Handoff: Jo&amp;#xEB;l







  • Handoff: Nate
    - Explain about SQL operators being translated to Mongo commands
    - Helps transitioning from relational DBs to Mongo
    - Aids in developing cross-functional plugins
  • Handoff: Jo&amp;#xEB;l
    Everything works because of the magical Query API
  • Handoff: Nate
  • Allows you to do &amp;#x201C;non-standard&amp;#x201D; stuff
  • Generates a SELECT statement with a subselect that finds all posts tagged foo, bar or baz

  • - For all your &amp;#x201C;enterprise&amp;#x201D; needs, we have an officially-maintained plugin for fully integrating with Doctrine 2
    - PDO abstraction layer with support for many RDBMS systems
    - Accessed through the same Model API as standard Lithium backends
  • Handoff: Jo&amp;#xEB;l




  • Transcript of "Tek-X: A Framework for People who Hate Frameworks - Lithium"

    1. 1. A Framework for People Who Hate Frameworks.
    2. 2. Find us on : http://joind.in/1592
    3. 3. A movement in 3 parts • Frameworks suck • Everything you know is wrong • Lithium tries to suck less
    4. 4. The first part.
    5. 5. Let’s get one thing out of the way:
    6. 6. Lithium sucks.
    7. 7. But that’s okay.
    8. 8. Because your framework sucks, too.
    9. 9. And yes, I mean:
    10. 10. And yes, I mean:
    11. 11. Why? (Besides the obvious attempt at being provocative)
    12. 12. Frameworks Suck
    13. 13. Frameworks Suck • Code you will never use.
    14. 14. Frameworks Suck • Code you will never use. • Complexity overhead.
    15. 15. Frameworks Suck • Code you will never use. • Complexity overhead. • You didn’t write it.
    16. 16. Also,
    17. 17. Also, Martin Fowler.
    18. 18. His name is the biggest, so it’s his fault.
    19. 19. His name is the biggest, so it’s his fault.
    20. 20. We’re not saying design patterns are bad. Quite the opposite.
    21. 21. Lithium implements many design patterns.
    22. 22. The Problem™
    23. 23. Some patterns only treat the symptoms, instead of the cause.
    24. 24. Some examples:
    25. 25. Object dependencies. “The principle of separating configuration from use.”
    26. 26. [CakePHP code] function initialize(&$controller, $settings = array()) { /* ...[snip]... */ $prefixes = Router::prefixes(); if (!empty($prefixes)) { foreach ($prefixes as $prefix) { /* ...[do something nasty to the global state]... */ } } if (Configure::read() > 0) { App::import('Debugger'); Debugger::checkSecurityKeys(); } }
    27. 27. Sucks. function initialize(&$controller, $settings = array()) { /* ...[snip]... */ $prefixes = Router::prefixes(); if (!empty($prefixes)) { foreach ($prefixes as $prefix) { /* ...[do something nasty to the global state]... */ } } if (Configure::read() > 0) { App::import('Debugger'); Debugger::checkSecurityKeys(); } }
    28. 28. Sucks. function initialize(&$controller, $settings = array()) { /* ...[snip]... */ $prefixes = Router::prefixes(); if (!empty($prefixes)) { foreach ($prefixes as $prefix) { /* ...[do something nasty to the global state]... */ } } if (Configure::read() > 0) { App::import('Debugger'); Debugger::checkSecurityKeys(); } } Sucks hard.
    29. 29. Configuration.
    30. 30. Everyone does it differently.
    31. 31. Sometimes in the same framework.
    32. 32. Sometimes in the same class.
    33. 33. function spam($emails) { $this->Email->replyTo = 'nigerian.prince@example.com'; $this->Email->from = 'Desmond Etete <nigerian.prince@example.com>'; $this->Email->template = 'mo_monies'; $this->Email->sendAs = 'annoying_html'; foreach ($emails as $email) { $this->Email->to = $email['address']; $this->Email->subject = "Good to you news I have {$email['name']}"; $this->Email->send(); } }
    34. 34. Sucks function spam($emails) { $this->Email->replyTo = 'nigerian.prince@example.com'; $this->Email->from = 'Desmond Etete <nigerian.prince@example.com>'; $this->Email->template = 'mo_monies'; $this->Email->sendAs = 'annoying_html'; foreach ($emails as $email) { $this->Email->to = $email['address']; $this->Email->subject = "Good to you news I have {$email['name']}"; $this->Email->send(); } }
    35. 35. Dependency injection.
    36. 36. A good idea!
    37. 37. ... or is it?
    38. 38. [variables changed to protect the innocent.]
    39. 39. [variables changed to protect the innocent.] [... not really. It’s Symfony.]
    40. 40. class User { function __construct($storage) { $this->storage = $storage; } } $storage = new SessionStorage('SESSION_ID'); $user = new User($storage);
    41. 41. Show that object who’s boss. class User { function __construct($storage) { $this->storage = $storage; } } $storage = new SessionStorage('SESSION_ID'); $user = new User($storage);
    42. 42. Of course, we want this to abstract this. Frameworks adore abstractions.
    43. 43. DI Container to the rescue!
    44. 44. class Container { static protected $shared = array(); // ... public function getMailer() { if (isset(self::$shared['mailer'])) { return self::$shared['mailer']; } $class = $this->parameters['mailer.class']; $mailer = new $class(); $mailer->setDefaultTransport($this->getMailTransport()); return self::$shared['mailer'] = $mailer; } }
    45. 45. But now you want an abstraction to manage the DI container. Duh.
    46. 46. So you create a “Service Container”.
    47. 47. class Container extends sfServiceContainer { static protected $shared = array(); protected function getMailTransportService() { return new Zend_Mail_Transport_Smtp('smtp.gmail.com', array( 'auth' => 'login', 'username' => $this['mailer.username'], 'password' => $this['mailer.password'], 'ssl' => 'ssl', 'port' => 465, )); } }
    48. 48. Of course, we now want to abstract the crap out of the Service Container. Double duh.
    49. 49. So lets use a Builder to configure the Services.
    50. 50. Yeah! require_once '/PATH/TO/sfServiceContainerAutoloader.php'; sfServiceContainerAutoloader::register(); $sc = new sfServiceContainerBuilder(); $sc-> register('mail.transport', 'Zend_Mail_Transport_Smtp')-> addArgument('smtp.gmail.com')-> addArgument(array( 'auth' => 'login', 'username' => '%mailer.username%', 'password' => '%mailer.password%', 'ssl' => 'ssl', 'port' => 465, ))-> setShared(false) ; $sc-> register('mailer', '%mailer.class%')-> addMethodCall('setDefaultTransport', array(new sfServiceReference('mail.transport'))) ;
    51. 51. And for good measure, have our configurations for Service Containers in XML files.
    52. 52. So....
    53. 53. So.... We have Dependency Injection.
    54. 54. So.... We have Dependency Injection. Managed by a Service Container.
    55. 55. So.... We have Dependency Injection. Managed by a Service Container. Parametrized with XML data.
    56. 56. So.... We have Dependency Injection. Managed by a Service Container. Parametrized with XML data. And the whole thing configured by a Builder.
    57. 57. So.... We have Dependency Injection. Managed by a Service Container. Parametrized with XML data. And the whole thing configured by a Builder. ...to fix one problem.
    58. 58. Anyone see what’s wrong here?
    59. 59. Many of these patterns were implemented in Java, to solve language problems that PHP just doesn’t have.
    60. 60. Try this some time...
    61. 61. The second part.
    62. 62. Everything you know is wrong.
    63. 63. The sun does not revolve around OOP Galileo facing the Roman Inquistion - Cristiano Banti (1857)
    64. 64. The sun does not revolve around OOP ...nevertheless, it moves. Galileo facing the Roman Inquistion - Cristiano Banti (1857)
    65. 65. Dependency injection.
    66. 66. Dependency Injection =
    67. 67. class User { public function create() { $logger = new Logger(); $logger->write('Creating a new user...'); $this->_doSomeInitialization(); $this->_databaseConnection->doATransaction($this)->create(); $logger->write('Finished creating user'); } } $user = new User(); $user->create();
    68. 68. class User { public function create() { $logger = new Logger(); $logger->write('Creating a new user...'); $this->_doSomeInitialization(); $this->_databaseConnection->doATransaction($this)->create(); $logger->write('Finished creating user'); } } $user = new User(); $user->create();
    69. 69. Dependency Injection
    70. 70. Dependency Injection • Fixes the problem of static dependencies
    71. 71. Dependency Injection • Fixes the problem of static dependencies • Ignores the problem of static relationships
    72. 72. Dependency Injection • Fixes the problem of static dependencies • Ignores the problem of static relationships • Same methods called on injected classes
    73. 73. Dependency Injection • Fixes the problem of static dependencies • Ignores the problem of static relationships • Same methods called on injected classes • No way to introduce new relationships
    74. 74. Dependency Injection • Fixes the problem of static dependencies • Ignores the problem of static relationships • Same methods called on injected classes • No way to introduce new relationships • Higher overhead, more boilerplate code
    75. 75. class Service extends lithiumcoreObject { protected $_classes = array( 'request' => 'lithiumnethttpRequest', 'response' => 'lithiumnethttpResponse', 'socket' => 'lithiumnetsocketContext' ); protected function _init() { $class = Libraries::locate('socket.util', $this->_classes['socket']); $this->_connection = new $class($this->_config); // ... } /* ...[snip]... */ public function send($method, $path = null, $data = null, array $options = array()) { /* ...[snip]... */ $response = $this->_connection->send($request, array('classes' => $this->_classes)); // ... } }
    76. 76. class Service extends lithiumcoreObject { protected $_classes = array( 'request' => 'lithiumnethttpRequest', 'response' => 'lithiumnethttpResponse', 'socket' => 'lithiumnetsocketContext' ); protected function _init() { $class = Libraries::locate('socket.util', $this->_classes['socket']); $this->_connection = new $class($this->_config); // ... } /* ...[snip]... */ public function send($method, $path = null, $data = null, array $options = array()) { /* ...[snip]... */ $response = $this->_connection->send($request, array('classes' => $this->_classes)); // ... } }
    77. 77. Coupling should be in proportion to domain relevance.
    78. 78. The problem of state.
    79. 79. If...
    80. 80. If... Configure::write('debug', 0);
    81. 81. If... Configure::write('debug', 0); is evil,
    82. 82. If... Configure::write('debug', 0); is evil, $this->debug = 0;
    83. 83. If... Configure::write('debug', 0); is evil, $this->debug = 0; is the
    84. 84. of evil.
    85. 85. class Service { protected $_timeout = 30; public function send($method, $data = null, array $options = array()) { // WTF does this do? $this->_prepare(); $response = $this->_connection->send($request, array( 'timeout' => $this->_timeout )); // ... } }
    86. 86. OO doesn’t make you think (about state).
    87. 87. Design patterns.
    88. 88. ActiveRecord Data Access Object Unit of Work Dependency Injection Registry Front Controller MVC Value Object Data Mapper Service Layer
    89. 89. L E F A
    90. 90. Design patterns
    91. 91. Design patterns • Each pattern is only useful in a limited context
    92. 92. Design patterns • Each pattern is only useful in a limited context • Layering many design patterns on top of each other often indicates poor design choices
    93. 93. Design patterns • Each pattern is only useful in a limited context • Layering many design patterns on top of each other often indicates poor design choices • Mis-application arises from trying to run before you can walk
    94. 94. Tools do not mean... ...you can build a house.
    95. 95. The third part.
    96. 96. Lithium tries to suck less.
    97. 97. Un-broken solutions.
    98. 98. Aspect-Oriented Design
    99. 99. Aspect-Oriented Design • Separation of concerns
    100. 100. Aspect-Oriented Design • Separation of concerns • Domain classes should not know or care about cross- cutting concerns
    101. 101. Aspect-Oriented Design • Separation of concerns • Domain classes should not know or care about cross- cutting concerns • Examples:
    102. 102. Aspect-Oriented Design • Separation of concerns • Domain classes should not know or care about cross- cutting concerns • Examples: • Caching
    103. 103. Aspect-Oriented Design • Separation of concerns • Domain classes should not know or care about cross- cutting concerns • Examples: • Caching • Logging
    104. 104. Aspect-Oriented Design • Separation of concerns • Domain classes should not know or care about cross- cutting concerns • Examples: • Caching • Logging • Access Control, etc.
    105. 105. Functional Programming
    106. 106. Functional Programming • Only possible when functions are first-class citizens
    107. 107. Functional Programming • Only possible when functions are first-class citizens • Referential transparency
    108. 108. Functional Programming • Only possible when functions are first-class citizens • Referential transparency • Function purity
    109. 109. Referential transparency is not...
    110. 110. Referential transparency is not... $this date() $_*
    111. 111. Referential transparency is not... $this date() $_*
    112. 112. These Are Not Design Patterns.
    113. 113. Less Suck
    114. 114. Less Suck • Draws on years of experience building web frameworks
    115. 115. Less Suck • Draws on years of experience building web frameworks • PHP 5.3+ only
    116. 116. Less Suck • Draws on years of experience building web frameworks • PHP 5.3+ only • Doesn’t assume you’re stupid
    117. 117. Ways we suck less:
    118. 118. Consistency.
    119. 119. <?php namespace applicationbar; use lithiumutilString; use lithiumutilCollection; class Foo extends lithiumcoreObject { protected $_classes = array( 'cache' => 'lithiumstorageCache', 'logger' => 'lithiumanalysisLogger' ); public function __construct(array $config = array()) { // ... } protected function _init() { // ... } } ?>
    120. 120. public function __construct(array $config = array()) <?php public function __construct(array $config = array()) namespace applicationbar; public function __construct(array $config = array()) use public function __construct(array $config = array()) lithiumutilString; use lithiumutilCollection; public function __construct(array $config = array()) class Foo extends lithiumcoreObject { public function __construct(array $config = array()) protected $_classes = array( public function'lithiumstorageCache', = array()) 'cache' => __construct(array $config 'logger' => 'lithiumanalysisLogger' public function __construct(array $config = array()) ); public function __construct(array $config = array()) { // ... } public function __construct(array $config = array()) protected function _init() { public function __construct(array $config = array()) // ... } public function __construct(array $config = array()) } public function __construct(array $config = array()) ?> public function __construct(array $config = array())
    121. 121. <?php namespace applicationbar; use lithiumutilString; use lithiumutilCollection; class Foo extends lithiumcoreObject { protected $_classes = array( 'cache' => 'lithiumstorageCache', 'logger' => 'lithiumanalysisLogger' ); public function __construct(array $config = array()) { // ... } protected function _init() { // ... } } ?>
    122. 122. <?php <?php namespace applicationbar; class Foo extends lithiumcoreObject use lithiumutilString; { use lithiumutilCollection; protected function _init() { class Foo extends lithiumcoreObject { $or = $some->highOverHead($operation); $or()->otherwise(HARD_TO_TEST)->code(); protected $_classes = array( 'cache' => 'lithiumstorageCache', } 'logger' => 'lithiumanalysisLogger' } ); ?> public function __construct(array $config = array()) { // ... } protected function _init() { 2 // ... } } ?>
    123. 123. <?php <?php namespace applicationbar; class Foo extends lithiumcoreObject use lithiumutilString; { use lithiumutilCollection; protected function _init() { class Foo extends lithiumcoreObject { $or = $some->highOverHead($operation); $or()->otherwise(HARD_TO_TEST)->code(); protected $_classes = array( 'cache' => 'lithiumstorageCache', } 'logger' => 'lithiumanalysisLogger' } ); ?> public function __construct(array $config = array()) { // ... } protected function _init() { 2 // ... } } $foo = new Foo(array('init' => false)); ?>
    124. 124. <?php namespace applicationbar; use lithiumutilString; use lithiumutilCollection; class Foo extends lithiumcoreObject { protected $_classes = array( 'cache' => 'lithiumstorageCache', 'logger' => 'lithiumanalysisLogger' ); public function __construct(array $config = array()) { // ... } protected function _init() { // ... } } ?>
    125. 125. <?php namespace applicationbar; use lithiumutilString; use lithiumutilCollection; class Foo extends lithiumcoreObject { protected $_classes = array( 'cache' => 'lithiumstorageCache', 'logger' => 'lithiumanalysisLogger' ); public function __construct(array $config = array()) { // ... } protected function _init() { // ... } } ?>
    126. 126. <?php namespace applicationbar; 3 use lithiumutilString; use lithiumutilCollection; new applicationbarFoo(); // loads app/bar/Foo.php class Foo extends lithiumcoreObject { protected $_classes = array( 'cache' => 'lithiumstorageCache', 'logger' => 'lithiumanalysisLogger' ); public function __construct(array $config = array()) { // ... } protected function _init() { // ... } } ?>
    127. 127. <?php namespace applicationbar; 4 use lithiumutilString; use lithiumutilCollection; class Foo extends lithiumcoreObject { protected $_classes = array( 'cache' => 'lithiumstorageCache', 'logger' => 'lithiumanalysisLogger' ); public function __construct(array $config = array()) { // ... } protected function _init() { // ... } } ?>
    128. 128. <?php namespace applicationbar; use lithiumutilString; use lithiumutilCollection; class Foo extends lithiumcoreObject { protected $_classes = array( 'cache' => 'lithiumstorageCache', 'logger' => 'lithiumanalysisLogger' 5 ); public function __construct(array $config = array()) { // ... } protected function _init() { // ... = $this->_classes['cache']; $cache } $cache::write(__CLASS__, $this->_someGeneratedValue()); } } } ?> ?>
    129. 129. <?php namespace applicationbar; use lithiumutilString; use lithiumutilCollection; class Foo extends lithiumcoreObject { protected $_classes = array( 'cache' => 'lithiumstorageCache', 'logger' => 'lithiumanalysisLogger' 5 ); $foo = new Foo(array('classes' => array( public function __construct(array $config = array()) { 'cache' => 'applicationextensionsCache' // ... ))); } protected function _init() { // ... = $this->_classes['cache']; $cache } $cache::write(__CLASS__, $this->_someGeneratedValue()); } } } ?> ?>
    130. 130. <?php namespace applicationbar; use lithiumutilString; use lithiumutilCollection; class Foo extends lithiumcoreObject { protected $_classes = array( 'cache' => 'lithiumstorageCache', 'logger' => 'lithiumanalysisLogger' ); public function __construct(array $config = array()) { // ... } protected function _init() { // ... = $this->_classes['cache']; $cache } $cache::write(__CLASS__, $this->_someGeneratedValue()); } } } ?> ?>
    131. 131. $options = array()
    132. 132. Keeps parameter lists short & Makes class APIs more extensible
    133. 133. $config = array()
    134. 134. Same idea. But...! Modifies class / object state.
    135. 135. Adaptable
    136. 136. Adaptable Auth Cache Catalog Connections Logger Session
    137. 137. use lithiumsecurityAuth; Auth::config(array( 'customer' => array( 'adapter' => 'Form', 'model' => 'Customer', 'fields' => array('email', 'password') ) ));
    138. 138. use lithiumstorageCache; Cache::config(array( 'local' => array('adapter' => 'Apc'), 'distributed' => array( 'adapter' => 'Memcached', 'servers' => array('127.0.0.1', 11211), ), 'default' => array('adapter' => 'File') ));
    139. 139. use lithiumdataConnections; Connections::config(array( 'old' => array( 'type' => 'database', 'adapter' => 'MySql', 'user' => 'bobby_tables', 'password' => '******', 'database' => 'my_app' ), 'new' => array( 'type' => 'MongoDb', 'database' => 'my_app' ) ));
    140. 140. use lithiumstorageSession; Session::config(array( 'cookie' => array( 'adapter' => 'Cookie', 'expire' => '+2 days' ), 'default' => array('adapter' => 'Php') ));
    141. 141. Also fun:
    142. 142. use lithiumstorageSession; Session::config(array( 'default' => array( 'adapter' => 'MyCustomAdapter', 'expires' => '+2 days', 'custom' => 'Whatevah!' ) ));
    143. 143. use lithiumstorageSession; Session::config(array( 'default' => array( 'adapter' => 'MyCustomAdapter', 'expires' => '+2 days', 'custom' => 'Whatevah!' ) ));
    144. 144. use lithiumstorageSession; Session::config(array( 'default' => array( 'adapter' => 'MyCustomAdapter', 'expires' => '+2 days', 'custom' => 'Whatevah!' ) )); public function __construct(array $config = array())
    145. 145. Multiple environments?
    146. 146. use lithiumstorageCache; Cache::config(array( 'default' => array( 'development' => array( 'adapter' => 'Apc' ), 'production' => array( 'adapter' => 'Memcached', 'servers' => array('127.0.0.1', 11211) ) ) ));
    147. 147. use lithiumstorageCache; Cache::config(array( 'default' => array( 'development' => array( 'adapter' => 'Apc' ), 'production' => array( 'adapter' => 'Memcached', 'servers' => array('127.0.0.1', 11211) ) ) ));
    148. 148. Works identically for all adapters.
    149. 149. If you remember nothing else about configuration state...
    150. 150. Immutability. Set it and forget it.
    151. 151. Performance.
    152. 152. Zoom?
    153. 153. Zoom? • Performance vs. speed of development is a series of trade-offs
    154. 154. Zoom? • Performance vs. speed of development is a series of trade-offs • Large-scale apps don’t use stock framework infrastructure, and that’s a good thing
    155. 155. Zoom? • Performance vs. speed of development is a series of trade-offs • Large-scale apps don’t use stock framework infrastructure, and that’s a good thing • A generalized framework will never be as fast as hand-tuned code
    156. 156. Zoom!
    157. 157. Zoom! • Choice is good
    158. 158. Zoom! • Choice is good • Use native extensions (PECL) whenever possible.
    159. 159. Zoom! • Choice is good • Use native extensions (PECL) whenever possible. • Don’t like a class? Change it. At runtime.
    160. 160. Zoom! • Choice is good • Use native extensions (PECL) whenever possible. • Don’t like a class? Change it. At runtime. • Profiled at every step of the way.
    161. 161. Example
    162. 162. use appmodelsPost; use lithiumactionResponse; use lithiumnethttpRouter; Router::connect('/frequent_api_call.json', array(), function($request) { return new Response(array( 'type' => 'application/json', 'body' => Post::recent()->to('json') )); });
    163. 163. Flexibility.
    164. 164. Lithium is the most flexible framework. (yeah, we said it)
    165. 165. Most class dependencies are dynamic.
    166. 166. class Service extends lithiumcoreObject { protected $_classes = array( 'request' => 'lithiumnethttpRequest', 'response' => 'lithiumnethttpResponse', 'socket' => 'lithiumnetsocketContext' ); } $service = new Service(array('classes' => array( 'socket' => 'mycustomSocket' )));
    167. 167. The Filter System: Aspect-Oriented Design for PHP.
    168. 168. Example: Caching & Logging
    169. 169. Example: Caching & Logging Post::find()
    170. 170. Example: Caching & Logging Logging Post::find()
    171. 171. Example: Caching & Logging Caching Logging Post::find()
    172. 172. Example: Caching & Logging Caching Logging Post::find()
    173. 173. Example: Caching & Logging Caching Logging Post::find()
    174. 174. Example: Caching & Logging Caching Logging Post::find()
    175. 175. Example: Caching & Logging Caching Logging Post::find()
    176. 176. Example: Caching & Logging Caching Logging Post::find()
    177. 177. Example: Caching & Logging Caching Logging Post::find()
    178. 178. Example: Caching & Logging Caching Logging Post::find()
    179. 179. Example: Caching & Logging Caching Logging Post::find()
    180. 180. use lithiumanalysisLogger; Post::applyFilter('find', function($self, $params, $chain) { // Generate the log message $conditions = $params['options']['conditions']; $message = 'Post query with constraint ' . var_export($conditions, true); Logger::write('info', $message); return $chain->next($self, $params, $chain); });
    181. 181. use lithiumanalysisLogger; Post::applyFilter('find', function($self, $params, $chain) { // Generate the log message $conditions = $params['options']['conditions']; $message = 'Post query with constraint ' . var_export($conditions, true); Logger::write('info', $message); return $chain->next($self, $params, $chain); });
    182. 182. use lithiumanalysisLogger; Post::applyFilter('find', function($self, $params, $chain) { // Generate the log message $conditions = $params['options']['conditions']; $message = 'Post query with constraint ' . var_export($conditions, true); Logger::write('info', $message); return $chain->next($self, $params, $chain); });
    183. 183. use lithiumanalysisLogger; Post::applyFilter('find', function($self, $params, $chain) { // Generate the log message $conditions = $params['options']['conditions']; $message = 'Post query with constraint ' . var_export($conditions, true); Logger::write('info', $message); return $chain->next($self, $params, $chain); });
    184. 184. use lithiumanalysisLogger; Post::applyFilter('find', function($self, $params, $chain) { // Generate the log message $conditions = $params['options']['conditions']; $message = 'Post query with constraint ' . var_export($conditions, true); Logger::write('info', $message); return $chain->next($self, $params, $chain); });
    185. 185. use lithiumanalysisLogger; Post Logger Post::applyFilter('find', function($self, $params, $chain) { // Generate the log message $conditions = $params['options']['conditions']; $message = 'Post query with constraint ' . var_export($conditions, true); Logger::write('info', $message); return $chain->next($self, $params, $chain); });
    186. 186. What about Observer?
    187. 187. What about Observer? • Dependent on a centralized publish/ subscribe system
    188. 188. What about Observer? • Dependent on a centralized publish/ subscribe system • Extra layer of abstraction
    189. 189. What about Observer? • Dependent on a centralized publish/ subscribe system • Extra layer of abstraction • Fewer possibilities
    190. 190. What about Observer?
    191. 191. What about Observer? • Filters are self-contained and attach directly to objects
    192. 192. What about Observer? • Filters are self-contained and attach directly to objects • Direct and intuitive
    193. 193. Features: Everything is an adapter. (well, almost)
    194. 194. Databases • 1st-class support for document-oriented databases • MongoDB & CouchDB: production ready • Relational databases in beta • Cassandra in the works, too
    195. 195. <?php $post = Post::create(array( 'title' => 'Ein bier, bitte', 'body' => 'Was ist los mit dir?' )); $post->save(); $post = Post::find($id); ?> <h2><?=$post->title; ?></h2> <p><?=$post->body; ?></p>
    196. 196. This works on...
    197. 197. This works on... • MongoDB
    198. 198. This works on... • MongoDB • CouchDB
    199. 199. This works on... • MongoDB • CouchDB • MySQL
    200. 200. This works on... • MongoDB • CouchDB • MySQL • SQLite (almost)
    201. 201. MongoDB + CouchDB: $post = Post::create(array( 'title' => 'Ein bier, bitte', 'body' => 'Was ist los mit dir?', 'tags' => array('PHP', 'Bier'), 'author' => array('name' => 'Nate') )); $post->save();
    202. 202. MongoDB: $posts = Post::all(array('conditions' => array( 'tags' => array('PHP', 'Bier'), 'author.name' => 'Nate' ))); // Translates to... db.posts.find({ tags : { $in : ['PHP', 'Bier'] }, 'author.name' : 'Nate' })
    203. 203. Databases • Adapter based, plugin aware • Will ship with MySQL, SQLite • SQL Server support via plugin • Query API
    204. 204. The Query API • Flexible data container • Allows each backend data store to only worry about features it implements • Keeps model API separate from backend data sources
    205. 205. The Query API $ages = User::all(array( 'group' => 'age', 'reduce' => 'function(obj, prev) { prev.count++; }', 'initial' => array('count' => 0) ));
    206. 206. The Query API $query = new Query(array( 'type' => 'read', 'model' => 'appmodelsPost', 'fields' => array('Post.title', 'Post.body'), 'conditions' => array('Post.id' => new Query(array( 'type' => 'read', 'fields' => array('post_id'), 'model' => 'appmodelsTagging', 'conditions' => array('Tag.name' => array('foo', 'bar', 'baz')), ))) ));
    207. 207. The Query API • Run simple queries via the Model API • Build your own complex queries with the Query API • Create your own adapter, or drop in a custom query optimizer
    208. 208. Btw, li3_doctrine
    209. 209. Plays nice with others • Easily load & use libraries from other frameworks: • Zend Framework, Solar, Symfony, PEAR, etc. • PSR-0 Class-loading standard
    210. 210. /* add the trunk */ Libraries::add("Zend", array( "prefix" => "Zend_", "includePath" => true, "bootstrap" => "Loader/Autoloader.php", "loader" => array("Zend_Loader_Autoloader", "autoload"), "transform" => function($class) { return str_replace("_", "/", $class) . ".php"; } )); /* add the incubator */ Libraries::add("Zend_Incubator", array( "prefix" => "Zend_", "includePath" => '/htdocs/libraries/Zend/incubator/library', "transform" => function($class) { return str_replace("_", "/", $class) . ".php"; } ));
    211. 211. namespace appcontrollers; use Zend_Mail_Storage_Pop3; class EmailController extends lithiumactionController { public function index() { $mail = new Zend_Mail_Storage_Pop3(array( 'host' => 'localhost', 'user' => 'test', 'password' => 'test' )); return compact('mail'); } }
    212. 212. This has been a presentation by: Nate Abele (@nateabele) Joël Perras (@jperras) Sucks. But check it out anyway.
    1. A particular slide catching your eye?

      Clipping is a handy way to collect important slides you want to go back to later.

    ×