Sunday, May 24, 2009

The new (not really) PHP.

Like everyone else, I learned basic PHP on my own in high school. It was a big fad back then, filling the role that Ruby on Rails fills now as the "cool" web development platform. It was also about as practical as Ruby on Rails is today (yeah Ruby on Rails is great for prototyping stuff, but it doesn't perform so hot and "convention over configuration" pretty much just means "I'm afraid I can't let you do that, Dave").

But after about 5-6 years, I finally took another look at PHP, and I'm really starting to like it. With the newer versions of PHP, you get OOP. That's right, you can make classes and objects in PHP! It also has a really nice set of Reflection classes that let you create and manipulate objects of unknown type dynamically.

I spent some time setting up a personal codebase for PHP-based websites, and I wanted to share a snippet from it. I'll probably post more later on (enough to make it reusable), but I'm really proud of this function. It's in a Command class that I wrote which I'm using for database interactions. Essentially, you give it a class name($className), some sql ($commandText), sql parameters ($parameters array of Parameter($type, $value)) and it'll convert each resulting row from the sql into an object of the given class. The only stipulation is you have to define a constructor in the class that takes in every property of the class. I might add a version that takes an Interface name and uses that to determine which properties to set.

The first half of the function binds a dynamic number of parameters to the prepared SQL statement.
The second half forms the objects form the result.

Enjoy!


public function fetchArray($className)
{
$this->ensureConnection();

$stmt = $this->connection->mysqli->prepare($this->commandText);

if(!$stmt) die("Statement could not be prepared.");
$paramVals = array();
$typesArr = array("");
foreach ($this->parameters as $parameter)
{
$typesArr[0] .= $parameter->type;
$paramVals[] = &$parameter->value;
}
call_user_func_array(
array(&$stmt, "bind_param"),
array_merge($typesArr, $paramVals));
if(!$stmt->execute())
die("Statement execution failed for:\n " .
$this->commandText . "\n\n with parameters(" .
implode(", ", $paramVals) . ")");

$reflectClass = new ReflectionClass($className);
$reflectProps = $reflectClass->getProperties();

//Create an array of variables.
//Then create an array of references to them (to pass to bind_result)
$propValueHolders = array();
$propRefHolders = array();
for($i = 0; $i < count($reflectProps); $i++)
{
$propValueHolders[] = 0;
$propRefHolders[] = &$propValueHolders[$i];
}

call_user_func_array(array(&$stmt, "bind_result"), $propRefHolders);
$ret = array();
while($stmt->fetch())
{
$ret[] = call_user_func_array(
array(&$reflectClass, 'newInstance'),
$propRefHolders);
}

$stmt->close();
return $ret;
}