parameters in MySQLi - php

I'm using PHP with MySQLi, and I'm in a situation where I have queries like
SELECT $fields FROM $table WHERE $this=$that AND $this2=$that2
So far I've written some code that splices up an array that I give it, for example:
$search = array(name=michael, age=20) //turns into
SELECT $fields FROM $table WHERE name=michael AND age=20
Is there a more efficient way to do this?
I'm rather worried about MySQL injections - this seems very vulnerable.
Thanks!

Oddly enough, the title to your question is basically the answer to it. You want to do something like this, using mysqli parameterized queries:
$db = new mysqli(<database connection info here>);
$name = "michael";
$age = 20;
$stmt = $db->prepare("SELECT $fields FROm $table WHERE name = ? AND age = ?");
$stmt->bind_param("si", $name, $age);
$stmt->execute();
$stmt->close();
More information in the mysqli section of the manual, specifically the functions related to MySQLi_STMT.
Note that I personally prefer using PDO over mysqli, I don't like all the bind_param / bind_result stuff that mysqli does. If I have to use it I write a wrapper around it to make it work more like PDO.

Related

MySql LIKE query in a form

So, I have this query:
FROM test.cliente, test.contratto
WHERE test.contratto.Codice_Cliente = test.cliente.Codice_Cliente
AND test.cliente.Denominazione = :name;
But I'm trying to work with something like this:
FROM test.cliente, test.contratto
WHERE test.contratto.Codice_Cliente = test.cliente.Codice_Cliente<
AND test.cliente.Denominazione LIKE "%:name%";
The reason I want to use this query is because the user puts a name into the html form, but I don't want him to type the same piece of data that I have on the database, because there's no way that what he'll type will be the same piece of data as it's written in the database.
The code goes on like this:
$name = $_POST['Denominazione'];
$statement = $connection->prepare($sql);
$statement->bindParam(':name', $name, PDO::PARAM_STR);
$statement->execute();
$result = $statement->fetchAll();
I also tried to type
$name = '%'.$_POST['Codice_Cliente'].'%';
but it didn't find me anything.
PDO prepared statements DO NOT allow % in SQL statements.
You have to use "FROM test.cliente, test.contratto
WHERE test.contratto.Codice_Cliente = test.cliente.Codice_Cliente
AND test.cliente.Denominazione LIKE :name";
And put the "%name%" inside the execute.
Please also see:
How do I create a PDO parameterized query with a LIKE statement?
PHP - Using PDO with IN clause array

Converting regular mysql into prepared statements

