<?php
/**
 * HTML Interface for drawing HTML elements
 * @version 0.9
 * @author Kees Schepers <kees.schepers@scooterbase.net>
 */
interface htmlInterface {
	/**
	 * Returns HTML content
	 * @return string html
	 */
	public function getHTML();
}
/**
 * Exceptions which will be thrown for this libary.
 * @author Kees Schepers <kees.schepers@scooterbase.net>
 */
class Form_Exception extends Exception {}
/**
 * A HTML element object which is used for generating parts of the form
 * @version 0.9
 * @author Kees Schepers <kees.schepers@scooterbase.net>
 */
abstract class Form_Html_Element implements htmlInterface {	
	/**
	 * The tagname of the current element drawing
	 *
	 * @var string
	 */
	protected $tagName;
	/**
	 * Indicates the HTMLtag is selfclosing or not
	 *
	 * @var unknown_type
	 */
	protected $selfClosing;
	/**
	 * An numeric array with HTML properties which are allowed
	 *
	 * @var array
	 */
	protected $allowedProperties;
	/**
	 * A set of properties, key is the name, value is the value
	 *
	 * @var array
	 */
	private $properties;	
	public function __construct() {
		$this->properties = array();
		$this->allowedProperties = array();
		$this->selfClosing = false;
		$this->tagName = null;
	}
	/**
	 * Set a desired property of this HTML element
	 *
	 * @param string $propertyName
	 * @param string $propertyValue
	 * @return boolean
	 */
	public function __set($propertyName,$propertyValue) {
		if(in_array($propertyName,$this->allowedProperties)) {
			$this->properties[$propertyName] = $propertyValue;
			return true;
		} else {
			throw new Form_Exception('Property ('.$propertyName.') is not known.');
		}
	}
	/**
	 * Get a desired property of the this HTML element
	 *
	 * @param string $propertyName
	 * @return string
	 */
	public function __get($propertyName) {
		switch ($propertyName) {
			case 'text' :
				return $this->text;				
			default :
				if(in_array($propertyName,$this->allowedProperties)) {
					return $this->properties[$propertyName];
				} else {
					throw new Form_Exception('Property ('.$propertyName.') is not known.');
				}
		}
	}
	/**
	 * Returns all of the properties
	 *
	 * @return array
	 */
	protected function getProperties() {
		return $this->properties;
	}
	/**
	 * Draw the property, and draw innerHTML of parent properties of neccasary
	 *
	 * @param string $innerHTML HTML contents
	 * @return string HTML contents
	 */
	public function getHTML($innerHTML=false) {
		if(is_null($this->tagName)) throw new Form_Exception('Tagname of Form_Html_Element not set yet.');
		
		$str = '';
		$str = '<'.$this->tagName.' ';
		
		foreach ($this->getProperties() as $name=>$property) {
			$str .= $name.'="'.$property.'" ';
		}
		
		if($this->selfClosing) {
			$str .= '/>';
		} else {
			$str .= '>'.(!$innerHTML ? '' : $innerHTML).'</'.$this->tagName.'>';
		}
		
		return $str;
	}
}
/**
 * Class the generate forms
 * 
 * @version 0.9
 * @author Kees Schepers <kees.schepers@scooterbase.net>
 *
 */
class Form extends Form_Html_Element {
	protected $fields;
	public function __construct() {
		parent::__construct();
		$this->fields = array();
		$this->allowedProperties[] = 'method';		
		$this->allowedProperties[] = 'action';		
		$this->allowedProperties[] = 'enctype';		
		$this->allowedProperties[] = 'id';		
		$this->allowedProperties[] = 'class';		
		
		$this->tagName = 'form';
		$this->selfClosing = false;
	}
	public function addField($label,Form_Field $field) {
		$this->fields[$field->name] = array('label' => $label, 'field' => $field);
	}	
	public function getHTML() {
		$innerHTML = '';
				
		foreach ($this->fields as $fieldElements) {			
			$innerHTML .= '<div>';
			
			if($fieldElements['label'] instanceof Form_Label ) {				
				$innerHTML .= $fieldElements['label']->getHTML();
			}
			$innerHTML .= $fieldElements['field']->getHTML();
			
			$innerHTML .= '</div>';
		}
		
		return parent::getHTML($innerHTML);
	}
}
/**
 * Abstract class for a form field. A form field can either be a input field or a textarea
 * 
 * @author Kees Schepers <kees.schepers@scooterbase.net>
 */
