Skip to main content
  • Home
  • Womblog
  • Contact
  • Login
By Wombats logo By Wombats
You should see them code...
Home

Defining Classes in Modules "Leanly"

In:
  • Drupal
  • Programming
  • Ubercart
29Sep2008

Through some personal development efforts and work on Ubercart, I've been confronted lately with how to best use classes in my Drupal modules. For this post, I'm really just curious how others are defining and using classes in their modules and if the "lean" approach I've outlined below is reasonable. (i.e. Is what I'm doing to ensure Drupal loads less code even worthwhile?)

The general idea I'm following is that I want as little unnecessary code to be in my .module file as possible. To that end, I'm following Drupal's core practice of using .admin.inc and .pages.inc files to hold page callbacks. I believe Views does something similar (coupled with some auto-inclusion), and we're using this in some places in Ubercart.

Now I'm doing some experimental development on making something like a shopping cart a class. I've used a method in my FreshBooks module (w.i.p.) where I define the class in a .class.inc. So, in the module I obviously can't use $object = new ClassName();, but instead I create a small function in the .module that includes the .class.inc and passes the arguments on to the constructor when it creates and returns the object. From there on out it's using an object like normal.

This means I can have a thousand lines of code tucked neatly into a .class.inc file that needn't be loaded and parsed on every page request... just those where the class is actually needed. It also helps me personally to isolate all this code in a single file when developing/debugging. It seems like this segmentation would provide a performance boost, but I don't know what sort of tests to do to establish that. Should I just assume less code loaded is better? What about the require_once() bits from Rasmus' Drupalcon session?

What about possible pitfalls? Another module can't simply extend my class right off, but it could just as easily include the .class.inc before trying to do so. Would the hassle and file access outweigh the memory difference? Thoughts?

  • Add new comment

Comments

#1 Sounds really similar to the

Submitted by Mikey P (not verified) on Mon, 09/29/2008 - 22:03.

Sounds really similar to the Drupal 7 code registry. Very close in fact. I would recommend taking a look at how they are doing things as it supports autoload for classes, yet adds a simple wrapper function (very similar to yours) for including files if they have a function defined in them (which it auto parses, and keeps a cache of all available functions and which files they are found in)

Seriously if you don't take a look at how the D7 code registry is doing this, you'll probably end up fighting it when you go to upgrade Ubercart to Drupal 7.x.

  • reply

#2 Oh, a subject near and dear

Submitted by Larry Garfield (not verified) on Mon, 09/29/2008 - 22:15.

Oh, a subject near and dear to me... Smiling

Splitting off code you rarely need is absolutely a boost. That's why we went to all that trouble in Drupal 6, and why Views 2 recently had a very late-in-the-game API change. Loading code can be quite expensive if you don't need it. For benchmarks, see the tests I did to Drupal 6 earlier this year.

Now in Drupal 6, that doesn't really affect the class-vs-function question. PHP 5 has an autoload mechanism but it's not used in Drupal 6, and haphazardly adding it piecemeal by module will probably just hurt overall performance. For that, the sort of "stub loader" you list here is probably the best bet.

Alternatively, if you have a function or class that you're calling indirectly as a callback you can absolutely emulate the page callback mechanism that the menu and theme systems use. You need a "registry" hook to do that, but can then specify a file and file path key and have your own code do the same sort of include that the menu system does. That can be a very powerful idiom to follow, and it's a direction that I think Drupal in general needs to move even further.

For Drupal 7, the new code registry neatly solves the problem for us. Classes can live literally anywhere and will be magically loaded when you use them with no further effort on your part. The system will even cache that fact and pre-load the file on the next request for the same page. For functions you need to call drupal_function_exists(), which will behave exactly like function_exists() but lazy-load the necessary file if appropriate.

The other thing to bear in mind, though, is that for Drupal 6 nothing but menu and theme know about split files (unless it's your own API), so you can't move hooks or other code that's intended to be called by other parts of the system directly out of the .module file. (Well, you can move them to an include file that is always included, but that's just for organizational purposes.) Also, you don't want to break things out too far. Every disk hit to load a file is more expensive IO that burns CPU. If you hit the disk for every function you'll end up hurting performance instead. Where the trade-off is depends on what you're doing, and takes some experimentation.

I hope that helps some. Smiling Ubercart is an especially large module so finding good ways to split things off should be a huge help. I especially recommend trying to move toward a registry style API (a la menu and theme) that uses file and file path keys for Drupal 6. That makes it much easier for other modules to plug into yours (alter hooks are dead simple at that point) and gives you a clean and standard way to avoid loading too much code at once.

Cheers!

  • reply

#3 Larry... you rock. Thanks

Submitted by Ryan on Mon, 09/29/2008 - 23:35.

Larry... you rock. Cool

