3v4l.org

run code in 300+ PHP versions simultaneously
<?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