I have included some thoughts with code context below:
include('php/connect.php');
Consider using require() here as my guess is that this script will not execute without this dependency. You should fail fast if dependencies are not met.
if(isset($_POST['slots_booked'])) $slots_booked = mysqli_real_escape_string($link, $_POST['slots_booked']);
if(isset($_POST['name'])) $name = mysqli_real_escape_string($link, $_POST['name']);
if(isset($_POST['akeri'])) $akeri = mysqli_real_escape_string($link, $_POST['akeri']);
if(isset($_POST['email'])) $email = mysqli_real_escape_string($link, $_POST['email']);
if(isset($_POST['phone'])) $phone = mysqli_real_escape_string($link, $_POST['phone']);
if(isset($_POST['typ'])) $typ = mysqli_real_escape_string($link, $_POST['typ']);
if(isset($_POST['fri'])) $fri = mysqli_real_escape_string($link, $_POST['fri']);
if(isset($_POST['booking_date'])) $booking_date = mysqli_real_escape_string($link, $_POST['booking_date']);
Validate this data before working with it. Validate and cast the string POST input to appropriate values for use within the script. I.e. validate email format, validate/cast numeric data types, validate non-zero lengths strings,
etc.
Take a look at PHP's filter_input_array
for good example of how you can better
approach this:
http://php.net/manual/en/function.filter-input-array.php
Also in this section, lines of code are too long. You should try to keep lines less than 80 characters in length. Use curly braces around code inside conditional for all but most trivial bits of logic to improve readability of code.
Example:
if(isset($_POST['slots_booked'])) {
$slots_booked = mysqli_real_escape_string($link, $_POST['slots_booked']);
}
if(isset($_POST['name'])) {
$name = mysqli_real_escape_string($link, $_POST['name']);
}
// etc.
If you properly validate the data up front and use in combination with
prepared statements, you don't need to perform mysqli_real_escape_string()
on the data.
$booking_array = array(
"slots_booked" => $slots_booked,
"booking_date" => $booking_date,
"name" => $name,
"email" => $email,
"Åkeri" => $akeri,
"phone" => $phone,
"typ" => $typ,
"fri" => $fri
);
Where is this used?
$explode = explode('|', $slots_booked);
Reconsider how you are POSTing data for this field. If you post it as an
array using array notation like:
<input name="slots_booked[]" ...>
then you can run your validation filter against each individual element of the array up in the validation steps and already have an array to work with, eliminating the need for this explode()
call.
foreach($explode as $slot) {
Your prepared statement initialization should happen before this foreach
loop.
Prepared statements were meant to leverage this use case, allowing a single prepare, then executing it multiple times. This skips some overhead on the MySQL end with not having to make a query plan for each execution. You can also perform your parameter binding here as well, as there is no need to perform this operation multiple times either.
if(strlen($slot) > 0) {
You should have already validated this input before ever getting to the point of
trying to make inserts in the database. Reference earlier notes on data
validation.
$stmt = $link->prepare("INSERT INTO bookings (date, start, name, email, phone, typ, fri) VALUES (?, ?, ?, ?, ?, ?, ?)");
What do typ
and fri
mean?
Use meaningful field names in your table and variable names in your code.
You gain nothing by cutting a field name short other than possible confusion
down the line.
Your current prepared statement logic assumes happy path execution. What happens if prepare()
fails?
$stmt->bind_param('sssssss', $booking_date, $slot, $name, $email, $phone, $typ, $fri);
Move this outside loop. Consider parameter typing here. If you truly want to store integers, or doubles in your DB table, then you should enforce that here as well so you are not accidentally invoking potentially unexpected type conversion behavior in the database.
Your current binding logic assumes happy path. What if binding fails?
$stmt->execute();
The code around query execution only considers happy path. What happens if insert fails? You do nothing to handle this. What happens with other rows that might have already been or will be inserted as you iterate in this loop? Does this booking need to happen transactionally (i.e. all inserts must succeed before committing change)?