_name = $name; $this->_value = $value; $this->_attr = is_array($attr) ? $attr : array(); $this->_required = NULL; $this->_valid = NULL; $this->_label = ''; $this->_id = NULL; $this->post_construct(); } protected function mkname() { if (is_object($this->_originform) && $fn = $this->_originform->name()) { return sprintf('%s[%s]', $fn, $this->_name); } else { return $this->_name; } } public function type() { $c = get_class($this); $c = preg_replace('/^form_(input_)?/', '', $c); $c = preg_replace('/_series$/', '', $c); return $c; } public function name() { return $this->_name; } public function origin_form($fm) { if (is_object($fm) && is_a($fm, 'form')) { $this->_originform = $fm; $this->_id = NULL; # reset it, so it gets recalculated against the new form } } public function attributes($newattr = NULL) { if (isset($newattr)) { $this->_attr = $newattr; } return $this->_attr; } public function value($submitted_value = NULL) { if (isset($submitted_value)) { $this->_value = $submitted_value; } if (!isset($this->_value)) { return $this->_defaultvalue; } return $this->_value; } # this exists solely to let children override how their # submitted values are interpreted # with checkboxes, if they are unchecked, nothing is submitted # however, we don't want "nothing" to change the value of the field # when it is submitted public function submitted_value($sv = NULL) { return $this->value($sv); } public function default_value($newdefault = NULL) { if (isset($newdefault)) { $this->_defaultvalue = $newdefault; } return $this->_defaultvalue; } protected function post_construct() { return; } public function required($r = true) { $this->_required = $r; return $this; } public function verify_using($func, $callalways = false) { if (is_callable($func)) { $this->_validationfunc = $func; $this->_call_validator_always = $callalways; } else { throw new Exception(var_dump($func, true)." is not callable"); } return $this; } public function verify() { if (is_bool($this->_valid)) { return $this->_valid; } if (isset($this->_required) && $this->_required && isset($this->_value) && empty($this->_value)) { $this->_valid = false; $this->_errormsg = 'required'; } if ((!isset($this->_valid) || $this->_call_validator_always) && $this->_validationfunc) { $r = call_user_func($this->_validationfunc, $this->_value, $this->_name, $this->_originform); if (!is_array($r) || count($r) != 3) { throw new Exception(var_dump($method, true)." did not return a three element array"); } $this->_valid = $r[0] ? true : false; # convert to boolean $this->_value = $r[1]; $this->_errormsg = $r[2]; } if (!isset($this->_valid)) { $this->_valid = true; } return $this->_valid; } public function message() { return $this->_errormsg; } public function html_open() { return ''; } public function html_close() { return ''; } public function html() { return $this->html_open().$this->html_close(); } public function label_html() { return sprintf('', $this->id(), $this->_label); } public function label_str() { return $this->_label; } public function id($newid = NULL) { if (isset($newid)) { $this->_id = $newid; } if (!isset($this->_id)) { if (is_object($this->_originform)) { $ofn = $this->_originform->name(); } else { $ofn = uniqid(); } $this->_id = sprintf('%s_%s', $ofn, $this->_name); } return $this->_id; } public function label($text) { $this->_label = $text; return $this; } public function notes($text = NULL) { $this->_notes = $text; return $this; } public function notes_html() { return $this->_notes; } protected function render_attributes($extra=NULL) { $r = ''; foreach ($this->_attr as $an=>$av) { if (substr($an, 0, 1) != '_') { $r .= "$an=\"$av\" "; } } if ($id = $this->id()) { $r .= sprintf('id="%s" ', $id); } return $r; } } class form_input_text extends formfield { public function html() { $r = sprintf('mkname(), $this->value()); $r .= $this->render_attributes(); $r .= '/>'; return $r; } } class form_input_hidden extends formfield { public function html() { $r = sprintf('', $this->mkname(), $this->value()); return $r; } } class form_input_password extends formfield { public function html() { $r = sprintf('mkname(), $this->value()); $r .= $this->render_attributes(); $r .= '/>'; return $r; } } class form_input_checkbox extends formfield { private $_value_when_checked; public function __construct($name, $value=NULL, $attr=array()) { parent::__construct($name, '', $attr); $this->_value_when_checked = $value; } public function html() { $checked = ''; if ($this->value()) { $checked = 'checked="CHECKED"'; } $r = sprintf('mkname(), $this->_value_when_checked, $checked); $r .= $this->render_attributes(); $r .= '/>'; if ($this->_label) { $r .= ""; } return $r; } } class form_textarea extends formfield { public function html() { $r = sprintf('"; return $r; } } class form_input_image extends formfield { # attributes should include a src value, or the template # needs to use modify the output to include it # we don't check this in post_construct (like type attribute # used for form_button) because the template needs to control # it public function html() { $r = sprintf('mkname()); $r .= $this->render_attributes(); $r .= '/>'; return $r; } } class form_button extends formfield { private $_value_when_clicked; public function __construct($name, $value=NULL, $attr=array()) { parent::__construct($name, '', $attr); $this->_value_when_clicked = $value; } protected function post_construct() { if (!isset($this->_attr['type'])) { throw new Exception("field \"".$this->_name."\" not constructed with a \"type\" attribute"); } if (!preg_match('/^submit|reset|button$/', $this->_attr['type'])) { throw new Exception("field \"".$this->_name."\" constructed with unknown type \"".$this->_attr['type']."\""); } } public function html_open() { $r = sprintf(''; } public function html() { if (!$this->_label) { throw new Exception("can not use form_button::html without calling ->label() first"); } return $this->html_open().$this->_label.$this->html_close(); } } class form_input_submit extends formfield { private $_value_when_clicked; public function __construct($name, $value=NULL, $attr=array()) { parent::__construct($name, '', $attr); $this->_value_when_clicked = $value; } public function html() { $r = sprintf('mkname(), $this->_value_when_clicked); $r .= $this->render_attributes(); $r .= '/>'; return $r; } } class form_input_reset extends formfield { public function html() { $r = 'render_attributes(); $r .= '/>'; return $r; } } class form_input_radio_series extends formfield { protected $__options; public function options($o) { if (is_array($o)) { $this->__options = $o; } return $this; } public function html() { if (!isset($this->__options) || !is_array($this->__options)) { throw new Exception($this->_name."(".get_class($this).") does not have an options list"); } $items = array(); $c = 0; $id = $this->id(); $value = $this->value(); $elmname = $this->mkname(); foreach ($this->__options as $v=>$l) { $c++; $iid = $id."_".$c; $checked = ($value == $v ? 'checked="checked"' : ''); $i = sprintf('', $elmname, $iid, $v, $checked, $iid, $l); $items[] = "
  • $i
  • \n"; } $r = "\n"; return $r; } } class form_input_select_series extends form_input_radio_series { public function html() { if (!isset($this->__options) || !is_array($this->__options)) { throw new Exception($this->_name."(".get_class($this).") does not have an options list"); } $items = array(); $c = 0; $id = $this->id(); $value = $this->value(); foreach ($this->__options as $v=>$l) { $c++; $iid = $id."_".$c; $checked = ($value == $v ? 'selected="selected"' : ''); $i = sprintf('', $iid, $v, $checked, $l); $items[] = " $i\n"; } $r = "\n"; return $r; } } class form implements Countable, ArrayAccess, Iterator { private $_name; private $_datasource; private $_data; private $_submit_method; private $_submit_action; private $_fields; protected $message; public function __construct($name, $datasource='REQUEST') { $this->_name = $name; $this->message = ''; $datasource = strtolower($datasource); switch($datasource) { case 'get': $this->_datasource = &$_GET; $this->method('get'); break; case 'post': $this->_datasource = &$_POST; $this->method('post'); break; case 'request': $this->_datasource = &$_REQUEST; $this->method('post'); break; default: throw new Exception("unknown datasource $datasource for form $name"); } if (!empty($this->_datasource[$name]) && is_array($this->_datasource[$name])) { $this->_data = $this->_datasource[$name]; $this->_datasource[$name.'-consumed'] = $this->_datasource[$name]; unset($this->_datasource[$name]); } else { $this->_data = false; } } public function name() { return $this->_name; } public function message() { return $this->message; } public function submitted() { return is_array($this->_data); } public function dump() { $x = array(); foreach ($this as $fn=>$o) { $x[$fn] = $o->value(); } return var_export(array('name'=>$this->_name, 'fields'=>$x, 'submitted'=>is_array($this->_data)), true); } public function method($m) { if (preg_match('/^get|post$/i', $m)) { $this->_submit_method = strtolower($m); } else { throw new Exception("$m is not an acceptable form submission method for form ".$this->name); } } public function action($a) { $this->_submit_action = $a; } public function verify() { $success = true; if (is_array($this->_data)) { foreach ($this as $name=>$field) { if (!$field->verify()) { $success = false; } } } return $success; } public function start() { $r = '
    _submit_action) { $r .= ' action="'.$this->_submit_action.'"'; } $r .= '>'; return $r; } public function end() { $r = ''; foreach ($this->_fields as $fname=>$i) { if (is_a($i, 'form_input_hidden')) { $r .= $i->html(); } } $r .= '
    '; return $r; } # Countable interface public function count() { return count($this->_fields); } # ArrayAccess interface public function offsetExists($offset) { return (isset($this->_fields[$offset]) && is_object($this->_fields[$offset])); } public function offsetGet($offset) { if (!isset($this->_fields[$offset]) || !is_object($this->_fields[$offset])) { error_log("field $offset does not exist in form ".$this->_name); return new formfield('empty'); } return $this->_fields[$offset]; } public function offsetSet($offset, $value) { # we purposely ignore the offset value if (is_object($value) && is_a($value, 'formfield')) { $value->origin_form($this); $fieldname = $value->name(); if (is_array($this->_data)) { # if the form was actually submitted $value->submitted_value(isset($this->_data[$fieldname]) ? $this->_data[$fieldname] : NULL); } $this->_fields[$fieldname] = $value; } } public function offsetUnset($offset) { unset($this->_fields[$offset]); } # Iterator interface public function current() { return current($this->_fields); } public function rewind() { return reset($this->_fields); } public function key() { return key($this->_fields); } public function next() { return next($this->_fields); } public function valid() { return current($this->_fields) ? true : false; } } ?>