tags:

views:

572

answers:

4

I have a class with static methods, and I would like to intercept method calls before the methods are called.

So if I call

$model = DataMapper::getById(12345);

then I want some method in DataMapper to be called before this method is called, and then optionally this intercepting method can subsequently call self::getById(12345). Is there a way to do this?

I am implementing Memcache on my server, so that is why I want to intercept method calls. I don't want the static methods to query the database if models are already cached, and I also don't want to have to modify hundreds of different mapper methods, redundantly, to support memcache.

I am running PHP 5.2.6.

+1  A: 

This'd do the job: http://stackoverflow.com/questions/1071894/triggering-call-in-php-even-when-method-exists

Just declare your static methods as protected so they're inaccessible outside the class and get the __callStatic() magic method to invoke them.

Edit: oops, you'll be needing 5.3 to do it...

brianreavis
Oh, shucks. I forgot I had already asked this exact same question. *ducks* Thanks.
Chad Johnson
Hahah, oh wow... I didn't even notice that was you. Ironic.
brianreavis
A: 

I guess you could have created some magic with runkit, but you would need to compile the extension from cvs, since the latest version does not support 5.2.x

Example:

<?php

/* Orig code */
class DataMapper {
  static public function getById($value) {
    echo "I'm " . __CLASS__ . "\n";
  }
}


/* New Cache Mapper */
class DataMapper_Cache {
  static public function getById($value) {
    echo "I'm " . __CLASS__ . "\n";
  }
}


// Running before rename and adopt
DataMapper::getById(12345);

// Do the renaming and adopt
runkit_method_rename('DataMapper', 'getById', 'getById_old');
runkit_class_adopt('DataMapper','DataMapper_Cache');

// Run the same code..
DataMapper::getById(12345);

?>

Output:
  I'm DataMapper
  I'm DataMapper_Cache
goddva
So, this is just another PHP extension like any other? What kind of performance hit would I take if I were to use runkit in this manner?
Chad Johnson
See example added to my answer...
goddva
I have not seen any speed performance issues - However, I don't have any runkit code in production.. You should use runkit in cases where you don't have any options.. :)
goddva
+1  A: 

This is one example where you might want to consider ditching static methods in favor of polymorphism. If your data-mapper was an interface then you could have two implementations, one for the database and one for memcache:

interface DataMapper {
    public function getById($id);
    // other data mapper methods
}

class DataMapper_DB implements DataMapper {

    public function getById($id) {
        // retrieve from db
    }
    // other methods
}

class DataMapper_Memcache implements DataMapper {

    private $db;        

    public function __construct(DataMapper_DB $db, $host, ...) {
        $this->db = $db;
        // other set up
    }

    public function getById($id) {

        // if in memcache return that

        // else 
        $record = $this->db->getById($id);

        // add record to memcache

        return $record
    }
    //other methods
}
rojoca
+1  A: 

I just came up with a way to intercept method calls in PHP - Check it out.

It's just a basic example, and classes that want to be interceptible have to "opt in" - you can't interfere with the behavior of classes that don't implement the two magic methods.

I don't know if this meets your needs - but this pattern can be implemented without code generation or runtime bytecode hacks, and that's gotta be a plus ;-)

mindplay.dk