I have made the following classes and objects.It was made to create forms programattically easily. It should produce highly readable, valid, and clean HTML.Care to give me some feedback? :)https://gist.github.com/2510553Full code:<?phpnamespace Forms;/** * This file is supposed to give a good way of generating forms programmatically with PHP. *//* * WARNING! WARNING! WARNING! WARNING! WARNING! WARNING! WARNING! WARNING! WARNING! WARNING! WARNING! WARNING! * ----------------------------------------------------------------------------------------------------------- * None of the strings you see in the following classes are escaped/secured against any kind of * SQL Injections, XSS attacks, or any other sort of attack for that matter! For your own safety * Implement the necessary protections on any strings you use in these classes before entering them! * ----------------------------------------------------------------------------------------------------------- * WARNING! WARNING! WARNING! WARNING! WARNING! WARNING! WARNING! WARNING! WARNING! WARNING! WARNING! WARNING! * @package Classes *//** * All containing nodes should implement this. */interface Contains_Nodes {    /**     * @abstract     *     * @param Node $node     *     * This method will add a node to the node list of the implementing object.     */    public function add(Node $node);}Node/** * Base object for all Nodes. */class Node {    /**     * @var string $element Will hold the element name (or tag name)     */    public $element;    /**     * @var array $attribute_list Will hold all of the rest of the form's attributes such as ID, class and whatnot.     */    public $attribute_list = array();    /**     * @var bool $self_contained Signifies whether the Node is self contained. Self contained nodes do not get a closing tag.     */    public $text;    public $self_contained = false;    const SELF_CONTAINED = true;    const NOT_SELF_CONTAINED = false;    const TAB =     ;    /**     * @param string $element     * @param string $text     * @param bool   $self_contained     * @param array  $attribute_list     *     * General constructor for nodes. Should be overridden regularly.     */    public function __construct($element, $text = , $self_contained = false, array $attribute_list = array()) {        $this->element        = $element;        $this->self_contained = $self_contained;        $this->text = $text;        $this->attribute_list = $attribute_list;    }    /**     * @return string     *     * General string generator for nodes. Should be overridden regularly.     */    public function __toString() {        //Open element        $result = <{$this->element};        //Node list        $result .= $this->string_attribute_list();        //Close element        $result .= >;        if (!$this->self_contained && !empty($this->text)) {            $result .= $this->text;        }        if (!$this->self_contained) {            $result .= </{$this->element}>;        }        return $result;    }    /**     * @return string     */    public function string_attribute_list() {        $result = ;        if (!empty($this->attribute_list)) {            foreach ($this->attribute_list as $attr => $value) {                $result .=  {$attr}=\{$value}\;            }        }        return $result;    }}Form/** * The Form object, will describe a single form. * After constructing it is done, it should format into a cool, simple, valid, readable, HTML code. */class Form extends Node implements Contains_Nodes {    public $element = form;    public $self_contained = false;    /**     * @var Node[] $node_list This will hold all of the nodes in the form. Including fields and inputs.     */    public $node_list;    /**     * @var string $action Will hold the action for the form. This is a required field.     */    public $action;    /**     * @var string $method Will hold the form submission method for the form. Defaults to POST.     */    public $method = Form::METHOD_POST;    const METHOD_POST = 'POST';    const METHOD_GET  = 'GET';    /**     * @param string $action         Page to which the form submits.     * @param string $method         POST or GET, will throw an exception otherwise.     * @param array  $attribute_list Miscellaneous attributes for the form element.     */    public function __construct($action, $method = self::METHOD_POST, array $attribute_list = array()) {        $this->action         = $action;        $this->method         = $method;        $this->attribute_list = $attribute_list;        if (($method != self::METHOD_GET) && ($method != self::METHOD_POST)) {            throw new \Exception(Form method must be either POST or GET);        }    }    /**     * @param Node $node Node to add.     *     * @return Form to not break the chain     */    public function add(Node $node) {        $this->node_list[] = $node;        return $this;    }    /**     * @return string Format and stringify the form.     */    public function __toString() {        //Open tag, usually <form ...>        $result = <{$this->element} action=\{$this->action}\ method=\{$this->method}\;        //Attribute list        $result .= $this->string_attribute_list();        //Close opening tag        $result .= >;        //Loop through the nodes        foreach ($this->node_list as $node) {            $result .= \n . self::TAB . str_replace(\n, \n . self::TAB, $node->__toString());            //The replace is to keep the code indented and formatted properly.        }        //Close form element        $result .= \n</{$this->element}>;        return $result;    }}Input/** * Class to describe a single input node. */class Input extends Node {    public $element = input;    public $self_contained = true;    public $type;    public $label;    public $name;    public $default_value;    public $label_before_input = true;    /**     * @param string $type     * @param string $name     * @param Label  $label     * @param string $default_value     * @param array  $attribute_list     *     * Constructor for input node.     */    public function __construct($type, $name, Label $label, $default_value = , array $attribute_list = array()) {        $this->type           = $type;        $this->name           = $name;        $this->label          = $label;        $this->default_value  = $default_value;        $this->attribute_list = $attribute_list;    }    /**     * @return string Formatted input HTML.     */    public function __toString() {        //Label element open (usually <label        $result = <{$this->label->element};        //Begin attribute list        $result .= $this->label->string_attribute_list();        //Close opening tag        $result .= >;        //If we want label before the input...        if ($this->label_before_input) {            $result .= \n . self::TAB . $this->label->text;        }        //Begin input element usually <input ...        $result .= \n . self::TAB . <{$this->element} type=\{$this->type}\ name=\{$this->name}\;        //Default value        if (!empty($this->default_value)) {            $result .=  value=\{$this->default_value}\;        }        //Attribute list        $result .= $this->string_attribute_list();        //Close input element        $result .= >;        //If we want label after input        if (!$this->label_before_input) {            $result .= \n . self::TAB . $this->label->text;        }        //Close label element (usually </label>        $result .= \n</{$this->label->element}>;        /*         * FINAL RESULT should look like:         * <label [label-attributes]>         *     [text-if-before]         *     <input type=[type] name=[name] value=[value] [input-attributes]         *     [text-if-after]         * </label>         */        return $result;    }}Label/** * Class to describe a single label node. * Labels should be contained inside of inputs on a 1:1 relationship. */class Label extends Node {    public $element = label;    /**     * @param string $text     * @param array  $attribute_list     */    public function __construct($text, array $attribute_list = array()) {        $this->text           = $text;        $this->attribute_list = $attribute_list;    }    /**     * @return string Returns label text only. Labels can only be part of inputs.     */    public function __toString() {        return $this->text;    }}Fieldset/** * Class to describe a single fieldset node. * * @implements Contains_Nodes */class Fieldset extends Node implements Contains_Nodes {    public $element = fieldset;    /**     * @var Node[]     */    public $node_list;    public $legend;    /**     * @param $legend     *     * Construct a fieldset element     */    public function __construct($legend) {        $this->legend = $legend;    }    /**     * @param Node $node     *     * @return Fieldset to not break the chain     *     * Add a node to the fieldset.     */    public function add(Node $node) {        $this->node_list[] = $node;        return $this;    }    /**     * @return string Generate a formatted node list of the fieldset.     */    public function __toString() {        //Open element (usually <fieldset)        $result = <{$this->element};        //Attribute list        $this->string_attribute_list();        //Close opening tag        $result .= >;        //Legend text        $result .= \n . self::TAB . <legend>{$this->legend}</legend>;        //Loop through node list        foreach ($this->node_list as $node) {            $result .= \n . self::TAB . str_replace(\n, \n . self::TAB, $node->__toString());            //The replace is to keep the code indented and formatted properly.        }        //Close fieldset tag        $result .= \n</{$this->element}>;        return $result;    }}Buttonclass Button extends Node {    public $element = button;    public function __construct($text, array $attribute_list = array()) {        parent::__construct($this->element,$text, Node::NOT_SELF_CONTAINED, $attribute_list);    }}Submitclass Submit extends Button {    public $type = submit;    public function __construct($text = Submit, array $attribute_list = array()) {        $attribute_list[type] = $this->type;        parent::__construct($text, $attribute_list);    }}Testing/* * Testing begins! */$form  = new Form(index.php, Form::METHOD_GET);$field = new Fieldset(Fieldset);$field->add(new Input(text, name, new Label(Input inside of Fieldset)));$form    ->add(    new Input(        text, //Type        test, //Name        new Label(Testing Input, array(class => label)), //Label        Woot, //Default value        array(id => test) //Attribute List    ))    ->add(new Node(hr, , Node::SELF_CONTAINED))    ->add(new Submit(Go));echo $form . \n\n;echo <pre>;echo print_r($form);echo </pre>;
PHP Form generator class
php;object oriented;form
I'm not particularly fond of the style of coding, not everything needs to be a class. But there is only one point that I feel warrants significant attention:Huge Point: Not escaping means not usefulWARNING! WARNING! WARNING! ...  -------------------------------------------- ...  None of the strings you see in the following classes are escaped/secured against any kind ofSQL Injections, XSS attacks, or any other sort of attack for that matter!There would be little point using the code as presented if it didn't make life easy, and not escaping any of the properties would make it cumbersome and error-prone to use.E.g. you've got this in the Testing sectionnew Label($label ...What is going to happen if $label is Categories > Food or any other innocent string that will cause malformed html as a result? Why is the class not taking care of that automatically? Note that there will be use cases where you want to put html in some tags - such as using an image for a label, or where tags are nested - attributes should always be escaped though.And that's just talking about how innocent users could break your code, not those with malicious intent who submit <script>document.location = 'http://mysite'; as their name.Big Point: Write real testsIf you write a real test (by which I mean using phpunit) you can quickly test normal and edge case scenarios (What if the $x property looks like this?), which will highlight any difficulties in using the code. It'll also permit you the confidence - if you choose to rewrite any of the code in the future - of knowing that it still works in the same way as it did originally.Mid Point: Inconsistent constructorsYou have all these different constructors:public function __construct($element, $text = , $self_contained = false, array $attribute_list = array()) {public function __construct($action, $method = self::METHOD_POST, array $attribute_list = array()) {public function __construct($type, $name, Label $label, $default_value = , array $attribute_list = array()) {public function __construct($text, array $attribute_list = array()) {public function __construct($legend) {public function __construct($text, array $attribute_list = array()) {public function __construct($text = Submit, array $attribute_list = array()) {Yet all classes extend Node. Why not just have one:public function __construct($args = array()) {A consistent constructor makes it easy to know how to use a class without having to continually refer to the docs or class definition. If that's something you do with none-constructor functions (not applicable here) your code would not be E_STRICT compliant. Having different constructors isn't particularly intuitive, and means you can't have simple override logic like so:public function __construct($args = array()) {    .. anything ..    parent::__construct($args);}You can enforce any mandatory args just the same, but given that everything is a class you should be able to set all of the currently-mandatory properties after instanciation anyway.Mid Point: Needless interfaceThe interface only defines one method, and where used has exactly the same code. You could just as easily add it to your Node class and override or configure it to be disabled in the cases where it's not applicable. Mid Point: Needless constantsWhat's the real benefit of these:const METHOD_POST = 'POST';const METHOD_GET  = 'GET';It's more to type and doesn't add any clarity.There's also this:const TAB =     ;Which is in fact set to four spaces. \t is easier and shorter to type.Minor Point: WhitespaceWhitespace in html is insignificant, so doing this: $result .= \n . self::TAB . <{$this->e...doesn't do anything for end users. It also doesn't do what you want, as it leads to ugly html. Look at the source of this page and look for <form> - if it were left aligned it would break out of the indentation level where it is. If you used similar code to that in the question to build all your html the indentation would be so wayward you wouldn't be able to read the raw html output without re-indenting it.It's something that is of no real value, because anyone who wants to see the html structure can just use firebug/devtools/their-tool-of-choice and it'll indent the code for them. If you really want to have indented html anyway, there are tools for that like htmltidy.