Array lookup value from dynamic string - php

I'm trying to build a dynamic associative array value lookup function (within a class):
class Family
{
public static $members = array(
'one' => array(
'child' => 0,
'children' => 5
),
'two' => array(
'child' => 2,
'children' => null
)
);
public static function resolveMemberValue()
{
$chain = func_get_args();
$lookup = 'members' . '[\'' . implode('\'][\'', $chain) . '\']';
var_dump( $lookup );
return static::$$lookup;
}
}
Family::resolveMemberValue('one', 'child');
But this results in:
string(23) "members['one']['child']"
Fatal error: Access to undeclared static property: Family::$members['one']['child'] in /family.php on line 23
PHP Fatal error: Access to undeclared static property: Family::$members['one']['child'] in /family.php on line 23
Though, copying the dumped value, and pasting inside the script + appending dollar sign, it returns what's expected:
var_dump( Family::$members['one']['child'] );
int(0)
Reason why I need this is, because it will be used with multiple variables, and called from generator functions.
What is wrong with the snippet?

Variable variables only substitutes in a string for the name of the variable. It can't evaluate the content of that string (in this case the string members['one']['child'])
Your code is looking for a static property literally with the named $members['one']['child'] not an element of the static array $members.
Try this instead:
$member = static::$members[$chain[0]];
return $member[$chain[1]];
Also, I'd recommend not using func_get_args(), but explicitly naming your parameters in the method declaration. Some features of PHP a best left behind....

Oh, had to just tinker a little - managed to make a helper function.
The function replaces the implode() and the explicit key definition.
function array_lookup()
{
$chain = func_get_args();
$array = array_shift($chain);
foreach ($chain as $key) $array = $array[$key];
return $array;
}
$test = array(
'one' => array(
'child' => 0,
'children' => 5
),
'two' => array(
'child' => 2,
'children' => null
)
);
var_dump($test, 'one', 'child'); // int(0)
I have left out any kind of error checking for this example, but it does what I was looking for.
And yes, for my example, it nails it.

Related

Does this usage of array_filter() result in undefined behavior?

So the manual for array_filter() says;
If the array is changed from the callback function (e.g. element added, deleted or unset) the behavior of this function is undefined.
and I stumbled upon the following code snippet in my codebase (example variable definitions added for reproducibility):
$enabledGroups = array(1, 3, 5);
$subgroups = array(
array('nid' => 1),
array('nid' => 2),
array(
'nid' => 3,
'children' => array(
array ('nid' => 4),
array ('nid' => 5)
)
)
);
$filterFunc = function (&$v) use ($enabledGroups, &$filterFunc) {
if($v['children']) {
$v['children'] = array_filter($v['children'], $filterFunc);
}
return in_array($v['nid'], $enabledGroups);
};
$subgroups = array_filter($subgroups, $filterFunc);
From the wording of the warning in the manual, I'm not totally sure, if this usage constitutes undefined behavior.
So my question is: Does a recursive usage of array_filter() like shown above, result in undefined behavior, or does it not?

PHP Function Array Default Values? [duplicate]

This question already has answers here:
PHP function with variable as default value for a parameter
(7 answers)
Closed 1 year ago.
I have a PHP function with a array within. I put the array inside so the parameters would be option and these would be the defaults. Example
/**
* Creates New API Key
*
* #return Response
*/
public function create(
$data = [
"user-id" => Auth::id(),
"level" => '1',
"ignore-limits" => '0',
]){
...
}
However I keep getting the error
syntax error, unexpected '(', expecting ']'
So I assume that you cant pass a array like this when constructing a function. What would be a better way to do this or a fix?
You can only use scalar types for the default values of function arguments.
You can also read this in the manual: http://php.net/manual/en/functions.arguments.php#functions.arguments.default
And a quote from there:
The default value must be a constant expression, not (for example) a variable, a class member or a function call.
EDIT:
But if you still need this value as default value in the array you could do something like this:
Just use a placeholder which you can replace with str_replace() if the default array is used. This also has the advantage if you need the return value of the function in the default array multiple times you just need to use the same placeholder and both are going to be replaced.
public function create(
$data = [
"user-id" => "::PLACEHOLDER1::",
//^^^^^^^^^^^^^^^^ See here just use a placeholder
"level" => '1',
"ignore-limits" => '0',
]){
$data = str_replace("::PLACEHOLDER1::", Auth::id(), $data);
//^^^^^^^^^^^ If you didn't passed an argument and the default array with the placeholder is used it get's replaced
//$data = str_replace("::PLACEHOLDER2::", Auth::id(), $data); <- AS many placeholder as you need; Just make sure they are unique
//...
}
Another idea you could do is set a default array which you can check and then assign the real array like this:
public function create($data = []){
if(count($data) == 0) {
$data = [
"user-id" => Auth::id(),
"level" => '1',
"ignore-limits" => '0',
];
}
//...
}
The issue here is the:
Auth::id()
This calls a method which is illegal to do in this context
I would solve it like this:
public function create(
$data = [
"user-id" => -1,
"level" => '1',
"ignore-limits" => '0',
]){
if($data['user-id'] === -1) {
$data['user-id'] = Auth::id()
}
...
}
More universal solution with array_mearge. This way you can rewrite any parameter without having to check each of them individually.
function create($somthing, $settings = [])
{
$default = [
'date' => date("Y-m-d H:i:s"),
'bold' => false,
'italic' => false,
];
$settings = array_merge($default, $settings);
...
}

