Validating OCL constraints in PHP objects

November 15th, 2007 by Ivo

Last week, I attended a Code Camp for the 'Xenerix' project, a research project at a local Computer Science faculty. In that project, we're using all kinds of technologies, including PHP, Ruby and Java.

In this particular session a major subject was OCL. For those who are not familiar with the term: Object Constraint Language, a formal method to specify constraints that objects in an object model should respect. To give a practical example: if you have a class 'Creditcard', you could define the constraints 'self.limit > 0' when withdrawing an amount.

While the goal of the code camp was to do some nifty things with OCL and Ruby, I wanted to see if I could create a method to easily implement OCL constraints in arbitrary PHP objects. (Mainly triggered by one of the students, who discovered that a search for 'OCL and PHP' only resulted in pages that were written in PHP, and about OCL. There weren't any pages about how OCL can be used in PHP.)

As a starting point I created a small testcase with 2 classes, based on a testcase that one of the students used to explain what OCL was. The testcase contains a class 'Creditcard' and a class 'Person'. A creditcard has a limit, an expiration date and an owner (of type Person). A person only has a name. The following constraints apply:

  • When setting the cardholders name of the card, this name should match the name of the owner.

  • You can only withdraw money when the card is not expired.
  • Whatever happens, the limit of the card may not be less than zero.

These constraints can be expressed in OCL like this:

  • self.name = owner.name

  • self.expirationdate > now
  • self.limit > 0

I needed a method to specify the OCL constraint in the class. Although I'm against using docblocks as annotations, annotations in itself is a nice principle, so I decided that for this experiment, I would use docblock based annotations to specify the constraints (until PHP supports true annotations).

Here are the simple classes, annotated with their constraints:

 
 
class Person
  {
    public $name;
 
    public function __construct($name)
    {
      $this->name = $name;
    }
  }
 

This is just a basic little class with a constructor that lets us pass a name. None of the constraints apply to the Person class so we don't specify any OCL here.

 
 
  /**
   * @constraint self.limit > 0
   */
  class CreditCard
  {
    public $limit;
    public $owner;
    public $expirationdate;
 
    /**
     * @constraint self.name = owner.name
     */
    public $name;
 
    public function __construct($limit)
    {
      $this->limit = $limit;
    }
 
    function setOwner($owner)
    {
      $this->owner = $owner;
    }
 
    function setName($name)
    {
      $this->name = $name;
      $this->validate($name);
    }
 
    /**
     * @constraint self.expirationdate > now
     */
    function withdraw($amount)
    {
      $this->limit -= $amount;
    }
  }
 

This is the Creditcard class, which has all the constraints added as annotations. Note that the location of the constraint matters: there's a class-level constraint (that always applies), there's a method-level constraint (that applies whenever someone wants to call the method) and a propertylevel constraint (when someone sets a property to a certain value).

The following code runs a small testcase that performs some method calls and property operations:

 
 
  $cc = new CreditCard(2000);
  $cc->owner = new Person("Hans");
  $cc->name = "Hans";
  $cc->expirationdate = "2006-08-12";
  $cc->withdraw(100);
  $cc->withdraw(3000);
 

Now I needed to find a way to validate the OCL constraints at runtime.

Getting the constraints from the docblocks would be the easy part, the Reflection API can help with that. The hard part would be actually applying them to the object.

My first thought was that every object could be derived from some OCLBaseObject that supports OCL validation. However, this would be very inflexible, as often, a class will already have a baseclass, and this would severely limit us in our class hierarchy.

Then, I thought about using magic methods (__call and __set) to intercept all calls and property set operations. However, this is not possible, as __call and __set only intercept calls and properties that do not exist.

The least clean solution would be to add something along the lines of $this->validate() to each and every method and setter of the class, for example:

 
  function withdraw($amount)
  {
      $this->validate(....)
      ....
  }
 

However, this would bloat all classes, it would be errorprone and it would not be clean.

Finally, I came up with a solution that roughly combines the three approaches above:

 
  try
  {
    $cc = new OCLWrapper(new CreditCard(2000));
    $cc->owner = new OCLWrapper(new Person("Hans"));
    $cc->name = "Hans";
    $cc->expirationdate = "2006-08-12";
    $cc->withdraw(100);
    $cc->withdraw(3000);
 
  }
  catch (Exception $e)
  {
    echo $e->getMessage()."n";
  }
 
  echo "Owner's name is: ".$cc->owner->name."n";
  echo "Cardholder's name is: ".$cc->name."n";
  echo "Card balance: ".$cc->limit."n";
 

When I run this example (I'll get to the new OCLWrapper class in a minute), every call that violates a constraint results in an Exception. The classes retain their original implementation, even returntypes are unaffected.

So how did I implement this solution? Instead of inheritance, I use composition, in the form of a Decorator design pattern. This way, classes can still have their own inheritance hierarchy, and each class instance can be decorated with an object that adds the OCL validation functionality.

To be able to raise an error without changing the methods or their returntypes, I used exception handling.

The third part of the solution is to call an OCL validation on each and every method call and property setter. Instead of doing this in the actual methods, I could easily do this in the decorator class, because it has no own methods and setters, and thus will funnel all calls through its __call/__set magic methods to the underlying actual object.

As you can see in the above example, all it takes now is to wrap an object in an OCLWrapper object, and the OCL constraints will be validated on each method call or property setter.

Finally, I had to write the OCLWrapper class itself. It would have to transparantly support method and property access forwarding to the original object, it would have to be able to fetch constraints from the docblocks and to validate constraints on an obect.

This was easier than I thought, using PHP's Reflection API and a set of magic methods. (The hardest part: actually parsing the OCL syntax; I've used an evil harcoded string replacer to parse simple statements for now: if someone likes to implement a true parser, be my guest :-) )

