User login

DrupalConDC Patterns and Object Orientation in Drupal Code

Drupal patterns

hook_menu, hook_theme, hook_views_data

In a sense, hook_menu and hook_form are declaritive programming.

Action hooks

hook_nodeapi, hook_user
See: passive observer
Use: Event-driven programming

Visitor hooks

hook_form_alter, hook_nodeapi, hook_user
Passive observer, Visitor
Use: Non-inheritance modification

Gets around the fact inheritance is one way.

Object "hook"

sort-of, kind of a way of doing inheritance. Couldn't figure out what this

Nodes

Same "object", different behavior based on data
Naked object
Use: Horizontal object extension pattern
See: State pattern

Very useful for adding things on.

All this applies to functional code as well as OO.

Render array

FAPI and derivatives
Use: UI toolkit
See: Visitor, Strategy patterns

Common OOP patterns

These aren't an exact science, these names are conventions but not declared by some on-high

Observer

Any hook is a passive observer pattern. Including module_invoke_all
Active observer pattern does not make sense in PHP. You'd have to register

CHX: Scince module depends on registry now, I suppose it's active now.

We build up a query

Crell: In a sense we are doing active observer. Still no OO code!

Someone who has read a

something happens, something else gets notified and does something in response

Visitor

This one is weird. Couldn't fit OO syntax for this on a single slide.
Any hook_node_alter is a visitor

have method
pass your state object to your action object which then does something to the state object

we already do it everywhere
every hook is a visitor pattern

Factory

There's not much Factory use in Drupal. Now in the database layer.

You want an object to talk to you in a certain way. So you tell the factory-- build me an object that meets this interface, I don't care which one.

Classic example-- pass the product, and the customer (so package size and weight, customer location)
Check out UPS, Post service

<?php
class MyFactory {
  function getShipper($product) {
    foreach ($this->shippers as $shipper) {
       // in here, find the cheapest
    }
  }
}
  $factory->getShipper($product, $customer);
  $shipper->ship($product, $customer);
?>

This makes a lot more sense in OO. Interface in object-oriented code fits this model.

Chx: Fields in core uses this

Facade

Simpler, unified

In a sense, node_load is a facade.

Singleton

Incredibly important pattern to understand.
In a sense a facade for a global.

Global access to unique object.
Allows lazy-loading.

You always have that one object and can access it from anywhere.

<?php
class DB {
  protected $instance;
  private __construct() {} // private so you can never instatiate it yourself!
  public static getInstance()

}
?>

deep inside db_query there is a Singleton doing this

Very simple and very easy to use and potentially dangerous.

Andy: Session a singleton?

Yes. Any global variable, $_

Objects a la Crell

"There is no problem in computer science that cannot be solved by another layer of indirection."

There are careers built on talking about object orientation. I won't go into that level of detail.

Just the above. (Except performance. Although even that sometimes you can help performance on a macro scale.)

Take a step back. Give yourself another level to work in.

  • Powerful
  • Sometimes expensive (sometimes cheap)
  • Harder to read/debug

on the micro scale, more CPU cycles. On the macro, can intelligently bypass

Powerful but has conceptual overhead.

OO is syntactic indirection.

Easier to read than function indirection:

<?php
$foo->bar($a, $b, $c);
// vs
call_user_func_array($foo . '_bar', array($a, $b, $c, $last));
?>

There are more people who understand the above line than the lower-- than are involved with Drupal period. I didn't know about call_user_func_array until I got involved with Drupal.

If you know OO in Java, or even C++, it's close to OO in PHP. If you know OO in actionscript, easier.

There's a broader base of people

Cheaper if used wisely

Powerful.

Every hook theme call is a

I'm not saying replace every call_user_func_array with

Indirection is testable

  • Mock objects
  • Lie to your code
  • Dependency injection
    • higher setup cost in code
    • much easier to reuse
    • much easier to test
  • Singletons are unmockable/untestable (something else somewhere may have played with the global... we have to get around this all the time in simpletest in drupal)

