Why is this SQL not inserting into the database? - php

PHP Version: 7.0
Script is sent data from a different website.
For some reason, the data is not being inserted into the database like it should be, and I don't think I have any SQL errors (this is done with PDO).
Here is the included functions code:
<?php
function escape($string){
return htmlspecialchars($string, ENT_QUOTES, 'UTF-8');
}
?>
Script Code:
<html>
<head>
<title>Data from Roblox</title>
<h3>Data from Roblox</h3>
</head>
<body>
<?php
include '../includes/connection.php';
include '../scripts/functions.php'; //Remove if unknown error as well as the escapes
error_reporting(E_ALL);
ini_set('display_errors', 1);
$array = json_decode(file_get_contents('php://input'),1);
$SenderName = escape($array['SenderName']);
$SenderID = escape($array['SenderID']);
$PlayerName = escape($array['PlayerName']);
$PlayerID = escape($array['PlayerID']);
$Reason = escape($array['Reason']);
$PlaceLink = escape($array['PlaceLink']);
if(!$Reason){ $Reason = "Reason not provided."; }
if($SenderName !=NULL and $SenderID != NULL and $PlayerName != NULL and $PlayerID !=NULL and $PlaceLink !=NULL){
$query = $handler->prepare("INSERT INTO PlayerBans (`ID`, `Username`,`Reason`, `BannedDate`, `BannedBy`, `BannedAt`) VALUES (:pid, :pname, :reason, NOW(), :sname, :pl)");
$query->bindParam(':pid', $PlayerID);
$query->bindParam(':pname', $PlayerName);
$query->bindParam(':reason', $Reason);
$sender = $SenderName . " - " . $SenderID;
$query->bindParam(':sname', $sender);
$query->bindParam(':pl', $PlaceLink);
$query->execute();
}
?>
</body>
</html>
When go to the script URL in my web browser, the HTML shows up, and no errors.

Your problem is almost certainly with the request coming in, but here are a few issues you could address with your code.
htmlspecialchars() is not for inserting into a database. It's used when you want to display something as HTML.
none of those values you're checking will ever be null, because you're running them through htmlspecialchars() which returns a string.
there's no need to use PDOStatement::bindParam() unless you need to do something special with data types. Just pass an array to PDOStatement::execute() instead.
it sounds like you're not recording any error messages. If you aren't using this page interactively, you need to have some way to know if there's a problem.
With that in mind, I'd recommend trying this:
<?php
include("../includes/connection.php");
error_reporting(E_ALL);
ini_set("display_errors", true);
ini_set("error_log", "/var/log/php.log");
$json = file_get_contents("php://input");
$array = json_decode($json, true);
$SenderName = $array['SenderName'] ?? null;
$SenderID = $array['SenderID'] ?? null;
$PlayerName = $array['PlayerName'] ?? null;
$PlayerID = $array['PlayerID'] ?? null;
$Reason = $array['Reason'] ?? "Reason not provided";
$PlaceLink = $array['PlaceLink'] ?? null;
if($SenderName !== null && $SenderID !== null && $PlayerName !== null && $PlayerID !== null && $PlaceLink !== null) {
// prepare using ? for a shorter query; don't mix placeholders with other values
$query = $handler->prepare("INSERT INTO PlayerBans (`ID`, `Username`,`Reason`, `BannedBy`, `BannedAt`, `BannedDate`) VALUES (?,?,?,?,?,NOW())");
// double quotes interpolate variables!
$sender = "$SenderName - $SenderID";
// pass the values directly to execute
$result = $query->execute([$PlayerID, $PlayerName, $Reason, $sender, $PlaceLink]);
// check the result of this call and log some details if there's a problem
if (!$result) {
$e = $query->errorInfo();
error_log("SQL Error $e[0]: $e[2] ($e[1]) while inserting data: $json");
}
}
?>
You'll want to make sure that you create the log file ahead of time, with the correct permissions for your web server to be able to write to it. On a Linux platform this might look like sudo touch /var/log/php && sudo chown www-data /var/log/php
Also I'm assuming you're using a current version of PHP that supports the null coalesce operator; you'll need to replace $foo = $bar ?? null with $foo = isset($bar) ? $bar : null if that's not the case.
One more point, if each user on your system has an entry in a user table, you should really have UserID and SenderID columns in the PlayerBans table that are foreign keys back to your users table. If you're querying this column regularly it makes a whole lot more sense than having an unstructured text column.

Related

How to deal with unencoded URL redirects to my website correctly?

