Good day everyone:
I'd like to parametrize my queries, creating a function that receive my query, connection and array with parameters expressed as "?".
My function is:
receiveQuery($query, $mysqli1, $array1)
I have read about sql injection I would like to know that if this is a proper way to avoid these.
I am planning to use this this function for INSERT, DELETE, UPDATE and SELECT.
Also I would like you to guide me how could I create some better handling for more than 1 parameter, because currently I am using a switch.
But every time I require more parameters, I am increasing the switch and I would like to create it dinamically.
SWITCH ($array1Length)
Any comments is helpful, regards.
Felipe
<?php
$mysqli1 = openConn();
$query = "INSERT INTO tblTest (field1 , field2 ) VALUES (?,?)";
$array1 = array($value1, $value2);
$result = receiveQuery($query, $mysqli1, $array1);
if($stmt->affected_rows == 1)
{
$success = "Success.";
}
if($stmt->affected_rows == -1)
{
$error = "Error.";
}
closeConn($stmt);
closeConn($mysqli1);
function openConn()
{
$mysqli1 = new mysqli('localhost', 'userTest', '123', 'dbTest');
if ($mysqli1->connect_error) {
die('Connect Error (' . $mysqli1->connect_errno . ') '
. $mysqli1->connect_error);
}
return $mysqli1;
}
function receiveQuery($query, $mysqli1, $array1)
{
global $stmt;
$stmt = $mysqli1->prepare($query);
if (false===$stmt)
{
echo $mysqli1->error;
die('Error');
}
$array1Length = count($array1);
SWITCH ($array1Length)
{
CASE 0: break;
CASE 1: $stmt->bind_param("s" , $array1[0]) ;break;
CASE 2: $stmt->bind_param("ss" , $array1[0],$array1[1]) ;break;
CASE 3: $stmt->bind_param("sss" , $array1[0],$array1[1],$array1[2]) ;break;
CASE 4: $stmt->bind_param("ssss", $array1[0],$array1[1],$array1[2],$array1[3]);break;
DEFAULT : echo "Error";
}
$stmt->execute();
$result = $stmt->get_result();
return $result;
}
function closeConn($mysqli1)
{
$mysqli1->close();
}
?>
You should be able to use the splat operator on your array.
$s = '';
for ($x = 0; $x < count($params); $x ++) {
$s .= 's';
}
$stmt->bind_param($s, ...$params);
https://secure.php.net/manual/en/migration56.new-features.php
I'd like to parametrize my queries, creating a function that receive
my query, connection and array with parameters expressed as "?"
My suggestion is that you rather use PDO than, the current mysqli that you using at the moment. PDO is easier to learn and can work easy with your current requirements.
Here's how you would do this with PDO.
page.php
<?php
define('DB_HOST', 'localhost');
define('DB_NAME', 'dbTest');
define('DB_USER', 'userTest');
define('DB_PASS', '123');
define('DB_CHAR', 'utf8');
class conn
{
protected static $instance = null;
protected function __construct() {}
protected function __clone() {}
public static function instance()
{
if (self::$instance === null)
{
$opt = array(
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_EMULATE_PREPARES => FALSE,
);
$dsn = 'mysql:host='.DB_HOST.';dbname='.DB_NAME.';charset='.DB_CHAR;
self::$instance = new PDO($dsn, DB_USER, DB_PASS, $opt);
}
return self::$instance;
}
public static function __callStatic($method, $args)
{
return call_user_func_array(array(self::instance(), $method), $args);
}
public static function receiveQuery($sql, $args = [])
{
if (!$args)
{
return self::instance()->query($sql);
}
$stmt = self::instance()->prepare($sql);
$stmt->execute($args);
return $stmt;
}
}
anotherpage.php
<?php
require 'page.php';
$params = array($value1, $value2);
$sql = "INSERT INTO tblTest (field1 , field2 ) VALUES (?,?)";
$stmt = conn::receiveQuery($sql, $params);
if($stmt->rowCount() > 0){
$success = "Success.";
}else{
$error = "Error.";
}
?>
To learn more about PDO you can follow this site : https://phpdelusions.net/pdo
Related
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 5 years ago.
Improve this question
My INSERT statement is not working. The number of rows affected is affected is 0. Here is my code :
<?php
require_once('class.config.php');
class userActions{
public $link;
function __construct(){
$db_connection = new dbConnection();
$this->link = $db_connection->connect();
return $this->link;
}
function registerUsers($username, $password, $ip_address){
$query = $this->link->prepare("INSERT INTO users (user_name,password,ip_address) VALUES (?,?,?)");
$values = array($username, $password, $ip_address);
$query->execute($values);
$counts = $query->rowCount();
return $counts;
}
}
$users = new userActions();
echo $users->registerUsers('bob', 'dave123','127.0.0.1');
?>
What have I done wrong?
EDIT : this is my class.config.php:
class dbConnection{
protected $db_conn;
public $db_host = "localhost:8080";
public $db_user = "root";
public $db_pass = "";
public $db_name = "facebook";
function connect(){
try{
$this->db_conn = new PDO("mysql:host = $this->db_host; dbname = $this->db_name",
$this->db_user, $this->db_pass);
$this->db_conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
return $this->db_conn;
}
catch(PDOException $e){
return "MySQL error : " . $e->getMessage();
}
}
}
Change your registerUsers function code to below
If you are using mysqli then use below
function registerUsers($username, $password, $ip_address){
$query = $this->link->prepare("INSERT INTO users (user_name,password,ip_address) VALUES (?,?,?)");
$query->bind_param("sss", $username, $password, $ip_address);
$query->execute();
}
EDIT
As I see you are using PDO.
Try to use like anyone of below
function registerUsers($username, $password, $ip_address){
$query = $this->link->prepare("INSERT INTO users (user_name,password,ip_address) VALUES (:username,:password,:ip_address)");
$query->bindParam(':username', $username);
$query->bindParam(':password', $password);
$query->bindParam(':ip_address', $ip_address);
$query->execute();
}
OR
function registerUsers($username, $password, $ip_address){
$query = $this->link->prepare("INSERT INTO users (user_name,password,ip_address) VALUES (:username,:password,:ip_address)");
$query->execute(array('username' => $username, 'password' => $password, 'ip_address' => $ip_address));
}
I would prefer to use bindParam as it is secure and it replaces my_real_escape_string.
this below can can help you to understand how to insert to database via oop programming
databse class:
public static $con;
public static function connect($hostname , $username , $password , $database)
{
self::$con = mysqli_connect($hostname , $username , $password , $database) or die('Error: '.mysqli_connect_error());
}
public static function disconnect()
{
mysqli_close(self::$con);
}
public static function insert($sql , $values)
{
$values = self::safeValues($values);
$sql = self::$con->prepare($sql);
mysqli_set_charset(self::$con , 'UTF8');
self::bindValues($sql , $values);
$save = $sql->execute();
return $save;
}
public function safeValues($values = null)
{
if($values != null)
{
for($i = 0 ; $i < count($values) ; $i++)
{
$values[$i] = trim($values[$i]);
$values[$i] = strip_tags($values[$i]);
mysqli_real_escape_string(self::$con , $values[$i]);
}
}
return $values;
}
public function bindValues($sql, $values = null)
{
if ($values != null)
{
// Generate the Type String (eg: 'issisd')
$types = '';
foreach($values as $value)
{
if(is_int($value))
$types .= 'i';
elseif (is_float($value))
$types .= 'd';
elseif (is_string($value))
$types .= 's';
else
$types .= 'b';
}
// Add the Type String as the first Parameter
$bind_names[] = $types;
// Loop thru the given Parameters
for ($i = 0 ; $i < count($values) ; $i++)
{
// Create a variable Name
$bind_name = 'bind'.$i;
// Add the Parameter to the variable Variable
$$bind_name = $values[$i];
// Associate the Variable as an Element in the Array
$bind_names[] = &$$bind_name;
}
// Call the Function bind_param with dynamic Parameters
call_user_func_array(array($sql,'bind_param') , $bind_names);
}
return $sql;
}
and in your home class you can insert like this:
home class:
DB::connect('localhost' , 'username' , 'password' , 'database');
DB::insert("INSERT INTO table(field1, field2) VALUES (? , ?)" , [ $val1 , $val2 ] );
i hope this can be helpful for you.
So I have solved my code by removing localhost:8080 with 127.0.0.1 in my class.config.php.
I would like to write a database connection class and I dont understand how I have to write the select method with bind_param-s. Here is the full code. And here the part of the code where I need the help:
public function select($sql){
$db = $this->connect(); //This methos connect to the DB
$stmt = $db->prepare($sql);
if($stmt === false){ //If the prepare faild
trigger_error("Wrong SQL", E_USER_ERROR);
}
$error = $stmt->bind_param("i", $id);
if($error){
return "Error: ".$stmt->error, $stmt->errno;
}
$err = $stmt->execute();
if($error){
return "Error: ".$stmt->error, $stmt->errno;
}
$result = $stmt->bind_result($id);
$stmt->close();
$dbConnection->closeConnection($db);
return $result;
}
I need to got it parameters or how can I slove it?
You need to pass your values into this function too. And eventually bind them into prepared statement.
Optionally you can pass string with types, but by default all "s" will do.
Also remember that you should connect only ONCE per script execution. and then use one single connection all the way throughout your code.
And get rid of all these error checks. Set mysqli in exception mode instead.
public function q($sql, $values = array(), $types = NULL)
{
$stm = $this->mysql->prepare($sql);
if (!$types)
{
$types = str_repeat("s", count($values));
}
if (strnatcmp(phpversion(),'5.3') >= 0)
{
$bind = array();
foreach($values as $key => $val)
{
$bind[$key] = &$values[$key];
}
} else {
$bind = $values;
}
array_unshift($bind, $types);
call_user_func_array(array($stm, 'bind_param'), $bind);
$stm->execute();
return $stm->get_result();
}
so it can be used like this
$res = $db->q("SELECT name FROM users WHERE id=?", [$id]);
or
$res = $db->q("SELECT name FROM users WHERE id=?", [$id], "i");
your other functions have to be changed as well.
class DB{
public $con;
function __construct()
{
$this->con = new mysqli("localhost", "root", "", "proba_fferenc");
}
public function select(...)
{
// as shown above
}
}
Hi all I think I designed my pdo mysql class rather badly, as I'm not able to put a query inside a while loop of another query because the new query inside the loop wipes out the old one, is there an easy way to fix this?
I'm too used to the really old style of php/mysql where you make a query, then assign that query inside the fetch($query) which you don't do with PDO. This has me in a muddle and it's been bugging me for too long.
My Code cut to the relevant parts
class mysql
{
// the query counter
public $counter = 0;
// the database connection
public $database;
// statement handler
public $STH;
// store all the queries for debugging
public $queries = '';
public function __construct($database_host, $database_username, $database_password, $database_db)
{
$options = array(
PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8',
);
$this->database = new PDO("mysql:host=$database_host;dbname=$database_db", $database_username, $database_password, $options);
$this->database->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION );
}
// A plain query
public function pquery($sql, $page = NULL)
{
global $core;
try
{
$this->STH = $this->database->prepare($sql);
$this->counter++;
$this->queries .= "<br />$sql";
return $this->STH->execute();
}
catch (Exception $e)
{
$core->message("Something went wrong. The admin will be notified and punished muhahaha for I am an evil overlord...I'm sure I will be fixed soon.", NULL, 1);
$this->pdo_error($e->getMessage(), $page, $sql);
}
}
// the main sql query function
public function sqlquery($sql, $objects = array(), $page = NULL, $referrer = NULL)
{
global $core;
try
{
$this->STH = $this->database->prepare($sql);
foreach($objects as $k=>$p)
{
// +1 is needed as arrays start at 0 where as ? placeholders start at 1 in PDO
if(is_numeric($p))
{
// we need to do this or else decimals always seem to end up 'x.00', pdo has no decimal check, odd
if ($this->contains_decimal($p) == true)
{
$this->STH->bindValue($k+1, $p, PDO::PARAM_STR);
}
else
{
$this->STH->bindValue($k+1, (int)$p, PDO::PARAM_INT);
}
}
else
{
$this->STH->bindValue($k+1, $p, PDO::PARAM_STR);
}
}
$this->counter++;
$this->queries .= "<br />$sql";
return $this->STH->execute();
}
catch (Exception $e)
{
$core->message("Something went wrong. The admin will be notified and punished muhahaha for I am an evil overlord...I'm sure I will be fixed soon.", NULL, 1);
$this->pdo_error($e->getMessage(), $page, $sql, $referrer);
}
}
public function fetch()
{
$this->STH->setFetchMode(PDO::FETCH_ASSOC);
return $this->STH->fetch();
}
I am trying to establish a data connection to the MySql and create prepared statements, where the query_f function takes in any number of parameters, where the first parameter is the sql statement, and the other parameters are the values that would be substituted in the prepared statement.
Here is what I have. The first error I got is when I am trying to bind the values to the statement.
function query_f(/* query, [...] */){
$user = "root";
$pass = "root";
$host = "localhost";
$database = "mcnair";
$conn = mysqli_connect($host,$user,$pass);
if(!$conn)
{
echo "Cannot connect to Database";
}
else
{
mysqli_select_db($conn, $database);
}
// store query
$query = func_get_arg(0);
$parameters = array_slice(func_get_args(), 1);
$param = "'".implode("','",$parameters)."'";
// Prepare the statement
$stmt = mysqli_prepare($conn, $query);
if ($stmt == false)
{
echo "The statement could not be created";
exit;
}
// Bind the parameters
$bind = mysqli_stmt_bind_param($stmt, 's', $param);
echo mysqli_stmt_error($stmt);
if ($bind == false)
{
echo "Could not bind";
}
else
{
echo "Bind successful";
}
// Execute the statement
$execute = mysqli_stmt_execute($stmt);
if ($execute = false)
{
echo "Could not execute";
}
// fetch the data
$fetch = mysqli_stmt_fetch($stmt)
if ($fetch == false)
{
echo "Could not fetch data";
}
else
{
return $fetch;
}
}
And the function call I am using is:
query_f("SELECT Hash FROM alumni WHERE Username = '?'", "zm123");
How about using a class (instead of a function) and using mysqli in the OO way and not in the procedural way?
This is a simplified version of what I use. Not perfect, so if anyone would like to suggest improvements, I'm all ears.
class Connection {
private $connection;
public function __construct()
{
//better yet - move these to a different file
$dbhost = '';
$dbuname = '';
$dbpass = '';
$dbname = '';
$this->connection = new mysqli($dbhost, $dbuname, $dbpass, $dbname);
}
/*
* This is the main function.
*
* #param $arrayParams = array (0 => array('s' => 'Example string'), 1 => array('s' => 'Another string'), 2 => array('i' => 2), 3 => array('d' => 3.5) )
*/
public function executePrepared($sql, $arrayParams)
{
$statement = $this->prepareStatement($sql);
if ($statement) {
$this->bindParameter($statement, $arrayParams);
$this->executePreparedStatement($statement);
$result = $this->getArrayResultFromPreparedStatement($statement);
//only close if you are done with the statement
//$this->closePreparedStatement($statement);
} else {
$result = false;
}
return $result;
}
public function prepareStatement($sql)
{
$statement = $this->connection->prepare($sql) or $this->throwSqlError($this->connection->error);
return $statement;
}
public function bindParameter(&$statement, $arrayTypeValues)
{
$stringTypes = '';
$arrayParameters = array();
$arrayParameters[] = $stringTypes;
foreach ($arrayTypeValues as $currentTypeVale) {
foreach ($currentTypeVale as $type => $value) {
$stringTypes .= $type;
$arrayParameters[] = &$value;
}
}
$arrayParameters[0] = $stringTypes;
call_user_func_array(array($statement, "bind_param"), $arrayParameters);
}
public function getArrayResultFromPreparedStatement(&$statement)
{
$statement->store_result();
$variables = array();
$data = array();
$meta = $statement->result_metadata();
while($field = $meta->fetch_field())
$variables[] = &$data[$field->name]; // pass by reference
call_user_func_array(array($statement, 'bind_result'), $variables);
$i = 0;
$arrayResults = array();
while($statement->fetch())
{
$arrayResults[$i] = array();
foreach($data as $k=>$v)
{
$arrayResults[$i][$k] = $v;
}
$i++;
}
return $arrayResults;
}
public function executePreparedStatement($statement)
{
$result = $statement->execute() or $this->throwSqlError($statement->error);
return $result;
}
public function closePreparedStatement($statement)
{
$statement->close();
}
public function throwSqlError()
{ ... }
}
I've tried disabling emulated prepares in PDO but I cannot get it to work. Everything else works. The query is successful. The reason I believe it's not working is because it does not escape quotes and such so I get syntax errors.
I've tried doing it two different ways.
$this->dbh->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
and
$insert = $database->$con->prepare($insert, array(PDO::ATTR_EMULATE_PREPARES => false));
I've also noticed that getAttribute does not work.
By doing this...
$emul = $database->$con->getAttribute(PDO::ATTR_EMULATE_PREPARES);
var_dump($emul);
...I get this error
SQLSTATE[IM001]: Driver does not support this function: driver does not support that attribute
And here's my database class where the action happens. (I might have left some unneccessary/stupid code in there while I was testing.)
<?php
class Database
{
public $dbh;
public $dbh1;
public $dbh2;
private static $instance;
public $numResults;
private $result = array(); // Results that are returned from the query
public function __construct()
{
try
{
$this->dbh = new PDO(DB_TYPE.':host='.DB_HOST.';dbname='.DB_NAME.';charset=utf8', DB_USER, DB_PASS);
$this->dbh->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
$this->dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$this->dbh1 = new PDO(DB_TYPE1.':host='.DB_HOST1.';dbname='.DB_NAME1.';charset=utf8', DB_USER1, DB_PASS1);
$this->dbh1->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
$this->dbh1->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$this->dbh2 = new PDO(DB_TYPE2.':host='.DB_HOST2.';dbname='.DB_NAME2.';charset=utf8', DB_USER2, DB_PASS2);
$this->dbh2->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
$this->dbh2->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
}
catch (PDOException $e)
{
die("Database Error: ". $e->getMessage() . "<br />");
}
}
public static function getInstance()
{
if (!isset(self::$instance))
{
$object = __CLASS__;
self::$instance = new $object;
}
return self::$instance;
}
private function tableExists($table, $con)
{
switch($con)
{
case 'dbh':
$db_name = DB_NAME;
break;
case 'dbh1':
$db_name = DB_NAME1;
break;
case 'dbh2':
$db_name = DB_NAME2;
break;
}
$database = Database::getInstance();
if(is_array($table))
{
for($i = 0; $i < count($table); $i++)
{
$tablesInDb = $database->$con->prepare('SHOW TABLES FROM '.$db_name.' LIKE "'.$table[$i].'"');
$tablesInDb->execute();
$rowCount = $tablesInDb->rowCount();
if($tablesInDb)
{
if($rowCount <> 1)
{
die('Error: Table does not exist'.$table[$i]);
}
}
}
}else
{
$tablesInDb = $database->$con->prepare('SHOW TABLES FROM '.$db_name.' LIKE "'.$table.'"');
$tablesInDb->execute();
$rowCount = $tablesInDb->rowCount();
if($tablesInDb)
{
if($rowCount <> 1)
{
die('Error: Table does not exist'.$table);
}
}
}
return true;
}
public function insert($con, $table, $values, $cols = null)
{
if($this->tableExists($table, $con))
{
$insert = 'INSERT INTO '.$table;
if($cols != null)
{
$cols = implode(',', $cols);
$insert.= '('.$cols.')';
}
for($i = 0; $i < count($values); $i++)
{
if(is_string($values[$i]))
$values[$i] = "'".$values[$i]."'";
}
$values = implode(',', $values);
$insert .= ' VALUES ('.$values.')';
$database = Database::getInstance();
$insert = $database->$con->prepare($insert, array(PDO::ATTR_EMULATE_PREPARES => false));
$insert->execute();
if($insert)
{
return true;
}else
{
return false;
}
}
}
public function getResult()
{
return $this->result;
}
}
?>
As manual states, getAttribute() don't support ATTR_EMULATE_PREPARES
There shouldn't be no escaping with native prepares at all.
To check if you are in emulation mode or not you can use LIMIT clause with lazy binding. It will raise an error if emulation is on.
Your main problem is whatever "syntax error" you mentioned and you have to solve it first.
As Álvaro G. Vicario noted in comments, you are not using prepared statements. It is apparently the root of the problem. PDO doesn't "escape" your data by itself. It can do it only if you are using placeholders to represent your data in the query. You can read more here