Get array value based on variable and indexes as string - php

I'm trying to execute variable which part of it is a string.
Here it is:
$row = array(
'index_1' => array(
'index_2' => 'my-value'
)
);
$pattern = "['index_1']['index_2']";
I tried to do it in following ways:
// #1
$myValue = $row{$pattern};
// #2
$myValue = eval("$row$pattern");
I also trying to get it working with variable variable, but not successful.
Any tips, how should I did it? Or myabe there is other way. I don't know how may look array, but I have only name of key indexes (provided by user), this is: index_1, index_2

You could use eval but that would be quite risky since you say the values come from user input. In that case, the best way might be to parse the string and extract the keys. Something like this should work:
$pattern = "['index_1']['index_2']";
preg_match('/\[\'(.*)\'\]\[\'(.*)\'\]/', $pattern, $matches);
if (count($matches) === 3) {
$v = $row[$matches[1]][$matches[2]];
var_dump($v);
} else {
echo 'Not found';
}

This can help -
$row = array(
'index_1' => array(
'index_2' => 'my-value'
)
);
$pattern = "['index_1']['index_2']";
$pattern = explode("']['", $pattern); // extract the keys
foreach($pattern as $key) {
$key = str_replace(array("['", "']"), '', $key); // remove []s
if (isset($row[$key])) { // check if exists
$row = $row[$key]; // store the value
}
}
var_dump($row);
Storing the $row in temporary variable required if used further.
Output
string(8) "my-value"

If you always receive two indexes then the solution is quite straight forward,
$index_1 = 'index_1';// set user input for index 1 here
$index_2 = 'index_2';// set user input for index 2 here
if (isset($row[$index_1][$index_2])) {
$myValue = $row[$index_1][$index_2];
} else {
// you can handle indexes that don't exist here....
}
If the submitted indexes aren't always a pair then it will be possible but I will need to update my answer.

As eval is not safe, only when you know what you are ding.
$myValue = eval("$row$pattern"); will assign the value you want to $myValue, you can add use return to make eval return the value you want.
here is the code that use eval, use it in caution.
demo here.
<?php
$row = array(
'index_1' => array(
'index_2' => 'my-value'
)
);
$pattern = 'return $row[\'index_1\'][\'index_2\'];';
$myValue = eval($pattern);
echo $myValue;

Related

PHP check if array contains any key other than some whitelisted

