<?php
class Stuff
{
public static function countObjTypes( array $data, array $types )
{
$return['Other'] = 0;
foreach ( $types as $type )
$return[$type] = 0;
foreach ( ( array ) $data as $obj ) {
$set = false;
array_walk( $types, function( $type ) use ( $obj, &$return, &$set )
{
if ( is_a( $obj, $type ) ) {
$return[$type]++;
$set = true;
}
} );
if ( !$set )
$return['Other']++;
}
return $return;
}
public static function getArrayValueByPath($array, $path) {
#$path = (array) array_filter( $path );
if( !empty( $path ) ) {
foreach ( $path as $key ) if( !isset( $array[$key] ) ) return NULL; $array = $array[$key];
return $array;
} else {
return false;
}
}
}
class formRenderer {
protected $dom;
public function __construct() { $this->dom = new DOMDocument; }
public function checkData( $data ) {
if( isset( $data[ 'node' ] ) )
throw new Exception('Invalid data, contained node');
return $data;
}
public function save( $type = 'HTML' ) {
$function = 'save'.strtoupper( $type );
return $this->dom->$function();
}
public function base( $data ) {
extract( $this->checkData( $data ) );
$node = $this->dom->createElement( 'input' );
$node->setAttribute( 'name', $name );
$node->setAttribute( 'id', $name );
$node->setAttribute( 'type', $type );
if( isset( $classes ) )
$node->setAttribute( 'class', $classes );
if( isset ( $value ) )
$node->setAttribute( 'value', $value );
if( isset ( $autocomplete ) )
$node->setAttribute( 'autocomplete', $autocomplete );
$this->form->appendChild( $node );
return $this->dom;
}
public function Label( $data ) {
extract( $this->checkData( $data ) );
$node = $this->dom->createElement( 'label' );
$node->setAttribute( 'for', $for );
$textNode = $this->dom->createDocumentFragment();
$textNode->appendXML( $value );
$node->appendChild( $textNode );
$this->form->appendChild( $node );
return $this->dom;
}
public function Form( $data ) {
extract( $this->checkData( $data ) );
$this->form = $this->dom->createElement( 'form' );
$this->form->setAttribute( 'method', $method );
$this->form->setAttribute( 'name', $name );
$this->dom->appendChild( $this->form );
return $this->dom;
}
public function HTML( $data ) {
extract( $this->checkData( $data ) );
$node = $this->dom->createDocumentFragment();
$node->appendXML( $html );
$this->form->appendChild( $node );
return $this->dom;
}
}
class Form
{
protected $name;
protected $type;
protected $data;
protected $structure;
protected $formRenderer;
public function __construct( $name , $method = 'POST' )
{
$cleanMethod = strtoupper( $method );
if ( $cleanMethod == 'POST' || $cleanMethod == 'GET' ) {
$this->method = $cleanMethod;
}
else
throw new Exception( "Unknown method requested for form $name");
$this->name = $name;
$this->collectData( );
}
private function collectData( )
{
$dataVar = '_'.$this->method;
if ( !empty( $GLOBALS[$dataVar] ) )
$this->data = $GLOBALS[$dataVar][$this->name];
}
public function addField( Field $field )
{
return $this->structure[] = $field->setForm( $this->name, $this->data );
}
public function addGroup( Group $group )
{
return $this->structure[] = $group->setForm( $this->name, $this->data );
}
public function render( formRenderer $renderer = NULL ) {
$this->renderer = ( isset( $renderer ) ) ? $renderer : new formRenderer;
$this->renderer->Form( array( 'method' => $this->method, 'name' => $this->name ) );
$renderer = $this->renderer;
foreach( $this->structure as $structure )
$renderer = $structure->render( $renderer );
return $renderer->save();
}
public function isSubmitted( )
{
// Check whether we have GET/POST data for some stuff, which will tell us if it was submitted
return ( isset( $this->data ) );
}
public function validate( )
{
foreach( $this->structure as $structure )
$structure->validate();
// Loop through validators. Pass in $data['FieldName'];
// Or maybe, get fields to validate themself, using validators. Point is, each $data == a value for a $this->fields
}
}
abstract class Group
{
protected $data;
protected $structure = array();
public function addFeild( Field $field )
{
$this->structure[] = $field;
}
public function setForm( $name, $data ) {
$this->data = $data;
foreach( $this->structure as $obj ) $obj->setForm( $name, $data );
return $this;
}
public function render( $renderer ) {
$dataCount = array();
foreach( $this->structure as $obj ) $dataCount[] = $obj->getDataGroup( true );
$iterations = ( max( $dataCount ) === 0 ) ? 1 : max( $dataCount ) ;
for ( $i = 0; $i < $iterations ; $i++ ) {
foreach( $this->structure as $obj ) { $obj->setIteration( $i ); $obj->render( $renderer ); }
}
return $renderer;
}
public function validate( ) {
foreach( $this->structure as $obj ) $obj->validate( );
}
}
class QuestionGroup extends Group
{
public function __construct( LabelField $label, Field $input ) {
$this->structure[0] = $label;
$this->structure[1] = $input;
}
}
class HoneyPotGroup extends Group
{
public function __construct( HoneyPotField $timeCheck, HoneyPotField $empty ) {
$this->structure[0] = $timeCheck;
$this->structure[1] = $empty;
}
}
abstract class Field
{
protected $vars = array();
private static $id = 0;
private $iteration = 0;
private $validators = array( );
private $staticValue = FALSE;
private $validate = FALSE;
private $data;
final public function setName( ) {
if( !isset( $this->vars['name'] ) )
{
$this->uid = self::$id++;
return $this->vars['name'] = $this->formName.'['.$this->uid.'][]';
}
else
{
throw new Exception("Attempted to create unique, unique already excists");
}
}
final public function setData( ) {
$this->data = Stuff::getArrayValueByPath( $this->formData, array( $this->uid ) );
}
final public function setIteration( $iteration = NULL) {
$this->iteration = $iteration;
}
final public function getDataGroup( $count = FALSE ) {
if( $count )
return count( array_filter( (array) $this->data ) );
else
return $this->data;
}
public function setType( $type )
{
$this->vars['type'] = $type;
}
public function setValue( $value, $type = NULL )
{
if( isset( $type ) )
$this->vars['value'.ucfirst( $type )] = $value;
else
$this->vars['value'] = $value;
}
public function getValue( $type = NULL )
{
if( isset( $type ) )
{
if( isset( $this->vars['value'.ucfirst( $type )] ) )
{
return $this->vars['value'.ucfirst( $type )];
}
} else {
return $this->vars['value'];
}
return NULL;
}
public function getValidatorValue() {
$value = $this->getValue( 'validator' );
if( isset( $value ) ) return $this->getValue( 'validator' );
else return $this->getValue();
}
public function setStaticValue( $value ) {
$this->staticValue = TRUE;
$this->setValue( $value );
}
public function setForm( $name, $data )
{
$this->formData = $data;
$this->formName = $name;
$this->setName();
$this->setData();
return $this;
}
public function validator( Validator $validator )
{
$this->validators[] = $validator;
}
public function validate( )
{
$this->validate = TRUE;
// if( $this->data )
// {
// foreach( $this->data as $value ) {
// foreach ( $this->validators as $validator ) {
// $validator->setValue( $value );
// $validator->validate( );
// }
// }
// }
}
public function render( $renderer ) {
$function = ucwords( $this->vars['type'] );
$value = Stuff::getArrayValueByPath( $this->data, array( $this->iteration ) );
if( $this->staticValue === FALSE ) $this->setValue( $value );
else $this->setValue( $value, 'validator' );
if( $this->validate )
{
foreach( $this->validators as $validator )
{
$validator->setValue( $this->getValidatorValue() );
var_dump( $validator->validate() );
}
}
if( method_exists( $renderer, $function ) )
$renderer->$function( $this->vars );
else
$renderer->base( $this->vars );
return $renderer;
}
}
class ActionField extends Field
{
public function __construct( $value = NULL )
{
$this->vars['type'] = 'submit';
if( isset( $value ) )
$this->vars['value'] = $value;
}
}
class UploadField extends Field {
public function __construct() {
$this->vars['type'] = 'file';
}
}
class LabelField extends Field {
public function __construct( $text = 'Untitled' ) {
$this->vars['type'] = 'label';
$this->setStaticValue( $text );
}
public function setForm( $name, $data )
{
parent::setForm( $name, $data );
$this->vars['for'] = $this->formName.'['.($this->uid + 1).'][]';
return $this;
}
}
class TextField extends Field {
public function __construct() {
$this->vars['type'] = 'text';
}
}
class HtmlField extends Field {
public function __construct( $html ) {
$this->vars['type'] = 'html';
$this->vars['html'] = $html;
}
}
class HiddenField extends Field {
public function __construct() {
$this->vars['type'] = 'hidden';
}
}
class HoneyPotField extends Field {
public function __construct( $value = NULL ) {
$this->vars['type'] = 'text';
$this->vars['classes'] = 'hc';
$this->vars['autocomplete'] = 'off';
$this->setStaticValue( $value );
}
}
class InputField extends Field {
public function __construct( $type ) {
$this->vars['type'] = $type;
}
}
class Validator
{
protected $failureText = array( );
protected $results = array( );
protected $rules = array( );
private $valid = TRUE;
final public function __construct( Rule $rule )
{
$this->rules[] = $rule;
return $this;
}
final public function addRule( Rule $rule )
{
$this->rules[] = $rule;
return $this;
}
public function valid( $returnFailureText = false )
{
return ( $returnFailureText ) ? $this->failureText[] = $returnFailureText : $this->valid;
}
private function invalidate( $failureText )
{
$this->valid( $failureText );
$this->valid = false;
}
public function setValue( $value )
{
foreach( $this->rules as $rule ) $rule->setValue( $value );
}
public function validate( )
{
foreach ( $this->rules as $rule ) {
$result = $this->results[] = $rule->validate( );
if ( $result === FALSE ) {
$this->invalidate( $rule->getFailureText() );
}
}
$this->getErrors();
return $this->valid;
}
public function getErrors() {
return $this->failureText;
}
}
abstract class Rule
{
protected $limit;
protected $value;
protected $failureText;
final public function __construct( $failureText, $limit = NULL, $value = NULL )
{
$this->failureText = $failureText;
$this->limit = $limit;
$this->value = $value;
}
final public function setValue( $value )
{
$this->value = $value;
return $this;
}
final public function getFailureText() {
return $this->failureText;
}
}
class GreaterThanRule extends Rule
{
public function validate( )
{
return $this->result = ( $this->value > $this->limit );
}
}
class LessThanRule extends Rule
{
public function validate( )
{
return $this->result = ( $this->value < $this->limit );
}
}
class ValidEmailRule extends Rule
{
public function validate( )
{
return $this->result = filter_var( $this->value, FILTER_VALIDATE_EMAIL );
}
}
class ValidUrlRule extends Rule
{
public function validate( )
{
return $this->result = filter_var( $this->value, FILTER_VALIDATE_URL );
}
}
class ValidNameRule extends Rule
{
public function validate( )
{
return $this->result = ctype_print( $this->value );
}
}
class NumericRule extends Rule
{
public function validate( )
{
return $this->result = is_numeric( $this->value );
}
}
class TextRule extends Rule
{
public function validate( )
{
return $this->result = ctype_alpha( $this->value );
}
}
class StringLengthGreaterThanRule extends Rule
{
public function validate( )
{
return $this->result = ( strlen( $this->value ) > $this->limit );
}
}
class StringLengthLessThanRule extends Rule
{
public function validate( )
{
return $this->result = ( strlen( $this->value ) < $this->limit );
}
}
class DateCheckRule extends Rule
{
public function validate( )
{
return $this->result = strtotime( $this->value );
}
}
class HoneyPotTimeRule extends Rule
{
public function validate( )
{
$limit = ( isset( $this->limit ) ) ? $this->limit : 2;
return $this->result = ( ( $this->value + $limit ) <= time() );
}
}
class EqualRule extends Rule
{
public function validate( )
{
return $this->result = ( $this->value === $this->limit );
}
}
// An example of how to use it
$form = new Form( 'Form', 'post' );
$honeyPot1 = new HoneyPotField( time() );
$honeyPot1->validator( new Validator( new HoneyPotTimeRule( 'This form was filled in too quickly, assumed bot, please allow 2 seconds to fill in the form' ) ) );
$honeyPot2 = new HoneyPotField();
$honeyPot2->validator( new Validator( new EqualRule( 'This field was unexpectedly filled in, assumed bot', '' ) ) );
$q1l = new LabelField( 'Question 1' );
$q1 = new InputField( 'text' );
$q2l = new LabelField( 'Question 2' );
$q2 = new InputField( 'text' );
$form->addGroup( new HoneyPotGroup( $honeyPot1, $honeyPot2 ) );
$form->addGroup( new QuestionGroup( $q1l, $q1 ) );
$form->addGroup( new QuestionGroup( $q2l, $q2 ) );
$form->addField( new UploadField( 'text' ) );
$form->addField( new ActionField( 'Send' ) );
if( $form->isSubmitted() )
$form->validate( );
echo $form->render( ); // outputs the form + elements
- Output for 5.3.3 - 5.3.29, 5.4.0 - 5.4.45, 5.5.0 - 5.5.38, 5.6.0 - 5.6.28, 7.0.0 - 7.0.14, 7.1.0
- <form method="POST" name="Form"><input name="Form[0][]" id="Form[0][]" type="text" class="hc" value="1375190828" autocomplete="off"><input name="Form[1][]" id="Form[1][]" type="text" class="hc" autocomplete="off"><label for="Form[3][]">Question 1</label><input name="Form[3][]" id="Form[3][]" type="text"><label for="Form[5][]">Question 2</label><input name="Form[5][]" id="Form[5][]" type="text"><input name="Form[6][]" id="Form[6][]" type="file"><input name="Form[7][]" id="Form[7][]" type="submit"></form>
- Output for 5.3.0 - 5.3.2
- <form method="POST" name="Form">
<input name="Form[0][]" id="Form[0][]" type="text" class="hc" value="1375190828" autocomplete="off"><input name="Form[1][]" id="Form[1][]" type="text" class="hc" autocomplete="off"><label for="Form[3][]">Question 1</label><input name="Form[3][]" id="Form[3][]" type="text"><label for="Form[5][]">Question 2</label><input name="Form[5][]" id="Form[5][]" type="text"><input name="Form[6][]" id="Form[6][]" type="file"><input name="Form[7][]" id="Form[7][]" type="submit">
</form>
preferences:
1637.33 ms | 1400 KiB | 176 Q