I'm trying to build a query in a script that relies upon an object's attributes in order to retrieve the correct information. What I have is this:
$query = "SELECT fields FROM table WHERE fieldA = $this->x";
//Processing of results here
I've seen queries like this used before with string variables but I'm not sure if the rules are different if you're using a variable that you know has a numerical value and the corresponding column for said value is declared as an integer or decimal. Would I need to include single quotes around $this->x?
Use PDO to do that:
$user = "username";
$password = "password";
$pdo = new PDO('mysql:host=localhost;dbname=dbname', $user, $pass);
$stmt = $pdo->prepare('SELECT fields FROM table WHERE fieldA = :value');
$stmt->execute(array('value' => $this->x);
$result = $stmt->fetchAll(PDO::FETCH_ASSOC);
Print the result:
echo '<pre>';
print_r($result);
echo '</pre>';
Or bind one value:
$sth->bindValue(':value', $this->x, PDO::PARAM_INT);
For MySQLi see:
How can I prevent SQL injection in PHP?
As you said $this->x is numeric. I make a small test. Its working fine.
class foo{
public $x;
public function showQuery (){
$this->x = 10;
return "SELECT fields FROM table WHERE fieldA = $this->x";
}
}
$ob = new foo();
echo $ob->showQuery();
OUTPUT :
SELECT fields FROM table WHERE fieldA = 10
Note : It depends upon your wish you want to use PDO or not. is it acceptable to use a standard string query and execute it with a mysqli database handler? : YES
Related
So i'm trying to pass PDO Query by using php, like this(index.php):
include("dbconn.php");
mysqlConnect("'SELECT * FROM users WHERE name =' . $conn->quote($name))", "jeff");
while my dbconn file that contains the function is(dbconn.php):
function mysqlConnect($queryString, $name) {
// DB Credentials
$dbName = 'db';
$dbUser = 'root';
$dbPass = '';
$dbHost = 'localhost';
try {
$conn = new PDO("mysql:host=$dbHost;dbname=$dbName", $dbUser, $dbPass);
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
// Here goes the first parameter, then it uses the second parameter as a variable
$data = $conn->query($queryString);
// So the output should be this:
// $data = $conn->query('SELECT * FROM myTable WHERE name = ' . $conn->quote($name));
foreach($data as $row) {
print_r($row);
}
} catch(PDOException $e) {
echo 'ERROR: ' . $e->getMessage();
}
}
So in my function call the php actually executes the $conn->quote($name)) code, making my application not work.
How should i do this? is this allowed in php?
Edit:
or in other words: i call a function and give it 2 parameters, one of the parameters(even tho it's in double quotes) is executed by php which shouldn't happen. How can i fix this
The way you wrote, it will never work. You just have to learn to distinguish a string literal from executable code.
Anyways, you don't need such a frankenstein at all. There is already a mechanism to put your variable in the query, called prepared statements. You just have to use them.
There are other issues with your code too. I've described them all in the article I wrote recently, The only proper guide on PDO, I am sure you will find it interesting - all the issues like wrong error handling, utterly wrong way to connect, lack of prepared statements - all described there. Having all of them solved, here goes the proper function you need:
function pdo($sql, $data=[])
{
global $pdo; // you can add a call to your favorite IoC here.
$stmt = $pdo->prepare($sql);
$stmt->execute($data);
return $stmt;
}
used as
include("dbconn.php");
$user = pdo("SELECT * FROM users WHERE name = ?", ["jeff"])->fetch();
var_dump($user);
this is how PDO have to be used.
By returning a statement, you'll be able to use all the power of PDO, getting data you need in one line, say a list
$news = pdo("SELECT * FROM news ORDER BY id DESC")->fetchAll();
var_dump($news); // already an array
or just a single value
$count = pdo("SELECT count(*) FROM posts WHERE author=?", [$id])->fetchColumn();
var_dump($count); // already a number
or simply by iterating results one by one
$news = pdo("SELECT * FROM news ORDER BY id DESC")->fetchAll();
foreach ($news as $row) {
var_dump($row);
}
and so on.
php code:
$con = new PDO($this->dsn, $this->login, $this->password);
$stmt = $con->prepare($selectSql);
$stmt->execute(array(
'login' => $login
));
and sql query:
$selectSql = 'SELECT id
FROM public.users
WHERE (numberType = :login OR stringType = :login)';
The problem is that I do not find it desirable to edit PHP Code and add new variables to the example 'loginNumber' => (int) $login.
How can I fix it so that the SQL search result itself would be transferred to number value?
numberType = (:login)::Integer parameter did not work.
I have revised my answer given new comments, you will need to detect what data format has been provided. This is tricky as it seems your data always comes as a string, even if the string contains a raw integer.
The sloppy way to do this is to do a quick is_numeric check... but this will parse any numeric value as true... even floats? On-top of that, variable binding assumes string by default in PDO, to bind anything else you must verbosely define it.
Here is some loose code to give you an idea, but you may have to improve the $isNumericLogin logic for more accurate results for numeric non-integer login values.
$con = new PDO($this->dsn, $this->login, $this->password);
// Generate correct query given the data format
$isNumericLogin = is_numeric($login);
$selectSql = $isNumericLogin ?
'SELECT id FROM public.users WHERE number = :login' :
'SELECT id FROM public.users WHERE string = :login';
$stmt = $con->prepare($selectSql);
// Bind the correct data format based
$isNumericLogin ?
$stmt->bindValue('login', $login, PDO::PARAM_INT) :
$stmt->bindValue('login', $login);
$stmt->execute();
I am trying to find a way to create a function in PHP that will wrap a SQL query given in the parameter so that I can prevent SQL Injection in the function that can then be called many times throughout my application. Rather than repeating the same statements for each and every query.
For example say I have the following PHP code that prepares and executes a query to prevent SQL injection:
$name = "$_POST['name']";
$stmt = $db->prepare('SELECT * FROM test_table WHERE test_name = ?');
$stmt->execute(array($name));
For each query my application will need to make these statements will need to be repeated. I want a way to prevent having to do this each time, rather I would simply want to call a function each time and pass in the query.
How would I wrap this in a function that can then be called whenever I need to make a query in my application, given that I do not know in advance the amount of parameters that would need to be parameterized. The above query has one parameterized query, but each query may have a different amount.
Note:
I am using PDO statements
Something like this:
public function query($query)
{
// statements here
}
Where the query is passed in as a parameter.
Does anyone know how I can achieve this?
Currently, I am using something like this that might work for you.
Example:
function superQuery($query, $params, $type = null) {
$pdo = new pdo(...);
$stmt = $pdo->prepare($query);
$stmt->execute($params);
if ($type === "select") {
$result = $stmt->fetchAll();
return $result;
} else {
return $stmt;
}
$query = "SELECT row FROM column WHERE row1 = ? AND row2 = ?";
$params = [$row1, $row2];
$type = "select";
$row = selectQuery($query, $params, $type);
// returns multidimensional array or true/false depending if argument is used //
There's lots of ways you can do it. You could also pass a count argument if you wanted to return a count instead of a result set. But hopefully this points you in the right direction and gives you some ideas.
Because I find PDO executions extremely hard to remember and find myself looking back at previous projects or other websites just to remember how to select rows from a database, I decided that I would try and create my own functions that contain the PDO executions and just plug in the data I need. It seemed a lot simpler than it actually is though...
So far I have already created a connect function successfully, but now when it comes to create a select function I'm stumped for multiple reasons.
For starters there could be a variating amount of args that can be passed into the function and secondly I can't figure out what I should pass to the function and in which order.
So far the function looks like this. To keep me sane, I've added the "id" part to it so I can see what exactly I need to accomplish in the final outcome, and will be replaced by variables accordingly when I work out how to do it.
function sql_select($conn, **what to put here**) {
try {
$stmt = $conn->prepare('SELECT * FROM myTable WHERE id = :id');
$stmt->execute(array('id' => $id));
$result = $stmt->fetchAll();
if ( count($result) ) {
foreach($result as $row) {
print_r($row);
}
} else {
return "No rows returned.";
}
} catch(PDOException $e) {
echo 'ERROR: ' . $e->getMessage();
}
}
So far what I've established that the function will need to do is
Connect to the database (using another function to generate the $conn variable, already done)
Select the table
Specify the column
Supply the input to match
Allow for possible args such as ORDER by 'id' DESC
Lastly from this I would need to create a function to insert, update and delete rows from the database.
Or, is there a better way to do this rather than functions?
If anyone could help me accomplish my ambitions to simply simplify PDO executions it would be greatly appreciated. Thanks in advance!
First of all, I have no idea where did you get 10 lines
$stmt = $conn->prepare('SELECT * FROM myTable WHERE id = ?');
$stmt->execute(array($id));
$result = $stmt->fetchAll();
is ALL the code you need, and it's actually three lines, which results with a regular PHP array that you can use wherever you wish. Without the need of any PDO code. Without the need of old mysql code.
Lastly from this I would need to create a function to insert, update and delete rows from the database.
DON'T ever do it.
Please read my explanations here and here based on perfect examples of what you'll end up if continue this way.
accomplish my ambitions to simply simplify PDO executions
That's indeed a great ambition. However, only few succeeded in a real real simplification, but most resulted with actually more complex code. For starter you can try code from the first linked answer. Having a class consists of several such functions will indeed improve your experience with PDO.
. . . and find myself looking back at previous projects or other
websites just to remember how to select rows from a database . . .
FYI, we all do that.
You had a problem with the PDO API and now you have two problems. My best and strongest suggestion is this: If you want a simpler/different database API, do not roll your own. Search http://packagist.org for an ORM or a DBAL that looks good and use it instead of PDO.
Other people have already done this work for you. Use their work and focus instead on whatever awesome thing is unique to your app. Work smart, not hard and all that.
Writting a wrapper, should start form connecting the DB, and all the possible method could be wrapped. Passing connection to the query method, doesn't look good.
A very rough example would be the code bellow, I strongly do not suggest this mixture, but it will give you the direction.
You connection should be made either from the constructor, or from another method called in the constructor, You can use something like this:
public function __construct($driver = NULL, $dbname = NULL, $host = NULL, $user = NULL, $pass = NULL, $port = NULL) {
$driver = $driver ?: $this->_driver;
$dbname = $dbname ?: $this->_dbname;
$host = $host ?: $this->_host;
$user = $user ?: $this->_user;
$pass = $pass ?: $this->_password;
$port = $port ?: $this->_port;
try {
$this->_dbh = new PDO("$driver:host=$host;port=$port;dbname=$dbname", $user, $pass);
$this->_dbh->exec("set names utf8");
} catch(PDOException $e) {
echo $e->getMessage();
}
}
So you can either pass connection credentials when you instantiate your wrapper or use default ones.
Now, you can make a method that just recieves the query. It's more OK to write the whole query, than just pass tables and columns. It will not make a whole ORM, but will just make the code harder to read.
In my first times dealing with PDO, I wanted everything to be dynamically, so what I achieved, later I realized is immature style of coding, but let's show it
public function query($sql, $unset = null) {
$sth = $this->_dbh->prepare($sql);
if($unset != null) {
if(is_array($unset)) {
foreach ($unset as $val) {
unset($_REQUEST[$val]);
}
}
unset($_REQUEST[$unset]);
}
foreach ($_REQUEST as $key => $value) {
if(is_int($value)) {
$param = PDO::PARAM_INT;
} elseif(is_bool($value)) {
$param = PDO::PARAM_BOOL;
} elseif(is_null($value)) {
$param = PDO::PARAM_NULL;
} elseif(is_string($value)) {
$param = PDO::PARAM_STR;
} else {
$param = FALSE;
}
$sth->bindValue(":$key", $value, $param);
}
$sth->execute();
$result = $sth->fetchAll();
return $result;
}
So what all of these spaghetti does?
First I though I would want all of my post values to be send as params, so if I have
input name='user'
input name='password'
I can do $res = $db->query("SELECT id FROM users WHERE username = :user AND password = :password");
And tada! I have fetched result of this query, $res is now an array containing the result.
Later I found, that if I have
input name='user'
input name='password'
input name='age'
In the same form, but the query remains with :user and :password and I submit the form, the called query will give mismatch in bound params, because the foreach against the $_REQUEST array will bind 3 params, but in the query I use 2.
So, I set the code in the beginning of the method, where I can provide what to exclude. Calling the method like $res = $db->query("SELECT id FROM users WHERE username = :user AND password = :password", 'age'); gave me the possibility to do it.
It works, but still is no good.
Better have a query() method that recieves 2 things:
The SQL string with the param names
The params as array.
So you can use the foreach() logic with bindValue, but not on the superglobal array, but on the passed on.
Then, you can wrap the fetch methods
public function fetch($res, $mode = null)
You should not directly return the fetch from the query, as it might be UPDATE, INSERT or DELETE.
Just pass the $res variable to the fetch() method, and a mode like PDO::FETCH_ASSOC. You can use default value where it would be fetch assoc, and if you pass something else, to use it.
Don't try to be so abstract, as I started to be. It will make you fill cracks lately.
Hum... IMHO I don't think you should try to wrap PDO in functions, because they're already "wrapped" in methods. In fact, going from OOP to procedural seems a step back (or at least a step in the wrong direction). PDO is a good library and has a lot of methods and features that you will surely lose if you wrap them in simple reusable functions.
One of those features is the BeginTransaction/Rollback (see more here)
Regardless, In a OOP point of view you can decorate the PDO object itself, adding some simple methods.
Here's an example based on your function
Note: THIS CODE IS UNTESTED!!!!
class MyPdo
{
public function __construct($conn)
{
$this->conn = $conn;
}
public function pdo()
{
return $this->conn;
}
public function selectAllById($table, $id = null)
{
$query = 'SELECT * FROM :table';
$params = array('table'=>$table);
if (!is_null($id)) {
$query .= ' WHERE id = :id';
$params['id'] = $id;
}
$r = $this->conn->prepare($query)
->execute($params)
->fetchAll();
//More stuff here to manipulate $r (results)
return $r;
}
public function __call($name, $params)
{
call_user_func_array(array($this->conn, $name), $params);
}
}
Note: THIS CODE IS UNTESTED!!!!
ORM
Another option is using an ORM, which would let you interact with your models/entities directly without bothering with creating/destroying connections, inserting/deleting, etc... Doctrine2 or Propel are good bets for PHP.
Howeveran ORM is a lot more complex than using PDO directly.
I'm using a factory(class) to present forms from a target database table - as defined at class instance. Then on submit, create a new instance of the class which then insert a new record in to the database. $_POST key names match the table column names.
My issue is dynamically assigning bind parameters when the variables are determined at class instance. I'm getting the following, whether I use Reflections method or inline.
Warning: mysqli_stmt::bind_param() [mysqli-stmt.bind-param]: Number of elements in type definition string doesn't match number of bind variables
The following method is called in the sub class after the post array has been contructed and assigned to the class property $array.
private function addrecord($array,$tbl,$_conn){
//define field name array for query statement
foreach ($array as $key=>$value){
$keyarr[]=$key;
}
//BUILD THE QUERY STATEMENT
$query = "INSERT INTO $tbl SET ";
foreach ($keyarr as $key){
$query .= ($key."=?, "); //clone and add next element
}
$query = rtrim($query,", "); //remove EOL whitespace and comma
//done
/*
//Hard code bind parameters works as expected
if (self::$_conn = new mysqli(DB_HOST,DB_UNAME,DB_UPWORD,DB_NAME)){
$stmt=self::$_conn->prepare($query);
$stmt->bind_param("sssss",$array['user_id'],$array['user_name'],$array['user_email'],$array['user_date'],$array['user_active']);
$stmt->execute();
$insertid=$stmt->insert_id;
$stmt->close();
echo "The record was created with id ".$insertid;
}
*/
//Tried re assigning post array as reference
//same error as just passing $array
//$array = $this->refValues($array);
//Binding params using Reflections, same error
self::$_conn = new mysqli(DB_HOST,DB_UNAME,DB_UPWORD,DB_NAME);
$stmt = self::$_conn->prepare($query);
$ref = new ReflectionClass('mysqli_stmt');
$method = $ref->getMethod("bind_param");
$method->invokeArgs($stmt,$array);
$stmt->execute();
$stmt->close();
self::$_conn->close();
}
//Pass By Reference required for PHP 5.3+, dev server 5.3.17
function refValues($arr){
if (strnatcmp(phpversion(),'5.3') >= 0){
$refar = array();
foreach($arr as $key => $value)
$refar[$key] = &$arr[$key];
return $refar;
}
return $arr;
}
Thanks in advance and much appreciated.
As you can see, mysqli is practically unusable with prepared statements.
So, I'd suggest you to either use PDO or, better, some intelligent library that can make safe query without prepared statments.
With such a library your function will be written in one line
private function addrecord($array,$tbl){
$this->conn->query("INSERT INTO ?n SET ?u", $tbl, $array);
}
please note that if $array is coming from the untrusted source, you have to filter it's content out first.
Per Common Sense, changed process to PDO. Works as expected. Should have done it sooner. Only issue remaining is UI feedback. Would like to return an insert id, however MySQL doesnt return a last insert id for PDO. And again, the class doesnt know the tables structure in advance so hard coding in not an option. Need a workaround. Any thoughts? Heres the new insert process using PDO.
try{
self::$_conn = new PDO('mysql:host='.DB_HOST.';dbname='.DB_NAME.'', DB_UNAME, DB_UPWORD);
$stmt = self::$_conn->prepare($query);
$arcount=count($array); //set number of bindParam loops
foreach($array as $key=>$value){
$stmtarr[]=$value; //convert assoc to numerated array
}
//re index array so increment will match up with placeholder position
$stmtarr = array_combine(range(1, count($stmtarr)), array_values($stmtarr));
for($i=1;$i<=$arcount;$i++){ //bind variable, one for each placeholder,
$stmt->bindParam($i,$stmtarr[$i]);
}
$stmt->execute();
} catch (PDOException $e){
print "Error: ".$e->getMessage()."<br>";
die();
}
Assume everything else is the same as above.