We are using CleverReach to redirect people to our website after they have double opt-in their mail account. We redirect the email as a query parameter to our website, like: example.com/thanks?email=foo#bar.com (by setting up a redirect in the CleverReach backend like example.com/thanks?email={EMAIL}). Apparently, the email parameter doesnt get urlencoded by cleverreach.
Now, in Drupal, if the URL is like so: example.com/thanks?email=hello+world#bar.com and using this code:
$request = \Drupal::request();
$email = $request->query->get('email');
$email is hello world#bar.com. Now, I dont know what the correct processing is here. Obviously, I cant tell CleverReach to urlencode their redirects beforehand. I dont even know if that would be best practice or if I need to imlement something...
The only thing I found out is that $_SERVER['QUERY_STRING'] contains the "real" string, which I can urlencode and then redirect, and then, by reading the query params, urldecode them. But I feel like I am missing some crucial inbuilt functionality.
TL;DR
If a website redirects to my website using not urlencoded query params, how do I read them?
My current approach:
<?php
public function redirectIfIllegalUri() {
$request = \Drupal::request();
$email = $request->query->get('email', '');
$needsRedirect = (false !== strpos($email, ' ') || false !== strpos($email, '#'));
if ($needsRedirect && isset($_SERVER['QUERY_STRING']) && false !== strpos($_SERVER['QUERY_STRING'], 'email=')) {
$sqs = $_SERVER['QUERY_STRING'];
$sqs = htmlspecialchars($sqs);
$sqs = filter_var($sqs, FILTER_SANITIZE_STRING);
$sqs = filter_var($sqs, FILTER_SANITIZE_ENCODED);
$sqs = urldecode($sqs);
$sqs = explode('&', $sqs);
foreach ($sqs as $queryParam) {
if (false === strpos($queryParam, 'email=')) continue;
$values = explode('=', $queryParam);
$email = $values[1];
}
$emailEncoded = urlencode($email);
$query = $request->query->all();
$query['email'] = $emailEncoded;
$refreshUrl = Url::fromRoute('<current>');
$refreshUrl->setOptions([
'query' => $query,
]);
$response = new RedirectResponse($refreshUrl->toString(), 301);
$response->send();
return;
}
}
$request = \Drupal::request();
$email = urldecode($request->query->get('email', false));
drupal request() docs
The problem you are facing is that the + will be treated as a space when you get the value from $_GET global variable.
Currently in PHP doesn't exist a method that returns these values without urldecoding and you need to build a custom function to achieve what you are asking:
A simple function will return not encoded input is by using this function:
function get_params() {
$getData = $_SERVER['QUERY_STRING'];
$getParams = explode('&', $getData);
$getParameters = [];
foreach ($getParams as $getParam) {
$parsed = explode('=', $getParam);
$getParameters[$parsed[0]] = $parsed[1];
}
return $getParameters;
}
This solution can be used if you do not have any other option. By using this function you will always get the data encoded.
If you can encode the value from cleverreach then the best approach is to encode it there.
Encoding the value in cleverreach for email hello+world#bar.com will give you this url example.com/thanks?email=hello%2Bworld%40bar.com and in $_GET you will have the email containing the + sign.

Why is PHP giving an undefined index notice for a POST variable when Ajax returns correct, and the POST variable does indeed exist?