Im new to database and i have written a LOT of PHP code that accesses a database using MySQL.
I didnt take into account SQL injection attacks so i have to re-write all that PHP code to use mysql prepared statements.
After looking at videos on how to used prepared SQL statements, to perform just ONE SQL command requires a whole lot of "prepared" statements. My existing code has lots of different SQL statements all over the place, it would be a nightmare to change all that code to pack and unpack all the required preparation for each "prepared" statement command.
Is there some kind of wrapper i can use to prevent turning one line of regular SQL into 6 or 7 lines of prepared statements?
For example use to do this line line of SQL
SELECT * from users where userid=10
needs many more lines of prepared SQL statements, especially if there are lots of other SQL statements too it now becomes very complex.
Is there was some sort of one line wrapper that i can call that accepts the template SQL string, plus the parameters, which also executes the command and returns the result in just one line of wrapper for different types of MYSQL statements it would be great and the code would be much less confusing looking and error prone.
For example
$users=WrapAndExecute($db,"SELECT * from users where userid=?","s",$userid);
$data=WrapAndExecute($db,"UPDATE table SET username=?,city=?","ss",$name,$city);
$result=WrapAndExecute($db,"DELETE from table where id=?","s",$userid);
$result=WrapAndExecute($db,"INSERT into ? (name,address) VALUES(?,?)","ss","users",$name,$address);
Each of those lines above would create a prepared statement template, do the bind, execute it and return the result that a regular MYSQL statement would. This would create minimal impact on existing code.
Anybody knows how to do this or if some easy php library or class already exists to do this, that i can just import and start using it?
Thanks
You don't need to change a query to a prepared statement if it has no PHP variables in it. If it has just constant expressions, it's safe from SQL injection.
$sql = "SELECT * from users where userid=10"; // Safe!
$stmt = $pdo->query($sql);
$data = $stmt->fetchAll();
You don't need to change a query that contains PHP variables, as long as the value of that variable is a constant specified in your code. If it doesn't take its value from any external source, it's safe.
$uid = 10;
$sql = "SELECT * from users where userid=$uid"; // Safe!
$stmt = $pdo->query($sql);
$data = $stmt->fetchAll();
You don't need to change a query that contains PHP variables, as long as you can filter the value to guarantee that it won't risk an SQL injection. A quick and easy way to do this is to cast it to an integer (if it's supposed to be an integer).
$uid = (int) $_GET['uid'];
$sql = "SELECT * from users where userid=$uid"; // Safe!
$stmt = $pdo->query($sql);
$data = $stmt->fetchAll();
That leaves cases where you are using "untrusted" values, which may have originated from user input, or reading a file, or even reading from the database. In those cases, parameters are the most reliable way to protect yourself. It's pretty easy:
$sql = "SELECT * from users where userid=?"; // Safe!
// two lines instead of the one line query()
$stmt = $pdo->prepare($sql);
$stmt->execute([$_GET['uid']]);
$data = $stmt->fetchAll();
In a subset of cases, you need one additional line of code than you would normally use.
So quit your whining! ;-)
Re your comment about doing prepared statements in mysqli.
The way they bind variables is harder to use than PDO. I don't like the examples given in http://php.net/manual/en/mysqli.prepare.php
Here's an easier way with mysqli:
$sql = "SELECT * from users where userid=?"; // Safe!
$stmt = $mysqli->prepare($sql);
$stmt->bind_param('i', $_GET['uid']);
$stmt->execute();
$result = $stmt->get_result();
$data = $result->fetch_all();
I don't like the stuff they do in their examples with bind_result(), that's confusing and unnecessary. Just use get_result(). So with mysqli, you need two more lines of code than you would with PDO.
I've written query wrappers for mysqli that emulate the convenience of PDO's execute() function. It's a PITA to get an array mapped to the variable-arguments style of bind_param().
See the solution in my answers to https://stackoverflow.com/a/15933696/20860 or https://stackoverflow.com/a/7383439/20860
I were in the same boat, and I wrote such a wrapper that works exactly the way you want, save for it's being a class, not a function.
$user = $sdb->getRow("SELECT * from users where userid=?s", $userid);
$sdb->query("UPDATE table SET username=?s, city=?s", $name, $city);
$sdb->query("DELETE from table where id=?s", $userid);
$sdb->query("INSERT into ?n (name,address) VALUES(?s,?s)","users", $name, $address);
The above is a working code, as long as you have somewhere in your bootstrap file
$db = mysqli_connect(...);
...
require 'safemysql.class.php';
$sdb = new SafeMySQL('mysqli' => $db);
Note that none of the other suggestions could do anything like that.
Also note that if I were writing it today, I would have used PDO, as this class is duplicating a lot of functionality already exists in PDO.
Take a look at the PDO extension in PHP - http://php.net/manual/en/intro.pdo.php: it it secure against injections thanks to prepared statements; also, it allows you to connect to many different databases (e.g. MySQL, MSSQL, etc.).
You can then build your own wrapper as you wish to keep it clean; for example your own wrapper could be as follows:
(following example will return user rows as objects)
// connect to DB
$GLOBALS['default_db'] = new DB('localhost','db_name','username','password') ;
// Get users and output results
$query = new DBQuery('SELECT * FROM users WHERE userid = ?',array(10)) ;
var_dump($query -> results()) ;
var_dump($query -> num_rows()) ;
// DB connection
class DB {
public $connection;
public function __construct($host , $dbname , $username , $password) {
$this->connection = new \PDO('mysql:host=' . $host . ';dbname=' . $dbname , $username , $password);
}
}
// Wrapper
class DBQuery {
private $num_rows = 0;
private $results = array();
public function __construct($query , $params = null , $class_name = null , DB $db = null) {
if ( is_null($db) ) {
$db = $GLOBALS['default_db'];
}
$statement = $db->connection->prepare($query);
$statement->execute($params);
$errors = $statement->errorInfo();
if ( $errors[2] ) {
throw new \Exception($errors[2]);
}
$fetch_style = ($class_name ? \PDO::FETCH_CLASS : \PDO::FETCH_OBJ);
$this->results = $class_name ? $statement->fetchAll($fetch_style , $class_name) : $statement->fetchAll($fetch_style);
$this->num_rows += $statement->rowCount();
while ( $statement->nextrowset() ) {
$this->results = array_merge($this->results,$class_name ? $statement->fetchAll($fetch_style , $class_name) : $statement->fetchAll($fetch_style));
$this->num_rows += $statement->rowCount();
}
}
public function num_rows() {
return $this->num_rows;
}
public function results() {
return $this->results;
}
}
Since a key requirement seems to be that you can implement this with minimal impact on your current codebase, it would have been helpful if you had told us what interface you currently use for running your queries.
While you could use PDO:
that means an awful lot of work if you are not already using PDO
PDO exceptions are horrible
Assuming you are using procedural mysqli (and have a good reason not to use mysqli_prepare()) its not that hard to write something (not tested!):
function wrapAndExecute()
{
$args=func_get_args();
$db=array_shift($args);
$stmt=array_shift($args);
$stmt_parts=explode('?', $stmt);
if (count($args)+1!=count($stmt_parts)) {
trigger_error("Argument count does not match placeholder count");
return false;
}
$real_statement=array_shift($stmt_parts);
foreach ($args as $k=>$val) {
if (isnull($val)) {
$val='NULL';
} else if (!is_numeric($val)) {
$val="'" . mysqli_real_escape_string($db, $val) . "'";
}
$real_statement.=$val . array_shift($stmt_parts);
}
return mysqli_query($db, $real_statement);
}
Note that this does not handle IS [NOT] NULL nicely nor a literal '?' in the statement nor booleans (but these are trivial to fix).

