<?php

//-------------------------------------------------------------------
// ToyModSer.php: serialize an object given metadata
//-------------------------------------------------------------------
// Copyright (c) 2007 Patrick Mueller
// Using the MIT license: 
//    http://www.opensource.org/licenses/mit-license.php
//-------------------------------------------------------------------
// 2007/05 - Patrick Mueller - pmuellr@yahoo.com
//-------------------------------------------------------------------

require_once 'Zend/Json.php';

class 
ToyModSer {

    private 
$className;
    private 
$properties;
    
    
//---------------------------------------------------------------
    // Create an instance of this class, for the specified model class
    //---------------------------------------------------------------
    
public function __construct($className) {
        
// save the name
        
$this->className $className;
        
        
// get the property information
        
$this->getPropertyMetadata();
        
        
// check the types to make sure they are valid.
        
foreach ($this->properties as $name => $type) {
            switch (
$type) {
                case 
"integer": break;
                case 
"string":  break;
                default:
                    
$msg "invalid type for property '$name': '$type'";
                    throw new 
Exception($msg);
            }
        }
    }
    
    
//---------------------------------------------------------------
    // convert the object passed in to JSON
    //---------------------------------------------------------------
    
public function toJSON($object) {

        
// make sure the class of the object matches the serializer
        
if (get_class($object) != $this->className) {
            
$msg "attempting to serialize instance of '" 
                    
get_class($object) . 
                    
"' with a serializer for '" .
                    
$this->className .
                    
"'";
            throw new 
Exception($msg);
        } 
        
        
// initialize the array to convert to JSON
        
$toSerialize = array();
        
        
// get the properties set on the object instance
        
$instanceProperties get_object_vars($object);
        
        
// for each model property ...
        
foreach ($this->properties as $name => $type) {
            
            
// get the value of the property
            
$value $instanceProperties[$name];
            
            
// check it's type
            
$wrongType false;
            switch (
$type) {
                case 
"integer":
                    if (!
is_int($value)) $wrongType true
                    break;
                case 
"string":
                    if (!
is_string($value)) $wrongType true
                    break;
            }
            
            
// if it's the wrong type, throw an exception
            
if ($wrongType) {
                
$msg "property '$name' is not the expected type";
                throw new 
Exception($msg);
            }

            
// looks good, save the value away
            
$toSerialize[$name] = $value;
        }

        return 
Zend_JSON::encode($toSerialize);
    }

    
//---------------------------------------------------------------
    // convert the json passed in to an object
    //---------------------------------------------------------------
    
public function fromJSON($string) {
        
$fromSerialize Zend_JSON::decode($string);
        
        
$result = new $this->className();
        
        
// for each model property ...
        
foreach ($this->properties as $name => $type) {
            
            
// get the value of the property
            
$value $fromSerialize[$name];
            
            
// check it's type
            
$wrongType false;
            switch (
$type) {
                case 
"integer":
                    if (!
is_int($value)) $wrongType true
                    break;
                case 
"string":
                    if (!
is_string($value)) $wrongType true
                    break;
            }
            
            
// if it's the wrong type, throw an exception
            
if ($wrongType) {
                
$msg "property '$name' is not the expected type";
                throw new 
Exception($msg);
            }
            
            
$result->$name $value;
            
        }
        
        return 
$result;
    }

    
//---------------------------------------------------------------
    // get the class meta data for the properties
    //---------------------------------------------------------------
    
private function getPropertyMetadata() {

        
// initialize the properties that we'll be setting
        
$this->properties = array();
        
        
// get the properties defined on the class
        
$refClass = new ReflectionClass($this->className);
        
$refProperties $refClass->getProperties();
        
        
// for each property ...
        
foreach ($refProperties as $refProperty) {

            
// get the property name and doc comment
            
$name       $refProperty->getName();
            
$docComment $refProperty->getDocComment();
            
            
// get the type from the doc comment, if available
            
$type $this->getTypeFromDocComment($docComment);
            if (
null == $type) continue;
            
            
// if a type was specified in the doc comment, set it
            
$this->properties[$name] = $type;
        }
    }

    
//---------------------------------------------------------------
    // get the class meta data for the properties
    //---------------------------------------------------------------
    
private function getTypeFromDocComment($docComment) {
        
        
// split the doc comment into lines
        
$lines explode("\n"$docComment);
        
        
// for each line ...
        
foreach ($lines as $line) {

            
// if it matches "@model type=foo", return "foo"
            
$pattern '/.*@model\s.*type\s*=\s*(\S+).*/';
            if (
preg_match($pattern$line,$matches)) {
                return 
$matches[1];
            }
        }
        
        
// no match, return null
        
return null;
    }
    
}

?>