I understand that similar general questions exist, but none of them follow my specific set of circumstances, and none of them really provide a solution.
Inside the same folder on the server, I have two files: "quiz_maker.php"
and "master_data.php."
I send a JSON object to "master_data.php" from "quiz_maker.php" with the following Ajax code:
if(localStorage.getItem("JSON Question Data Object") != null){
//The JSON object was stringified before saving to localStorage.
var dataString = localStorage.getItem("JSON Question Data Object");
$.ajax({
method: "POST",
url: "master_data.php",
data: { jsonDataObject: dataString },
success: function(msg){
console.log(msg + "\n");
}
});
}
Then, in "master_data.php", I receive it as follows:
if(isset($_POST['jsonDataObject'])){
echo "set";
$masterQuestionData = $_POST['jsonDataObject'];
$masterQuestionData = json_decode($masterQuestionData, TRUE);
//Perform MySQL Queries here.
}
else{
echo "not set";
}
When I run the code on "quiz_maker.php", the Ajax success handler fires, and I receive the string "set" in the console as I would expect. However, if I look at the "master_data.php" file, the string "not set" gets echoed out, and the following notice is displayed:
Notice: Undefined index: `jsonDataObject` in
/home/sites/5a/0/03891393e8/public_html/master_data.php on line 35
Furthermore, all the MySQL queries execute perfectly using the allegedly "undefined index" "jsonDataObject".
What would be the reason why Ajax's success handler fires, gives me the string "set" and all of the queries work, but I get an undefined index notice on master_data.php?
Thank you.
As requested, here is the whole master_data.php file:
<?php
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);
session_start();
$booleanSuccessfulOne = false;
$booleanSuccessfulTwo = false;
$servername = "[REDACTED]";
$username = "[REDACTED]";
$password = "[REDACTED]";
// Create connection
$link = mysqli_connect($servername, $username, $password, $username);
// Check connection
if (mysqli_connect_error()) {
$alert = "Oops! We're having trouble publishing your questions and
answers right now. Please try again later.";
die($alert);
}
if(isset($_POST['jsonDataObject'])){
echo "set";
$masterQuestionData = $_POST['jsonDataObject'];
// Unescape the string values in the JSON array
$masterQuestionData = $_POST['jsonDataObject'];
// Decode the JSON array
$masterQuestionData = json_decode($masterQuestionData, TRUE);
$maxQuestions = $masterQuestionData["statistics"][0]["totalQuestions"];
for($i = 1; $i <= $maxQuestions; $i++){
$question = $masterQuestionData["block"][$i-1]["question"];
$answer = $masterQuestionData["block"][$i-1]["answer"];
$query = "INSERT INTO `master` (`id`, `question`, `solution`)
VALUES('".$i."', '".$question."', '".$answer."') ON DUPLICATE KEY
UPDATE `id` = '".$i."', `question` = '".$question."', `solution` =
'".$answer."'";
mysqli_query($link, $query);
}
$query = "DELETE FROM `master` WHERE `id` > '".$maxQuestions."'";
mysqli_query($link, $query);
}
else{
echo "not set";
}
There shouldn't be spaces in the item key for localstorage. The rest of my suggestions are in the chat comments.
localStorage.getItem("JSON Question Data Object");
Can you try the following below and let us know what happens? Change all of your getItem() and setItem() to have no spaces.
localStorage.getItem("JSON");
I believe it should fix up this issue because the rest of your ajax & php looks fine.
That said, you can use bracket notation if you want
localStorage['JSON Question Data Object']

Create list of records and insert them later PHP MYSQL