abstract class Form_Field extends Form_Html_Element {}
/**
 * The abstract class for generating the labels for a form field. The children tell the class
 * wether it's a div or something else.
 * 
 * @author Kees Schepers <kees.schepers@scooterbase.net>
 */
abstract class Form_Label extends Form_Html_Element {
	const SAFE_LABEL = true;
	protected $text;
	public function __construct($text) {		
		parent::__construct();
		$this->text = self::SAFE_LABEL ? htmlentities($text) : $text;
	}
	/**
	 * The inner text can be protected against HTML code
	 *
	 * @param string $propertyName
	 * @param string $propertyValue
	 * @return boolean
	 */
	public function __set($propertyName,$propertyValue) {
		switch ($propertyName) {
			case 'text':				
				$this->text = self::SAFE_LABEL ? htmlentities($propertyValue) : $propertyValue;
				return true;		
			default:
				return parent::__set($propertyName,$propertyValue);				
		}
	}
	/**
	 * Text is special property, returns property value
	 *
	 * @param string $propertyName
	 * @return string
	 */
	public function __get($propertyName) {
		switch ($propertyName) {
			case 'text' :
				return $this->text;				
			default :
				return parent::__get($propertyName);
		}
	}	
}
/**
 * Input elements
 * 
 * @author Kees Schepers <kees.schepers@scooterbase.net>
 */
final class Form_Field_Input extends Form_Field {
	public function __construct($type,$name) {
		parent::__construct();
		
		$this->allowedProperties[] = 'class';
		$this->allowedProperties[] = 'name';
		$this->allowedProperties[] = 'type';
		$this->allowedProperties[] = 'value';
		
		$this->selfClosing = true;
		$this->tagName = 'input';
		
		$this->type = $type;
		$this->name = $name;
		
	}
}
/**
 * Class for drawing textarea's
 * 
 * @author Kees Schepers <kees.schepers@scooterbase.net>
 */
final class Form_Field_TextArea extends Form_Field {
	const SAFE_LABEL = true;
	protected $text;
	/**
	 *
	 * @param string $text innertext of the element
	 * @param string $name html property name
	 * @param integer $cols number of editable columns
	 * @param integer $rows number of editable rows
	 */
	public function __construct($text,$name,$cols,$rows) {
		parent::__construct();
		$this->allowedProperties[] = 'name';
		$this->allowedProperties[] = 'id';
		$this->allowedProperties[] = 'class';
		$this->allowedProperties[] = 'cols';
		$this->allowedProperties[] = 'rows';
		
		$this->text = self::SAFE_LABEL ? htmlentities($text) : $text;
		$this->tagName = 'textarea';
		$this->selfClosing = false;
		
		$this->name = $name;
		$this->cols = $cols;
		$this->rows = $rows;
	}
	/**
	 *
	 * @param string $propertyName
	 * @param string $propertyValue
	 * @return boolean
	 */
	public function __set($propertyName,$propertyValue) {
		switch ($propertyName) {
			case 'text':				
				$this->text = self::SAFE_LABEL ? htmlentities($propertyValue) : $propertyValue;
				return true;		
			default:
				return parent::__set($propertyName,$propertyValue);				
		}
	}
	/**
	 *
	 * @param string $propertyName
	 * @return string
	 */
	public function __get($propertyName) {
		switch ($propertyName) {
			case 'text' :
				return $this->text;				
			default :
				return parent::__get($propertyName);
		}
	}
	/**
	 *
	 * @return string
	 */
	public function getHTML() {
		return parent::getHTML($this->text);
	}
}
/**
 * A field label with a DIV before it.
 * @author Kees Schepers <kees.schepers@scooterbase.net>
 */
final class Form_Label_Div extends Form_Label {
	/**
	 *
	 * @param string $text innertext of the element
	 */
	public function __construct($text) {
		parent::__construct($text);
				
		$this->text = $text;
		$this->tagName = 'div';
		$this->selfClosing = false;
	}
	/**
	 *
	 * @return string HTML contents
	 */
	public function getHTML() {		
		return parent::getHTML($this->text);
	}
}

/**
 * Voorbeeld
*/
$form = new Form();
$form->addField(new Form_Label_Div('Gebruikersnaam'),new Form_Field_Input('text','username'));
$form->addField(new Form_Label_Div('E-mail'),new Form_Field_Input('text','e-mail'));
$form->addField(null,new Form_Field_TextArea('Test','comments',40,10));
echo $form->getHTML();
?>