preg_replace - don't replace in already replaced parts - php

Given SQL-query with placeholders:
SELECT * FROM table WHERE `a`=? AND `b`=?
and query parameters ['aaa', 'bbb'], i would like to replace ?-placeholders with corresponding params. So, I do it like this:
$sql = preg_replace(array_fill(0, count($params), '#\?#'), $params, $sql, 1);
(we do not concentrate on mysql-escaping, quoting etc. in this question).
Everything works fine and I get
SELECT * FROM table WHERE `a`=aaa AND `b`=bbb
But if our first parameter looks like this: "?aa", everything fails:
SELECT * FROM table WHERE `a`=bbba AND `b`=?
obviously, first replacement pass changes "a=?" into "a=?aa", and second pass changes this (just inserted) question mark into "bbb".
The question is: how can I bypass this confusing preg_replace behaviour?

You can use preg_replace_callback to use one item from $params at a time for each replacement.
$sql = 'SELECT * FROM table WHERE `a`=? AND `b`=?';
var_dump('Original: ' . $sql);
$params=['aaa','bbb'];
$sql = preg_replace_callback("/\\?/",function($m) use (&$params) {
return array_shift($params);
}, $sql);
var_dump('Result: ' . $sql);
Let me know

I would not do this with preg_replace or str_replace. I would use preg_split so empty returns can be removed (If explode had empty removal option I'd use that). For there iterate over the return and add in values. You also can quote the values with this. I presume the purpose of this is for debugging parameterized queries.
$sql = 'SELECT * FROM table WHERE `a`=? AND `b`=?';
$v = array('1?1', "222");
$e = preg_split('/\?/', $sql, NULL, PREG_SPLIT_NO_EMPTY);
$c = '';
foreach($e as $k => $v1){
$c .= $v1 . "'" . $v[$k] ."'";
}
error_log($c);
Then your error log will have:
SELECT * FROM table WHERE `a`='1?1' AND `b`='222'

Related

PHP - execute function doesn't work with implode

Okay, I don't know what exactly the problem is. So, I decided to post it here to discuss it with you.
The Problem is that, When I use php implode function in PDO execute(array(implode(",",$imploded)))); It doesn't work
When I use php implode function " the same function with the same variables " in the select statment, it works normally !
I've doubts that using it in the statment is a chance for SQL Injection.
Here's My Full Code :
$exCat = explode(",", $article['Category']);
$getCats = $con->prepare("SELECT * FROM `Categories` WHERE `ID` IN (?)");
if (is_array($exCat)) {
$getCats->execute(array(implode(",", $exCat))); /* This Is only displaying the first element */
} else {;
$getCats->execute(array($exCat));
}
$getCATS = $getCats->fetchAll();
This Works fine with me. However, I've doubts that using it in the statment is a chance for SQL Injection.
$exCat = explode(",", $article['Category']);
$anotherStmt = $con->prepare("SELECT * FROM `Categories` WHERE `ID` IN (" . implode(",", $exCat) . ")"); /* This Works fine */
$anotherStmt->execute();
$anotherCATS = $anotherStmt->fetchAll();
explode returns an array in every instance so is_array is not needed.
You need to use a placeholder for every value you want bound. I'd use str_repeat and rtrim to generate your placeholders then just pass the exploded array to the execute.
$exCat = explode(",", 'non commaed list, commaed');
$placeholders = rtrim(str_repeat('?,', count($exCat)), ', ');
$getCats = $con->prepare("SELECT * FROM `Categories` WHERE `ID` IN ({$placeholders})");
$getCats->execute($exCat);

Preg_replace with a query to database