Variable binding in aura/sqlquery when using mysqli_* connection

I've got a legacy app that uses mysqli_*() functions (actually, it uses mysql_*() functions. Gah!). I am using aura/sqlquery as a SQL query generator. For example:
$queryFactory = new Aura\SqlQuery\QueryFactory('mysql');
$select = $queryFactory->newSelect();
$select->from('sometable AS t')
->where('t.field1 = 0')
->where("t.field2 <> ''");
Then we get the raw SQL by casting to string:
$sql = (string) $select;
Now I want to do do some variable binding in a where():
$select->where('t.somefield = ?', $somevalue);
When I cast to string, the escaping/binding never seems to be occur. It appears that the binding only takes place when one uses PDO and prepared statements.
Any ideas how to get variable binding in aura/sqlquery when using a mysqli connection?
If your PHP version is >= 5.6, here is a function that you can use to run a query from aura/sqlquery against mysqli
function mysqli_query_params($mysqli, $query, $params, $types = NULL)
{
$statement = $mysqli->prepare($select);
$types = $types ?: str_repeat('s', count($params));
$statement->bind_param($types, ...$params);
$statement->execute();
return $statement;
}
used like this
mysqli_query_params($mysqli, $select->getStatement(), $select->getBindValues())
You can use $select->getBindValues() to get the bind values.
I will say make use of Aura.Sql than pdo for it helps you in certain other cases like IN () query.
Taking an example from readme.
// a PDO connection
$pdo = new PDO(...);
// prepare the statment
$sth = $pdo->prepare($select->getStatement());
// bind the values and execute
$sth->execute($select->getBindValues());
Let me know in case you need more clarification for the same.
Thank you.

Is there an INSERT or UPDATE equivalent to PDO::FETCH_CLASS?