Thanks for the lengthy response and pointers (and thanks to Mikey P)! Will certainly mull this over. My ideal would only be doing what is necessary in D6 to provide performance enhancements without totally recreating custom features that will be in D7. If we hit the "stepping stone" just right, we should simply be able to move from the intermediate solution to the core solution when we update to D7.

One example is the little drupal_js() wrapper I used in Ubercart on D5 to add a query string onto sensitive .js files handling preview stuff in checkout. That's now a core feature in D6 and we should either be able to remove or modify the wrapper as need be and ideally improve the core offering with some of our own thoughts.

Anyways, I'm excited to hear about this work in the D7 area! My hats off to all you folks making it easier for contributors to contribute. Eye

  • reply

#4 A good module for D6 might

Submitted by Matt Farina (not verified) on Tue, 09/30/2008 - 06:25.

A good module for D6 might be an autoloader module. It would require PHP 5.1.2 or newer. It could provide a hook_autoload_register where an array of functions and classes are listed with their corresponding file locations. Then the spl_autoloader in PHP could be used for classes and drupal_function_exists could be used for functions and mimic the D7 core implementation.

Just a thought.

  • reply

#5 Possibly. I've debated

Submitted by Larry Garfield (not verified) on Tue, 09/30/2008 - 12:07.

Possibly.

I've debated whether or not that would be useful. I'd been thinking no, as backporting the registry would be highly non-trivial. However, making it a required registry hook where you define your classes and their files would be simplier, and reasonably straightforward to do.

Ryan, and others, would that be of use to you?

  • reply

#6 Hmm.. not sure what you mean

Submitted by Ryan on Tue, 09/30/2008 - 14:33.

Hmm.. not sure what you mean by making it a required hook. Puzzled Are you talking about D7 there or a contributed D6 module?

  • reply

#7 No, a D6 module where you'd

Submitted by Larry Garfield (not verified) on Tue, 09/30/2008 - 16:18.

No, a D6 module where you'd manually register the location of a class. Something like:

<?php
function mymodule_autoload() {

 
$classes['MyClassName'] = 'myfile.class.inc';

  return
$classes;
}
?>

And then this fictional autoload module would register the following autoload function:

<?php
function autoload_autoloader($class) {
 
$files = module_invoke_all('autoload');
  if (!empty(
$files[$class])) {
    require
$files[$class];
  }
}
?>

(OK, it would be more complex than that for performance reasons but that's the general idea.)

So if you actively register your class, then PHP5's autoloading magic happens without any further intervention on your part.

Actually, thinking about it, it's probably not that difficult a module to write. Hmmm...

  • reply

#8 Does the autoloader take

Submitted by Ryan on Wed, 10/01/2008 - 09:24.

Does the autoloader take care of multiple requires? i.e., will it try to require the same class file multiple times? (If so, should we use a static variable to track inclusion instead of using require_once, or have I already forgotten everything Rasmus said about require_once and system calls? Sticking out tongue )

I like the idea in general and could use it for both Ubercart and FreshBooks. However, I then get into having YAD - yet another dependency, while a lot of our D6 work has been to remove those. I know it's not possible to remove them all, and perhaps it wouldn't be as big a deal since I know this dependency will be removed as soon as we update to D7.

So... I'd support it and most likely use it. I'm already requiring Token, why not another low level performance enhancing module? Smiling

  • reply

#9 Does the autoloader take

Submitted by Larry Garfield (not verified) on Wed, 10/01/2008 - 15:37.

Does the autoloader take care of multiple requires? i.e., will it try to require the same class file multiple times?

Nope, that's the beauty of PHP5's autoloader.

<?php
$foo
= new Foo();
?>

If class Foo has been defined, it is used as is. If it has not been defined, the autoload functions fire and then the engine tries again. If the class is now defined, it is used. If not, fatal error. Thus the second time you call that line the class is already defined and the autoload functions are short circuited.

Excuse me a moment while I go shoot myself for adding another module to my todo list... Sticking out tongue

  • reply

#10 lol - Well, say the word and

Submitted by Ryan on Wed, 10/01/2008 - 15:59.

lol - Well, say the word and I'll co-maintain it with you (or at least make sure some of our Ubercart dev time among the team is devoted toward keeping that module sane). Good thing is you won't have to worry about updating it to D7. Cool

  • reply
Entries (RSS)

About Ryan

Ryan Szrama is a Drupal e-commerce developer for Commerce Guys, focusing on Drupal Commerce. Aside from his work, he loves his wife, his daughter, his church, and a good book over a white mocha.

You can find him elsewhere online at:

Commerce Guys logo Druplicon Ubershield Twitter

Bible Books Christianity Drupal Drupalcon drupalconszeged2008 Family Food Homeownership Marriage Ministry Music Programming Space Ubercamp Ubercart Vacation Work
more tags
Creative Commons License © 2009 Ryan Szrama

Drupal port by 3rdWorld : Created by Design Disease