I know "preg_replace" function but not how to use a query inside it. For instance I'm able to convert __ into <em>:
$text = preg_replace('/__(.+?)__/s', '<em>$1</em>', $text);
I am looking for something more powerful.
I would like to replace some pre-formatted text (i.e. [TTT]112233[/TTT]) into another text which is the result of a query (i.e. echo "$text2"; ).
The variable $text2 is the result of a query like "SELECT * FROM table WHERE id=112233";
Is there an embedded php function that do this? Many thanks, Fabio
Here's a way to do this. Probably not the most efficient, but it's a way. It's based in preg_replace_callback
$line = preg_replace_callback(
'|[TTT](\d+)[/TTT]|',
function ($matches) use ($dbConnection) {
$q = "SELECT textColumn FROM table WHERE id=? LIMIT 1";
$stmt = mysqli_prepare($dbConnection,$q);
mysqli_bind_param("i",$matches[1]);
mysqli_execute($stmt);
mysqli_bind_result($result);
mysqli_fetch($stmt);
return "[TTT]$result[/TTT]";
},
$line
);
Note this assumes mysqli but your method may differ.

$params->set Array between square bracket

i'm using K2 in Joomla 3.3.
I'm trying to set params (items ids ) to module k2_content from item.php file.
The result must to be between brackets, something like:
["96","68"]
My code is:
$query = "SELECT * FROM #__k2_items WHERE extra_fields_search = '$myautor' AND catid !=1 " ;
$db->setQuery($query);
$losautores = $db->loadObjectList();
$result = array();
foreach ($losautores as $key => $value) {
$result[] = '" '.$value->id.' "';
}
$string_version = implode(',', $result);
$autoresfinal = '['.$string_version.']';
If i test using print, looks ok.
But passing the var to pramas, i get 1064 error.
$params->set('items', $autoresfinal);
To test I tried
$autoresfinal = ["96","68"];
And works fine.
Any idea why doesn't work?
Thank you.
If you assign ["x","y"] you are assigning an array. Here you are transforming the array in a string.
Try simply
$result = [ ];
foreach ($db->loadObjectList() as $key => $value) {
$result[] = $value->id;
}
$params->set('items', $result);
Also, if you wanted to convert the array into a string (possibly JSON), a faster and safer way is to use json_encode (with the appropriate options).
UPDATE
The above remains true, but I had missed your complaint about error 1064. That is a SQL syntax error and it happens before you encode the results.
The reason - as noticed by Fred -ii- - is that in this query, #__k2_items needs escaping with backticks:
$query = "SELECT * FROM #__k2_items WHERE
extra_fields_search = '$myautor' AND catid !=1 " ;
should be:
$query = "SELECT * FROM `#__k2_items` WHERE
extra_fields_search = '$myautor' AND catid !=1 " ;
Also, you probably want to use prepared statements and parameterized queries (find an example here) instead of just plugging $myautor into a string. If you had an author called D'Artagnan, the query would become
....search = 'D'Artagnan' AND ...
which would again fail. Or if I called an author ' OR ''=', the query would become
...search = '' OR ''='' AND ...
which, since '' is always equal to '', would match for all the records in your table.

Split mysql query and delete a part of that

I have many conditions in PHP function which every of them produces a mysql query.All conditions work correctly except one query which ends with AND operator.Before returning the query result I need to check if query ends with AND it should remove AND and then returnes the query.
This is the sample of query:
$query="select * from case where case_name='name' AND case_status='102' AND";
If this kind of query is produced I need to do:
1-If it ends with AND
2-remove AND
3-return the query without last AND
The result should be like this:
$query="select * from case where case_name='name' AND case_status='102' ";
I do not have much experience to work with PHP functions.How can I do this?
Thnaks for your help.
Try this,
$query="select * from case where case_name='name' AND case_status='102' AND"
$query = trim($query,'AND');
quick fix:
$query = preg_replace( "/AND$/", "", $query);
You should fix the logic of condition though.
like
$cond[] = "....";
$cond[] = "...."
....
then
$query = $query_first_half + implode ( " AND " , $cond );
Ultimately please use sql library like PDO
http://fi1.php.net/manual/en/class.pdo.php
explode the string and pop the last element .
$arr = explode(" ", $query);
$last = array_pop($arr);
if($last != "and")
{
array_push($arr,$last);
}
$query = implode(" ",$arr);
Run the $query them it should work
First your table name CASE is mysql reserved keyword you should rename your table to something else or escpae it by backticks `
you could use query without AND , and when you add other query just start by AND .
like that :
$query="select * from `case` where case_name='name' AND case_status='102'";
$query .= " AND .........";
so like that , your condition is not true then just first query will work , if condition is true then second query will work and it start by AND. You dont need to remove the AND.

MySQL where clause equals anything (SELECT * WHERE col = ANY_VALUE)

I'd like to create a query in MySQL that has an optional value. When the value is specified the query is filtered by that value, when the value is not all rows are returned. Here's the idea:
public function doQuery($item = 'ANY_VALUE') {
$query = "SELECT * FROM table WHERE item = ?";
db->fetchAll($query,array($item))
...
}
doQuery(); // Returns everything
doQuery($item='item1'); // Returns only rows where item = 'item1'
Is there an easy way to do this without creating two query strings depending on the value of $item?
As far as I know, no such "any" placeholder exists.
If you can use LIKE, you could do
SELECT * FROM table WHERE item LIKE '%'
if you can append a condition, you could nullify the item clause like this:
SELECT * FROM table WHERE item = ? OR 1=1
(won't work in your example though, because you are passing "item" as a parameter)
That's all the options I can see - it's probably easiest to work with two queries, removing the WHERE clause altogether in the second one.
This would probably work, but I*m not sure whether it's a good idea from a database point of view.
public function doQuery($item = 'ANY_VALUE') {
$query = "SELECT * FROM table WHERE item = ? OR 1 = ?";
db->fetchAll($query,array($item, ($item == 'ANY_VALUE' ? 1 : 0))
...
}
Better way to do this is first generate sql query from the parameter you need to bother on, and then execute.
function doQuery($params) {
$query = 'SELECT * FROM mytable ';
if (is_array($params) // or whatever your condition ) {
$query .= 'WHERE item = ' . $params[0];
}
$query .= ' ;';
// execute generated query
execute($query);
}
You cannot get distinct results without giving distinct query strings.
Using $q = "... WHERE item = '$item'" you DO create distinct query strings depending on the value of $item, so it is not that different from using
$q = "..." . ($item=='ANY_VALUE' ? something : s_th_else);.
That said I see two or three options:
use function doQuery($item = "%") { $query = "SELECT ... WHERE item LIKE '$item'"; ...}
But then callers to that function must know that they must escape a '%' or '_' character properly if they want to search for an item having this character literally (e.g. for item = "5% alcoholic solution", giving this as argument would also find "50-50 sunflower and olive oil non alcoholic solution".
use function doQuery($item = NULL) { $query = "SELECT ..."; if ($item !== NULL) $query .= " WHERE item = '$item' "; ...} (where I use NULL to allow any other string or numerical value as a valid "non-empty" argument; in case you also want to allow to search for NULL (without quotes) you must choose another "impossible" default value, e.g., [], and you must anyway use a distinct query without the single quotes which however are very important in the general case), or even:
use function doQuery($item = NULL) { if($item === NULL) $query = "SELECT ..."; else $query = "SELECT ... WHERE item = '$item' "; ...}, which is more to type but probably faster since it will avoid an additional string manipulation (concatenation of the first and second part).
I think the 2nd & 3rd options are better than the first one. You should explain why you want to avoid these better solutions.
PS: always take care of not forgetting the quotes in the SQL, and even to properly escape any special characters (quotes, ...) in arguments which can depend on user input, as to avoid SQL injections. You may be keen on finding shortest possible solutions (as I am), but neglecting such aspects is a no-no: it's not a valid solution, so it's not the shortest solution!

Categories