PHP function references

7 October 2008

It's amazing, the random pieces of code you traverse to do the simplest stuff in your job.

I recently had to figure out how to pass functions as variables to other functions in PHP. Aside from figuring out how to properly Google the subject, it's really pretty simple; you just represent the function's name as a string within a variable, then call the variable. Since that's a little convoluted, think of it like so:


function testFunction()
{
  print 'Function works!';
}

$_function = 'testFunction';
$_function();

// prints "Function works!".

Nice and easy. Okay, honestly, it's pretty ugly. Perl and C both do it better, in my opinion; I'm definitely uneasy about metaprogramming through strings, but this certainly works.

To the situation at hand. We've got a homebrew DB wrapper, to allow us to swap in different DB drivers at a moment's notice (admittedly, there are existing open-source solutions for this, but they were taking longer to implement than a write-from-scratch approach); this seems silly, but we're occasionally flipping from Oracle to MySQL to ATOMIX (don't ask) to WhateverElse, and eventually it was worth our while to abstract the problem. Now, if you have a DB wrapper, you're going to have errors. You can — and probably should — handle errors by throwing exceptions, but if you're writing a homebrew wrapper, why not take care of the error handling from within the encapsulating class?

Sure, it sounds dubious, but bear with me here. What if you generally want one kind of error handling, but occasionally something else? Sounds like a parameter to the wrapper, with some sort of default; otherwise, you're either:

Neither of which is cool, in my book. So let's check out how to implement this. In a more generic sense — keep it really easy — we can try something like:


class OuterFunctionClass
{
  function __construct( $function )
  {
    $this->function = $function;
  }

  function runFunction()
  {
    $this->function();
  }
}

function testFunction()
{
  print 'Function works!';
}

$ofc = new OuterFunctionClass('testFunction');
$ofc->runFunction();

Alright, we have a class that takes a function as a parameter, and the ability to call that function via runFunction. We pass it a function that should print "Function works!", and we get...

Fatal error: Call to undefined method OuterFunctionClass::function() in function.reference.php on line 10

Damn! So, what can we do here? At this point, it was all guesswork, but under the pretense that maybe it had something to do with the scope of $this, it's not unreasonable to flip this out for:


class OuterFunctionClass
{
  function __construct( $function )
  {
    $this->function = $function;
  }

  function runFunction()
  {
    $_function = $this->function;
    $_function();
  }
}

function testFunction()
{
  print 'Function works!';
}

$ofc = new OuterFunctionClass('testFunction');
$ofc->runFunction();

Which results in...

Function works!

Win!

Long story short: if you want to pass functions to class members, you'll need to put the function's name (as a string) into a temporary variable (possibly to reset scope?) if you actually want to execute it. I consider this a PHP bug, but I'm willing to be corrected.

Included \(\LaTeX\) graphics are generated at LaTeX to png or by MathJax.

contemporary entries

comments

there are no comments on this post