I have found the PDO::FETCH_CLASS very useful. My classes map to tables. I just do a
$query = $pdo->query("SELECT * FROM fixedTime WHERE
transmissionProgramID = '$transmissionProgramID'");
$query->setFetchMode(PDO::FETCH_CLASS, 'FixedTime');
and voila.
I would like to be able to do the reverse: ie instantiate an object load up the values to UPDATE or INSERT and once again voila.
Have looked but cannot see if this is available.
Well, yes. To some extent you can use something similar for insert or update.
But to achieve that, you have to learn how to use PDO properly. So, first we have to fix your select code:
$sql = "SELECT * FROM fixedTime WHERE transmissionProgramID = ?";
$stmt = $pdo->prepare($sql);
$stmt->execute([$transmissionProgramID]);
$stmt->setFetchMode(PDO::FETCH_CLASS, 'FixedTime');
$ftime = $stmt->fetch();
See - we are using prepared statements here, that you should be always using anyway. And at the same time that's the key for the [semi-]automation we can use with updates. Because with prepared statements you can use the object itself to provide values for the prepared query.
So, as long as you have object properties reflect table fields you can use a code like this:
$user = new stdClass();
$user->name = "foo";
$user->pass = "bar";
$sql = "INSERT INTO users VALUES (NULL, :name, :pass)";
$pdo->prepare($sql)->execute((array)$user);
But for the real automation you have to consider using an ORM, which is doing exactly what you're looking for. You can take a look at Eloquent for example. So, the code would be as simple and straightforward as
$ftime = new fixedTime;
$ftime->value = time();
$ftime->save();

How to use a quoted string variable in a MySqli prepared statement?

Sorry if this seems a really stupid question, but I'm struggling to get to grips with changing from Mysql to Mysqli and prepared statements.
So in mysql, I would have done this:
$q=('SELECT * FROM table WHERE field="'.$variable.'"');
$result = mysql_query($q);
I now know this is not good. So I now have the below:
$stmt = $mysqli->prepare('SELECT * FROM table WHERE field=? LIMIT 1');
$stmt->bind_param('s', $variable);
$stmt->execute();
Problem is that the query doesn't work. Say the ? is actually "tree". So the query becomes:
'SELECT * FROM table WHERE field=tree LIMIT 1'
If I tried to run that query in say phpmyadmin I get "Unknown column tree in where clause". Obviously if I put quotes around it then it works, hence the original query. So how can I get this to work if I can't use quotes, since then you are looking for the literal question mark?
For reference I am then using this code:
$meta = $stmt->result_metadata();
while ($field = $meta->fetch_field()) {
$parameters[] = &$row[$field->name];
}
call_user_func_array(array($stmt, 'bind_result'), $parameters);
while ($stmt->fetch()) {
foreach($row as $key => $val) {
$x[$key] = $val;
}
$results[] = $x;
}
As I can't use get_result() which is very annoying. I have PHP version 5.4, and even the mysqlnd driver, but can't enable it as I'm on a VPS and my host says it might affect other sites on that server. Consequently what is actually just two lines in MySql is actually now something like 15 lines in the 'improved' mysqli. Great.
Any help would be appreciated!
This:
$stmt = $mysqli->prepare('SELECT * FROM table WHERE field=? LIMIT 1');
$stmt->bind_param('s', $variable);
is not equivalent to this:
SELECT * FROM table WHERE field=tree LIMIT 1
Prepared statement placeholders are not the same as copy and pasting in values. You are binding the value "tree" as a string here, the database will actually understand this. The ? is not simply being replaced by the bound value, the database understands the difference between your query structure with its placeholders and the values you're binding into them. Binding the parameter this way is equivalent to running:
SELECT * FROM table WHERE field='tree' LIMIT 1
Consequently what is actually just two lines in MySql is actually now something like 15 lines in the 'improved' mysqli. Great.
Mysqli is not intended to be used as is. It is but a building material for the higher level library. When used wisely, it can give you data in one line:
$data = $db->getAll('SELECT * FROM table WHERE field=?s', $variable);
(BTW, the same goes for the old mysql ext as well)

Categories