<?php

/* 
* ClassGen
*
* @author Dave Houston
* 
* Description
*  Progmatically define a PHP class.
*
* Usage
*  $cg = new ClassGen("MyClass");
*  $cg->add_method( "hello_world", "argname", 'echo "Hello, $argname";');
*  $cg->add_property( "myproperty", "public", 'a default value');
*  $cg->make();
* 
* .. later ..
*  
*  $myclass = new MyClass();
*  $myclass->hello_world('world'); // Hello, world
* 
*/

class ClassGen { 
   private $def;
   private $has_errors;
   private $error_list = array();

   function __construct( $classname ) { 
      if( $this->_check_name( $classname ) ) { 
         $this->def['_class_name'] = $classname;
         $this->def['_properties'] = array();
         $this->def['_method'] = array();
      } else { 
         $this->has_errors = 1;
         array_push( $this->error_list, "Invalid classname: $classname");
      }
      
   }

   // Implement class inheritence
   function extend( $parent ) { 
      if( $this->_check_name( $parent ) ) { 
         $this->def['_extends'] = $parent;
      } else { 
         $this->has_errors = 1;
         array_push( $this->error_list, "Invalid base class: $parent" );
      }
   }

   // Create a new method
   function add_method( $method, $arguments, $body ) {
      if( $this->_check_name( $method ) ) { 
        array_push( $this->def['_method'], array(
            "name" => $method,
            "args" => preg_split("/\s+/", $arguments),
            "body" => $body
         ));
      } else { 
         $this->has_errors = 1;
         array_push( $this->error_list, "Invalid method name: $method" );
      }
   }

   // Add a new property
   function add_property( $name, $type, $default ) { 
      if( $this->_check_name( $name ) ) { 
         array_push($this->def['_properties'], array( 
            "name" => $name,
            "type" => $type, 
            "default" => $default 
         ));
      } else { 
         $this->has_error = 1;
         array_push( $this->error_list( "Invalid property name: $name ") );
      }
   }

   // evaluates 
   function make() { 
      $str = "class " . $this->def['_class_name'] ;
      if( $this->def['_extends'] ) {
         $str .= ' extends ' . $this->def['_extends'];
      }
      $str .= "{ \n";

         // Methods
         foreach( $this->def['_method'] as $method ) { 
            $str .= $this->_build_method( $method );
         }

         // properties
         foreach( $this->def['_properties'] as $p ) { 
            $str .= $p['type'] . ' ';
            $str .= '$' . $p['name'] . ' ';
            if( $p['default'] ) { 
               $str .= '= ' . $p['default'];
            }
            $str .= ';';
         }
      $str .= '}';
      eval( $str );
   }

   // Check for valid property/class/method names
   function _check_name( $name ) { 
      $match = preg_match( "/[\w\d]+/", $name );
      return $match;
   }

   // Build a method definition
   function _build_method( $method ) { 
      $arg_array = array();
      foreach( $method['args'] as $a ) { 
         if( preg_match("/./", $a) ) { 
            array_push( $arg_array, '$' . $a );
         }
      }
      $m = 'function ' . $method['name'] . '(';
      $m .= join(', ', $arg_array );
      $m .= ") {\n";
      $m .= $method['body'];
      $m .= "}\n";
      return $m;
   }
}

?>