Unable to recreate object from var_exported data (in PHP)

A script running on on our live server sends out data from the following line:
$log_output .= '<br>'.__LINE__.'<br>recordings_data='.var_export($recordings_data,TRUE);
Which looks like this:
recordings_data=stdClass::__set_state(array( 'RecordingLongResponse'
=> array ( 0 => stdClass::__set_state(...), 1 => stdClass::__set_state(), 2 => stdClass::__set_state(), 3 =>
stdClass::__set_state(array( 'roomStartDate' => '1321977120000',
'roomEndDate' => '1321977120000', 'recordingURL' => 'serverURL1',
'secureSignOn' => false, 'recordingId' => '1287268130290',
'creationDate' => '1321977120000', 'recordingSize' => '6765975',
'roomName' => 'Stakeholder Analysis', 'sessionId' => '1287268130229',
)), ...), ))
I'm not sure how to 'recreate' the object. I tried unserializing it:
$recording_data_ser= file_get_contents('elm-ser-data.txt'); // where I've saved everything after the '='
$recording_data = unserialize($recording_data_ser);
serialize() and unserialize() are the generally accepted methods for dumping/loading PHP objects. You can also do it with json_encode() and json_decode(). Is there a reason you want to use var_export()?
Edit: you can only unserialize() the result of serialize() - var_dump() is in a completely different format and isn't designed for importing unless you use eval().
For example:
$arr = array('foo' => 'bar');
var_export($arr);
#=> array (
#=> 'foo' => 'bar',
#=> )
echo serialize($arr);
#=> a:1:{s:3:"foo";s:3:"bar";}
echo json_encode($arr);
#=> {"foo":"bar"}
Once you have the serialized data (via serialize() or json_encode()), you can use the opposite method (unserialize() or json_decode(), respectively) to recreate the object again.
var_export() is intended to dump a data structure to a file. You then have to eval() or include() or require() that file. the serialize() format is completely different from var_export.
var_export produces valid PHP code to define a data structure, as if you'd written out the array/object definition yourself. serialize/unserialize write out in a completely different format, and they're not interchangeable.
If you call var_export() on an instance of stdClass, it attempts to export it using ::__set_state(), which, for some reason, is not implemented in stdClass.
However, casting an associative array to an object usually produces the same effect (at least, it does in my case). So I wrote an improved_var_export() function to convert instances of stdClass to (object) array () calls. If you choose to export objects of any other class, I'd advise you to implement ::__set_state() in those classes.
<?php
/**
* An implementation of var_export() that is compatible with instances
* of stdClass.
* #param mixed $variable The variable you want to export
* #param bool $return If used and set to true, improved_var_export()
* will return the variable representation instead of outputting it.
* #return mixed|null Returns the variable representation when the
* return parameter is used and evaluates to TRUE. Otherwise, this
* function will return NULL.
*/
function improved_var_export ($variable, $return = false) {
if ($variable instanceof stdClass) {
$result = '(object) '.improved_var_export(get_object_vars($variable), true);
} else if (is_array($variable)) {
$array = array ();
foreach ($variable as $key => $value) {
$array[] = var_export($key, true).' => '.improved_var_export($value, true);
}
$result = 'array ('.implode(', ', $array).')';
} else {
$result = var_export($variable, true);
}
if (!$return) {
print $result;
return null;
} else {
return $result;
}
}
// Example usage:
$obj = new stdClass;
$obj->test = 'abc';
$obj->other = 6.2;
$obj->arr = array (1, 2, 3);
improved_var_export((object) array (
'prop1' => true,
'prop2' => $obj,
'assocArray' => array (
'apple' => 'good',
'orange' => 'great'
)
));
/* Output:
(object) array ('prop1' => true, 'prop2' => (object) array ('test' => 'abc', 'other' => 6.2, 'arr' => array (0 => 1, 1 => 2, 2 => 3)), 'assocArray' => array ('apple' => 'good', 'orange' => 'great'))
*/
// Example implementation in context of OP
$export = improved_var_export($data, true);
file_put_contents(dirname(__FILE__).'/data.php', '<'.'?php return '.$export.'; ?'.'>');
// "Unserialization" (evaluation)
$import = include dirname(__FILE__).'/data.php';
?>