class God {
function hackCore() {
$kitten = new Kitten();
$lightning = new Lightning()

So you pass in a fake kitten when testing.

Singletons

    anything shared that cannot be faked is a singleton
    • Global variables
    • Static variables
    • Static methods
    • Functions

    Cannot be unit tested
    Makes repurposing harder

PROBLEM: All functions are singletons. Every time you call db_query, variable_get, node_load.
Drupal is a

It's very easy to write spaghetti in OO code as well as functional code.

Drupal is a pile of
then you want to change one thing

OO Traps

Indirection requires a bridge. You need to know in advance that you need a kitten and a lightening bolt.
If you want to modify God to only kill kittens on Tuesday afternoons, you can't communicate that.
Whereas with a singleton, you can always call the function, get the global, and go.
So bad OO frameworks
Imagine a widget in CCK where you could never access the database. You could do a lot, but you could dig yourself into a deep hole.
You can always climb out of a hole with a singleton, but they have own issues.

If no object, no access.

But in PHP:
Object bootstrap more expensive than functions
Objects autoload (PHP will find it-- but with D7 registry we have already told it where to find everything) Avoid parsing code, and par
Stack calls not cheap
http://www.garfieldtech.com/blog/magic-benchmarks

In Java everything runs all the time, so the initialization time not so bad.
In PHP every request is a new

This is why most heavy OO frameworks, this can take a while (200 milliseconds) even if you have opcode cache.

Drupal right now is incredibly deep stack -- look at drupal_render in a debugger. (ooh, i have. yup. I'm so used to that in Drupal)

Good API design is about affordance (concept from product design to make things "easy and natural to do" -- like the doors in this room, has a push-bar and they push outward -- good.

Now we have hooks.

Hooks are awesome, hooks are great.

Hammers are for nails.
Screwdrivers are for screws.

If we use objects, we now have an approach that

Whither objects?

Singleton behavior: Function
Locally-shared data: Object
Easy indirection: Object and do dependency injection
Observer/Visitor: Hook (mostly, no reason to introduce objects for this)
Factory: Function returning objects - could do factories and objects. You could make a Factory Factory-- then you have Ruby on Rails. Don't over-engineer your OO code.

For most objects a function is sufficient.

Common problem, common approach, lowers barrier of entry for new programmers.

OOP Dos.

Always have an interface
easier for you to interface
makes it easier to hack your way out of a hole
I don't like how foo works, it's already bee

Don't overdo it on inheritance-- it is high on conceptual overhead. Go overboard, and you end up with Java.

Always type hint on an Interface (NOT on the class itself)
Makes it possible to hack your way out of a whole

Centralize "new"
- In the database. you are using objects all the time, and usually know this, but you done't have to type new. Ever.
- testability, maintainability, target optimization

Never be private
Unnecessary limitations. [Amen! Damn you, previous developer!]

Avoid statics
make code harder to understand and test
static == global == singleton
No inheritance in PHP < 5.3 (not out yet)

Beware over-engineering
OOP is a very big shovel (if procedural is a shovel, OOP is a backhoe)

Do not fear the arrow ->
An elegant weapon for a more civilized age

Further reading
Gang of Four
Design Patterns: Elements of Reusable Object-Oriented Software

Martin Fowler (one of the people who has written the most on the subject)

Never use a private variable?
they are there for a reason when you are doing super-high-level

private by definition means: I don't trust anyone.
We're drupal developers

protected is perfectly legitimate
because a

getters and setters like javabeans are over-engineered

PHPs magic methods for every class-- __get, __set, __call (for a method that doesn't exist)

Extenders in Drupal 7 database layer

Centralizing new: a good example is JQuery (even though JavaScript uses a different object module than PHP). The database layer in fact shares a lot of concepts with JQuery.

Q: Object orientation for dynamic loading of node data -- basic node data loaded, and then get the data when requested. Lazy loading.

Crell: I've been pushing for that for a year. I think that's a great idea, there are a lot of pitfalls to it.

Background on that:
http://groups.drupal.org/node/8001

Avoiding use of static variables? It's physically implemented as a global variable in PHP. There are good use cases for it. And there are dangers of digging

Remember to unset it.

Resolution

Comments

Post new comment

The content of this field is kept private and will not be shown publicly.
  • You may post code using <code>...</code> (generic) or <?php ... ?> (highlighted PHP) tags.
  • You can use Markdown syntax to format and style the text. Also see Markdown Extra for tables, footnotes, and more.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Allowed HTML tags: <a> <em> <strong> <cite> <code> <ul> <ol> <li> <dl> <dt> <dd> <img> <blockquote> <small> <h2> <h3> <h4> <h5> <h6> <sub> <sup> <p> <br> <strike> <table> <tr> <td> <thead> <th> <tbody> <tt> <output>
  • Lines and paragraphs break automatically.

More information about formatting options

By submitting this form, you accept the Mollom privacy policy.