Navigation Menu

Skip to content

Instantly share code, notes, and snippets.

@MadaraUchiha
Created April 27, 2012 16:23
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save MadaraUchiha/2510553 to your computer and use it in GitHub Desktop.
Save MadaraUchiha/2510553 to your computer and use it in GitHub Desktop.
Form class - Generate valid good looking forms with PHP.
<?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);
}
/**
* 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;
}
}
/**
* 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;
}
}
/**
* 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;
}
}
/**
* 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;
}
}
/**
* 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;
}
}
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);
}
}
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 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>";
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment