No replacement of question mark in prepared SQL statement - php

I use PDO in PHP on a MariaDB to filter rows where text field contains $search_terms. However, I only retrieve the rows which contain a question mark. It seems that the content of $search_terms is not replaced in the prepared statement.
$query = $db->prepare('SELECT * FROM notes WHERE text LIKE \'%?%\'');
$query->execute(array($search_terms));
$data['notes'] = $query->fetchAll(PDO::FETCH_ASSOC);
Why ?

Not sure if $search_terms is intended to hold one or more than one search term. Your query implies the former, and if so, then use:
$search_term = "%apple%";
$query = $db->prepare('SELECT * FROM notes WHERE text LIKE ?');
$query->execute($search_term);
$data['notes'] = $query->fetchAll(PDO::FETCH_ASSOC);
Note that we bind the search term with the % wildcards to the statement. The statement just holds a single ? placeholder.

Related

search not returning query

Sorry my english. I'm trying to search mysql database that contains hashtags, but it returns all.
eg Search #NBA returns: #NBA, #NBA2021, #NBAscoreBoard, etc
I've tried every preg_replace on here. eg #(^|\s)#(\w*[a-zA-Z_]+\w*)# But how do I break after the specific search is met?
$_GET["tag"]='#nba'; // $_GET is from a query string
$fulltag = preg_replace("/[^a-zA-Z0-9_]/", '', $_GET["tag"]); //trying to isolate this problem
$sql='SELECT * FROM status WHERE data LIKE "%#'.$fulltag.'%" LIMIT 12';
//
//
//
echo //the result containing the "# $fulltag";
As I said in my comment--just doing two queries with one to test if data = fullTag, and then if that returns nothing, then doing the wildcard search--is probably going to be simpler.
However, if you really want this to be a single query, you could do something like this, wherein you test to see if it is an exact match within a sub-query, then order by whether it's an exact match so that if there is an exact match, it will be the first result.
SELECT *
FROM (
SELECT
data,
CASE
WHEN data = "#NBA"
THEN 1
ELSE 0
END AS is_exact
FROM status
WHERE data LIKE "%#NBA%"
LIMIT 12
) AS matches
ORDER BY is_exact DESC
A separate note: You code right now is very vulnerable to SQL Injection. You should try using parameterized prepared statements instead of manually building your queries. See PDO or MySQLi prepared statements.
Here is an example of the above query using PDO, and this method of using CONCAT to safely add wildcards to your query:
$_GET["tag"]='#nba'; // $_GET is from a query string
$fulltag = preg_replace("/[^a-zA-Z0-9_]/", '', $_GET["tag"]); //trying to isolate this problem
$pdo = new PDO(/* your connection details */);
$sql =
'SELECT *
FROM (
SELECT
data,
CASE
WHEN data = CONCAT("#", :tag1)
THEN 1
ELSE 0
END AS is_exact
FROM status
WHERE data LIKE CONCAT("%#", :tag2, "%")
LIMIT 12
) AS matches
ORDER BY is_exact DESC
';
$stmt = $pdo->prepare($sql);
$stmt->execute([
':tag1' => $fulltag,
':tag2' => $fulltag,
]);
Here's your simpler query using the same, safer approach:
$_GET["tag"]='#nba'; // $_GET is from a query string
$fulltag = preg_replace("/[^a-zA-Z0-9_]/", '', $_GET["tag"]); //trying to isolate this problem
$pdo = new PDO(/* your connection details */);
$sql = 'SELECT * FROM status WHERE data LIKE CONCAT("%#", :fullTag, "%") LIMIT 12';
$stmt = $pdo->prepare($sql);
$stmt->execute([':fullTag' => $fulltag]);

Set PHP variable to MySQL wildcard '%' [duplicate]

This question already has answers here:
pdo prepared statements with wildcards
(2 answers)
Closed 5 years ago.
I'm trying to query posts using PDO where the database column 'tags' = something.
My problem is: I want my query to work even if there's no $_GET['tag'] request is set and here's my code.
if (!isset($_GET['tag'])) {
$tags = '%';
} else {
$tags = $_GET['tag'];
}
$get_recipes = $con->prepare ("SELECT * FROM recipes WHERE tags = ?");
$get_recipes->execute(array($tags));
$recipes = $get_recipes->fetchAll();
Is it valid to set the PHP variable $tags to the MySQL wildcard %? if not possible then what should I do to make my query work?
When I run that code and there's not $_GET['tag'] is written the query will not fetch any posts from the database.
Using Wildcards in Prepared Statements With PDO
When using a wildcard in MySQL you must use the LIKE operator. It is correct to bind the wildcard with parameters in PDO.
You would prepare your statement like so.
$get_recipes = $con->prepare ("SELECT * FROM recipes WHERE tags LIKE ?");
And then you would bind your parameter using the % character, like so.
$get_recipes->execute(array('%'));
While that is the correct way to use a wildcard in the way you've proposed, that is not the correct solution to do what you're trying to do.
How to achieve what you're trying to achieve
In your code it looks like you want to select all rows if $_POST['tags'] is not set, and if it is set you want to select all rows that have the tags column set to the value of $_POST['tags']. To do this, you would want to prepare your statement inside the conditional, like so.
if (!isset($_GET['tag'])) {
$get_recipes = $con->prepare ("SELECT * FROM recipes");
$get_recipes->execute();
} else {
$get_recipes = $con->prepare ("SELECT * FROM recipes WHERE tags = ?");
$get_recipes->execute(array($_GET['tag']));
}
$recipes = $get_recipes->fetchAll();

