views:

137

answers:

5

I have a collection of PHP classes that can be instantiated traditionally using constructors. Now I want to add the ability to instantiate these objects using YAML configuration files.

Here's an example of a configuration file describing a web form:

title: Contact
fields:
    message:
        type: text
        required: true
    topic:
        type: dropdown
        options: ["HTML", "CSS", "PHP"]

This should result in a Form object with two corresponding Field objects. In other words, the configuration files sometimes describe objects that contain other objects.

I don't want simply a mapping from YAML key/value pairs to PHP properties because I want to allow shorthand syntax in the configuration files. For instance, I want to abbreviate some property names and also be able to type something like default: today, where "today" is not interpreted as a string literal but rather transformed into the current timestamp.

Since many of the classes are subclasses, I'm looking for a solution that also lets me somehow "inherit" configuration logic from the superclass.

I want to avoid modifying the existing classes, but I am willing to do so if it allows for a simpler solution. On second thought, I don't really mind modifying the existing classes; I'm just interested in the best solution.

How do I best create objects from configuration files in this manner?

A: 

The first thing I would do would be to see if this wheel had been already invented by someone- which might involve looking at some of the ORM suggestions offered here: http://stackoverflow.com/questions/108699/good-php-orm-library

Edit: Or possibly more usefully in this case taking a look at YAML Symfony: http://components.symfony-project.org/yaml/ - this also lets you embed PHP in your configuration file, which should get around the shorthand field names as well.

I won't go into the details of implementing things yourself beyond that because there's a lot of different ways you might choose to do that and not having implemented them myself I wouldn't want to set you wrong, but I will suggest that if you're using shorthand syntax like that you will want some way of marking out that it is shorthand syntax - coding for default: today is much more likely to be error prone than default : %%today%% or other more specialised markup.

glenatron
Aren't ORMs merely used for mapping tables in relational databases to OOP classes?
Jakob
That is where they started out, certainly, but I'm pretty sure that the more mainstream ORM libraries like the Hibernate family also facilitated xml serialisation of object data. You've got me doubting myself now, though. Perhaps I'm just going senile.
glenatron
A: 

Write your configuration in PHP.

$ContactConfig=array(
  $title='Contact',
  $fields=array(
    'message'=>array(
      'type'=>'text',
      'required'=>true
    ),
    'topic'=>array(
      'type'=>'dropdown',
      'options'=>array('HTML','CSS','PHP')
    )
  )
);
codez
A: 

I haven't written much PHP myself, but I could see something like this working if you rolled your own with the help of Symfony's YAML. You could use the PHP Reflection class to get all the properties of an object and sub-objects and then use the Symfony YAML code to map YAML to an array, then map the array to properties. In your code that maps the array to the properties of your PHP objects, you could look for your special things like "today" and make them do special things.

You may need a way to mark properties to react a certain way to different inputs. I don't know if its possible in PHP, but in .NET you can mark properties with an Attribute class to let properties know to do special stuff.

Justin
+2  A: 

You could take the following approach:

Each of your model-classes to be populated from the configuration-file should implement an interface with, for example, a method fromArray($data).

On the top-level you know the type of Object (a Form) you want to create. This object is instanciated and the data from your config-file passed to the fromArray-method. The object then traverses the configuration-data and "knows" what to do with each of the entries.

For each element of the 'fields'-array it can then pass the data on to the child-objects which can themselves traverse and handle the configuration properly (including things like interpreting 'today' in a special way for any field that accepts a date).

Just split up the responsibility of creating object into special methods that know how to handle this kind of object.

marvesmith
That's actually the approach I've been using so far, and it's working better than I thought it would when I wrote the question. I'm still not sure how to handle inheritance with it, though: Let's say I have a `Checkbox` class that extends the `Field` class and I want `Checkbox` to be read the same way as `Field` except for a few new (or different) properties. Since the `fromArray` method of the superclass returns an object of that particular class (`Field`) I can't simply call `parent::fromArray` in the subclass. Any ideas on how to handle inheritance?
Jakob
tricky - you wouldn't have this problem if fromArray wasn't a static method. In that case you need to instanciate the object (Checkbox) and use fromArray to let it configure itself.
marvesmith
A: 

Write a PHP code generator which essentially converts the YAML config to PHP code. When you do it this way, an opcode cache (APC, eAccelerator, XCache, etc.) will make the script more efficient at runtime, instead of PHP having to interpret the YAML file every time it is loaded.

stillstanding