PHP

...now browsing by tag

 
 

How Recess Solves Common PHP/MySQL Issues

Friday, November 28th, 2008

Justin Carmony wrote a great article titled ‘PHP Design - Biggest Database Oversights‘. The article points out 5 naive PHP/MySQL design decisions that will come back to haunt projects if they’ve been made. As I’m getting closer to the public preview release of the Recess! Framework this article discusses how Recess! addresses some of the most common oversights in database development.

Re: Oversight #1 - No Data Access Layer

Quality software design and engineering is all about abstracting away details and finding the proper layers of functionality. As Justin points out, if there is no layer of abstraction which wraps data access code then low-level data access code will appear everywhere. PHP projects which interleave data connections and queries throughout their code are difficult to maintain.

The database stack in Recess! has four major components: 1) SQL statement builder, 2) Data Sources, 3) Data Sets, 4) Object-Relational Mapped Models. The notion of a ‘data access layer’ is taken care of by Data Sources. Data sources wrap properly around PDO and introduce new methods beyond PDO’s, for example: getTables and getColumns($table). The implementation of these methods is specific to the RDBMS so Recess! also has the notion of a vendor specific driver called a DataSourceProvider. Currently Sqlite and MySql are supported.

Re: Oversight #2 - Design for Only One Database Connection

Small, greenfield projects often start out with a single database. This leads to naive data access layers which only support connections to a single database. Recess! supports multiple, named Data Sources. Instead of using a singleton pattern it uses a registry to store the Data Sources by name.

Having named Data Sources in an application gets really powerful at higher layers of abstraction, such as at the ORM layer, where we can mark-up a model with a single annotation to define which Data Source the model is persisted in.

Re: Oversight #3 - No Developer Logging

This isn’t as fully fleshed out yet as I’d like but the plans and hooks are in place to be able to log queries, their stack traces, as well as their EXPLAIN’ed strategies for execution in the RDBMS. In Recess! there is a distinct difference between Development mode and Production mode. Sql logging will be a toggle option on by default in development and off by default in production.

Re: Oversight #4 - Queries Written in Procedural Processes

My dislike of queries being written in procedural code may run even deeper than Justin’s! Recess! abdicates SQL string manipulation to a low-level SqlBuilder class. An instance of SqlBuilder allows SQL query strings to be built incrementally using chained method calls. Let’s take a look at some examples:

$sqlBuilder = new SqlBuilder();
$sql = $sqlBuilder
            ->from('people')
            ->like('name','Kris')
            ->orderBy('age')
            ->toSql();
// $sql is now:
// 'SELECT people.*
//    WHERE people.name = :people_name
//    ORDER BY people.age
$args = $sqlBuilder->getPdoArguments();
// $args is now array( 'people_name' => 'Kris' );

// Let's add another criterion
$sql = $sqlBuilder->equal('home_city', 'Charlotte')->toSql();
// $sql is now:
//  SELECT people.*
//      WHERE people.name LIKE :people_name
//      AND people.home_city = :people_home_city
//      ORDER BY people.age

$args = $sqlBuilder->getPdoArguments();
// $args is now array(
//                   'people_name' => 'Kris',
//                   'people_home_city' => 'Charlotte' );

SqlBuilder can do more complex things like joins, as well as inserts, updates, and deletes. Recess! users, though, will likely never use SqlBuilder because it’s still too low level of an abstraction. Internally, however, having the ability to incrementally construct query strings has made the framework code quite pretty.

Here’s code at the level of abstraction Recess! developers can expect to write:

class Person extends Model { }
$person = new Person();
$person->name = 'Kris';
$person->homeCity = 'Charlotte';
$people = $person->find()->orderBy('name');
foreach($people as $person) {
   echo $person->name, ' ', $person->age, '<br />';
}

This example just scrapes the surface of Recess! object-relational mapping. Relationships, CRUDS, cascading deletes, etc. are all handled as well. In a future post I’ll step through the data access stack’s capabilities in more detail. Needless to say, in Recess! queries won’t be mixed with procedural code!

Re: Oversight #5 - No Separation of Reads and Writes

Once projects outgrow a single server RDBMS the next step is often to do a Master/Slave setup in MySQL. In a Master/Slave setup expensive and less frequent writes are channeled to the Master server while reads are channeled to the Slave server.

In Recess!, by using named data sources as described in #2, we can handle Data Sources on a per model basis. A natural extension of this is handling reads and writes independently on a per model basis. The pseudo-code below shows how this may look on a model:

/**
 * !Source master, For: Writes
 * !Source (slave1, slave2, slave3), For: Reads
 * !HasMany books
 */
