I'm attempting to learn prepared statements right now in PHP/MYSQL because of many suggestions around here. I keep getting this error:
Fatal error: Cannot pass parameter 2 by reference in C:\xampp\htdocs\blog\admin\create.php on line 57
Can anyone tell me how to fix this problem? I've been searching around and I can't find anything that will help me solve this.
Here is my code:
<?php
require_once '../config.php';
// Check to see if the title was entered from new.php
if ($_POST['title'])
{
$title = $_POST['title'];
} else {
echo "No title was entered. Please go back. <br />";
}
// Check to see if the body was entered from new.php
if ($_POST['body'])
{
$body = $_POST['body'];
} else {
echo "No body was entered. Please go back. <br />";
}
// Get the date
$date = time();
// ID = NULL because of auto-increment
$id = 'NULL';
// If magic_quotes_gpc returns true then it's enabled on the serever and all variables will be
// automatically escaped with slashes. If it isn't true then it's done manually
if (!get_magic_quotes_gpc())
{
$title = addslashes($title);
$body = addslashes($body);
$date = addslashes($date);
}
// Connect to the database
$db = new mysqli('localhost','username','password','database');
// Check to see if the connection works
if ($db->connect_errno)
{
echo 'Error: Could not connect to database. Please try again.';
exit;
}
// Prepared statement for a query to place something in the database
if(!($stmt = $db->prepare("insert into pages (id, title, body, date) values (?,?,?,?)")))
{
echo "Prepare failed: (" .$db->errno . ")" . $db->error;
}
// THIS IS THE LINE WHERE I'M RECEIVING THE ERROR!!!!!!!!
if (!$stmt->bind_param('isss', ''.$id.'', ''.$title.'',''.$body.'',''.$date.''))
{
echo "Binding parameters failed: (" .$stmt->errno. ")" . $stmt->error;
}
if (!$stmt->execute())
{
echo "Execute failed: (" .$stmt->errno . ") " .$stmt->error;
}
$db->close;
?>
You should have a look at the corresponding mysqli_stmt::bind_param documentation. More precisely, have a look at the function's definition:
bool mysqli_stmt::bind_param ( string $types , mixed &$var1 [, mixed &$... ] )
Notice the mixed &$var1 part? This basically states that your paramters are passed by reference and not by value (which would look like mixed $var1 - the & makes the difference).
Now, the problem with your invocation is that you are trying to pass an expression rather than a variable by reference. From the PHP documentation:
The following things can be passed by reference:
- Variables, i.e. foo($a)
- New statements, i.e. foo(new foobar())
- References returned from functions, [...]
The simple remedy is to first call the binding with uninitialized variables which are then assigned your processed input data, i.e.
// Prepared statement for a query to place something in the database
$stmt = $db->prepare("insert into pages (id, title, body, date) values (?,?,?,?)");
if ( !$stmt ) {
echo "Prepare failed: (" .$db->errno . ")" . $db->error;
}
if ( !$stmt->bind_param('isss', $stmt_id, $stmt_title, $stmt_body, $stmt_date) ) {
echo "Binding parameters failed: (" .$stmt->errno. ")" . $stmt->error;
}
$stmt_id = (int) $id;
$stmt_title = (string) $title;
$stmt_body = (string) $body;
$stmt_date = (string) $date;
if ( !$stmt->execute() ) {
echo "Execute failed: (" .$stmt->errno . ") " .$stmt->error;
}
Related
I am hoping that I've just been looking at and debugging this code too long (days now!) and I'm just not seeing the problem.
I'm obviously trying to add an entry in to a MySQL database via my PHP code. To use the classic phrase "this code has always worked before and now it doesn't and I didn't change anything" ;-)
My code, with my current debugging traps, looks like this:
// Prepare SQL Insert
$strInsert = "INSERT INTO Horses ( HorseName, HorseYOB, HorseCOB, HorseSex, HorseYOD, HorseDead, FAM, FDM) " .
"VALUES (:HORSENAME, :HORSEYOB ,:HORSECOB, :HORSESEX, :HORSEYOD, :HORSEDEAD, :FAM, :FDM)";
$DBInsertHorse = $DB->prepare($strInsert);
// Insert new Horse
$iCtr = 0;
do {
try {
$DBInsertHorse->execute(array(
'HORSENAME' => strtoupper($HorseName),
'HORSEYOB' => $YOB,
'HORSECOB' => $COB,
'HORSESEX' => strtoupper($HorseSex),
'HORSEYOD' => $YOD,
'HORSEDEAD' => $bDead,
'FAM' => $FAM,
'FDM' => $FDM)
);
}
catch (Exception $error) {
die($error->getMessage());
}
} while ($find($DB, strtoupper($HorseName), $YOB, $COB) == false && ++$iCtr < MAX_INSERT_ATTEMPTS);
// Could not insert
if ($iCtr == MAX_INSERT_ATTEMPTS) {
// DEBUG HORSE IMPORT
if (is_null($HorseName))
$HorseName = 'NULL';
if (is_null($YOB))
$YOB = -2;
if (is_null($COB))
$COB = 'NULL';
if (is_null($HorseSex))
$HorseSex = 'NULL';
if (is_null($YOD))
$YOD = -2;
if (is_null($bDead) || !$bDead)
$Dead = -2;
if (is_null($FAM))
$FAM = 'NULL';
if (is_null($FDM))
$FDM = 'NULL';
error_log('INSERT ERROR: Horse: \'' . strtoupper($HorseName) . '\' - YOB: ' . $YOB . ' - COB: \'' . $COB . '\' - SEX: \'' . strtoupper($HorseSex) . '\' - YOD: ' . $YOD . ' - Dead: ' . $bDead . ' - FAM: ' . $FAM . ' - FDM: ' . $FDM);
return(false);
}
If I go my favourite SQL editor (SQLPro for MySQL) and I enter in the insert manually it works fine:
INSERT INTO Horses (HorseName, HorseYOB, HorseCOB, HorseSex, HorseYOD, HorseDead, FAM, FDM)
VALUES ('HorseName', 2001, null, 'M', null, false, null, null)
For info:
The find() function used is my own and wraps a "SELECT FROM ..." query and works fine. If I do a SELECT from the Horses table in the database afterwards the Horse was never added.
MAX_INSERT_ATTEMPTS is my constant and the value is currently set at 5.
I'm working with MaMP PRO and I've looked in my PHP error log, where only the message I sent there appears and no other errors and in my MySQL error log, where no error message appears.
I can't figure out where to look next and I'm hoping whatever my stupid error might be is going to jump out at someone else looking at my code.
Thanks for looking and for any ideas, suggestions or corrections you may have.
UPDATED CODE WITH DEBUGGING
do {
try {
$DBInsertHorse->bindValue(':HORSENAME', strtoupper($this->Name));
$DBInsertHorse->bindValue(':HORSEYOB', $this->YOB);
$DBInsertHorse->bindValue(':HORSECOB', $this->COB);
$DBInsertHorse->bindValue(':HORSESEX', strtoupper($this->Sex));
$DBInsertHorse->bindValue(':HORSEYOD', $this->YOD);
$DBInsertHorse->bindValue(':HORSEDEAD', (int)$this->Dead);
$DBInsertHorse->bindValue(':FAM', $this->FAM);
$DBInsertHorse->bindValue(':FDM', $this->FDM);
$DBInsertHorse->execute();
}
catch (PDOException $e) {
error_log('SQL INSERT ERROR: ' . $e->getMessage());
}
} while($this->find($DB, strtoupper($this->Name), $this->YOB, $this->COB) == false && ++$iCtr < MAX_INSERT_ATTEMPTS);
As #BillKarwin mentioned I was missing a proper call to
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
Once that was in place I was able to see where the error was. This error apparently has appeared since my upgrade to PHP 7.2.10.
I also changed my call to array to a list of calls to bindValue... I may go back to array() but that's not important. ;-)
The PHP code did not like my passing "false" to a tinyint field. I had to type the variable with a call to (int)varname and everything works fine now.
(int)$this->Dead;
THANK YOU ALL!
Check this, it's tested and it works. Also check do while statement.
try {
$conn = new PDO("mysql:host=$servername;dbname=$dbname", $username, $password);
// set the PDO error mode to exception
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
// prepare sql and bind parameters
$stmt = $conn->prepare("INSERT INTO Horses ( HorseName, HorseYOB, HorseCOB, HorseSex, HorseYOD, HorseDead, FAM, FDM)
VALUES (:HORSENAME, :HORSEYOB ,:HORSECOB, :HORSESEX, :HORSEYOD, :HORSEDEAD, :FAM, :FDM)");
$stmt->bindParam(':HORSENAME',$HorseName);
$stmt->bindParam(':HORSEYOB', $YOB);
$stmt->bindParam(':HORSECOB', $COB);
$stmt->bindParam(':HORSESEX', $HorseSex);
$stmt->bindParam(':HORSEYOD', $YOD);
$stmt->bindParam(':HORSEDEAD', $bDead);
$stmt->bindParam(':FAM', $FAM);
$stmt->bindParam(':FDM', $FDM);
// insert a row
$HorseName = strtoupper($HorseName);
$YOB = "John";
$COB = "John";
$HorseSex = strtoupper($HorseSex);
$YOD = "John";
$bDead = "John";
$FAM = "John";
$FDM = "John";
$stmt->execute();
echo "New records created successfully";
}
catch(PDOException $e)
{
echo "Error: " . $e->getMessage();
}
$conn = null;
ok so I have written some code that is doing a backup of configuration items. This script runs fine for everything else, but fails when it gets to this long query for this specific configuration item.
for sanity purposes I am just going to include the code for this section.
function writepool($pool, $device){
$pooltext = implode('', $pool['list']);
$sql = "INSERT into pools (deviceid, name, config) VALUES (
'".$device."',
'".$pool['pool']."',
'".addslashes($pooltext)."'
)";
echo $sql;
$addpool = insertdb($sql);
}
function insertdb($sql){
include('/var/www/db_login.php');
$conn = new mysqli($db_host, $db_username, $db_password, "F5");
// check connection
if ($conn->connect_error) {
trigger_error('Database connection failed: ' . $conn->connect_error, E_USER_ERROR);
}
if($conn->query($sql) === false) {
trigger_error('Wrong SQL: ' . $sql . ' Error: ' . $conn->error, E_USER_ERROR);
} else {
$last_inserted_id = $conn->insert_id;
$affected_rows = $conn->affected_rows;
}
$result["lastid"] = $last_inserted_id;
$result["affectedrow"] = $affected_rows;
return $result;
$conn->close();
}
The error message I get is as follows
Fatal error: Wrong SQL: INSERT into pools (deviceid, name, config) VALUES ( '71', 'shopping.a.prod_pool_53601', 'ltm pool /Common/shopping.a.prod_pool_53601 { load-balancing-mode weighted-least-connections-node members { /Common/10.216.26.55:53601 { address 10.216.26.55 priority-group 1 } /Common/10.216.26.57:53601 { address 10.216.26.57 priority-group 1 } /Common/10.216.26.58:53601 { address 10.216.26.58 priority-group 1 } /Common/10.216.26.59:53601 { address 10.216.26.59 priority-group 1 } /Common/10.216.26.60:53601 { address 10.216.26.60 priority-group 1 } /Common/10.216.26.61:53601 { address 10.216.26.61 priority-group 1 } /Common/10.216.26.62:53601 { address 10.216.26.62 priority-group 1 } /Common/10.216.26.66:53601 { in /var/www/html/functions.php on line 2286
Note the query is extremely long. This particular configuration item is huge. I am storing this inside a BLOB in MYSQL.
If I echo the $sql variable I can see my entire query string. The query is too long to place here.
'If I copy the query string to MYSQL it works.
Also if I copy the query string from my echo. and use a test page and put $sql= to the string echo'd by my failed script. it works. I wish I could post the query but it is too long due to blob data.
****** UPDATE *********
I did what tadman suggested, I moved to a prepared statement. However, now I am not getting any data input into the blob in my table.
function writepool($pool, $device){
$pooltext = implode('', $pool['list']);
/*
$sql = "INSERT into pools (deviceid, name, config) VALUES (
'".$device."',
'".$pool['pool']."',
'".addslashes($pooltext)."'
)";
*/
#$addpool = insertdb($sql);
include('/var/www/db_login.php');
$mysqli = new mysqli($db_host, $db_username, $db_password, "F5");
// Check connection
if($mysqli === false){
die("ERROR: Could not connect. " . $mysqli->connect_error);
}
// Prepare an insert statement
$sql = "INSERT into pools (deviceid, name, config) VALUES (?, ?, ?)";
if($stmt = $mysqli->prepare($sql)){
// Bind variables to the prepared statement as parameters
$stmt->bind_param("ssb", $db_device, $db_pool, $db_text);
// Set parameters
$db_device = $device;
$db_pool = $pool['pool'];
$db_text = addslashes($pooltext);
// Attempt to execute the prepared statement
if($stmt->execute()){
#echo "Records inserted successfully.";
} else{
echo "ERROR: Could not execute query: $sql. " . $mysqli->error;
}
} else{
echo "ERROR: Could not prepare query: $sql. " . $mysqli->error;
}
// Close statement
$stmt->close();
// Close connection
$mysqli->close();
}
$db_text is a section of configuration data generated by a system file. I was storing this as a blob since it is huge (1600+ lines long).
I have a database table which has two columns, business and tourist.
I ask a user to select one of them from dropdown list, then use the result in a SELECT statement in MySQL. I assign this column to $cclass, then I make this statement SELECT $cclass FROM flights ....
But it always returns NULL. Why does it return NULL and how do I fix this?
My code:
$check = mysql_query("SELECT $cclass FROM flights WHERE flight_no = '$flightno'");
while ($result = mysql_fetch_assoc($check))
{
$db_seats = $result['$cclass'];
}
you should replace this line:
$db_seats = $result['$cclass'];
with this:
$db_seats = $result[$cclass];
string between 2 single quotes doesn't parsed:
Strings
Have you tried doing the following:
$check = mysql_query("SELECT".$cclass." FROM flights WHERE flight_no = '$flightno'");
First of all, this code has a serious security issue, as it is vulnerable to SQL Injection. You should be using the MySQLi extension instead, and properly filtering your input.
Try something like this:
<?php
/* Create the connection. */
$mysql = new mysqli("localhost", "username", "password", "myDB");
if ($mysql->connect_error)
{
error_log("Connection failed: " . $mysql->connect_error);
die("Connection failed: " . $mysql->connect_error);
}
/* Sanitize user input. */
if (!in_array($cclass, array('business', 'tourist')))
{
error_log("Invalid input: Must be 'business' or 'tourist'");
die("Invalid input: Must be 'business' or 'tourist'");
}
$statement = $mysql->stmt_init();
$statement->prepare("SELECT $cclass FROM flights WHERE flight_no = ?");
$statement->bind_param("s", $flightno);
if (!$statement->execute())
{
error_log("Query failed: " . $statement->error);
die("Query failed: " . $statement->error);
}
if ($statement->num_rows < 1)
{
echo "No results found.";
}
else
{
$statement->bind_result($seats);
while ($statement->fetch())
{
echo "Result: $seats";
// Continue to process the data... You can just use $seats.
}
}
$mysql->close();
However, the reason your original example is failing, is that you're quoting $cclass:
$db_seats = $result[$cclass];
However, please do not ignore the serious security risks noted above.
So, I am trying to use the get method while protecting from injections. I'm trying to get data from the database and echo it out to a page. I think it's pretty obvious what I'm trying to do with the code below but i need help with using the right syntax.
Can someone show me the right syntax for the prepare statement to get data from a database using mysqli that is protected from injections?
I've looked on this site can't seem to find what I'm looking for and the PHP site I couldn't find an up to date method. Thanks for all the help.
<?php
$mysqli = new mysqli("", "", "", "");
if ($mysqli->connect_error) {
echo "Failed to connect to MySQL: (" . $mysqli->connect_error . ") " . $mysqli->connect_error;
}
$stmt = $mysqli->stmt_init();
if($stmt->prepare("SELECT 'name,name' FROM 'table' WHERE 'name, name' = ?,?")) {
}
if (!$stmt->bind_param('si', $_GET['name'], $_GET['name'])); {
echo "Binding parameters failed: (" . $stmt->error . ") " . $stmt->error;
}
if (!$stmt->execute()) {
echo "Execute failed: (" . $stmt->error . ") " . $stmt->error;
}
if (!$stmt->fetch()); {
echo "Binding parameters failed: (" . $stmt->error . ") " . $stmt->error;
}
$stmt->close();
?>
your sql is wrong:
if($stmt->prepare("SELECT 'name,name' FROM 'table' WHERE 'name, name' = ?,?")) {
must be
if($stmt->prepare("SELECT name, name FROM table WHERE name=? AND name=? ")) {
Double the expressive name, I used only because of the question.
The following is clearer:
if($stmt->prepare("SELECT astring, ainteger FROM table WHERE astring=? AND ainteger=? "))
{
if (!$stmt->bind_param('si', $_GET['astring'], $_GET['ainteger'])) {
Take out some time to write the question carefully. If two variables are used, then designate different, everything else just confused.
Update :
Before you use bind_param()
You have to test all $_GET["xx"].
if (isset($_GET['name']))
When you call a function, terminated with ; for example:
if (!$stmt->bind_param('si', $_GET['name'], $_GET['name'])); {
Then the curly braces are useless, no matter the if gets true or false!
The following code after if (!$stmt->bind_param(...)); will always be executed, because the command, is finished.
if (!$stmt->bind_param('si', $_GET['name'], $_GET['name'])); {
echo "Binding parameters failed: (" . $stmt->error . ") " . $stmt->error;
}
It took a long time until I found this error. It is easily overlooked.
That's why you always get your own error messages.
to protect from sql injection, you should first make a connection to your mysql database and after that you should surround your $_GET with mysql_real_escape_string(), like this:
mysql_real_escape_string($_GET['name'])
or to use the newer function
mysqli_real_escape_string($_GET['name'])
This is better solution if you whant to protect all GET inputs:
function GET($name=NULL, $value=false)
{
$content=(!empty($_GET[$name]) ? trim($_GET[$name]) : (!empty($value) && !is_array($value) ? trim($value) : false));
if(is_numeric($content))
return preg_replace("#([^0-9])#Ui", "", $content);
else if(is_bool($content))
return ($content?true:false);
else if(is_float($content))
return preg_replace("#([^0-9\,\.\+\-])#Ui", "", $content);
else if(is_string($content))
{
if(filter_var ($content, FILTER_VALIDATE_URL))
return $content;
else if(filter_var ($content, FILTER_VALIDATE_EMAIL))
return $content;
else if(filter_var ($content, FILTER_VALIDATE_IP))
return $content;
else if(filter_var ($content, FILTER_VALIDATE_FLOAT))
return $content;
else
return preg_replace("#([^a-zA-Z0-9\+\-\_\*\#\$\!\;\.\?\#\:\=\%\/\ ]+)#Ui", "", $content);
}
else false;
}
Just instead $_GET['something'] you use GET('something') and you have option to put default value if GET value don't exists. And lather in MySQL query you can use escape string or prepared state to full protect your query.
I am learning some myqli and would like to make a simple check.
Basically, A user will enter their email addess then submit a form, if the email address is already contained in a certain mysql table, then the script must stop with an error.
This is my example:
$userEmail = sanitize($_POST['specials']);
// Check to see if email already exists, if not proceed
if ($stmt = $link->prepare("SELECT email FROM specials WHERE email=$userEmail"))
{
$specialsErrorFocus = 'autofocus="autofocus"';
$specialsInfo = 'This email address: $userEmail, is already in our database.';
include "$docRoot/html/shop/home.html.php";
exit();
}
This code does not do as I have intended it to as described.
Could someone please explain where I am going wrong with this, or possibly offer a better solution for this task.
Thanks in advance!
You need to execute the query first, as simply preparing the statement is not sufficient. See the documentation as it is a multi stage process.
First, you prepare the statement:
$stmt = $link->prepare("SELECT `email` FROM `specials` WHERE `email` = ?")
if (!$stmt) {
echo $link->errno . " : " . $link->error;
}
Next, bind the parameters:
if (!$stmt->bind_param("s", $userEmail)) {
echo $stmt->errno . " : " . $stmt->error;
}
Finally, execute the query:
if (!$stmt->execute()) {
echo $stmt->errno . " : " . $stmt->error;
}
Get the results:
$stmt->store_result();
if ($stmt->num_rows) {
# Email exists
}
Prepare does not execute the statement. You can use mysql::query to execute the statement.
Your Example would become:
$result = $link->query("SELECT email FROM specials WHERE email=$userEmail");
if ( $result ) {
if ( $result->num_rows > 0 ) {
$specialsErrorFocus = 'autofocus="autofocus"';
$specialsInfo = 'This email address: $userEmail, is already in our database.';
include "$docRoot/html/shop/home.html.php";
exit();
}
}