Finding a value inside a nested array having only a string with the keys

I have one array that contains some settings that looks like basically like this:
$defaults = array(
'variable' => 'value',
'thearray' => array(
'foo' => 'bar'
'myvar' => array('morevars' => 'morevalues');
);
);
On another file, i get a string with the first level key and it's childs to check if there is a value attached to it. Using the array above, i'd get something like this:
$option = "thearray['myvar']['morevars']";
I need to keep this string with a similar format to the above because I also need to pass it to another function that saves to a database and having it in an array's format comes in handy.
My question is, having the array and the string above, how can i check for both, existance and value of the given key inside the array? array_key_exists doesn't seem to work below the first level.
You could use a simple function to parse your key-string and examine the array like:
function array_deep_exists($array, $key)
{
$keys = preg_split("/'\\]|\\['/", $key, NULL, PREG_SPLIT_NO_EMPTY);
foreach ($keys as $key)
{
if ( ! array_key_exists($key, $array))
{
return false;
}
$array = $array[$key];
}
return true;
}
// Example usage
$defaults = array(
'variable' => 'value',
'thearray' => array(
'foo' => 'bar',
'myvar' => array('morevars' => 'morevalues')
)
);
$option = "thearray['myvar']['morevars']";
$exists = array_deep_exists($defaults, $option);
var_dump($exists); // bool(true)
Finally, to get the value (if it exists) return $array where the above returns true.
Note that if your array might contain false, then when returning the value you'll have to be careful to differentiate no-matching-value from a successful false value.
You need to eval this code, and use isset function in an eval string, and don't forget to add $ character in right place before code eval
example:
eval("echo isset(\$defaults['varname']['varname2']);")
this will echo 0 or 1 (false or true) You can do anything in eval, like a php source

PHP Convert variable names to lowercase?

I have an api listener script which takes in get parameters. But I seem to be having issues when users tend to pass mixed case variable names on the parameters.
For example:
http://mylistenerurl.com?paramName1=Hello&paramname2=World
I need my listener to be flixible in such a way that the variable names will be interpreted case-insensitively or rather still all in lower case like after I process the query string on some function, they are all returned as lower-cased variables:
extract(someFunction($_GET));
process($paramname1, $paramname2);
Can anybody shed some light on this?
*much appreciated. thanks!
This should do the trick:
$array_of_lower_case_strings = array_map( "strtolower", array( "This Will Be ALL lowercase.", ... ) );
So in your case:
$get_with_lowercase_keys = array_combine(
array_map( "strtolower", array_keys( $_GET ) ),
array_values( $_GET )
);
One thing I'll mention is you should be VERY careful with extract as it could be exploited to allow unexpected variables to be injected into your PHP.
Apply to your global variables ($_GET, $_POST) when necessary:
e.g. setLowerCaseVars($_GET); in your case
function setLowerCaseVars(&$global_var) {
foreach ($global_var as $key => &$value) {
if (!isset($global_var[strtolower($key)])) {
$global_var[strtolower($key)] = $value;
}
}
}
Edit: Note that I prefer this to using array_combine because it will not overwrite cases where the lower-case variable is already set.
PHP has had a native function (array_change_key_case()) for this task since version 4.2 to change the case of first level keys. To be perfectly explicit -- this function can be used to convert first level key to uppercase or lower case BUT this is not a recursive function so deeper keys will not be effected.
Code: (Demo)
parse_str('paramName1=Hello&paramname2=World&fOo[bAR][BanG]=boom', $_GET);
var_export($_GET);
echo "\n---\n";
$lower = array_change_key_case($_GET);
var_export($lower);
Output:
array (
'paramName1' => 'Hello',
'paramname2' => 'World',
'fOo' =>
array (
'bAR' =>
array (
'BanG' => 'boom',
),
),
)
---
array (
'paramname1' => 'Hello', # N changed to n
'paramname2' => 'World',
'foo' => # O changed to o
array (
'bAR' => # AR not changed because not a first level key
array (
'BanG' => 'boom', # B and G not changed because not a first level key
),
),
)

Categories