The class is a bit big to post here, so I've attached it as a download. Rename it to class.oclwrapper.php once you downloaded it (thank you wordpress for mangling the filename :-) ).

If you want to run the sample: place the class.oclwrapper.php file in a directory, put the example code in a separate file, include the class.oclwrapper.php, and run the sample code. You'll see exceptions when you change the testcase to do stuff that's not allowed (such as withrdawing more than the limit).

This was, for me at least, a nice experiment that demonstrates how PHP5's OO features can be used to implement a solution for 'annotation based OCL validation'.

5 Responses to “Validating OCL constraints in PHP objects”

  1. November 15, 2007 at 2:48 pm, PHPDeveloper.org said:

    Ivo Jansch’s Blog: Validating OCL constraints in PHP objects…

    While attending a code camp for Xenerix, Ivo Jansch decided to ……

  2. November 15, 2007 at 3:21 pm, Travis Swicegood said:

    I played around with something very similar to this in Argil for controlling how models worked. I played the constraints on the properties themselves in the form of an annotation:

    /**
    * @is string
    */
    /**
    * @greaterThan 0
    */

    etc, etc. That works extremely nicely and allows you to develop your models independent of the framework. Until this post, however, I’ve yet to see anything resembling it coded in PHP.

  3. November 17, 2007 at 8:42 am, Do You PHP はてな said:

    [PHP]OCL(Object Constraint Language:オブジェクト制約言語)…

    今頃、目から鱗。やっぱりこの辺は全然追いつけてないというか、やってないというか。。。 OCL(Object Constraint Language:オブジェクト制約言語)を簡単に言うと、「インスタンスレベル…

  4. November 27, 2007 at 4:01 am, Adam said:

    Fascinating stuff! Thank you for providing me with a good read.

  5. November 20, 2010 at 11:53 pm, Jordi Cabot said:

    Very nice post. I never read before about validating OCL in PHP. If you are interested in going forward with this topic, one of the main problems you´ll need to face is how to implement the validation in the most efficient way possible (i.e. on each function you should only check the constraints that can be affected by the modifications performed during the execution of that particular function).

    I did a full PhD Thesis on this topic. A summary with some ideas that you could use to provide an efficient implementation of your OCL validation was published in Jordi Cabot, Ernest Teniente: Incremental Integrity Checking of UML/OCL Conceptual Schemas. Journal of Systems and Software, vol 82, issue 9, pp. 1459-1478. Read it online here: http://jordicabot.com/papers/JSS09.pdf