Ending Rails Envy in PHP5
__call for fun and proﬁt!
Slides with notes at http://www.siverstripe.com/rails-envy/
Who am I? Oooh, a white slide!
The concepts discussed today have been used extensively
in our BSD-licensed CMS, in production use on around 100
SilverStripe is a BSD-licensed CMS focused on providing a productive development framework on top
of a CMS that it aimed squarely at content authors, not developers.
It’s relatively new (ﬁrst open-sourced November 2006) but our developer community is growing well
and we’re always looking for new developers.
Why the Envy?
Gets the job done, mostly
platform with lots of cool stuff
The language people love to hate Very trendy
A variety of hosting options Harder to Host
Good NZ government
Too new for government
acceptance (They like OSS!)
Rails was one of the main options I considered when developing SilverStripe. However, there were
other advantages to PHP that trumped it - in short, Rails would cut down our user-base too much.
What to do?
• Use PHP5
• Build frameworks to make it productive, and
maybe even trendy.
We decided to use PHP, but focus on building a productive development framework, a similar goal to
the Rails team.
THIS IS NOT A LANGUAGE WAR
I know very little about the details of Rails vs.
what is presented today, in fact, I’m pretty sure
that my understanding of the beneﬁts of using
Rails is pretty naïve.
I just thought the title would be edgy.
(besides, I still envy Rails. Just a little.)
The possiblities are endless with this handy little method...
It lets us create “magic methods” are are implicitly deﬁned.
The __call() method of a class will handle any unrecognised method call. Similar methods for
properties, __get() and __set(), can be used to create “magic properties” too.
The ﬁrst use-case
The way that I want it to be:
An ORM is a great way of wrapping access to a database into a nice package. We wanted to add
relationship information to our data classes, and have these accessible by set classes that implement
Iterator, but need more
The code shown in the presentation is criminally oversimpliﬁed. In particular, in a real implementation
we would want to use For a more complete implementation, the SilverStripe codebase is a good place
Here we have deﬁned a __call() method on the DataObject that
• __call contains a lot of program-speciﬁc
• The if / elseif construct lower performance as
• Won’t scale well to more kinds of magic
Map of method handlers
Deﬁne methods makes a number of calls to $this->addWrapperMethod(). This is how you deﬁne your
__call() implemented in Object
The addWrapperMethod() method, and the __call() handler, are deﬁned in a generic Object class.
* addWrapperMethod() adds to Object::$extraMethods
* __call() interrogates Object::$extraMethods to know what to do.
Once per class, not once per object, for performance.
Calling deﬁneMethods() for every object can get really slow. You typically have to instanciate a large
number of DataObjects to generate a single page.
Adding Versioning to DataObject
Note: Criminally Oversimpliﬁed
This is a very simple illustration of how aggregation might be used to help implement versioning &
staging. In this case, we’ve just used it to add publish() and rollback().
In a real implementation, you need a way of modifying the code for reading/writing DataObjects to
actually change the data model. But that is beyond the scope of this talk.
What can deﬁneMethods do?
• addWrapperMethod(‘Products’, ‘getComponents’);
• addMethodsFrom(‘extensions’, ‘Versioned’);
• Other possible
• BUT: the more we have, the slower things get... So be
In addition to addWrapperMethod(), it’s worth adding addMethodsFrom()
Performance in the __call() method is crucial because it will be used so often by your application.
Step 1: Implement addMethodsFrom() that will be called by deﬁneMethod(). It should add more stu! to
Step 2: Update __call() to handle the new data that we’ve put into Object::$extraMethods
Note: this isn’t a particularly well optimised sample. Improving on it is left as an exercise for the
We can now use the addMethodsFrom() methods to implement aggregation, by updating
deﬁneMethods() in the Object class.
• I’m Lazy
• I’m Picky
• (I like over-engineering?)
• Copy of the slides
• Sample code
• More notes
• Or, email me on firstname.lastname@example.org
Thank you for listening!