class Person extends Model {}

/**
 * !Source master
 * !BelongsTo person
 */
class Books extends Model {}

In a future post I’ll describe the Recess! annotations system which will explain the constructs you’re seeing in the DocComments. Recess! annotations give us a way of providing static metadata about a class and are built-in to the framework’s core. Recess! annotations should be familiar to Java and .Net programmers who have used annotations.

Conclusion

So that’s a quick look at how Recess! handles Justin Carmony’s commonly made MySQL/PHP mistakes. These issues are abstracted away in the internals of Recess. If you’re a PHP developer check out RecessFramework.com and sign-up to be notified when it is publicly released “very soon now”.

Dynamic Properties in PHP and StdClass

Thursday, November 27th, 2008

Languages like JavaScript and Python allow object instances to have dynamic properties. As it turns out, PHP does too. Looking at the official PHP documentation on objects and classes you might be lead to believe dynamic instance properties require custom __get and __set magic methods. They don’t.

Simple, Built-in Dynamic Properties

Check out the following code listing:

class DynamicProperties { }
$object = new DynamicProperties;

print isset($object->foo) ? 't' : 'f'; // f

// Set Dynamic Properties foo and fooz
$object->foo = 'bar';
$object->fooz = 'baz';

// Isset and Unset work
isset($object->foo); // true
unset($object->foo); 

// Iterate through Properties and Values
foreach($object as $property => $value)  {
     print($property . ' = ' . $value . '<br />');
}
// Prints:
//   fooz = baz

Using the built-in dynamic instance properties is an order of magnitude faster (30x, by my profiling) than using magic __get and __set methods. Built in dynamic property accesses happen without invoking a method call back to PHP script.

So when does it make sense to use __get and __set? If you need more complex behavior, like calculated properties, you must use __get and __set. Also, as an astute comment points out, if you would prefer not to have dynamic properties on a class you can throw errors from __get and __set.

StdClass: Anonymous Objects

Sometimes all that is necessary is a property bag to throw key value pairs into. One way is to use array, but this requires quoting all keys. Another way is to use dynamic properties on an instance of StdClass. StdClass is a sparsely documented class in PHP which has no predefined members.

$object = new StdClass;
$object->foo = 'bar';
json_encode($object);

Next I’ll touch on the SPL’s Countable and ArrayAccess as a means of being able to accomplish the following in PHP:

class MyClass implements Countable, ArrayAccess { ... }

$myObject = new MyClass();
// Using array access notation
$myObject[0] = 'hello';
$myObject[1] = 'world';
$myObject['foo'] = 'bar';

Thanks to the folks pointing out that you don’t need to extend from StdClass in order to have dynamic properties!

Hello Again, Old Friend: Revisiting a PHP Framework

Wednesday, October 8th, 2008

I’m in the process of replumbing the lightweight PHP application framework I wrote with Joel Sutherland over three years ago. Its original design was inspired by the Java Struts Framework. It enabled us to rapidly develop the first version of New Media Campaignswebsite management software. Two summers ago we did a major redesign inspired by the DRY nature of Ruby on Rails. Since then Joel and Josh Lockhart have been tweaking the framework by addressing the issues which crop up after their intensive use while rewriting New Media’s system. 

Energized by what I saw at the Web 2.0 Expo in NY I’m back at it again. My three big goals with this take:

1) Get it in the wild: Open source with an MIT license. This is going to happen before the New Year. Hopefully sooner. We intended to do this with V2. It actually was publicly available in 2006 for a brief period of time but without any real plan for evangelism. I’ve been using open source software for a long time and its high time to give back.

2) Play nice in the new RESTful world. If I had to bet on a paradigm for interacting with web APIs I would bet the farm on REST. It is perfectly aligned with the grain of the web. Most existing frameworks written without an emphasis on REST have made awkward face lifts to adapt. When revisiting our own framework and considering how to make it properly RESTful this would have held true if not for…

3) Signficantly Improving the Architecture. Revisiting old code is a joy. It’s easy to forget how clever you were and how much work you did. Yet it’s very disturbing to realize how hacked some of the fundamental design was. As mentioned, the current version was influenced significantly by what Rails plumbing looked like circa-2006. Since then two really important things have changed: 1) the emphasis on tying closer to HTTP protocol, and 2) two additional years of experience with systems design under the belt.

So, here’s to take 3! Bits available for download in upcoming months. E-mail me at krisjordan/gmail if interested in being copied on barely functional, pre-release bits in the mean time. Otherwise, stay tuned.