Started on a new framework

Posted by John Kleijn • Wednesday, March 24. 2010 • Category: PHP

Yet again.

Actually, using bits and pieces from previous works of art and a lot of red bars that turned green eventually, it is actually already a usable framework. Which allows me to focus on the more interesting stuff that are going to set this framework apart. Starting with the data layer.


Now obviously that is going to include an ORM. I think it is vital that the PHP community gets a viable alternative for Doctrine and Propel. Forget about Zend_Db_Table. What PHP needs is something that is based on the Data Mapper pattern, the domain objects completely decoupled from their persistence. In addition, it needs to be easy to switch to a completely new storage paradigm. Think CouchDB and HBase.

One of the issues with restoring objects from storage in PHP has been the limitations of restoring state to an object. Objects needed to be of a type that supports taking a value object of some sort (or an array) that has a mechanism that will restore state. Even if you create a supertype for it (which we rather wouldn't, because inheritance breaks encapsulation), properties would have to be "protected" instead of private to be able to set state, breaking encapsulation even more. The way around that I tried before is serializing an object, extracting the properties values using string parsing. This works fine for simple objects but reconstructing the object becomes a PITA with large object graphs. It's doable though, but since PHP 5.3 it also has become unnessacary thanks to the new method ReflectionProperty::setAccessible(). Yay! Now we can do this:


public function export($object)
{
        $output = array();
       
        $refl = new \ReflectionObject($object);
 
        foreach($refl->getProperties() as $property)
        {
                $property->setAccessible(true);
               
                $value = $property->getValue($object);
               
                if(is_object($value))
                {
                        $value = $this->export($value);
                }
                elseif(is_array($value))
                {
                        foreach($value as $key => &$aValue)
                        {
                                if(is_object($aValue))
                                {
                                        $aValue = $this->export($aValue);
                                }
                        }
                }
               
                $output[$property->getName()] = $value;
        }
       
        return $output;
}
 

And export an entire object graph to an array. Although arguably we could already do that by recursively casting to an array. More importantly, we can set values on an object without having to assemble a large string of serialized objects. I am going to have to simplify this a bit or it'll be a very long post and I don't want to do that to you.


public function import($className, array $data)
{
        $this->_refl = new \ReflectionClass($className);
       
        $object = unserialize(sprintf('O:%d:"%s":0:{}', strlen($className), $className));
       
        foreach($this->_refl->getProperties() as $property)
        {
                $property->setAccessible(true);
               
                if(array_key_exists($value, $data))
                {
                        $property->setValue($object, $data[$value]);
                }
        }
       
        return $object;
}
 

This obviously does not handle aggregate objects. For that you need a bit of meta data. You could include that in the exported data, but a more flexible solution is to use a separate configuration.


if(array_key_exists($property->getName(), $data))
{
        $value = $data[$value];
       
        $spec = !isset($this->_config[$className][$property->getName()]) ?: $this->_config[$className][$property->getName()];
       
        if($spec)
        {
               
                if($spec['type'] == 'array')
                {
                        foreach($value as &$data)
                        {
                                $data = $this->import($spec['contenttype'], $data);
                        }
                }
                else
                {
                        $value = $this->import($spec['type'], $data);
                }
               
        }
       
        $property->setValue($object, $value);
}
 

This is still (very) far from an ORM, but at least it is a simple way to extract and restore state from/to objects. Anyway, a new ORM to sink your teeth in is not the only thing you'll find in this framework. The data layer is going to be abstracted to a degree where you can transparently change the storage from an RDBMS to (at least) CouchDB or HBase. Both are gaining popularity, and especially HBase is interesting for developers of applications with mammoth data stores.

The rest of the framework (the part that is already functioning) is fairly standard, you'll find some similarities with Zend Framework. It is definitely not modeled after ZF, but there are similarities in the concepts of observers to the dispatch process, view and controller helpers, and the use of PHP as views. I have found these aspects very useful in real life, so I see no reason to reinvent the wheel there.

A second aspect I will be paying a lot of attention too is search. I may implement a native search engine, a simple one for small projects, I may not. The value of such would probably be limited, yet it would probably be labor intensive. The goal is to provide a unified interface to some of the better third party search engines, starting with Solr. I have also been looking into Xapian. Assuming I can get it to run as a standalone daemon without the php extension, there is a good chance that it will be the bundled, default search engine for the framework.

Finally there will be support for jQuery. It will probably include client side validators for their server side counter parts, written in jQuery. I am still thinking about support for ExtJS, it will probably include some tools to ease development with it, as I use it for work more and more often.

That's all for now, I hope to be able to spend some time on in the near future, and that I can convey to you my pride in the first official release. Thanks for reading.

P.S. This will be GPLed. If you want to join this effort, send me an email.

0 Trackbacks

  1. No Trackbacks

2 Comments

Display comments as (Linear | Threaded)
  1. For PHP 5.2 and earlier, you can also do the following instead of parsing the output of serialize():

    class Foo
    {
        private $test = 'bar';
    }

    $foo = new Foo();

    $tmp = (array) $foo;
    echo $tmp[chr(0).'Foo'.chr(0).'test'];

    Of course using reflection when you have it in 5.3 is much nicer.

    By the way, it's called ReflectionProperty::setAccessible(), not ReflectionClass ;-)

  2. Hi Daniel,

    Yes, I do mention that ("arguably we could already do that by recursively casting to an array").

    Thanks for pointing out the mistake in the text, pretty sloppy of me.

Add Comment

You can use [geshi lang=lang_name [,ln={y|n}]][/geshi] tags to embed source code snippets.
Standard emoticons like :-) and ;-) are converted to images.

To prevent automated Bots from commentspamming, please enter the string you see in the image below in the appropriate input box. Your comment will only be submitted if the strings match. Please ensure that your browser supports and accepts cookies, or your comment cannot be verified correctly.
CAPTCHA


Antiquities and such