PHP implode function explanation - php

I am relatively new to PHP. The textbook im working from covers PHP5.2 and mentions nothing of implode. The information im getting from PHP manual is a bit unclear to me. Im looking for a short clear explanation and example on implode
What exactly is its purpose?
Why would you use it?
Is it secure?
I know implode returns a string of the elements of its array but how is the following 2 exmples different, and why would you use one over the other:
example 1
$query = 'SELECT `name`, `position`
FROM `player_info`
WHERE `player_id` IN (' . implode(',', $player_ids) . ')';
example 2
$result2 = mysql_query("SELECT `fixture_id`, `opponents`
FROM `fixtures` ") or die (mysql_error());
Thank you

I'm going to ignore the fact that you're using mysql_query() in your code - as that is a more vulnerable library in the PHP reportoire. When you get more comfortable, or if you can right away, use PDO.
To understand implode() you need to understand arrays(). I'm going with the assumption that you know what arrays are.
In SQL, when you use IN() - it's equivalent to multiple "OR"s.
e.g.
SELECT * FROM table t WHERE t.id IN ( 1,2,3 )
is essentially the same as:
SELECT * FROM table t WHERE t.id = '1' OR t.id = '2' OR t.id = '3' OR t.id = '3'
When you have an array in PHP - it will look like this:
$array = array ( 1 , 2 , 3 );
If you wanted to dump it into the SQL statement, it'll fail, because a query like:
SELECT * FROM table t WHERE t.id IN ( $array )
would output:
SELECT * FROM table t WHERE t.id IN ( Array )
What you want are the values within the array. That's where implode() comes in. Implode would create delimiter - which is equivalent to a consistent value that goes between each value in your array (in your example, a comma) and will output the necessary string that you need in SQL.
$array = array ( 1 , 2 , 3 );
$query = "SELECT * FROM table t WHERE t.id IN ( ".implode("," , $array)." ) ";
Is the same as:
$query = "SELECT * FROM table t WHERE t.id IN ( 1,2,3 ) ";
Hope that helps

Implode can be particularly useful for lists. Say you have an array of ingredients, you might echo implode("\n",$ingredients) to show them on their own lines.
In your first example, you need to be very careful. It will only work properly if $player_ids is an array of numbers. implode() by itself is perfectly secure, however improper use can leave gaping holes in your security. A more "correct" version of that code might look like this:
$query = "SELECT `name`, `position` FROM `player_info`
WHERE `player_id` IN ('".implode("','",array_map("mysql_real_escape_string",$player_ids))."')";
The above code also handles the case where $player_ids is empty. In your code, it will result in a MySQL syntax error (IN () is not valid), whereas in this code it will simply look for IN (''), which will match no rows.
Another use for implode might be when packing numbers. Say you have an array of digits, you might implode("",$digits) to pack them into a single string (and str_split($string) to unpack them).
In short, there are many potential uses for this function, and generally it will be obvious when the function applies. Try not to think too hard about it ;)

Example 1 extracts players information of the players who's ID's are $player_ids. Second example extracts two columns without WHERE condition.
What is your question exactly? As long as you are 100% sure the $player_ids is an array of integers! then it is safe.

Implode will take an array and transform it in a string where each element of original array will be separated by "first parameter" of implode function.
What do you mean with "secure"? I don't understand this question but I suppose that you are asking it because you seen implode() used into a db query (in the example). implode() has nothing to do with db query so no sql injection (*) and so on.
We should use it instead of loop over the whole array
(*) obviously you should pay attention of what array to implode is

implode creates a string from an array.
You must have some basic understanding of array, before you start working with it.
Thus, if you have a set of same-type data you store it in arrays. The first parameter in array is a delimiter. Second - is the array.
$animals = ("cats", "dogs", "cows");
echo implode(" &", $animals) . " are animals" ;
Will produce:
cats & dogs & cows are animals
In your first example, there is IN construction which can accept several parameters. Arrays are just suitable to work with it.
$player_ids = array(1,2,3);
"IN (".implode(", ", $player_ids).")" will result in IN (1, 2, 3)

Related

Find multiple needles in FIND_IN_SET

