Prepared queries in mysqli return numeric database columns as PHP numbers, as does the mysqlnd driver using the MYSQLI_OPT_INT_AND_FLOAT_NATIVE option.
I am modernizing a legacy codebase to use parameterized queries. Other areas of the code rely on the query results having string data types, e.g.:
if ($object->isTrue() === '1') { }
The best solution I've come up to make the output match the expectations of the existing code is to manually cast each result back to strings:
$data = [];
while ($row = $result->fetch_assoc()) {
array_walk($row, function(&$field) { $field = (string) $field; });
$data[] = $row;
}
Is there a more efficient/effective way to do this?
There is no way to force mysqli prepared statements to cast all values to a string. Binary protocol transfers the data using native types and there is no option in mysqli to cast the values to string.
If you have such a requirement as part of a legacy project, you can cast it yourself by mapping each value in every row as a string.
$arr = [];
foreach ($stmt->get_result() as $row) {
$arr[] = array_map(strval::class, $row);
// or if you support only PHP 8.1
// $arr[] = array_map(strval(...), $row);
}
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).
How to prevent SQL Injection while fetching data from the database when using parameters received from the user input:
if(isset($_GET['cityval']) && $_GET['cityval'] !=''){
$city = $this->request->query('cityval');
$searching .= " and college_city in ($city) ";
} else {
$searching .= "";
}
if(isset($_GET['scholarship']) && $_GET['scholarship'] !=''){
$searching .= " and college_scholarship = '".$_GET['scholarship']."' ";
} else {
$searching .= "";
}
And my main query is below
$search = $this->Search->query("select * from colleges where college_id!='' and status='active' $searching order by $order desc limit $start, 10 ");
Don't use raw queries. Simply use the query builder CakePHP provides, and it will prevent injection for you. See the online CakePHP book for more information.
It is SUPER rare to need to use raw queries in CakePHP.
What you try to do is obviously to search by get parameters. There is a wonderful plugin that makes it pretty easy https://github.com/FriendsOfCake/search
It could be actually that easy with the plugin:
$query = $this->Colleges->find('search', [
'search' => $this->request->query
]);
$this->set('results', $this->Paginator->paginate($query));
The search params itself will be handled in the model layer, check the plugins documentation on that. And the framework will take care of sanitizing the input.
It seems that Cake ORM uses PDO:
Underneath the covers, the query builder uses PDO prepared statements
which protect against SQL injection attacks.
Reference: https://book.cakephp.org/3.0/en/orm/query-builder.html
Unfortunately the way you're creating the query is vulnerable as you're not using Cake ORM neither PDO prepared statements.
If you want to use raw queries you could do something like this to protect your code:
// Add this in the beginning of the file/code:
use Cake\Datasource\ConnectionManager;
// Replace connection_name with the name of your connection (maybe it's "default")
$connection = ConnectionManager::get('connection_name');
$bindList = [];
$city = $this->request->query('cityval');
// PDO as a limitation for the parameter markers. See in the comments below
$cityList = array_filter(explode(',', $city), function($item) {
return preg_match('/^\d+$/', $item);
});
$csvCity = implode(',', $cityList);
$scholarship = $this->request->query('scholarship');
if (!empty($csvCity)) {
$searching .= " and college_city in ($csvCity)";
}
if (!empty($scholarship)) {
$searching .= " and college_scholarship = :scholarship";
$bindList['scholarship'] = $scholarship;
}
$stmt = $connection->prepare($searching);
$stmt->bind($bindList);
$stmt->execute();
// Read all rows.
$rows = $stmt->fetchAll('assoc');
// Read rows through iteration.
foreach ($rows as $row) {
// Do work
}
PDO has a limitation and because of that there's no proper way to use the SQL IN() clause in a prepared statement (to bind the values to it), so we need to parse manually the values to be inside that clause as I did in the code.
From the PDO Prepare manual page:
Note: Parameter markers can represent a complete data literal only.
Neither part of literal, nor keyword, nor identifier, nor whatever
arbitrary query part can be bound using parameters. For example, you
cannot bind multiple values to a single parameter in the IN() clause
of an SQL statement.
References
CakePHP 3.3 Cookbook - Database basics
PHP Manual - PHP Data Objects
PHP The Right Way
I am trying to load a list of IDs into a PHP array which I can loop through. The SQL query I am using returns 283 rows when I run it in PHPMyAdmin. However, when I run the following PHP script, it only returns a single row (the first row). How can I modify my code to include all the rows from the resulting SQL query in my PHP array?
Code:
//Get active listing IDs
$active = "SELECT L_ListingID FROM `markers`";
$active = mysql_query($active) or die(mysql_error());
if(is_resource($active) and mysql_num_rows($active)>0){
$row = mysql_fetch_array($active);
print_r($row);
};
Thanks,
Using mysql_fetch_array will return only the first row and then advance the internal counter. You need to implement it as part of a loop like the following to get what you want.
while($row = mysql_fetch_array($active)) {
// Your code here
}
Keep in mind that mysql_ functions are now also deprecated and slated to be removed in future version of php. Use mysqli_ functions or PDO.
In PDO it's rather straight forward:
$rows = $conn->query($active)->fetchAll();
See PDO::queryDocs and PDOStatement::fetchAllDocs.
With mysqli you would use mysqli_result::fetch_all and with PDO there's PDOStatement::fetchAll to fetch all rows into an array.
Code for mysqli
$sql = "SELECT L_ListingID FROM `markers`";
$result = $mysqli->query($sql);
if ($result !== false) {
$rows = $result->fetch_all();
}
with PDO it's nearly the same
$sql = "SELECT L_ListingID FROM `markers`";
$result = $pdo->query($sql);
if ($result !== false) {
$rows = $result->fetchAll();
}
I have a MySQL list with a few categories and a lot of rows of data. I want to simply output that in PHP/HTML. How would I do that?
<?php
$query = "SELECT * FROM TABLE";
$res = mysql_query($query,$connection);
while($row = mysql_fetch_array($res)) {
print_r($row);
}
?>
To expand on what was already said: http://www.anyexample.com/programming/php/php_mysql_example__display_table_as_html.xml
This will produce a nice html table out of a query.
Please note that the mysql_* functions, as offered by some answers to this question, have been deprecated for quite some time. It is recommended to use either the mysqli_* functions (MySql Improved, which uses a newer underlying library for accessing mysql), or the PDO (PHP Data Objects, an object-oriented interface for connecting to various databases).
For example:
// Create a new PDO connection to localhost.
$dbh = new PDO("mysql:localhost;dbname=testdb", $user, $password);
// Create a PDO Statement object based on a query.
// PDO::FETCH_ASSOC tells PDO to output the data as an associative array.
$stmt = $dbh->query("SELECT * FROM Table", PDO::FETCH_ASSOC);
// Iterate over the statement, using a simple foreach
foreach($stmt as $row) {
echo $row['column1'].' and '.$row['column2'];
}