Don’t build strings for your database inserts or queries manually. Use something like https://idiorm.readthedocs.io/en/latest/models.html#creating-new-records (or something better/more popular) if you can’t be bothered to learn PDO & Prepared Statements.
OK, I am trying to learn this PDO stuff.
I am using https://phpdelusions.net/pdo_examples/insert (INSERT query with named placeholders) as an example of how to insert the data into the database. Would what I have listed with the $variable = $_POST[‘Variable’] be listed under the $data section of the example shown on the model webpage?
Or would it be better to use the $variable = $POST[‘Variable’] under the execute portion of the INSERT query with positional placeholders section?
Or is there an easier way to do it using an if (isset($_POST)) function?
Programming is already a tedious, error prone, typing task. You should use the simplest method that works. Named placeholders have you spending your time typing out column names three times for each insert query. Since you should always list the column names in the 1st part of the insert query, you have to list them at least once. Why not save time by using ? (positional) place-holders?
Again, don’t spend your time typing out line after line of code for each possible form field. Instead of you using the computer as a tool to accomplish a task, you are being a slave to the computer spending your time beating on a keyboard doing repetitive work. Also, if code isn’t doing any processing on the value in a variable, there’s no good reason to even be copying variables to other variables. Just use the original variables. A good reason to make a copy of all the submitted post data is if you trimmed it so that you can detect if all white-space characters were entered. You can do this using just one single line of code, by treating the data as a set and using php’s array functions to operate on the data.
For that specific line of code, $_POST is always set, even if it is empty. Wherever you saw that, it’s wrong.
In one of my replies above in this thread, I listed out the things your form processing code should ALWAYS do. Please re-read it. Using the items on this list will result in the simplest code, that’s secure, will provide a good user experience, and will either work or it will tell you (display/log) the reason why it didn’t work.
@phdr Since I am trying to learn this, I am going by your checklist above. So far, would item #1 and item #2 be correct with:
if (isset($_POST['Name_of_submit_button'])) {
$_POST = array_map('trim', $_POST);
}
As far as item #3, and item #4, since the form data is coming from a different page, wouldn’t the validation happen there?
With items #5 and #6, is this where the $stmt and $stmt -> execute() should be?
In Item #7, would https://dev.mysql.com/doc/connector-python/en/connector-python-api-errors-error.html be something like what I need?
I don’t think I am comparing values, as described in Item #8.
And item #9, you mean to put the from processing code in the header?
Almost. There are cases where the submit button won’t be included in the $_POST data. A more general solution is to just test if the request method is post. If you have multiple forms submitting to the same page, you should use a hidden form field that submits a unique value to identify which form processing code to execute.
if($_SERVER['REQUEST_METHOD'] == 'POST')
{
// put all the form processing code here...
// if more than one form submits to this code, add a hidden form field and then corresponding logic here to detect which form processing code to run.
}
Since the trim operation modifies the data, you should store the trimmed data in a different variable name. This also leaves the original data available for use by other parts of the application. If any of the form fields are arrays, instead of using php’s trim function in the array_map() statement, you would write a recursive trim call-back function to use instead.
External data can be anything and cannot be trusted. You MUST validate data on the server, inside the form processing code, before you use it.
Yes.
For errors that your application should handle, i.e. recoverable application errors, as the result of something the visitor to the site did that he can possibly correct, such as submitting a duplicate or out of range value, you would have a try/catch block for the execution of the query. You would NOT however output raw database errors as part of this error handling logic. You would test the error number from the query and if it is one that your code is designed to handle, you would set up an error message telling the visitor exactly what was wrong with the submitted data. For error numbers that your code is not going to handle, you would re-throw the exception and let php handle it. For a database dependent page, all other types of database statement errors are not recoverable, i.e. the visitor to the site cannot do anything about the problem, doesn’t need to know anything about why the page failed, and telling hackers that something they did caused more than just a http 500 error, will cause them to do more specific types of attacks. In these cases, you would not have any try/catch logic in your code, let php catch the exception, where it will either display or log the actual error information based on the current php display_errors or log_errors settings.
Here -
The extra ===TRUE is a waste of typing and unnecessary clutter. Also, when using exceptions for errors, your ‘main’ code only sees error free execution, so there’s no need for conditional logic at all. On an error, execution transfers to the exception handling logic, either your catch block, if present, or to php’s exception handler.
Not sure what header you mean. If you mean the head of the html document, no, put it before the <!DOCTYPE tag. The reason for putting any post method form processing code (and the get method ‘business’ logic) before the start of the html document, is so that if the result of those two pieces of code you decide you are not going to display the html document at all (redirect elsewhere), you can do a header() redirect and you won’t waste any time outputting anything on the web page that won’t be used. Your html document should come near the end of the code for any page and should only contain simple php statements needed to output the dynamic portion of the content.
For the validations, the only way I have seen is to do each variable separately. Is there a way to validate dynamically with one line of code? Or is this the best place for the variable names that will be used in the PDO? Some of the data submitted may be NULL, as not all fields will apply to everyone.
This is the page so far, with (I hope) the correct callback function.
<?php
session_start();
require('connect.php');
if($_SERVER['REQUEST_METHOD'] == 'POST') {
$form_data = filter_var($_POST, \FILTER_CALLBACK, ['options' => 'trim']);
}
?>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert</title>
</head>
<body>
</body>
</html>
AFAIK, filter_var only operates on a single value, not an array. You would need to use filter_var_array, however, AFAIK, it is not recursive, so it would only trim 1st level data when used with a call-back function (some experimentation would determine if this is so.) For the trimming of data that may contain one or more levels of arrays, use array_map() with a recursive, user written, call-back function.
Yes -
I personally don’t use the php filter (validate) functions, because they are a go/no-go indication only. They don’t tell you exactly what was wrong with the value that didn’t pass validation. You are left with displaying the wrong value and the expected range, type, or format and expect the visitor to figure out how his value relates to the test that was performed.
What I recommend is making an array that defines the expected form fields (similar to the $args array for the php filter_var_array() function), but defines a list (array) of user defined validation functions to call for each input to perform the desired validation. Each validation test has a ‘template’ message to use if the test fails. One of the validation tests you define is for ‘required’ (not an empty string) data.