I would like to check all keys of a global GET array and do something, if it contains keys, other than some whitelisted ones from an array.
Let's say the current url is:
.../index.php?randomekey1=valueX&randomkey2=valueY&acceptedkey1=valueZ&randomkey3=valueA&acceptedkey2=valueB
Just for better visualization:
These GET parameters are all available in the global GET variable which looks something like this:
$GET = array(
"randomekey1" => "valueX",
"randomkey2" => "valueY",
"acceptedkey1" => "valueZ",
"randomkey3" => "valueA",
"acceptedkey2" => "valueB"
);
The keys I accept and want to let pass, I put into an array too:
$whitelist = array(
"acceptedkey1",
"acceptedkey2",
"acceptedkey3",
);
Now I want to check whether $GET contains any key other than the whitelisted. So in the URL example from above it should return "true", because there are keys in the $GET array which aren't in the whitelist.
Not only the existence of such an unknown (none whitelisted) key should trigger a true, but please also its emptyness!
Another example would be the following url:
.../index.php?acceptedkey1=valueZ&acceptedkey3=valueB
This should return false, because no other key other than the ones in the whitelist were found.
Unfortunately I was not able to find any modification of the in_array function or array_search which would fit these requirements, because as far as I know these functions are only looking for something specific, whereas in my requirements I am also looking for something specific (the whitelist keys), but at the same tme I have to check if some unknown keys exist.
Thank you.
It seems you want to determine whether an array contains keys that don't exist in a whitelist.
One way to find the difference between arrays is to use array_diff():
array_diff ( array $array1 , array $array2 [, array $... ] ) : array
Returns an array containing all the entries from array1 that are not present in any of the other arrays.
So, it can be used to return all keys from the URL that are not present in the whitelist:
$extrasExist = !empty( array_diff( array_keys($GET), $whitelist ) );
var_dump($extrasExist);
Here's a demonstration:
$get1 = array(
"randomekey1" => "valueX",
"randomkey2" => "valueY",
"acceptedkey1" => "valueZ",
"randomkey3" => "valueA",
"acceptedkey2" => "valueB"
);
$get2 = array(
"acceptedkey1" => "valueZ",
"acceptedkey2" => "valueB"
);
$whitelist = array(
"acceptedkey1",
"acceptedkey2",
"acceptedkey3"
);
$extrasExist = !empty(array_diff(array_keys($get1),$whitelist));
var_dump($extrasExist);
$extrasExist = !empty(array_diff(array_keys($get2),$whitelist));
var_dump($extrasExist);
bool(true)
bool(false)
Everything in PHP doesn't have to be all "lets find function that does exactly what I'm looking for". Just do a simple foreach loop, which can accomplish what you're looking for:
function clear_filter() {
$whitelist = array( "project", "table_name", "filterDates", );
foreach ($_GET as $gkey => $gval) {
if (!in_array($gkey, $whitelist)) {
return false;
}
}
return true;
}
You can also write it more simply, with one foreach loop like below:
function isValid() {
// Copy the array
$temp = $_GET;
// Loop through the array, and remove any whitelisted elements
foreach ($whitelist as $wkey) {
unset($temp[$wkey]);
}
// If count($temp) > 0, there are non whitelisted entries in the array.
return count($temp) === 0;
}
You can use the following function.
$check = checkWhitliest( $_GET, $whitelist ) );
var_dump ($check );
You can call the above function as
function checkWhitliest( $array, $whitelist ) {
$allKeys = array_keys ( $array); //Get all Keys from the array.
$diff = array_diff( $allKeys, $whitelist); //Get the values which are not in whitelist.
if( count ( $diff ) > 0 ) { //If the count is greater than 0, then there are certain extra kesy in $_GET
return true;
}
return false;
}
There are only two techniques that I would recommend for this task and both make key comparisons for performance reasons. Using array_diff() or in_array() will always be slower than array_diff_key() and isset(), respectively, because of how PHP treats arrays as hash maps.
If you don't mind iterating the entire $GET array (because its data is relatively small), then you can concisely flip the whitelist array and check for any key differences.
var_export(
(bool)array_diff_key($GET, array_flip($whitelist))
);
If performance is more important than code brevity, then you should craft a technique that uses a conditional break or return as soon as a non-whitelisted key is encountered -- this avoids doing pointless iterations after the outcome is decided.
$hasNotWhitelisted = false;
$lookup = array_flip($whitelist);
foreach ($GET as $key => $value) {
if (isset($lookup[$key])) {
$hasNotWhitelisted = true;
break;
}
}
var_export($hasNotWhitelisted);
Or
function hasNotWhitelisted($array, $whitelist): bool {
$lookup = array_flip($whitelist);
foreach ($array as $key => $value) {
if (isset($lookup[$key])) {
return true;
}
}
return false;
}
var_export(hasNotWhitelisted($GET, $whitelist));
All of the above techniques deliver a true result for the sample data. Demo of all three snippets.

Variable Variables for Array Key

Currently I am attempting to call a multidimensional array, using a string as a key or keys. I would like to use the following code, but I think the key is being interpreted as a string. Any solution?
$data= [];
$data['volvo'] = "nice whip";
$test = "['volvo']";
$data['drivers']['mike'] = "decent";
$test2 = "['drivers']['mike']";
echo $data$test; // should read 'nice whip'
echo $data$test2; // should read 'decent'
You just use the variable (which should just be the string and not PHP syntax) in place of the string literal.
$cars = [];
$cars['volvo'] = 'nice whip';
$test = 'volvo';
echo $cars[$test];
If you need a dynamic array access solution, you could also write a function, which does the actual array access like this:
function path($array, $path) {
$path = is_array($path) ? $path : explode('.', $path);
$current = $array;
while (count($path)) {
$seg = array_shift($path);
if (!isset($current[$seg])) throw new Exception('Invalid path segment: ' . $seg);
$current = $current[$seg];
}
return $current;
}
In your case, this would look like this
echo path($data, 'volvo');
echo path($data, 'drivers.mike');
or
echo path($data, ['volvo']);
echo path($data, ['drivers', 'mike']);
The problem is you can't pass multiple levels in one string like that. (If so, PHP would have to start looking for code fragments inside string array keys. And how would it know whether to interpret them as fragments and then split the string key up, or keep treating it as one string??)
Alt 1
One solution is to change the structure of $data, and make it a single level array. Then you supply keys for all the levels needed to find your data, joined together as a string. You would of course need to find a separator that works in your case. If the keys are plain strings then something simple like underscore should work just fine. Also, this wouldn't change the structure of your database, just the way data is stored.
function getDbItem($keys) {
// Use this to get the "old version" of the key back. (I.e it's the same data)
$joinedKey = "['".implode("'],['", $keys)."']";
$joinedKey = implode('_', $keys);
return $data[$joinedKey];
}
// Example
$data = [
'volvo' => 'nice whip',
'drivers_mike' => 'decent'
];
var_dump(getDbItem(['drivers', 'mike'])); // string(6) "decent"
Alt 2
Another way is to not change number of levels in $data, but simply traverse it using the keys passed in:
$tgt = $data;
foreach($keys as $key) {
if (array_key_exists($key, $tgt)) {
$tgt = $tgt[$key];
}
else {
// Non existing key. Handle properly.
}
}
// Example
$keys = ['drivers', 'mike'];
// run the above code
var_dump($tgt); // string(6) "decent"

