With the Alpha3 Release of PHP 5.3 out and about it’s looking like the much anticipated late static binding functionality is starting to solidify. Once I finally got PHP 5.3 running (see my previous post) I worked through creating an ActiveRecord-like class to see if I could make a PHP object do act in a similar way similar to Ruby’s Active Record.
Lets see where I ended up…
class ActiveRecord { public static function find($id){ $called_class = get_called_class(); //a new function as of a2 return new $called_class(); } //A derived example to show the late static binding in action public static function testLSB(){ return static::$table; } } class BasicModel extends ActiveRecord { public static $table = 'basicModel'; } //Output examples echo get_class(BasicModel::Find(1)); //Output - BasicModel echo BasicModel::testLSB(); //Output - basicModel echo BasicModel::$table; //Output - basicModel
First place I saw the get_called_class() method was on the PHP Late Static Binding Manual Page
The above example is celarly a bit derived, but I’m quite excited at where this is heading. I still have a bit of work to do if I want to end up accessing all my models in the Rails/Active Record fashion.
$user = new User(1); //by passing a user id directly to the constructor $user = User::Find(1); //by static::find(); $users = Users::FindAll(); //by static::findAL() returning an array of User objects.
I don’t see these requirements too difficult to achieve but it will require some trickery. The problem will lie if I decide to put all the database logic into static::Find(). If static::Find() returns a new instance of the class, and the constructor of each class blindly returns the results of a static::Find() call, it won’t work. The code below doesn’t work because a constructor can’t return a new object instance and $this can’t be reassigned (like you can in some languages).
public function __construct($id){ if (is_array($id)){ $this->data = $id; }else{ $this = static::find($id); //fatal error, can't reassign $this return static::find($id); //also won't do anything useful } }
From fist impressions I think the idea of using is_array() to check if the $id parameter is a good way for static::Find() to pass an array to the constructor without ending up in a loop. This suggests that there needs to be a similar check inside static::Find() to return only an array in some cases instead of a new object.
So I’ve come up with this idea to solve the above issue.
public function __construct($id){ if (is_array($id)) $this->data = $id; else $this->data = static::find($id,true); //static::find() returns an array; } public static function Find($id,$returnArray = false){ $foundResults = insert_SQL_function_here(); if ($returnArray === true) return $foundResults; //return basic array else{ $called_class = get_called_class(); return new $called_class($foundResults); } }
I’m not 100% convinced this is the best way to solve this. Ideally I’d like a solution that didn’t require the checks in __construct() and static::find(), but this is a start, and it’s a a reasonable exercise in using LSB.