I need to control who and when entered each subpage, and insert this information into MySQL. I have MYSQL table which will get the information(userid,subpage,subpageid, datetime).
function logstore(){
global $DB, $USER, $CFG;
$protocol = strpos(strtolower($_SERVER['SERVER_PROTOCOL']),'https')
=== FALSE ? 'http' : 'https';
$host = $_SERVER['HTTP_HOST'];
$script = $_SERVER['SCRIPT_NAME'];
$currentUrl = $protocol . '://' . $host . $script;
$newsurl = $CFG->wwwroot.'/news/view.php';
$produrl = $CFG->wwwroot.'/prod/view.php';
$materialurl = $CFG->wwwroot.'/material/index.php';
$datetime = date_create()->format('Y-m-d H:i:s');
$records = new stdClass();
$records->userid = $USER->id;
if($currentUrl == $newsurl){
$records->activity = 'news';
$records->activityid = $_GET['id'];
}elseif ($currentUrl == $produrl){
$records->activity = 'prod';
$records->activityid = $_GET['id'];
}
elseif ($currentUrl == $materialurl){
$records->activity = 'material';
$records->activityid = $_GET['id'];
}
$records->datetime = $datetime;
$DB->insert_record('logstore', $records);}
Im calling this function on top of /news/view.php, /prod/view.php, /material/index.php.
Its working fine, but i need to optimize it a little. Is there any way i can insert those records when DB is free, or create list and insert them each hour?
//edit
I tried INSERT DELAYED but its not working with my DB :(
There is no standard way in Moodle/PHP to delay queries till the DB is free. "INSERT DELAY" is depracated in latest MySQL as you can see here : https://dev.mysql.com/doc/refman/5.7/en/insert-delayed.html
I can suggest two options:
Check your DB performance using http://mysqltuner.pl - maybe more memory/cache can help in performance.
Use another server for this log - so the load will be distributed on more servers.

foreach Data Not Updateing in database

In my local server this script works fine. When I upload this script on live it does not work properly.
It inserts only 126 rows of data into the database, but I need to upload at least 500 rows at a time.
<?php
include 'database-config.php';
foreach($_POST['classroll'] as $row=>$classroll)
{
$sclassroll = $classroll;
$mark = $_POST['mark'][$row];
$type = $_POST['rtype'];
$session = $_POST['rsession'];
$department = $_POST['rdepartment'];
$examtype = $_POST['rextype'];
$examyear = $_POST['rexyear'];
$examsubject = $_POST['rexmarksubject'];
$stmt = $dbh->prepare("INSERT INTO exammarks(studnettype, studentsession, studentdepartment, studentclassroll, examtype, examyear, examsubjec, exammarks) VALUES (:studnettype, :studentsession, :studentdepartment, :studentclassroll, :examtype, :examyear, :examsubjec, :exammarks)");
$stmt->bindParam('studnettype', $type);
$stmt->bindParam('studentsession', $session);
$stmt->bindParam('studentdepartment', $department);
$stmt->bindParam('studentclassroll', $sclassroll);
$stmt->bindParam('examtype', $examtype);
$stmt->bindParam('examyear', $examyear);
$stmt->bindParam('examsubjec', $examsubject);
$stmt->bindParam('exammarks', $mark);
$stmt->execute();
}
header('Location: ../home.php');
?>
It is possible that your exammarks table definition on your live server contains a unique index that is not present on your local host server. If that were true some of your INSERT operations might fail.
The code you showed us doesn't check for errors. Obviously, when your program deals with high value data (such as the results of student examinations) you should check for errors.
Try this instead:
if( !$stmt->execute()) {
print_r( $arr = $stmt->errorInfo() );
}
else {
/* INSERT statement completed correctly */
}

PHP Session Not Saving Upon Refresh

So, I have a PHP class that has a method which updates a session variable called $_SESSION['location']. But the problem is, each time the method is called, it doesn't find the saved session variable, and tells me it isn't set. It's supposed to store a location ID, and the method pulls the next location from a MySQL database based on the session variable, then storing the new ID. But the place in the SQL code, that's supposed to include the variable, is empty.
I do have session_start() at the beginning of the page. I've tried manually setting the variable, and it doesn't do anything either. Also tried to reach that variable from another PHP page, and no luck either. Please help.
Small sample of my code:
class location {
#session_start();
function compass($dir) {
$select = $_SESSION['location'];
if($dir == "north") {
$currentlat = mysql_result(mysql_query("SELECT `lat` FROM `locationdb` WHERE id=".$select), 0, "lat");
$currentlon = mysql_result(mysql_query("SELECT `lon` FROM `locationdb` WHERE id=".$select), 0, "lon");
$sql = "[THE SQL CODE THAT GETS THE NEXT LOCATION]";
$id = mysql_result(mysql_query($sql), 0, "id");
$_SESSION['location'] = $id;
$return['loc'] = $this->display_location($id);
$return['lat'] = $this->display_lat($id);
$return['long'] = $this->display_long($id);
$return['id'] = $id;
}
return $return;
}
}
I have tested your code
**Dont use session_start() in this file.
For simple testing first add this inside your compass() function.
$_SESSION['location'] .= 'World';
Then create a php script with these codes.
<?php
session_start();
$_SESSION['location'] = 'Hello';
include_once('*your name of class file*');
$obj = new location();
$obj -> compass('north');
echo $_SESSION['location'];
?>
Run this script
If the output is "HelloWorld" then your $_SESSION['location'] is working.
Check your phpinfo(), to see if the session save path is defined. If not, define a directory to store the sessions. In your code:
session_save_path('/DIRECTORY IN YOUR SERVER');
Then try again.
This is closer to what your method should look like. There are some settings that will help reduce errors being thrown when running the function. With this function, and other suggestions, you should be able to remove the error your are getting.
class location
{
public function compass($dir = '')
{
// Set the $select by $_SESSION or by your function
$select = (isset($_SESSION['location']))? $_SESSION['location']: $this->myFunctionToSetDefault();
// I set $dir to empty so not to throw error
if($dir == "north") {
$currentlat = mysql_result(mysql_query("SELECT `lat` FROM `locationdb` WHERE id=".$select), 0, "lat");
$currentlon = mysql_result(mysql_query("SELECT `lon` FROM `locationdb` WHERE id=".$select), 0, "lon");
$sql = "[THE SQL CODE THAT GETS THE NEXT LOCATION]";
$id = mysql_result(mysql_query($sql), 0, "id");
$_SESSION['location'] = $id;
$return['loc'] = $this->display_location($id);
$return['lat'] = $this->display_lat($id);
$return['long'] = $this->display_long($id);
$return['id'] = $id;
}
// This will return empty otherwise may throw error if $return is not set
return (isset($return))? $return:'';
}
}

Categories