PHP form array notation, reversed?

So, I have this form that is rather complicated, and the form fields are named to comply with PHP array notation, as such:
<input name='service[123][info]' value=''>
And this of course works as intended and I get the array in $_POST, which I sanitize and then keep in $in. But now I want to reverse-engineer this, when I am iterating over the form again, I have this in $in:
array(
123 => array(
"info" => "foo"
)
)
And when I come to any given form field, I know that the field name is "service[123][info]" but how do I find "foo" in the sent array? Basically, I want to set the value="" parameter in the input when I have data for this field, and the data is kept in the $in array but the only reference to the data I have is the string "service[123][info]". Should I use regexp to parse that string? Sounds inflexible.
Basically, I would like something like:
$name = "service[123][info]";
$value = form_array_lookup($name, $in);
That sets $value to the correct value from $in as referenced by $name. I hope I am making myself clear. Thanks for any comment.
This is a very case-specific (and therefore, not very desirable) example, but the general idea is to use only one delimiter between items, explode the string, and then loop through the result, checking if each item index exists.
function parse_array_path( $string,array $subject ){
// remove ending brackets
$string = str_replace( "]","",$string );
// "[" is now the sole delimiter
$part = explode( "[",$string );
// loop and check for each index in subject array
$i = reset( $part );
do{
if( ! isset( $subject[$i] ) ){
return null;
}
$subject = $subject[$i];
}
while( $i = next( $part ) );
return $subject;
}
example usage:
<?php
$a = array(
"service"=>array(
123=>array(
"info"=>"hello, world"
)
)
);
$s = "service[123][info]";
print parse_array_path( $s,$a ); // "hello, world"
Use a 'foreach' to loop through the array and you can reassign the keys or values in any order you wish.
foreach ($in AS $in_key => $in_val) {
foreach ($in_val AS $in_val_key => $in_val_val) {
// ASSIGN VALUES TO A NEW ARRAY IF YOU WISH
// YOU NOW HAVE $in_key, $in_val_key, $in_val_val
// THAT YOU CAN WORK WITH AND ASSIGN
$array[$in_val_val] = $in_val_key; // OR WHATEVER
}
}

Get all the current Session Variables that being with a certain string

All,
I've created a whole bunch of session variables based on an id. So for example I can have the following variables:
$_SESSION['test_variable_1'];
$_SESSION['test_variable_2'];
$_SESSION['test_variable_3'];
I'd like to read all of these with PHP and basically return a JSON array of the id at the end of the variable. So I'd like 1, 2 and 3 returned in this example. The session variables I wnat to look at will always start with test_variable_ and followed by the ID I want to obtain. What is the best way to do this?
Thanks!
While the better idea would've been to just create a sub-array within the session superglobal, so that you could use $_SESSION['test_variable'][1] and the like, you can use something like preg_grep to scan for these keys:
$keys = array_keys($_SESSION);
$matches = preg_grep('/^test_variable_\d+$/', $keys);
foreach($matches as $key) {
$digit = substr($key, 13); // extract the digit.
echo $_SESSION[$key];
}
Something like this?
$ids = array();
foreach($_SESSION as $var => $value) {
if (strpos($var, 'test_variable_') === 0) {
$ids[] = str_replace('test_variable_', '', $var);
}
}

Whats the best way to manage QUERY_STRING in php?