PDO adds the apostrophe to the mySQL query

After years of reading it's time to ask first question :)
My problem is that after migrating the code from mySQLi to PDO we have got a problem as it seems PDO adds the apostrophes to the query.
PHP code goes like that:
$sort = $_GET['sort']; << table column name (mySQL VARCHAR only columns)
....
$query = 'SELECT * FROM table WHERE xxx > 0';
$query .= ' ORDER BY :sort ASC ;';
$qry_result= $db->prepare($query);
$qry_result->execute(array(':sort'=>$sort));
mysqli version went smoothly but now queries (mysql log file) looks like this:
SELECT * FROM table where xxx > 0 ORDER BY 'SORT_VAR_VALUE' ASC;
^ 2 problems ^
So the table is NOT sorted, as sort order (from mySQL point of view) is wrong.
phpinfo() does not get any results for search on "magic" nor "quotes" btw.
Any idea ??
The placeholders in PDO statements are for values only. If you want to add actual SQL to the query you need to do it another way.
First, you should sanitize $sort and surround it with backticks in the query.
$sort = preg_replace('/^[a-zA-Z0-9_]/', '', $sort);
Then you could double quote the query string and PHP will replace $sort with it's value for you:
$query = "SELECT * FROM table WHERE xxx > 0 ORDER BY `$sort` ASC";
Or you could replace it with preg_replace like so:
$query = 'SELECT * FROM table WHERE xxx > 0 ORDER BY `:sort` ASC';
$query = preg_replace('/:sort/', $sort, $query, 1);
I would use the preg_replace method because it allows you to reuse the query if you assign the results from preg_replace to another variable instead of overwriting the original variable.
by default pdo binds values as strings.
To fix this you will want to check that the column is actually a valid name and then add it to the query, you can do it the following way:
function validName($string){
return !preg_match("/[^a-zA-Z0-9\$_\.]/i", $string);
}
if(validName($sort)){
$db->prepare("SELECT * FROM table where xxx > 0 ORDER BY $sort ASC");
}
With PDO it's not possible to bind other things that variables in the WHERE statement. So you have to hard code the names of the columns you order by.
See How do I set ORDER BY params using prepared PDO statement?
or Can PHP PDO Statements accept the table or column name as parameter? for further explanations.

Does whether a variable needs to be quoted or not depend on if the category in the database is marked as varchar or int?

For example, say my query is this:
$query = "SELECT * FROM statements WHERE user_id_created_by=$users_id";
Does whether $user_id need to be quoted or not depend on if the category in the database is marked as varchar or int?
Also, I don't quite understand whether or not spacing affects querys:
Is this the same as the one above:
$query = "SELECT * FROM statements WHERE user_id_created_by = $users_id";
You shouldn't be using a variable in a query at all. Use prepared statements to prevent sql injection, which could allow an attacker to steal/modify/delete anything they want.
PDO prepared statement (with named parameters):
$params = [
':id' => $users_id
]
$query = "SELECT * FROM statements WHERE user_id_created_by=:id";
$sth = $dbh->prepare($query);
$sth->execute(array($params);
mysqli prepared statement:
$stmt = mysqli_prepare($link, "SELECT * FROM statements WHERE user_id_created_by=?")
mysqli_stmt_bind_param($stmt, "s", $users_id);
mysqli_stmt_execute($stmt);
Regarding the spaces in a query, those shouldn't affect anything.
First: Use PDO and prepared statements as m59 says!
The thing with the quotes is the following:
Imagine a varchar with spaces like "this is an example".
The query unquoted query would than look like:
SELECT * FROM statements WHERE user_id_created_by=this is an example
(I guess) mysql will then think is an example doesnt belong to the passed varchar.

Using LIKE in bindParam for a MySQL PDO Query [duplicate]

This question already has answers here:
How do I create a PDO parameterized query with a LIKE statement?
(9 answers)
Closed 4 months ago.
I've read multiple examples on how these queries should be written but I'm struggling to get this specific like to run when using bindParam
Would this be the correct way to match usernames that begin with a?
$term = "a";
$term = "'$term%'";
$sql = "SELECT username
FROM `user`
WHERE username LIKE :term
LIMIT 10";
$core = Connect::getInstance();
$stmt = $core->dbh->prepare($sql);
$stmt->bindParam(':term', $term, PDO::PARAM_STR);
$stmt->execute();
$data = $stmt->fetchAll();
No, you don't need the inner single quotes so just $term = "$term%";
The statement you're running now would try to match 'a%' instead of a%
bindParam will make sure that all string data is automatically properly quoted when given to the SQL statement.
You can use bindValue , suppose you are having a $query = "search string"
$stmt->bindValue(':term', $query.'%'); // this will do like search for "search term XXXXX"
similarly
$stmt->bindValue(':term', '%'.$query.'%');
or
$stmt->bindValue(':term', '%'.$query);

Categories