I have a variable that may contain a comma separated list. In my database, I also have a column with a column separated list.
I know I can find a single value in that db column by using FIND_IN_SET(needle, haystack)
However, if my variable contains a list such as "a,b,c" how can I check if at least one item in the list matches at least one item in the column? Is this possible?
SELECT `column_A` REGEXP CONCAT('(,|^)(', REPLACE( `column_B` ,',','|'), ')(,|$)');
SELECT '123,456,789' REGEXP CONCAT('(,|^)(', REPLACE( '1234,456,6789' ,',','|'), ')(,|$)');
This is my solution.
It sounds like you probably need some linking tables in your database. Storing comma separated lists in columns to compare against other comma separated lists is going to hurt your performance if you get to any sort of scale.
I'd highly suggest you read more about linking tables (associative entities) to perhaps convince you to change your database design somewhat:
https://en.wikipedia.org/wiki/Associative_entity
To answer your question about how you would use FIND_IN_SET to perform multiple searches in a single query, you would need to build your query dynamically.
Here is a basic example to simply show you how to build a query dynamically. Please take proper steps to prevent SQL injection (http://php.net/manual/en/security.database.sql-injection.php).
// This is the list you want to search against - for your actual implementation you would use your column name
$haystack_str = '2,4,6,8,10';
// This is the comma separated list you want to use as your needle
$search_str = '1,2,3';
// Break the string apart into separate values
$search_array = explode(',', $search_str);
// Store your query fragments
$query_array = array();
// Loop through each string value you want to use as your needle
foreach ($search_array as $needle) {
// PLEASE TAKE PRECAUTIONS AGAINST SQL INJECTION !!!
$query_array[] = sprintf('FIND_IN_SET("%s","%s")', $needle, $haystack_str);
}
// Join all pieces together using OR
$query_str = implode(' OR ', $query_array);
// Show your example query
echo 'SELECT ' . $query_str . ';';
Example: https://eval.in/867963
This produces the following query:
SELECT FIND_IN_SET("1","2,4,6,8,10") OR FIND_IN_SET("2","2,4,6,8,10") OR FIND_IN_SET("3","2,4,6,8,10");
Returns a value in the range of 1 to N if the string str is in the string list strlist consisting of N substrings.
Returns 0 if str is not in strlist or if strlist is the empty string.
https://dev.mysql.com/doc/refman/5.7/en/string-functions.html#function_find-in-set
The sample query will produce 1 which indicates one of your search values is in your set.

Fat Free Framework PHP Sql statement with in expression

I'm trying to include a list of strings to be used in an "in" expression in a sql statement for example:
select * from poop where id in ('asd','sas','ser')
I want to pass the in parameter from a variable. The quoting is really screwing me up. Should I be passing this as a string which I have been trying to no avail by making a comma seperated string that looks like this:
282366381A,240506808A,244154247A,491404349A,242443808B,328409296A,239723812A,383423679M
or "282366381A","240506808A","244154247A","491404349A","242443808B","328409296A"
or
'282366381A','240506808A','244154247A','491404349A','242443808B','328409296A'
None of these work or is there a different way using an array of values?
This is the statement I'm using with the string:
$cernerResults = $this->cernerdb->exec( "select
pat as HICN,
from pat
where
HICN in ( ? )", $hicsString );
Edit:
I was able to get around this by constructing the entire query as a string like this:
$query = "select pat as HICN from pat where HICN in (".$hicsString.")";
$hicsString has single quotes around each item like so:
'282366381A','240506808A','244154247A','491404349A','242443808B','328409296A'
The problem is that providing the string to the exec would result in no results. When looking at the freetds log file the in expression values would be double quoted as a whole or each one would be double single quoted and if i used no quotes they would not be quoted at all.
All of these would make the statement return no results. I should also mention that this is a Sybase database.
I think your problem may come from the fact that PDO parser needs to have one value per question mark so it is able to validate it. So your "hack" with one question mark which is assigned to more than one value is where it fails IMHO.
This is how I handle case like that:
$values = ['asd','sas','ser'];
$count = count($values);
$results = $db->exec(
"select * from poop where id in ( ?".str_repeat(", ?", $count-1).")",
$values
);
In general I would advice you using data mappers instead of running the queries on a DB object. It is easier to iterate through them and it is more secure.

MySQL IN clause - String and INT comparison

I have a stored procedure which takes in a single String parameter - the value passed into this parameter is a comma separated list of ID's from PHP - something like 2,3,4,5
`DECLARE tags_in VARCHAR(255);`
Within the Stored procedure I would like to select the rows which have ids corresponding to the ids in the parameter - the query would be like
`SELECT * from tags WHERE tag_id IN (tags_in)`
I pass in the values from PHP to MySQL using the following statement binding the value as a string
`$stmt->bindParam(':tags', '2,3,4', PDO::PARAM_STR);`
Problem - the actual query being executed by MySQL is as below - where the parameters passed in are considered as one string
`SELECT * from tags WHERE tag_id IN ('2,3,4')`
When the query I want executed is as below where the parameters are considered as individual integers
`SELECT * from tags WHERE tag_id IN (2,3,4)`
Any suggestions on I can accomplish this?
SQL placeholders can represent only SINGLE values. If you pass in some comma separated values, they won't be seen as multiple individual values with commas, they'll just be treated like a monolithic string.
e.g.
... WHERE foo IN (:bar)
... WHERE foo = :bar
are functionally identical as far as the SQL parser are concerned, and it won't make allowances for passing in your CSV values. Both will execute the same way:
... WHERE foo IN ('1,2,3')
... WHERE foo = '1,2,3'
You'll either have to limit yourself to only as many values as you have placeholders, or dynamically build your SQL and put in a placeholder for each individual value you're trying to put into the IN clause.
e.g.
$placeholders = array_fill(0, count($values_to_check) -1, '?');
$in_clause = implode(',', $placeholders);
/// builds ?,?,?,?,?,....?
$sql = "SELECT ... WHERE foo IN ($in_clause)";
$stmt = $dbh->prepare($sql);
$stmt->execute($values_to_check);
This is one place where prepared statements fall flat on their faces, and you have to fall back to good old "build some sql dynamically".
There is sometimes another way to accomplish the desired result by casting the integer you're trying to compare as a string surrounded by commas and checking if the result is contained in your list of possible values (with added commas on either side as well). It's not the most efficient for performance maybe, but it allows you to do what you want in a single procedure or query.
For example (in your case) something like this might work:
SELECT * from tags WHERE INSTR (CONCAT(',', tags_in, ','), CONCAT(',', tag_id, ',') );
MySql is a little bit weird in that it does the conversion from int to char within the CONCAT function, some other databases require explicit casting.

What is the optimal string format for converting to an array?

I'm producing a string with GROUP_CONCAT in MySQL.
There are 2 columns I need to select for each row being id and name, how should I format the string in the query so I can produce an array that couples the ids and names in PHP at optimal speed?
For example (my attempt):
GROUP_CONCAT producing
{"id":1,"name":"python"},{"id":2,"name":"ruby"}
Then convert this to an array in PHP.
Something like this -
<?php
$db = new PDO('mysql:dbname=test;host=127.0.0.1', 'user', 'pass');
$stmt = $db->prepare("SELECT some_field, GROUP_CONCAT(CONCAT_WS('::', id, name) SEPARATOR '||') AS grp
FROM `table`
GROUP BY some_field");
$stmt->execute();
while($row = $stmt->fetchObject()) {
foreach(explode('||', $row->grp) as $pair) {
$tmp = explode('::', $pair);
$array[$row->some_field][$tmp[0]] = $tmp[1];
}
}
print_r($array);
As pointed out by safarov you need to be aware of the group_concat_max_len limitation.
Is your GROUP_CONCAT() producing a JSON string? In that case, json_decode($string, true) (json_decode() on PHP docs) will do the thing.
But that will not be faster than doing it like The Jumping Frog suggested above. mysql_fetch_array() will parse the string for you (use MYSQL_NUM to avoid extra overhead) and you'll also avoid the MySQL extra time on the GROUP_CONCAT();
If you just want to store some JSON data on the DB (this will complicate future searches and indexing, but is a good choice sometimes when you don't want to create a ton of DB fields, Wordpress does it all the time), you can use json_encode() before storing and json_decode() to retrieve the original array/object for that purpose. You can also use PHP serialize() and unserialize(), but I think JSON is preferable because you mentioned you where using Javascript.
I dont know why you are attempting this. But this query should work
SELECT * FROM `yourtable` GROUP_CONCAT(
CONCAT('{"id":"',id,'","name":"',name,'"}')
separator ',')

Imploded PHP integer array for Mysql NOT IN clause

I've been trying to use a PHP integer array for a MySQL query that uses the NOT IN clause, but despite no errors it seems to always return the results I want filtered out.
Example:
$IDS = $_SESSION['Posts'];
$Select = 'SELECT *
FROM status
WHERE (W_ID = '.$ID.')
AND (ID NOT IN ("'.implode(',', $IDS).'"))
ORDER BY ID DESC
LIMIT '.$Begin.', '.$Number.'';
$Select = mysql_query($Select) OR DIE(mysql_error());
I'm pretty sure this is a logical syntax error.
What I've tested for:
I've made sure that $IDS is treated as an array. Also I have tested to see whether there are values stored within the array. I have also not quoted the integer array, but then I got a mysql syntax error for not having them.
The problem is the two ” in the beginning and the end of the IN block. They cause the entire implode array to become a comma-separated string.
Your actual query will look like this:
ID NOT IN ("1,2,3,4")
"1,2,3,4" is one string, not several values. Get rid of the " quotes.
You could try to use FIND_IN_SET rather than an IN clause.
$IDS = mysql_real_escape_string(implode(',', $IDS));
$Select = "SELECT * FROM status WHERE (W_ID=$ID)
AND (NOT FIND_IN_SET(ID, '$IDS'))
ORDER BY ID DESC LIMIT $Begin, $Number";
Anyway in SQL you are required to use single quotes for strings, not double quotes. That works with MySQL, but not for all configurations. Also gets more readable if you do it the other way round. (Single quotes in PHP for performance is retarded advise!)

Categories