\n"; print_r($struct); echo "\n\n"; } #------------------------------------------------------------------------------# //////////////////////////////////////////////////////////////////////////////// class RazorForm { var $rows = array(); var $err_count = 0; var $curr_row = 1; var $layout = "default"; var $label_sep = " "; var $row_punc = ""; var $input_punc = ""; var $def_errmsg; var $in; var $subsequent = false; var $user_vars = array(); var $inp_defaults = array(); var $hiddens = array(); var $attrs = array(); var $lab_cell_valign = "top"; var $form_table_class = "form-table"; var $form_row_class = "form-row"; var $field_lab_class = "field-lab"; var $field_lab_err_class = "field-lab-err"; var $field_lab_cell_class = "field-lab-cell"; var $field_class = "field"; var $field_err_class = "field-err"; var $field_cell_class = "field-cell"; var $field_block_class = "field-block"; var $errmsg_class = "errmsg"; var $field_note_class = "field-note"; var $radio_div_class = "radio-div"; #--------------------------------------------------------------------------# function RazorForm($args=false, $in=false) { if (!$in) $this->in = $_REQUEST; if (isset($this->in['rf_subsequent'])) $this->subsequent = true; if (is_array($args)) { while (list($varname, $val) = each($args)) $this->set($varname, $val); } elseif (is_string($args)) { $res = (strpos($args, "parse_form_def($formdef); } else { $this->parse_form_def($args); } } } #--------------------------------------------------------------------------# // Takes an argument of the formdef string and breaks it up into an array // of just the RazorForm element definitions like , , // , etc. Any angle brackets found inside the angle brackets of the // whole element are just passed along like anything else (which is why I // didn't use a regular expression for this part). function get_defs($formdef) { $defs = array(); $def = ""; $def_on = false; $nested = 0; for ($i = 0; $i < strlen($formdef); $i++) { $char = $formdef{$i}; if ($char == "<") { if ($def_on) { $nested++; $def .= $char; } $def_on = true; } elseif ($char == ">") { if ($nested > 0) { $def .= $char; $nested--; } else { $defs[] = $def; $def = ""; $def_on = false; } } elseif ($def_on) $def .= $char; } return $defs; } #--------------------------------------------------------------------------# function parse_form_def($formdef) { $defs = $this->get_defs($formdef); $tags = "RazorForm|default|var|row|input|option|\/RazorForm"; $defre = "/^($tags)(\s+(.*)?)?$/s"; $attr_re = "/((\w+)\s*=\s*(\w+|(\"|').*?\\4))|(\w+)/s"; foreach ($defs as $def) { $defsegs = array(); if (!preg_match($defre, $def, $defsegs)) { $this->bad_def_format($def); continue; } $elem_type = $defsegs[1]; if ($elem_type == "/RazorForm") { break; } elseif ($elem_type == "RazorForm") { $obj =& $this; } elseif ($elem_type == "row") { $this->add_row(new RazorFormRow()); $obj =& $this->rows[count($this->rows)-1]; } elseif ($elem_type == "input") { if (count($this->rows) == 0) { // the only inputs allowed before a row def are hidden $args = array("layout" => "hidden"); $this->hiddens[] = new RazorFormInput($args); $obj =& $this->hiddens[count($this->hiddens)-1]; } else { $row =& $this->rows[count($this->rows)-1]; $row->add_input(new RazorFormInput()); $row_inputs =& $row->get_inputs_ref(); $obj =& $row_inputs[count($row_inputs)-1]; foreach ($this->inp_defaults as $key => $val) $obj->set($key, $val); } } elseif ($elem_type == "option") { $row =& $this->rows[count($this->rows)-1]; $row_inputs =& $row->get_inputs_ref(); $inp =& $row_inputs[count($row_inputs)-1]; $inp->add_option(new RazorFormOption()); $options =& $inp->get_options_ref(); $obj =& $options[count($options)-1]; } if (isset($defsegs[3])) { $attr_seg = $defsegs[3]; $groups = array(); if (!preg_match_all($attr_re, $attr_seg, $groups)) { $this->bad_def_format($def); continue; } $keyvals = array(); for ($i = 0; $i < count($groups[1]); $i++) { if ((empty($groups[1][$i])) && (!empty($groups[5][$i]))) { // handle the stand-alone words $key = $groups[5][$i]; $val = true; } else { // handle the key-value pairs $key = $groups[2][$i]; $val = $groups[3][$i]; // don't let them specify "value" in the definition // when they probably mean "default" if ($elem_type == "input") { $hsi = false; foreach ($keyvals as $kv) { list ($k, $v) = each($kv); if ($k == "type") { if (in_array($v, array("hidden", "submit", "image", "static"))) { $hsi = true; break; } } } if ((strtolower($key) == "value") && (!$hsi)) { $msg = "
RazorForm error: You cannot specify value in an input definition for an input of any type other than hidden, static, or submit. Perhaps you meant default? (Also keep in mind that for hidden and submit inputs, the value must follow the type declaration.) The offending definition:
$def
"; exit($msg); } } // strip the quotes if ((($val{0} == "'") && (substr($val, -1) == "'")) || (($val{0} == "\"") && (substr($val, -1) == "\""))) $val = substr($val, 1, -1); } $keyvals[] = array($key => $val); } foreach ($keyvals as $kv) { list($key, $val) = each($kv); // check to see if they've requested a user var for the val while (list($uk, $uv) = each($this->user_vars)) { if ($val == $uk) $val = $uv; } reset($this->user_vars); if ($elem_type == "var") $this->add_user_var($key, $val); elseif ($elem_type == "default") $this->add_default($key, $val); else $obj->set($key, $val); } } } } #--------------------------------------------------------------------------# function bad_def_format($def) { $msg = "RazorForm error: The RazorForm element definition $def is not in a correct format. Skipping.\n"; echo $msg; } #--------------------------------------------------------------------------# function add_user_var($key, $val) { $this->user_vars[$key] = $val; } #--------------------------------------------------------------------------# function add_default($key, $val) { $rf_defaults = array("layout", "row_punc", "input_punc", "lab_cell_valign", "label_sep"); if ($key == "errmsg") $this->def_errmsg = $val; elseif (in_array($key, $rf_defaults)) $this->{$key} = $val; else $this->inp_defaults[$key] = $val; } #--------------------------------------------------------------------------# function set($varname, $val) { if (in_array($varname, array_keys(get_class_vars("RazorForm")))) $this->{$varname} = $val; else $this->attrs[$varname] = $val; } #--------------------------------------------------------------------------# function get($varname) { if (isset($this->{$varname})) return $this->{$varname}; else return false; } #--------------------------------------------------------------------------# function add_row($row) { $row->set_inherited($this); $this->rows[] = $row; } #--------------------------------------------------------------------------# function get_form($formtag=true) { $this->start_form($formtag); $this->get_rows(); $this->end_form($formtag); } #--------------------------------------------------------------------------# function start_form($formtag=true) { $output = ""; if ($formtag) { $str .= "attrs)) $str .= " $attr=\"$val\""; $str .= ">"; $output .= pretty($str); } $args = array("type" => "hidden", "name" => "rf_subsequent", "value" => "true"); array_unshift($this->hiddens, new RazorFormInput($args)); foreach ($this->hiddens as $inp) $output .= pretty($inp->get_input()); if ($style = $this->get_style()); $output .= $style; $layout_obj = $this->get_layout_obj(); if ($str = $layout_obj->get_start()) $output .= $str; echo $output; } #--------------------------------------------------------------------------# function get_style() { $ret_output = false; $output = pretty(""); if ($ret_output) return $output; else return false; } #--------------------------------------------------------------------------# function end_form($formtag=true) { $output = ""; $layout_obj = $this->get_layout_obj(); if ($str = $layout_obj->get_end()) $output .= $str; if ($formtag) $output .= pretty(""); echo $output; } #--------------------------------------------------------------------------# function get_rows($num=false, $endrow=false) { if ($num) { if ($endrow) { $startrow = $num; } else { $startrow = $this->curr_row; $endrow = $startrow + $num - 1; } if ($endrow > count($this->rows)) $endrow = count($this->rows); } else { $startrow = $this->curr_row; $endrow = count($this->rows); } $this->curr_row = $endrow + 1; // print out the rows for ($i = $startrow; $i <= $endrow; $i++) $this->get_row($i); } #--------------------------------------------------------------------------# function get_row($rowid=false) { if (!$rowid) $rowid = $this->curr_row; if ($rowid > count($this->rows)) { $n = count($this->rows); $msg = "RazorForm error: You are asking for row $rowid, but there are only $n rows.

\n"; exit($msg); } $this->curr_row = $rowid + 1; if (is_int($rowid)) $rowid--; $row = $this->rows[$rowid]; echo $row->get_output(); } #--------------------------------------------------------------------------# function reset_curr_row() { $this->curr_row = 1; } #--------------------------------------------------------------------------# function get_layout_obj() { $layout_class = "layout_" . $this->layout; return new $layout_class($this); } #--------------------------------------------------------------------------# function check_errors() { if (!$this->subsequent) return false; $in = $this->in; $this->populate($in, true); $inputs = $this->get_rf_inputs(); for ($i = 0; $i < count($inputs); $i++) { $inp =& $inputs[$i]; $name = $inp->get("name"); $value = $inp->get("value"); if ($inp->is_required()) { if ($inp->has_error()) $this->err_count++; } if ($twin_name = $inp->get_sameas()) { if ($in[$name] != $in[$twin_name]) { $this->err_count++; $inp->set_sameas_err(); for ($j = 0; $j < count($inputs); $j++) { $twin =& $inputs[$j]; if ($twin->get("name") == $twin_name) { $twin->set_sameas_err(); break; } } } } } return $this->err_count; } #--------------------------------------------------------------------------# // This function is internally used by check_errors() and populate(). It // returns an array of all the input objects in this form. function get_rf_inputs() { $inputs = array(); for ($i = 0; $i < count($this->rows); $i++) { $row =& $this->rows[$i]; $row_inputs =& $row->get_inputs_ref(); for ($j = 0; $j < count($row_inputs); $j++) $inputs[] =& $row_inputs[$j]; } return $inputs; } #--------------------------------------------------------------------------# // This function accepts a string-indexed array of data with which to // populate the form. For every key in the array that also exists as an // input in the form definition, that key's value will be transferred to // the corresponding input field. function populate($data) { $inputs = $this->get_rf_inputs(); for ($i = 0; $i < count($inputs); $i++) { $inp =& $inputs[$i]; $name = $inp->get("name"); $name = str_replace("[]", "", $name); $type = $inp->get("type"); if (isset($data[$name])) { if ((is_array($data[$name])) && ($type != "multiselect")) $inp->set("value", array_shift($data[$name])); else $inp->set("value", $data[$name]); } } } #--------------------------------------------------------------------------# } //////////////////////////////////////////////////////////////////////////////// class RazorFormRow { var $inputs = array(); var $has_error = false; var $error_tested = false; var $inherited = array( "layout", "label_sep", "row_punc", "input_punc", "def_errmsg", "subsequent", "lab_cell_valign", "form_table_class", "form_row_class", "field_lab_class", "field_lab_err_class", "field_lab_cell_class", "field_class", "field_err_class", "field_cell_class", "field_block_class", "errmsg_class", "field_note_class", "radio_div_class" ); #--------------------------------------------------------------------------# function RazorFormRow($args=array()) { while (list($varname, $val) = each($args)) $this->set($varname, $val); } #--------------------------------------------------------------------------# function set($varname, $val) { $this->{$varname} = $val; } #--------------------------------------------------------------------------# function get($varname) { if (isset($this->{$varname})) return $this->{$varname}; else return false; } #--------------------------------------------------------------------------# function add_input($input) { $input->set_inherited($this); $this->inputs[] = $input; } #--------------------------------------------------------------------------# function &get_inputs_ref() { return $this->inputs; } #--------------------------------------------------------------------------# function get_inputs() { return $this->inputs; } #--------------------------------------------------------------------------# function get_label() { if (isset($this->label)) { if ($this->has_error()) $label = "
field_lab_err_class}\">"; else $label = "
field_lab_class}\">"; $label .= $this->label; $label .= "
"; return $label; } else return false; } #--------------------------------------------------------------------------# function has_error() { if ($this->error_tested) return $this->has_error; foreach ($this->inputs as $inp) { if ($inp->has_error()) { $this->has_error = true; break; } } $this->error_tested = true; return $this->has_error; } #--------------------------------------------------------------------------# function has_note() { foreach ($this->inputs as $inp) { if ($inp->get_note()) return true; } return false; } #--------------------------------------------------------------------------# function set_inherited($rf) { foreach ($this->inherited as $prop) { if (!isset($this->{$prop})) $newval = $rf->get($prop); if (strlen($newval) > 0) $this->set($prop, $newval); } for ($i = 0; $i < count($this->inputs); $i++) { $inp = $this->inputs[$i]; $inp->set_inherited($this); $this->inputs[$i] = $inp; } } #--------------------------------------------------------------------------# function get_output() { $layout_class = "layout_" . $this->layout; $layout_obj = new $layout_class($this); return $layout_obj->get_output(); } #--------------------------------------------------------------------------# } //////////////////////////////////////////////////////////////////////////////// class RazorFormInput { var $name; var $label; var $type; var $attrs = array(); var $default; var $value; var $note; var $subsequent; var $input_punc; var $def_errmsg; var $error_stuff = array(); var $errors = array(); var $error_checked = false; var $options = array(); var $inherited = array( "subsequent", "input_punc", "def_errmsg", "field_lab_class", "field_lab_err_class", "field_lab_cell_class", "field_class", "field_err_class", "field_cell_class", "field_block_class", "errmsg_class", "field_note_class", "radio_div_class" ); #--------------------------------------------------------------------------# function RazorFormInput($args=array()) { while (list($varname, $val) = each($args)) $this->set($varname, $val); } #--------------------------------------------------------------------------# function set($varname, $val=false) { $bi_re = "/builtin:\s*(\w+)/"; $class_vars = array_keys(get_class_vars("RazorFormInput")); $err_elems = array("req", "re", "chars", "sameas", "errmsg", "function"); if (($varname != "options") && (preg_match($bi_re, $val))) $val = $this->get_builtin($val); if ($varname == "options") { $this->get_builtin_options($val); } elseif (in_array($varname, $err_elems)) { $this->set_err_elem($varname, $val); } elseif (preg_match("/^array\(.*?\);?$/", $val)) { if (substr($val, -1) != ";") $val .= ";"; $cmd = '$this->{$varname} = ' . $val; eval($cmd); } elseif (in_array($varname, $class_vars)) { $this->{$varname} = $val; } else $this->attrs[$varname] = $val; } #--------------------------------------------------------------------------# function get($attr) { if (in_array($attr, array_keys(get_class_vars("RazorFormInput")))) return $this->{$attr}; else return $this->attrs[$attr]; } #--------------------------------------------------------------------------# function get_builtin_options($val) { $bi_re = "/(builtin:\s*)?(\w+)/"; $groups = array(); $res = preg_match($bi_re, $val, $groups); $func = "builtin_{$groups[2]}"; if (function_exists($func)) { $new_opts = $func(); $this->options = array_merge($this->options, $new_opts); } } #--------------------------------------------------------------------------# function get_builtin($val) { $bi_re = "/builtin:\s*(\w+)/"; $groups = array(); $res = preg_match($bi_re, $val, $groups); $bi_name = $groups[1]; $func = "builtin_$bi_name"; if (function_exists($func)) return $func(); else die("RazorForm error: there is no built-in called $bi_name."); } #--------------------------------------------------------------------------# function set_err_elem($elem, $val) { // re, sameas, and function just get set as is if (in_array($elem, array("re", "sameas", "function"))) { $this->error_stuff[] = array("type" => $elem, "val" => $val); return; } // req is a shortcut for a regex of a min of 1 character if ($elem == "req") { $this->error_stuff[] = array("type" => "re", "val" => "/.+/"); return; } // chars is a shortcut for a regex requiring a minimum number // of characters if ($elem == "chars") { $this->error_stuff[] = array("type"=>"re", "val"=>"/.\{$val}/"); return; } // errmsg is associated with the last element added if ($elem == "errmsg") { $last_elem = false; for ($i = count($this->error_stuff)-1; $i >= 0; $i--) { $last_elem =& $this->error_stuff[$i]; break; } if ($last_elem) { $last_elem['errmsg'] = $val; } else { $msg = "RazorForm error: You have specified an errmsg without an re, a req, a chars, or a sameas before it. The offending message: $val
"; echo $msg; return; } } } #--------------------------------------------------------------------------# function is_required() { if (count($this->error_stuff) > 0) return true; else return false; } #--------------------------------------------------------------------------# function has_error() { // don't do all the error checking stuff if this is the first form view if (!$this->subsequent) return false; // we don't want to have to run through all the error checking stuff // for each time this gets called, so we have a flag called // error_checked if ($this->error_checked) return (count($this->errors) > 0); $use_default = false; foreach ($this->error_stuff as $elem) { $elem_err = false; if ($elem['type'] == "re") { if (!preg_match($elem['val'], $this->value)) $elem_err = true; } if ($elem['type'] == "function") { $function = $elem['val']; $res = $function($this->value); if ($res == false) continue; if (is_bool($res)) $res = array(); elseif (is_string($res)) $res = array($res); if (!is_array($res)) { $msg = "RazorForm error: The error checking function $function must return either true, a string containing an error message, or an array of strings containing error messages, or false
\n"; echo $msg; continue; } $elem_err = true; $this->errors = array_merge($this->errors, $res); } if ($elem_err) { $errmsg = isset($elem['errmsg']) ? $elem['errmsg'] : false; if ($errmsg) { $this->errors[] = $errmsg; $use_default = false; } elseif (count($this->errors) == 0) $use_default = true; } } if ($use_default) { if (isset($this->def_errmsg)) $this->errors[] = $this->def_errmsg; else $this->errors[] = ""; } $this->error_checked = true; return (count($this->errors) > 0); } #--------------------------------------------------------------------------# function reset_error_checking() { $this->error_checked = false; } #--------------------------------------------------------------------------# function get_errmsg() { $errmsg = implode(" ", $this->errors); $errmsg = "
errmsg_class}\">$errmsg
"; return $errmsg; } #--------------------------------------------------------------------------# function get_errmsg_array() { $errors = array(); foreach ($this->errors as $err) { if (!empty($err)) { $err = "
errmsg_class}\">$err
"; $errors[] = $err; } } return $errors; } #--------------------------------------------------------------------------# function get_sameas() { foreach ($this->error_stuff as $elem) { if ($elem['type'] == "sameas") return $elem['val']; } return false; } #--------------------------------------------------------------------------# function set_sameas_err() { $errmsg = ""; foreach ($this->error_stuff as $elem) { if (($elem['type'] == "sameas") && (isset($elem['errmsg']))) { $errmsg = $elem['errmsg']; break; } } $this->errors[] = $errmsg; } #--------------------------------------------------------------------------# function get_note() { if (isset($this->note)) { $output = "
field_note_class}\">"; $output .= $this->note; $output .= "
"; return $output; } else return false; } #--------------------------------------------------------------------------# function add_option($option) { $this->options[] = $option; } #--------------------------------------------------------------------------# function &get_options_ref() { return $this->options; } #--------------------------------------------------------------------------# function set_inherited($row) { foreach ($this->inherited as $prop) { if (!isset($this->{$prop})) { $newval = $row->get($prop); if (strlen($newval) > 0) $this->{$prop} = $newval; } } } #--------------------------------------------------------------------------# function get_label() { if (!empty($this->label)) { if ($this->has_error()) { $label = "
field_lab_err_class}\">"; } else { $label = "
field_lab_class}\">"; } $label .= $this->label; $label .= "
"; return $label; } else return false; } #--------------------------------------------------------------------------# function get_input() { // see if this is a checkbox and has a global default of checked, in // which case we need to set this instance's default if ($this->type == "checkbox") { $checked_vals = array("on", "yes", "true", "checked"); if (in_array($this->attrs['checked'], $checked_vals)) $this->default = "on"; } // populate value from default if there is one if ((!isset($this->value)) && (!empty($this->default))) { // only populate checkboxes from default the first time if (!(($this->type == "checkbox") && ($this->subsequent == true))) $this->value = $this->default; } // we don't want to add the formatting stuff to hiddens if ($this->type == "hidden") return $this->get_hidden_input(); if ($this->has_error()) $input = "
field_err_class}\">"; else $input = "
field_class}\">"; switch ($this->type) { case "text": $input_text = $this->get_text_input(); break; case "password": $input_text = $this->get_password_input(); break; case "select": $input_text = $this->get_select_input(); break; case "multiselect": $input_text = $this->get_multiselect_input(); break; case "textarea": $input_text = $this->get_textarea_input(); break; case "checkbox": $input_text = $this->get_checkbox_input(); break; case "radio": $input_text = $this->get_radio_input(); break; case "static": $input_text = $this->get_static_input(); break; case "submit": $input_text = $this->get_submit_input(); break; case "image": $input_text = $this->get_image_input(); break; case "hidden": $input_text = $this->get_hidden_input(); break; } $input .= $input_text; $input .= "
"; return $input; } #--------------------------------------------------------------------------# function get_text_input() { $reject_attrs = array("rows", "cols", "checked"); $output = "name}\""; foreach ($this->attrs as $attr => $val) { if (in_array($attr, $reject_attrs)) continue; $output .= " $attr=\"$val\""; } if (isset($this->value)) $output .= " value=\"{$this->value}\""; $output .= ">"; return $output; } #--------------------------------------------------------------------------# function get_password_input() { $reject_attrs = array("rows", "cols", "checked"); $output = "name}\""; foreach ($this->attrs as $attr => $val) { if (in_array($attr, $reject_attrs)) continue; $output .= " $attr=\"$val\""; } if (isset($this->value)) $output .= " value=\"{$this->value}\""; $output .= ">"; return $output; } #--------------------------------------------------------------------------# function get_select_input() { $reject_attrs = array("size", "maxlength", "rows", "cols", "checked"); $output = "name}\""; foreach ($this->attrs as $attr => $val) { if (in_array($attr, $reject_attrs)) continue; $output .= " $attr=\"$val\""; } $output .= ">\n"; foreach ($this->options as $opt) { $output .= "