I have a form that is mostly generated by a class named "buildform".
Now every part of the form validates itself on the position it is.
This looks like this:
echo $frm->create_input("customer_nr", "Customer Nr.", "text:empty:int");
The third attribute here are the conditions (here: text field, not empty only integer).
The function "create_input" calls some more function that are validating the form field directly on place when the form is submitted.
I do it this way for multiple reasons:
I can directly color the forms to red when there's an error
I only have to tell the form attributes one time instead of when creating it and validating.
This is really comfortable and until now has made creating forms very easy.
The validation errors for then the users will be stored in a class variable like this:
function add_err($n_errmsg) {
$this->errmsgs[] = $n_errmsg;
return;
}
At the end of the form I show the errors like this:
if(isset($_POST["sbm"])) {
$ret_err = $frm->ret_err();
if(!empty($ret_err)) {
echo $ret_err;
}
else {
// send success mail
}
}
This all works without any problems.
Now I had the idea to hide the form when it's submitted. But then I would have to make sure there are no errors before the form even is loaded!
I wanted to something like
else {
$hideform = true;
// send success mail
}
This currently is not possible as the errors are generated while "generating" the form fields.
If I move the errors to the top the errors are always "empty" there because the validation is done later below...
Do you have an idea how I can solve this?
Do I have to validate the forms before loading them or is there another way?
Thanks!
Best Regards
Toby
Ok, well there are a few ways to solve this.
Basically you are echoing the form html as you go along:
echo $frm->create_input(...);
echo $frm->create_input(...);
what you could do instead is save the html into a string:
$formhtml = '';
$formhtml .= $frm->create_input(...);
$formhtml .= $frm->create_input(...);
if($frm->ret_error()){
echo $formhtml;
//other logic
}else{
//send mail
}
Along the same lines, you could change your form class, so that this is done internaly, and add a show method:
private $html = '';
function create_input(...){
//retrun $ret;
$this->html .= $ret;
function show(){
echo $this->html;
}
$frm->create_input(...);
$frm->create_input(...);
if($frm->ret_error()){
$frm->show();
//other logic
}else{
//send mail
}
Related
I am new to php, this is my first post, my apologies about the formatting and the sloppiness of my coding. I'm not sure exactly how to post code. but I believe my question is fairly simple.
I am learning php OOP and trying to re-display an html form in PHP that is called from an object function displayContactForm() using method='POST' that I created from an object class with other various member functions that just simply echo html tags and display a simple `contact.php page with an html form.
All i'm trying to do is re-display the form with blank values after user hits submit and validation fails, so that the form essentially is cleared and user has to re-enter information. I wrote one if/else statement to handle the submit which will check if $_POST is set, and fields are !empty, (else) "redisplay cleared form (or) clear form somehow"? I posted my code for my php page below. The if-else statement is line 31-38. I can't use header relocation. The suggested line for my else statement I was supposed to use for this project was $this->displayContactForm($this->form); to redisplay the form. But it just duplicates the form beneath the original form that is on the page, and I end up with two forms? i'm confused?... Any help is appreciated.
<?php
require('./PageLib.php');
require('./ContactFormLib.php');
//created a class called ContactPage that extends class Page
class ContactPage extends Page{
public $form;
public function __construct($arg=NULL) {
$this->form = new ContactForm();
}
//OVERRIDDEN Page::displayPage()
public function displayPage(){
echo "<html>\n<head>\n";
//HEAD/////////////////
$this->displayTitle();
$this->displayStyles();
echo "</head>\n<body>\n";
//HEADER///////////////
$this->displayHeader();
$this->displayNav($this->navLinks);
//CONTENT//////////////
$this->displayContactForm($this->form);
echo $this->content;
$this->displayFooter();
if(isset($_POST['name'], $_POST['phone'], $_POST['email']) && !empty($_POST['name']) && !empty($_POST['phone']) && !empty($_POST['email'])){
echo " Checked form values. Now echoing content to page.";
}
else {
$this->displayContactForm($this->form);
}
echo "\n</body>\n</html>\n";
public function displayContactForm($f) {
$f->displayForm();
}
}
$contactpage = new ContactPage();
$contactpage->pageTitle = "Contact Page";
$contactpage->headerTitle = "Contact - OOP php Page Builder";
$contactpage->content="<h3>Contact Page Content</h3>\n";
$contactpage->displayPage();
?>
This is not possible with just PHP. You will have to use Ajax in combination with PHP to perform this task. PHP by itself requires a page refresh to "re_display" content.
I have a couple of fields in the <form>... $fieldset is array of the fields name, it loop them with isset() checking.
I want to apply validation (eg: required input, email) to a few fields, how to apply this from my code logic?
public function actionProfile($id = null) {
$profileModel = new ProfileModel;
// <input> fields name
$fieldset['name'] = array('FirstName', 'LastName');
$fieldset['address'] = array('HouseNumber', 'StreetName', 'Town', 'Location');
$formError = array();
if (isset($_POST['profile'])) {
// Process input event
foreach ($fieldset as $legend => $fields) {
foreach ($fields as $field) {
if (!isset($_POST['profile'][$field])) {
$formError[$legend] = $field;
} else {
$form[$legend][$field] = $_POST['profile'][$field];
}
}
}
if (count($formError) == 0) {
if ($profileModel->saveAddress($form['address'])) {
//Saved to the database.
}
}
}
// Get data from the database
$data['profile'] = $profileModel->find($id);
$view = new View($this->layout, $data)->render();
}
In the view file, it would look something like this:
<input type='text' value=<?php echo $profile['first_name'] name='profile[FirstName]' ?>
<input type='text' value=<?php echo $profile['last_name'] name='profile[LastName]' ?>
Edit: When editing the record via form.. If there is an error (validation) - I want to put user input value back into <input> value instead of value from the database. How can it be done from my code?
You are currently putting validation logic inside the controller. That should go in the Domain Business Object (read more: here and here).
Also, "model" is not a class. Model is a layer in MVC architecture. This layers mostly consists of two types of instances: Domain Objects and Data Mappers. Each with quite different responsibilities.
I think it would be wise to split the verification code from the actual update function.
Have it run through a validator first, checking for length and required inputs. When that passes, you can send all that (formatted) data to the action. If it doesn't pass the validation, return it to the view with additional error information so you can guide the user to fix the problem.
I hope you understand what I'm trying to explain to you. :-).
Use the PHP Filter functions:
http://www.php.net/manual/en/ref.filter.php
Use the Variable Handlers
http://us2.php.net/manual/en/ref.var.php
In case of user submitted text, when outputting to the page, what text filter do you use both in input and output?
As I understand it, using $this->input->post('something',true) will clean XSS content from the input data, so there is no other thing to do to be secure? Something like htmlspecialchars(), strip_tags(), etc.?
Also i would like to know if for example htmlspecialchars() is good to use, why CI security library doesn't applyes htmlspecialchars() by default to the passed string?
You should use the form_validation library. You can do rule based checking and filtering. This is a much more robust way of validating input data.
Here are the built in rules and any defined function that takes one parameter can be used as a filter/rule.
required
matches
min_length
max_length
exact_length
greater_than
less_than
alpha
alpha_numeric
alpha_dash
numeric
integer
decimal
is_natural
is_natural_no_zeroetc
valid_email
valid_emails
valid_ip
valid_base64
Kinda depends on what you're doing with this input, but most likely you're going to want to run the string through htmlspecialchars() also.
To my understanding, you would like to store user submitted text in a database, and then later display it on a page -- kind of like a basic commenting system or something. You just don't want any naughty/incomplete HTML characters breaking your page when outputting it.
Whenever you have user submitted data, you want to utilize the form_validation library to clean it up and sanitize it as much as possible as a good security measure. If it goes to your database, you should use Active Records or Query Binding to get additional security from Codeigniter, such as escaping the strings, etc.
Let me show my solution on submitting and outputting user's input on a website. There are probably better ways to do this, but this will get the job done.
<?php
/*Controller
**************************************************/
class Something extends CI_Controller {
function comments_or_whatever() {
//Required -> trim value -> max_length of 100 -> strip HTML tags -> remove additional HTML entities missed by strip tags
$this->form_validation->set_rules('input_1', 'The First User Input', 'required|trim|max_length[100]|xss_clean|strip_tags|callback__remove_html_entities');
$this->form_validation->set_rules('input_2', 'The Second User Input', 'trim|exact_length[11]|xss_clean|strip_tags|callback__remove_html_entities');
if ($this->form_validation->run() == FALSE) {
//form didn't validate.. try again display error messages
$this->load->view('your_view');
}
} else {
$input_1 = $this->input->post('input_1');
$input_2 = $this->input->post('input_2');
$submission_array = array(
'db_field_1' => $input_1,
'db_field_2' => $input_2
);
$this->load->model('comments');
$result = $this->comments->submit_comments_or_whatever($submission_array);
if ($result['is_true'] == TRUE) {
//creates a temporary flash message and redirects to current page
//if on a windows server use 'refresh' instead of 'location'
$this->session->set_flashdata('message', '<div class="message">'.$result['message'].'</div>');
redirect('something', 'location');
} else {
$data['message'] = $result['message'];
$this->load->view('your_view', $data);
}
}
}
// Very important to get rid calling HTML Entities via HTML number codes such as < etc. Strip_tags does not do this.
// This is privately called during validation from the callback__remove_html_entities custom callback
function _remove_html_entities($submission) {
$submission = preg_replace("/&#?[a-z0-9]{2,8};/i","",$submission);
return $submission;
}
}
/* Model
****************************************/
class Comments extends CI_Model {
function submit_comments_or_whatever($submission_array) {
// Active record escapes string and does additional security
$query = $this->db->insert('comments', $submission_array);
if ($query == TRUE) {
$data['is_true'] = TRUE;
$data['message'] = 'Your message has been successfully shared!';
return $data;
} else {
$data['is_true'] = FALSE;
$data['message'] = 'Sorry, but there was an error dude inserting your message into the database.';
return $data;
}
}
}
/* View -> your_view.php
****************************************/
<?php echo validation_errors('<div class="message">', '</div>'); ?>
<?php echo $this->session->flashdata('message'); ?>
<?php if (!empty($message)) echo '<div class="message">'.$message.'</div>'; ?>
<?php echo form_open('something/comments_or_whatever'); ?>
<?php echo form_label('The First User Input', 'input_1'); ?><br>
<?php $input_1_form = array('name' => 'input_1', 'id' => 'input_1', 'value' => set_value('input_1')); ?>
<?php echo form_input($input_1_form); ?><br>
<?php echo form_label('The Second User Input', 'input_2'); ?><br>
<?php $input_2_form = array('name' => 'input_2', 'id' => 'input_2', 'value' => set_value('input_2')); ?>
<?php echo form_input($input_2_form); ?><br>
<?php echo form_submit('submit', 'Dude, submit my user inputed text!'); ?>
<?php echo form_close(); ?>
This code assumes you autoload the Form Validation, Sessions, and Database Libraries and the Form Helper. Now, all your user inputed data is stripped to a bare minimum of plain text using a custom Regular Expression call back during form validation. All naughty HTML characters are gone/sanitized, completely. You can now be worry-free to output the submitted data anywhere you'd like on a webpage without it breaking or being a security concern.
The problem with just doing HTMLSpecialChars() and html decode is it doesn't account for incomplete HTML tags. Hopefully this helps, best of luck dude, and as always, nothing is ever completely secure.
I'm working in PHP to build a form. I know how to display the form and then take submitted values from the $_POST variable, and I know how to validate those variables and display a "Thank You" or an "Error" page depending on the input.
What I don't know how to do, though, is create a client-side-like system wherein despite having my users hit a "back" button a separate screen I can then take the information I gathered from the first submission and display dynamic error messages like "Please provide a valid email address" or "First name is a required field" next to the fields that were entered incorrectly. I'd also like to retrieve any previously submitted data that was valid and have it populate in the form so users don't get frustrated by losing everything they entered.
What is the right approach to accomplishing something like this in PHP? I originally thought if I could pass back an array of error messages with an input type="hidden" tag I could then pull my values and display messages dynamically with PHP, but I keep getting stuck in that approach.
You could add the errors a php session, but this creates issues for users who have multiple browser tabs open.
My preferred method is to have the form submit to the same page and put the errors directly on that page so the user does not have to click the back button. That way you can highlight the fields directly in the form (make the background or outline red or something similar.)
<input type="text"
<?php (empty($_POST['field']?'style="backgroung-color: red;"':''))?>
name="field" value="<?php echo $_POST['field']?>" />
You can put <input type="text" name="field" value="<?php echo $_POST['field']?>" /> to get the old value.
Because the web is, by definition, stateless, there is no really good way to track what the user does when they hit the back button. There are hacks that work using a hidden iframe, but that is way more trouble that what you are looking for.
Don't mix client logic with server logic. The exact same script can output the form and take it's input. In case input successfully validates, it goes on. If not, it will display the form again, this time with error messages and the already-entered data.
Next time the user submits the form, validation starts again until it passes successfully.
So you extend the form with input values and error messages in the first place, but you only display them if flagged/set.
This can be done just with additional variables next to $_POST - or if you like it - by using a complete form abstraction from a framework, like zend framework (which might be overhead for what you like to do) or just with a library/component like the popular HTML_QuickForm2.
Edit:
This is some very bare code to demonstrate the overall methodology, if you use a library it is much nicer (and you don't have to code it instead you can concentrate on the actual form like the definition on top). This code is more for reading and understanding the flow than for using, I quickly typed it so it (most certainly has) syntax errors and it's not feature complete for a full blown form. This one has only one email field and is even missing the submit button:
/* setup the request */
$request->isSubmit = isset($_POST['submit']);
/* define the form */
$form->fields = array
(
'email' => array
(
'validate' => function($value) {return filter_var($value, FILTER_VALIDATE_EMAIL);},
'output' => function($value, $name) {return sprintf('<input type="text" value="%s" id="%s">', htmlspecialchars($value), htmlspecialchars($name)},
'default' => 'info#example.com',
),
);
/**
* Import form data from post request
*
* #return array data with keys as field names and values as the input strings
* or default form values.
*/
function get_form_post_data($form, $request)
{
$data = array();
foreach($form->fields as $name => $field)
{
$data[$name] = $field->default;
if ($request->isSubmit && isset($_POST[$name]))
{
$data[$name] = $_POST[$name];
}
}
return $data;
}
/**
* Validate form data
*/
function validate_form_data($form, $data)
{
foreach($form->fields as $name => $field)
{
$value = $data[$name];
$valid = $field['validate']($value);
if (!$valid)
{
$form->errors[$name] = true;
}
}
}
function display_form($form, $data)
{
foreach($form->fields as $name => $field)
{
$value = isset($data[$name]) ? $data[$name] : '';
$hasError = isset($form->errors[$name]);
$input = $field['output']($name, $value);
$mask = '%s';
if ($hasError)
{
$mask = '<div class="error"><div class="message">Please Check:</div>%s</div>';
}
printf($mask, $input);
}
}
// give it a run:
# populate form with default values -or- with submitted values:
$form->data = get_form_post_data($form, $request);
# validate form if there is actually a submit:
if ($request->isSubmit)
{
validate_form_data($form, $form->data);
}
# finally display the form (that can be within your HTML/template part), works like echo:
display_form($form, $form->data)
Use the form to submit to the same page, and if the form validates, use a header to redirect the user into the thank you page.
header("Location: thank-you.php");
If the form fails validation, you could easily display all the errors on the same page.
I am new to PHP and I was hoping someone could help me determine what the best way to go about creating a reusable view would be. What I have is a form that will be used to Add or Edit data. Therefore the view will be identical except in the case of an Edit the form fields will be populated.
So I am creating an HTML helper that takes 2 parameters. The first will be form data (if there is any{edit}) and the second will be a bool that flags whether this is an insert or and edit(to change form action).
My question is... how should I handle the first parameter if the form is to used to Add data and therefore does not contain data? Optional parameter?
EDIT--
I am using CodeIgnitor as my MVC framework. That is what those form functions are being inherited.. fyi
Thanks..
<?php
if(!defined('BASEPATH') ) exit('No direct script access allowed');
if(!function_exists('WorkOrderForm'))
{
function WorkOrderForm($array = array('$query'),$edit)
{
$formHtml = "";
$attributes = array('class'=>'order','id'=>'orderForm');
if($edit)
{
$formHtml += form_open('order/update',$attributes);
}
else
{
$formHtml += form_open('order/add',$attributes);
}
$formHtml += form_input('name',$query[0]->name);
$formHtml += form_textarea('name',$query[0]->description);
$dropOptions = array('nstarted'=>'Not Started','complete'=>'Done','started'=>'In Progress');
$formHtml += form_dropdown('status',$dropOptions,$query[0]->status);
$formHtml += form_input('name',$query[0]->startDate);
$formHtml += form_input('name',$query[0]->endDate);
$formHtml += form_close();
return $formHtml;
}
}
?>
What are you guys doing? A reusable view is so much easier than this. Simply create a view and save it in the views folder. Add the fields that will appear both when adding and editing data and use an if statement in the value parameter to determine if it has data.
E.g.
Controller:
public function add()
{
$data['method'] = 'add';
$data['form_data'] = $this->some_model->get_something();
$this->load->view('reusable_view', $data);
}
public function edit($id)
{
$data['method'] = 'edit';
$data['form_data'] = $this->some_model->get_something($id);
$this->load->view('reusable_view', $data);
}
View:
<form method="post" action="my_controller/" . <?php echo $method; ?>>
<input type="text" value="<?php if ( isset($form_data['something']) ) {echo $form_data['something'];} " />
</form>
I see no reason to populate a form in a controller as that's not the way MVC works. Using a helper in order to populate the form is also weird, I think you've slightly missed the point of how Codeigniter works.
First off, the default argument should be on the right. And I would default it to 'false' or NULL.
function WorkOrderForm($edit, $array = false)
Then maybe check if $array is not true, and set all the $query[0] to NULL? So something like...
if(!$array) {
$query[0]->name = $query[0]->description = $query[0]->status = null;
}
There might be a more direct approach, but that's one way.
Firstly, if you're sending your query to the helper function as an array, there is no reason to turn it into another array. ie $array = array('$query') should just be $query this way you can access properties like this: $query->name as opposed to $query[0]->name. Secondly, if you're not editing the form entry, your $query would be empty, so you can use that as the trigger for what to return (either the blank form or the populated form):
function WorkOrderForm($query)
{
if($query!='')
{
//$formHTML=populated form
} else {
//$formHTML=empty form
}
return $formHTML;
}
Okay? But, there's a problem... The code you have in your helper won't work. You're using an arithmetic operator += to (assuming) concatenate the form data. What this does is try to add 1 to a string, which will always equal 0. What you're looking for is the .= operator; this will concatenate the form as would be expected. However, this offers you little control over how the form will look (as is, it will put all the form elements side-by-side -- not too pretty). What you could do is instead of concatenating them all together, push them into an array, then echo the form elements out one-by-one:
if($query!=''){
$form_array=array();
array_push($form_array,form_open('order/update',$attributes));
array_push($form_array,form_input('name',$query->name));
array_push($form_array,form_textarea('name',$query->description));
$dropOptions = array('nstarted'=>'Not Started','complete'=>'Done','started'=>'In Progress');
array_push($form_array,form_dropdown('status',$dropOptions,$query->status));
array_push($form_array,form_input('name',$query->startDate));
array_push($form_array,form_input('name',$query->endDate));
array_push($form_array,form_close());
}else{
$form_array=array();
array_push($form_array,form_open('order/add',$attributes));
array_push($form_array,form_open('order/update'));
array_push($form_array,form_input('name'));
array_push($form_array,form_textarea('name'));
$dropOptions = array('nstarted'=>'Not Started','complete'=>'Done','started'=>'In Progress');
array_push($form_array,form_dropdown('status',$dropOptions));
array_push($form_array,form_input('name'));
array_push($form_array,form_input('name'));
array_push($form_array,form_close());
}
return $form_array;
Then to present the form elements, iterate through the $form_array array that was returned:
$form_data='';//blank for new item, or data to populate form with to edit an item
$form_el = WorkOrderForm($form_data);
foreach($form_el as $key=>$val){
echo $val.'<br>';//I just added a <br> here so each form element will be on a new line; change to fit your needs
}
Hope this helps...