One of my sites has some very complicated sorting functions, on top of a pagination, and the various variables add up to a pretty complex URL, which is a huge pain to manage. Is there a way to manage the QUERY_STRING efficiently?
By this I mean... if the url is index.php?catid=3&sort=date&year=2009&page=2 and I wish to have the user jump to page 3, or change the sort method..... how would I preserve the remaining vars without checking for each individual var with an if/else condition and echo them out in the link that would link to page 3 or alternate sort method.
To handle actual query strings (string format), you can use parse_str(). When you want to build a query string, use http_build_query().
There's quite a few examples on those documentation pages.
If for some reason you can't use http_build_query, check out my question about the fastest way to implode an associative array.
<?php
$QueryString = 'catid=3&sort=date&year=2009&page=2'; // sample querystring
parse_str($QueryString, $HttpQuery);
print_r($HttpQuery); // will be an associative array
$HttpQuery['page'] = 3; // change any values
$NewQueryString = http_build_query($HttpQuery); // rebuild the querystring
PHP supplies you a global $_GET which holds your query string all nicely parsed into elements
$_GET['catid'] will be '3'
$_GET['sort'] will be 'date'
$_GET['year'] will be '2009'
etc
You could unset the ones you don't want to pass along and then move to new url via something like:
$get = array_intersect_key($_GET, array_keys($_GET));
unset($get['sort']);
$url = 'target.php?' . http_build_query($get);
If you mean that you would like that the link to page 3 would be only
index.php?page=3
or link to changing the sort method would be only
index.php?sort=date
You would have to store the other variables in session variables (or cookies, if you want them to persist longer).
Something like this:
<?php
session_start();
foreach($_GET as $var => $val) {
// filter any malicious stuff and invalid variables out of $var and $val here
// like
if (in_array($var, $array_of_valid_variable_names)) {
if ($var == 'page') $val = intval($val);
$_SESSION[$var] = $val;
}
}
// do stuff based on the values stored in $_SESSION
echo 'Next page';
?>
Although most of the solutions provided here will work, I think the most simple way to do this will be
// parse query string into an array
$queryData = array();
parse_str($_SERVER['QUERY_STRING'], $queryData);
/*
* ... manipulate array $queryData
*/
// rebuild query string
$queryString = http_build_query($queryData, null, '&'); // or use & if you don't need XHTML compliance
That's it. Please see documentation on http_build_query() and parse_str() (that's one of those functions whose name was completey messed up - nobody would expect the function to do what it does just by looking at the name).
I have had the exact same problem with a general "build me a sortable, pageable Table" class. This is why someone invented subprocedures called "functions" in php.
You have to create a function that handles exactly the link-building process. like so:
/**
* $set: associative array of changing vars
* $unset : normal array of vars to delete
**/
function get_link($set,$unset=array())
{
$set+=$_GET;
if ($unset && is_array($unset))
foreach($unset as $idx)
unset($set[$idx]);
if ($set)
foreach($set as $name=>$value)
$pairs[]=$name.'='.urlencode($value);
return $_SERVER['SCRIPT_NAME']."?".join('&',$pairs);
}
UNTESTED CODE! Use your brains
or you could use $_SESSION-vars for pageing and sorting (an' stuff) and have links only change those (which is what i mostly do nowadays)
Add the variable name and value at the end of the query string (changing page to 3):
index.php?catid=3&sort=date&year=2009&page=2&x=page&y=3
Then, after extracting $_GET, use a simple function to check if x and y are set.
If they are, set the variable whose name is contained in x to the value y.
You can use the same link everywhere with a simple addition at the end, and the amount of programming is minimal.
Don't manage the string directly but manage an array ( key => value ) representation of it and only translate it back to string when needed.
One possible way of doing it:
function explodeQueryString( $queryString )
{
$parts = array();
if ( strlen( $queryString ) > 1 && substr( $queryString, 0, 1 ) == '?' )
{
$q = explode( '&', substr( $queryString, 1 ) );
foreach( $q as $part )
{
list( $key, $value ) = explode( '=', $part );
$parts[ urldecode( $key ) ] = urldecode( $value );
}
}
return $parts;
}
function implodeQueryString( array $arguments )
{
$parts = array();
foreach( $arguments as $key => $value )
{
$parts[ ] = sprintf( '%s=%s', urlencode( $key ), urlencode( $value ) );
}
return sprintf( '?%s', implode( '&', $parts ) );
}
// $query = $_GET;
$query = '?something=1&somethingelse=2&page=1&yetsomethingelse=3';
$q = explodeQueryString( $query );
print_r( $q );
$q[ 'page' ] += 1;
$query = implodeQueryString( $q );
echo $query;

Categories