Is it possible with PHP PDO to use named placeholders that effectively match everything?
Take as example: $sql = "SELECT * FROM users WHERE targetGroup = :targetGroup AND userId = :userId"
And take $statement = $this->dbh->prepare($sql);
Can I then somehow bind values such that I get the following behaviours:
Original query (this can be done obviously).
Parameters: :targetGroup = 1, :userId = 5.
Resulting query: $sql = "SELECT * FROM users WHERE targetGroup = 1 AND userId = 5
A query to show all users
Parameters: Somehow set :targetGroup and :userId to some special value ALL.
Resulting query: $sql = "SELECT * FROM users
I would like to use this such that I can define queries once somewhere in my program, as static strings, and then reuse them to my likings.
You can't do that. You'll have to prepare different statements for different queries.
$findOneStmt = $pdo->prepare("SELECT * FROM user WHERE target_group = :tg AND user_id = :uid");
$findAllStmt = $pdo->prepare("SELECT * FROM users");
Later, when you'll need one of this queries just call:
$findOneStmt->execute(array(':tg' => 123, ':uid' => 321));
$user = $findOneStmt->fetch();
// ...
$findAllStmt->execute();
$users = $findAllStmt->fetchAll();
You could put a conditional in your SQL that lets you show everything. For example:
SELECT *
FROM users
WHERE
1 = :showAll OR (
targetGroup = :targetGroup
AND userId = :userId
)
Then you can set the :showAll to 0 when you want to filter by :targetGroup and :userId or to 1 when you want to ignore :targetGroup and :userId.
I believe best practice however would be to have two separate queries.
The problem is that you're essentially performing two entirely different queries. They may look very similar, but a SELECT * with no where clause is quite different to one with a where clause.
Rather than define SQL queries in one place and then reuse, abstract the functionality out and use that method call as and when needed, it'll provide you with the ability to have the query defined once, but you will have two queries there now. This way is much safer and much better terms of readability and usage.
Related
I have multiple tables in my database named teacher1, teacher2.... I am trying to access them using a variable $id.
I have written down the following code.
$query = "SELECT * FROM table.$id";
How could i access those different tables using a variable.
I'm not clear from the question text whether your $id variable contains the full table name, or some kind of number.
However, in either case you have to make a slight tweak to your $query variable.
If $id contains the full name of the table (i.e. teacher1):
$query = "SELECT * FROM " . $id;
If $id contains a number used to identify which teacher table it is (i.e. 1, 2, 3, etc):
$query = "SELECT * FROM teacher" . $id;
Learn to use parameters!
$stmt = $conn->prepare($query = "SELECT t.* FROM table t where t.id = ?");
$stmt->bind_param("i", $id);
$stmt->execute();
Don't munge query strings with parameter values! This introduces SQL injection risk, can introduce syntax errors that are hard to debug, and can degrade performance.
i am doing a mini project of social networking , i am having a doubt about table .
let see , i need to provide post in their page. to do this,should i have to create table for each user or just create one table and use it for multiple users (data can be fetched by selecting particular user name and display it in their page ).
which is the best way?
my php code:
<?php
$query="select * from table_name where user=$username order by time desc;";
?>
To answer your question
It's best to just use 1 table of users and have a separate able for your posts. Your users table should have all the information for each specific users with 1 unique value that is automatically generated by the MySQL database. (Use auto-increment) And in the posts table you should have all the data for each post with a user_id column that holds the unique value from the users table, this way you know who posted it.
Here is a mockup table structure:
Users table:
uid | name | email
Posts table:
uid | user_id | message
user_id in the posts table should always be equal to some uid in the users table.
Every single table should always have some unique value that is assigned its primary value
My real concern
I am very concerned with the security of your application. Prepared statements are WAY easier to use, and WAY more secure.
In the code snippet that you shared:
<?php
$query="select * from table_name where user=$username order by time desc;";
?>
this query is very insecure, as Bobby Tables would tell you. I'm not sure why type of database connection you are using, but I suggest PDO. I wrote a function that makes this very very easy, here is the snippet for that:
This is a file I usually call connection.php that you can import on any page you need to use your database.
<?php
$host = 'localhost';
$db = '';
$user = '';
$pass = '';
$charset = 'utf8';
$dsn = "mysql:host={$host};dbname={$db};charset={$charset}";
$opt = [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_EMULATE_PREPARES => false,
];
$pdo = new PDO($dsn, $user, $pass, $opt);
function pdoQuery($query, $values = []) {
global $pdo;
if(!empty($values)) {
$stmt = $con->prepare($query);
$stmt->execute($values);
} else {
$stmt = $con->query($query);
}
return $stmt;
}
?>
This function allows you to EASILY use prepared statements by just
including the connection.php page and writing queries in a way that is
readable, clean, and secure. As I'm sure a lot of people reading this are not used to Prepared Statements or know how they
work, the rest of this post will explain that.
One of the biggest differences here is that instead of using String
Interpolation in
your query, you will set variables as question marks ?, so your
query looks like this: UPDATE table SET user=? instead of UPDATE
table SET user='$user' - and the variables will be sent later for
safety, so this prevents SQL Injection.
This it the way your query would look now:
pdoQuery("SELECT * FROM `table_name` WHERE user=? ORDER BY `time` DESC", [$username]);
This is basically how it works:
pdoQuery(string $query, array $variables)
If you pass no variables, it automatically uses the query() function, if you do pass variables it automatically binds and executes the statements. No matter what query you do, the function always returns the query as an object, so you can act on it using any method you can normally use on a PDO query after the execute.
If you know how these work, you can stop reading here :) I put some
exmaples below of some of the ways you can manipulate the return data
to do what you need to do.
This function returns the object of the query you requested, so if you wanted to loop through all of the results of your query you use it like this:
$stmt = pdoQuery("SELECT * FROM `table_name` WHERE `user`=? ORDER BY time DESC", [$username])->fetchAll();
foreach($stmt as $row) {
$row['name']."<br>";
}
Or if you just wanted to get a single column from a specific row, you could use it like this:
$username = pdoQuery("SELECT `username` FROM `users_table` WHERE uid=? ORDER BY `time` DESC", [$uid])->fetchColumn();
Which will return the username from user where uid=$uid as a string
or if you wanted several values from 1 specific row, you could do
$user = pdoQuery("SELECT `username`,`name`,`email` FROM `users_table` WHERE uid=? ORDER BY time DESC", [$uid])->fetch();
Which will return to $user as an array that has the username, name, and email of the user.
You can also use this function to INSERT, UPDATE, or basically any type of query you can think of. This is how you insert:
pdoQuery("INSERT INTO `table_name` (`name`,`col2`, `col3`) VALUES (?,?,?)", [$name, $col1, $col2]);
My PDO Class
Since writing this post, I have created a new database wrapper class called GrumpyPDO (Hosted on Github).
This class method returns the object of the query you requested, so if you wanted to loop through all of the results of your query you use it like this:
Fetch All
GrumpyPDO Long Syntax
$stmt = $db->run("SELECT * FROM `table_name` WHERE `user`=? ORDER BY time DESC", [$username])->fetchAll();
GrumpyPDO Short Syntax
$stmt = $db->all("SELECT * FROM `table_name` WHERE `user`=? ORDER BY time DESC", [$username]);
Loop:
foreach($stmt as $row) {
$row['name']."<br>";
}
Single Column Return
Or if you just wanted to get a single column from a specific row, you could use it like this:
//Long Syntax
$username = $db->run("SELECT `username` FROM `users_table` WHERE uid=? ORDER BY `time` DESC", [$uid])->fetchColumn();
//Short
$username = $db->cell("SELECT `username` FROM `users_table` WHERE uid=? ORDER BY `time` DESC", [$uid]);
Which will return the username from user where uid=$uid as a string
Entire Row Return
or if you wanted several values from 1 specific row, you could do
//Long Syntax
$user = $db->run("SELECT `username`,`name`,`email` FROM `users_table` WHERE uid=? ORDER BY time DESC", [$uid])->fetch();
//Short Syntax
$user = $db->row("SELECT `username`,`name`,`email` FROM `users_table` WHERE uid=? ORDER BY time DESC", [$uid]);
Which will return to $user as an array that has the username, name, and email of the user.
DML Queries
You can also use this function to INSERT, UPDATE, or basically any type of query you can think of. This is how you insert (All DML's are similar):
$db->run("INSERT INTO `table_name` (`name`,`col2`, `col3`) VALUES (?,?,?)", [$name, $col1, $col2]);
I have a simple table
Via $Poles OR $Power (or combination of both) I am selecting a list of rows
if($_GET["Power"]!="*" and $_GET["Poles"]!="*") $query = "SELECT * FROM `TABLE 2` WHERE Power=".$_GET["Power"]." AND Poles=".$_GET["Poles"]."";
elseif($_GET["Poles"]!="*") $query.= "SELECT * FROM `TABLE 2` WHERE Poles=".$_GET["Poles"]."";
elseif($_GET["Power"]!="*") $query.= "SELECT * FROM `TABLE 2` WHERE Power=".$_GET["Power"]."";
else $query= "SELECT * FROM `TABLE 2` ORDER BY Power";
I am absolute beginner but I feel that this is a wrong way to do it.
Please advise me, how should it be done?
If Power is set and Poles is * - I want all rows with this power.
If Poles is set and Power is * - I want all rows with this poles.
If both are set to any value - I want to see this exact row;
If both are set to * - I want all rows from the table
With just the information provided I think your logic generally accomplishes what you're going for. You have several scenarios that to me are distinctly different queries, so I would handle the logic for building the queries in PHP.
I'm not sure if you are using any kind of framework or not, but I'll assume you are not, though I would suggest that you do if it's practical to do so. A good framework will provide input scrubbing and database management to make life easier, and also more security options.
For security purposes, since you are constructing a query with input directly from the user, I would suggest you use the PDO class for interacting with your database (if you aren't). The PDO class will allow you to bind the user's input to the query, and then it will escape the input automatically for you. This greatly reduces your risk of SQL injection attacks (users doing malicious things to your database using what they submit to you) and is considered standard practice in PHP now.
More information on the PDO class can be found here: http://php.net/manual/en/book.pdo.php
More information on SQL injection attacks can be found here: http://www.w3schools.com/sql/sql_injection.asp
So here would be my untested code putting it all together:
$dsn = "localhost"; //Where is your DB located?
$username = "username";
$password = "password";
//Create new DB connection (http://php.net/manual/en/pdo.construct.php)
$dbh = new PDO($dsn, $username, $password);
//Craft and execute the appropriate query
//Each query will include placeholders for data (like :power)
//Placeholders will be replaced with the data passed to execute()
//The PDO class will escape the user input to prevent SQL injection
if ($_GET["Power"] != "*" && $_GET["Poles"] != "*") {
$stmt = $dbh->prepare("SELECT * FROM `TABLE 2` WHERE `Power` = :power AND `Poles` = :poles;");
$stmt->execute([
':power' => $_GET["Power"],
':poles' => $_GET["Poles"]
]);
} elseif ($_GET["Power"] != "*") {
$stmt = $dbh->prepare("SELECT * FROM `TABLE 2` WHERE `Power` = :power");
$stmt->execute([':power' => $_GET["Power"]]);
} elseif ($_GET["Poles"]) {
$stmt = $dbh->prepare("SELECT * FROM `TABLE 2` WHERE `Poles` = :poles;");
$stmt->execute([':power' => $_GET["Poles"]]);
} else {
$stmt = $dbh->prepare("SELECT * FROM `TABLE 2` ORDER BY `Power` ASC");
$stmt->execute();
}
if ($stmt->rowCount()) {
//The query returned results, which we can fetch in a variety of ways
//http://php.net/manual/en/pdostatement.fetch.php
//http://php.net/manual/en/pdostatement.fetchall.php
$results = $stmt->fetchAll(PDO::FETCH_ASSOC);
} else {
//No results, handle appropriately
}
If that if/elseif/else block looks gnarly, you can actually accomplish the same thing with a switch statement. Pass true as the value to match, and make your case statements the logic that defines each of your cases. The first case that matches will run, while default will run if no cases match.
Here's what that looks like:
switch (true) {
case $_GET["Power"] != "*" && $_GET["Poles"] != "*":
$stmt = $dbh->prepare("SELECT * FROM `TABLE 2` WHERE `Power` = :power AND `Poles` = :poles;");
$stmt->execute([
':power' => $_GET["Power"],
':poles' => $_GET["Poles"]
]);
break;
case $_GET["Power"] != "*":
$stmt = $dbh->prepare("SELECT * FROM `TABLE 2` WHERE `Power` = :power");
$stmt->execute([':power' => $_GET["Power"]]);
break;
case $_GET["Poles"] != "*":
$stmt = $dbh->prepare("SELECT * FROM `TABLE 2` WHERE `Poles` = :poles;");
$stmt->execute([':power' => $_GET["Poles"]]);
break;
default:
$stmt = $dbh->prepare("SELECT * FROM `TABLE 2` ORDER BY `Power` ASC");
$stmt->execute();
break;
Edit 1:
For what it's worth, I tend to avoid exposing any of my underlying technologies as much as possible, so using default SQL keywords/operators that a user can see and potentially manipulate is problematic (to me). As a result, I don't use * or % as values that a user can pass to me. Instead I go with some other placeholder I can use in my logic, like an empty string, 0, or -1, depending on what the field accepts.
The use of PDO and bound parameters negates the risk of users doing malicious things with their input, so just doing that alone takes away a lot of the risk of using * or %. In that case it just comes down to whether or not you want the user to be able to guess your underlying storage engine.
From a performance standpoint, I believe the equality operator (=) will be faster than the LIKE operator in a WHERE clause, but you would likely only notice the performance hit in a large production environment, so no real worries there.
$query = "SELECT * FROM `TABLE 2` WHERE Power LIKE ".$_GET["Power"]." AND Poles LIKE ".$_GET["Poles"]."";
Using this query will be able to get you the result you want. Notice that I have change the = operator to LIKE.
Then in your userform or the way you pass values to $_GET you need to change * to %. % is MySQL's wilcard symbol.
Additionally
you are using input from the user in database queries and as such you should ALWAYS use a prepared statement to avoid SQL injection.
Please refer to This SO Q&A which two very easy to follow samples of how to perform a proper prepared statement
I have the following code that works and does what I want it to but I feel I'm using more code than is necessary. All I want to do is get the value in a database cell and check if it is '1' and if so run another query.
$isComplete = $database -> prepare("SELECT completed FROM projects WHERE id = $project_id");
$isComplete -> execute();
$result = $isComplete -> fetchAll();
$result = count($result);
if($result == 1) { $database -> exec("UPDATE projects SET num_complete = num_complete - 1 WHERE id = $parent_id"); }
First, your code is indeed non optimal from the amount of code point of view.
It is also contradicts with your own description
And - worse of all - it is prone to SQL injection.
Also, your variable naming is inconsistent and confusing.
Here is the proper code to check if selected value = 1.
$stmt = $database->prepare("SELECT completed FROM projects WHERE id = ?");
$stmt->execute(array($project_id));
$isComplete = $stmt->fetchColumn();
if ($isComplete) ...
However, I doubt you need such a code at all. To get the number of completed subtasks is a matter of one simple query. Are you really sure you need this num_complete field at all?
Instead of a subquery in the WHERE clause, you can put one in a JOIN clause to get around MySQL's limitation against targeting a table both in an UPDATE and a subquery. Also, instead of selecting all rows with a given project ID and counting them in PHP, you can perform the calculation in the SQL query. Something like:
UPDATE projects p0
JOIN (SELECT id, count(*) AS nSiblings
FROM projects
WHERE id=:project GROUP BY id)
AS p1
ON p0.id=p1.id
SET p0.num_complete=p0.num_complete+1
WHERE p1.nSiblings=1
Note that since it's an inner join, specifying the ID in the subquery is sufficient. You could also probably drop the GROUP BY id, but if you ever adapt the statement for another use, it could introduce a bug.
There may be other issues with the table design that affect this query (and other aspects), but since no schema was provided, there's no way to provide feedback.
You can use a subquery to check the condition in the update statement. Something like this:
UPDATE projects SET num_complete = num_complete - 1 WHERE id = $parent_id
and (select completed from projects where id = $project_id) = 1
For example:
$st = $database->prepare("UPDATE projects SET num_complete = num_complete - 1 WHERE id = :parent_id
and (select completed from projects where id = :project_id) = 1");
$st->bindParam(':parent_id', $parent_id, PDO::PARAM_INT);
$st->bindParam(':project_id', $project_id, PDO::PARAM_INT);
$st->execute();
Alt A below is a statement from a php-mysql tutorial. It works as it should.
I found the id-value rather obfuscated and tested alt B. This also worked!
What is the point with the id-value of alt A?
MySQL 5.0.51, PHP 5.2.6
// Alt A :
$sql = "SELECT * FROM example WHERE id = '".$q."'";
// Alt B :
$sql = "SELECT * FROM example WHERE id = $q";
This are just two different approaches to building a string from static and variable data.
Alternative A uses concatenation, or the joining of string and variable tokens using the concatenation operator.
Alternative B uses variable expansion, wherein the variables inside a double-quote-delimited string are expanded to their values at evaluation time.
Neither is necessarily better or preferred, but if you have to have single-quote-delimited strings, for example, then you would need to use alternative A.
Of course, neither of these is preferable to building SQL queries with bound parameters, as not doing so leaves you vulnerable to SQL injection attacks.
Theres two reasons to use the example in 'Alt A'. First is if the string is enclosed in single quotes '', the variable's name will be used in the string instead of it's value.
$id = 7;
'SELECT * FROM table WHERE id = $id' //works out to: WHERE id = $id
"SELECT * FROM table WHERE id = $id" //works out to: WHERE id = 7
Secondly, it's useful to combine strings with the results of a function call.
"SELECT * FROM table WHERE id = '".getPrimaryId()."'"
Outside of what has already been said I've found it best practice, if I'm writing a query, to write it as so:
$sql = "SELECT * FROM table WHERE uid=" . $uid . " LIMIT 1";
The reason for writing SQL like this is that 1. MySQL query doesn't have to parse the PHP variables in the Query and 2 you now easily read and manage the query.
When PHP communicates with MySQL, it is actually (in essence) two languages communicating with each other. This means that a string will be processed by the first language before being sent to the other. It also means that it is important to think in terms of the receiving language
In this case:
$q = 'some_name';<br/>
$query = "SELECT * FROM exempel WHERE id = $q";<br/>
you are telling MySQL to
"SELECT * FROM example1 WHERE id = some_name.
In this case:
$q = 'some_name';<br/>
$query = "SELECT * FROM exempel WHERE id = '$q'";<br/>
and this case:
$q = 'some_name';<br/>
$query = "SELECT * FROM exempel WHERE id = '".$q."'";<br/>
you are telling MySQL to
"SELECT * FROM example1 WHERE id = 'some_name'.
The first example should cause an error as some_name is not a valid part of a MySQL query (in that context). On the other hand, the next two will work fine, because MySQL will look for the String "some_name".
You can also do this:
$sql="SELECT * FROM exempel WHERE id = {$q}";
which is useful for setting off things like:
$sql="SELECT * FROM exempel WHERE id = {$row[id]}";
in 'alt B', $q must be an int or float or other numeric
in 'alt A', $q can be anything a string, int, etc.
The single quote makes that possible. It's just hard to see sometimes if you are looking at it for the first time.