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/2510553
Full code:
<?php
namespace 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;
}
}
Button
class 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);
}
}
Submit
class 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>";
<input />
)? This looks like it could be used for any HTML, why do you say it is only for forms? Would you mind terribly separating this code by class so that we don't have to continuously scroll the same text box while reading your code? It makes it quite difficult. I will finish reading your code in a bit and have an answer ready shortly after. \$\endgroup\$$self_contained
field for it in theNode
class. \$\endgroup\$