I am trying to create a function to iteratively bind variables. This is what I have so far:
function prepareQuery($db, $query, $args) {
// Returns a prepared statement
$stmt = $db->prepare($query);
foreach ($args as $arg => $value) {
$stmt->bindParam($arg, $value);
}
return $stmt;
}
This is how I'm using it:
$stmt = prepareQuery($db, "SELECT * FROM `Licenses` WHERE `verCode`=:verCode", Array(":verCode" => $verCode));
$verCode = "some_string";
$stmt->execute();
while ($info = $stmt->fetch()) {
print_r($info);
}
Though it doesn't print anything. I know the database entry exists, and the same query works from PHPMyAdmin. So, I think it's just a problem in how my function tries to create the bindings. How can I fix this? Thanks!
Do not create a function to iteratively bind variables. PDO can do it already
function prepareQuery($db, $query, $args) {
$stmt = $db->prepare($query);
$stmt->execute($args);
return $stmt;
}
If it doesn't print anything, then it didn't find anything. As simple as that.
You don't even need this prepare query function actually. Just amend PDO very little like this
class myPDOStatement extends PDOStatement
{
function execute($data = array())
{
parent::execute($data);
return $this;
}
}
$user = 'root';
$pass = '';
$dsn = 'mysql:charset=utf8;dbname=test;host=localhost';
$opt = array(
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_EMULATE_PREPARES => TRUE,
PDO::ATTR_STATEMENT_CLASS => array('myPDOStatement'),
);
$pdo = new PDO($dsn, $user, $pass, $opt);
and you'll be able to write such a neat chain:
$sql = "SELECT * FROM `Licenses` WHERE `verCode`=:verCode";
$code = "some_string";
$data = $pdo->prepare($sql)->execute([$code])->fetchAll();
foreach ($data as $info) {
print_r($info);
}
Related
This question already has an answer here:
A helper function for mysqli that dynamically binds params in prepared statement?
(1 answer)
Closed 2 years ago.
I want to create function that take SQL string as parameter and return me an array
I write this function:
function getgquery($I_sql){
$mysqli = new mysqli("localhost","root","","ACLUB");
$sql = $I_sql;
$result = $mysqli->query($sql);
$Data = array();
if(mysqli_num_rows($result)>0){
while($row = mysqli_fetch_assoc($result)){
$Data [] = $row;
}
}
return $Data;
}
And then call the function:
print_r(getgquery('SELECT * FROM `poeple`'));
But I get this error:
Warning: mysqli_num_rows() expects parameter 1 to be mysqli_result,
bool given in N:\xampp\htdocs\test\functions.php on line 9
This is a good idea!
You are getting the error because you haven't enabled error reporting and you do not see the error you are getting for the typo you made in SQL.
The correct function should look something like this:
// Enable mysqli error reporting and open connection
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
$mysqli = new mysqli('localhost', 'username', 'password', 'db_test');
$mysqli->set_charset('utf8mb4'); // always set the charset
function getgquery(mysqli $mysqli, string $I_sql, array $params = []): ?array {
$stmt = $mysqli->prepare($I_sql);
if ($params) {
$stmt->bind_param(str_repeat("s", count($params)), ...$params);
}
$stmt->execute();
if ($result = $stmt->get_result()) {
return $result->fetch_all(MYSQLI_BOTH);
}
}
// with the typo fixed:
print_r(getgquery($mysqli, 'SELECT * FROM `people`'));
You need to connect once outside of your function and pass the connection as a parameter.
You need to use prepared statements!
Get rid of this useless loop and num_rows
If you think that passing the connection to this function every time is too much you can create a subclass of mysqli, for example something like this:
class DBClass extends mysqli {
public function __construct(
$host = null,
$username = null,
$passwd = null,
$dbname = null,
$port = null,
$socket = null
) {
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
parent::__construct($host, $username, $passwd, $dbname, $port, $socket);
$this->set_charset('utf8mb4');
}
public function safeQuery(string $sql, array $params = []): ?array {
$stmt = $this->prepare($sql);
if ($params) {
$stmt->bind_param(str_repeat("s", count($params)), ...$params);
}
$stmt->execute();
if ($result = $stmt->get_result()) {
return $result->fetch_all(MYSQLI_BOTH);
}
return null;
}
}
and then use it like this:
$mysqli = new DBClass('localhost', 'username', 'password', 'db_test');
$result = $mysqli->safeQuery('SELECT * FROM people WHERE id=?', [$myId]);
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
I have moved away from mysqli_query() due to server requirements on a new project. However, I am having issues with updating some of my queries.
Connection file
<?php
define('DB_HOST', 'localhost');
define('DB_NAME', 'nj2kfa3j_sublift');
define('DB_USER', 'nj2kfa3j_web');
define('DB_PASS', 'prum9wR4');
define('DB_CHAR', 'utf8');
class DB
{
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 run($sql, $args = [])
{
if (!$args)
{
return self::instance()->query($sql);
}
$stmt = self::instance()->prepare($sql);
$stmt->execute($args);
return $stmt;
}
}
?>
Problem Query Statement:
$stmt = DB::run("SELECT * FROM admin WHERE username='$manager' AND password='$password' LIMIT 1");
$existCount = $stmt->fetchColumn();
if ($existCount == 1){
$id;
$full_name;
var_dump($stmt);
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)){
$id = $row["id"];
$full_name = $row["full_name"];
echo 'test';
}
}
I have done other queries in more or less the same way and they're working, like this:
$stmt = DB::run("SELECT * FROM categories");
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)){
What is going on here? In the query above the var_dump() is returning:
object(PDOStatement)#2 (1) { ["queryString"]=> string(81) "SELECT * FROM admin WHERE username='********' AND password='********' LIMIT 1" }
But it is not entering the while loop and hitting the echo 'test';
Please use prepared statements. Your function is designed to accept prepared statements. Also, don't store your password as plain text; use password_hash() and password_verify().
You can fetch your results as an array with fetchall() and then count the array elements.
<?php
$params = [$manager,$password];
$sql = "SELECT * FROM admin WHERE username= ? AND password= ? LIMIT 1";
$stmt = DB::run($sql,$params);
$results = $stmt->fetchall(PDO::FETCH_ASSOC); // Returns an array
if (count($results) > 0){
$id;
$full_name;
foreach($results as $key=>$row){
$id = $row["id"];
$full_name = $row["full_name"];
echo 'test';
}
}
?>
As discussed in the comments, I was looking for an alternative to mysqli_num_rows() and found rowCount().
The following now works for me:
$stmt = DB::run("SELECT * FROM admin WHERE username='$manager' AND password='$password' LIMIT 1");
$existCount = $stmt->rowCount();
if ($existCount == 1){
I'm tinkering with a class that 'should' allow me to easily execute a fetchall query and display the results within a foreach statement. I assume all is working correctly as I have no errors. As for the foreach - this must be the problem? How would I foreach the results gained from the $connect->query()? I'm new to using any database OOP framework in my functions so I could be along the wrong lines completely.
<?
error_reporting(1);
class dbconnect {
private $host;
private $database;
private $username;
private $password;
private $pdo;
private $error;
public function __construct() {
$this->host = "localhost"; // Host
$this->database = "images"; // Database Name
$this->username = "*"; // Username
$this->password = "*"; // Password
$options = array(
PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8'
);
try {
$this->pdo = new PDO("mysql:host={$this->host};dbname={$this->dbname};charset=utf8", $this->username, $this->password, $options);
}
catch(PDOException $e) {
$this->error = $e->getMessage();
}
}
public function query($query) {
try {
$stmt = $this->pdo->prepare($query);
$stmt->execute();
} catch(PDOException $ex) {
die("Failed to run query: " . $ex->getMessage());
}
$rows = $stmt->fetchAll();
return $rows;
}
}
$connect = new dbconnect;
$rows = $connect->query("select * from photos");
foreach($rows as $row):
print $row['id'];
endforeach;
?>
The $rows variable you're declaring inside query is not accessible to the outside, it is local to that function. Most likely, you simply want to return those results to the caller:
$rows = $stmt->fetchAll();
return $rows; // return value from function...
and have the caller capture that return value in its own variable:
$rows = $connect->query("select * from images"); // ... is received by caller
foreach($rows as $row):
Also check out dougjore's answer, you're mixing $this->stmt and $stmt inside your query method.
Pretty sure you aren't ever actually executing the query:
$this->stmt = $this->pdo->prepare($query);
$stmt->execute();
I believe (I could be wrong, I'm rather new to PDO myself and I haven't built a class for it), that you need to say $this->stmt->execute();
You could do
//PDO::FETCH_ASSOC: returns an array indexed by column name as returned in your result set
$this->stmt = $this->pdo->prepare($query);
$this->stmt->execute();
while ($result = $this->stmt->fetch(PDO::FETCH_ASSOC))
{
//do something with the result
}
Have a look here for more options to fetch PDO query results:
http://php.net/manual/en/pdostatement.fetch.php
$connect = new dbconnect;
$sql="select * from photos";
$stmt=$connect->pdo->prepare($sql);
$stmt->execute();
$result=$stmt->fetch(PDO::FETCH_ASSOC);
foreach($result as $key => $value) {
echo $key . "-" . $value . "<br/>";
}
I want to know how to use named parameters in a prepared statement with pdo class, so the call to pdo look something like following.
$query = $bdd->prepare('SELECT * FROM table WHERE login = :login AND pww = :pww');
$query->execute(array('login' => $login, 'pww' => $pww));
And I want to integrate this on a class regardless of the number of parameters.
Currently, I have this code
require_once 'constants.php';
class Mysql extends PDO {
private $con;
public function __construct() {
try {
$this->con = parent::__construct(DB_DSN, DB_USER, DB_PASS);
if ($this->getAttribute(PDO::ATTR_DRIVER_NAME) == DB_TYPE)
$this->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, TRUE);
return $this->con;
} catch (PDOException $e) {
die('Error:' . $e->getMessage());
}
}
public function select($reqSelect) {
try {
$this->con = parent::beginTransaction();
$result = parent::prepare($reqSelect);
$result->execute();
//$this->con = parent::commit();
$this->con = parent::rollBack();
return $result;
$result->closeCursor();
} catch (Exception $e) {
die('Error:' . $e->getMessage());
}
}
public function selectAll($reqSelect) {
$result = parent::prepare($reqSelect);
$result->execute();
$resultat = $result->fetchAll();
return $resultat;
$result->closeCursor();
}
}
And for parameters, I use somethings like ( which is wrong and vulnerable to injection )
require_once 'classes/Mysql.class.php';
$mysql = new Mysql();
$sql = 'SELECT * FROM articles WHERE id = '.$_GET['id'].' LIMIT 1';
$data = $mysql->select($sql);
Thanks.
So it's seems that I have figured it out, the trick was adding an optional parameter to the function, you use it whenever you need to work with prepared statements (named parameters).
So the function is something like
public function selectAll($reqSelect, $param = null) {
$result = parent::prepare($reqSelect);
//Check whether the parameter was passed or not
if (is_null($param)) {
$result->execute();
$resultat = $result->fetchAll();
return $resultat;
} else {
//Binding the parameters
$result->execute($param);
$resultat = $result->fetchAll();
return $resultat;
}
$result->closeCursor();
}
And for applying it, it goes like
//First param, the SQL. Here we have named parameters, so we need them to get bind
$sql = 'SELECT * FROM articles WHERE publish = :number';
//Second param, the parameters that will get bind with the named ones
$param = array(':number' => 1);
$query = $mysql->selectAll($sql, $param);
foreach ($query as $row) {
extract($row);
echo $title . '<br />';
}
I don't know if this, is considered the best practice, secured or even correct. if I'm mistaken feel free to correct me.