I have the following code:
function filterUsers(array $setOfAllUsers) {
if (empty($setOfAllUsers)) {
return array(array(), array());
}
$activeUsers = array();
$inactiveUsers = array();
foreach($setOfAllUsers as $userRow) {
$var = ($userRow['IsActive'] ? '' : 'in') . 'activeUsers';
$$var[$userRow['CID']]['Label'] = $userRow['UserLabel'];
// Error happens here ---^
$$var[$userRow['CID']]['UserList'][$userRow['UID']] = array(
'FirstName' => $userRow['FName'],
'LastName' => $userRow['LName'],
... More data
);
}
return array($activeUsers, $inactiveUsers);
}
I get the following error: Warning: Illegal string offset 'Label' in ...
How can I fix this? I tried defining Label part first like this: $$var[$userRow['CID']] = array(); $$var[$userRow['CID']]['Label'] = ''; but did not work.
To make things clear what I am trying to achieve is this:
if ($userRow['IsActive']) {
$activeUsers[$userRow['CID']]['Label'] = $userRow['UserLabel'];
$activeUsers[$userRow['CID']]['UserList'][$userRow['UID']] = array(
'FirstName' => $userRow['FName'],
'LastName' => $userRow['LName'],
... More data
);
} else {
$inactiveUsers[$userRow['CID']]['Label'] = $userRow['UserLabel'];
$inactiveUsers[$userRow['CID']]['UserList'][$userRow['UID']] = array(
'FirstName' => $userRow['FName'],
'LastName' => $userRow['LName'],
... More data
);
}
Instead of repeating above in if/else I wanted to achieve it using $$
Try using ${$var} instead of $$var.
EDIT
From PHP Manual (http://php.net/manual/en/language.variables.variable.php):
In order to use variable variables with arrays, you have to resolve an
ambiguity problem. That is, if you write $$a[1] then the parser needs
to know if you meant to use $a[1] as a variable, or if you wanted $$a
as the variable and then the [1] index from that variable. The syntax
for resolving this ambiguity is: ${$a[1]} for the first case and
${$a}[1] for the second.
function filterUsers(array $setOfAllUsers) {
if (empty($setOfAllUsers)) {
return array(array(), array());
}
$users = array(
'inactiveUsers' => array(),
'activeUsers' => array()
);
foreach($setOfAllUsers as $userRow) {
$status = ($userRow['IsActive'] ? '' : 'in') . 'activeUsers';
$users[$status][$userRow['CID']] = array();
$users[$status][$userRow['CID']]['Label'] = $userRow['UserLabel'];
$users[$status][$userRow['CID']]['UserList'] = array(
$userRow['UID'] => array(
'FirstName' => $userRow['FName'],
'LastName' => $userRow['LName'],
)
);
}
return $users;
}
Try the following:
$myUsers = $$var;
$myUsers[...][...] = ...;
The problem with using $$var[...][...] is that first $var[...][...] is evaluated and then it tries to find the variable named $$var[...][...].
Don't use var-vars like this. You run into a PHP syntax glitch. And even if there wasn't a syntax glitch, you shouldn't be using var-vars in the first place. They make for near-impossible-to-debug code.
$x = 'foo';
$foo = array();
$$x[0] = 1;
var_dump($x); // string(3) "foo"
var_dump($foo); // array(0) { }
$$x[1][2] = 3;
PHP Notice: Uninitialized string offset: 2 in php shell code on line 1
Note how the array didn't get modified by the $$x[0] assignment, and how the 2+ dimensional assigment causes "undefined string offset". Your var-var isn't being treated as an array - it's being treated as a STRING, and failing, because it's a syntax glitch. You can treat strings as arrays, but not using var vars:
$bar = 'baz';
$bar[1] = 'q';
echo $bar; // output: bqz
There appears to be NO way to use a var-var as an array, especially a multi-dimensional array.
Related
I have the following array:
$people['men'] = [
'first_name' => 'John',
'last_name' => 'Doe'
];
And I have the following flat array:
$name = ['men', 'first_name'];
Now I want to create a function that "reads" the flat array and gets the value from the multidimensional array, based on the sequence of the elements of the flat array.
function read($multidimensionalArray,$flatArray){
// do stuff here
}
echo read($people,$name); // must print 'John'
Is this even possible to achieve? And which way is the way to go with it? I'm really breaking my head over this. I have no clue at all how to start.
Thanks in advance.
This should to the trick:
<?php
$people['men'] = [
'first_name' => 'John',
'last_name' => 'Doe'
];
$name = ['men', 'first_name'];
echo read($people,$name);
function read($multidimensionalArray,$flatArray){
$cur = $multidimensionalArray;
foreach($flatArray as $key)
{
$cur = $cur[$key];
}
return $cur;
}
Link: https://3v4l.org/96EnQ
Be sure to put some error checking in there (isset and the likes)
You can use a recursive function to do this.
function read(&$array, $path) {
// return null if one of the keys in the path is not present
if (!isset($array[$key = array_shift($path)])) return null;
// call recursively until you reach the end of the path, then return the value
return $path ? read($array[$key], $path) : $array[$key];
}
echo read($people, $name);
You could also use array_reduce
$val = array_reduce($name, function($carry, $item) {
return $carry[$item];
}, $people);
looks like you just want:
echo $multidimensionalArray[$flatArray[0]][$flatArray[1]];
I have an array like this:
$cmds = array(
'info' => call_user_func('_info', $cmd1),
'e1' => 'something'
);
The func itself:
function _info($cmd1){
if(strlen($cmd1) < 2){
die('Invalid Username');
}else{
echo 'cmd1 > 2';}
}
The problem is it being executed before i call it giving undefined variable error because $cmd1 not exist yet, it will be exist when i call it in a foreach function.
Been searching for a solution for hours but didn't find.
I tried setting the array as this 'info' => _info($cmd1) and 'info' => '_info' and putted $cmd1 directly in the func like this
function _info(){
if(strlen($cmd1) < 2){die('invalid');
}
But i still get the same error as it being called before i want to be.
You can send parameters to be executed later
$cmds = array(
'info' => [ '_info', '$cmd1' ], // single quotes
'e1' => 'something'
);
In the meantime your cmd1 is defined
$cmd1 = "hello";
foreach ($cmds as $key => $cmdset) {
if (!is_array($cmdset)) {
// do something with e1 => something
} else {
$function = array_shift($cmdset);
foreach ($cmdset as &$param) {
// Only variables are treated below, values are passed through as is
if (stripos($param, '$') === 0) {
// Remove $
$cmd1 = substr($param, 1);
// Set param to the value of cmd1
$param = $$param; // variable varaible $$
}
}
call_user_func_array($function, $cmdset);
}
}
While this works it is really bad practice to do like this. Depending on a variable function that is not clear if it is set or not. The code is hard to understand.
You should think about another solution where you can ensure that $cmd1 is set or can be retrieved and if it is not have some way to deal with that as well.
Is there a shortcut method to assigning $_GET['values'] to variables?
I currently do like others do:
if(isset($_GET['type'],$_GET['case'])
$type = $_GET['type'];
$case = $_GET['case'];
Is there a cleaner method to do this instead of doing like below separately.
$type = $_GET['type'];
$case = $_GET['case'];
http://docs.php.net/extract
I think you're looking for extract function.
extract($_GET); //now, all of the functions are in current symbol table
Well, with array map you can you get the case not just once, but all at once, and you can also check for isset() and empty() at the same time too.
Suppose, you have this URL: read.php?id=1&name=foo&job=student&country=Brazil
Your problem is fetching the $_GET type, and you may need to check if is it empty/isset or not right?
Well, first you create a function to iterate through it.
function checkGet($val){
return (isset($val) && !empty($val)) ? $val : null;
}
Then, you callback that function with array_map()
$check = array_map('checkGet', $_GET);
And that is it!
If you were to do var_dump($check); now, you would get get all the types, and values:
array (size=4)
'id' => string '1' (length=1)
'name' => string 'foo' (length=3)
'job' => string 'student' (length=7)
'country' => string 'Brazil' (length=6)
Meaning, after this, instad of doing:
if(isset($_GET['something']) && !empty($_GET['something']))
$var = $_GET['something'];
echo $var;
Just do:
echo $check['something']
The only one-line code I can think of, to make sure that you still do the necessary checks, is
$type = (isset($_GET['type'])) ? $_GET['type'] : 'a default value or false';
Reading comments, I understand you may want to do this:
foreach($_GET as $key=>$value) {
$$key = $value;
}
I would suggest though, to always initialize the variables you need only. The above code will result in getting unknown variables, which may actually give the user a way to manipulate your script.
Example:
index.php?ExpectedVar=1&UserDefinedVar=2
will generate the following variables in your code:
$ExpectedVar // 1 <- you wanted this one
$UserDefinedVar // 2 <- but what about this?
What if you had this script called by some other script?
Then even if you have this code at the top of your file, you may have some variables overwritten from a user defined $_GET!
Disaster case Scenario:
script1.php
<?php
$tableToDelete = "old_products";
include("script2.php");
?>
script2.php
<?php
foreach($_GET as $key=>$value) {
$$key = $value;
}
// user added &tableToDelete=users
// DROP TABLE $table
// will gloriously delete users
?>
Instead by writing a few lines with the original code I posted, you can get the variables you need at the start of your php script and use them with a clear mind.
Try like
foreach($_GET as $key=>$value) {
$get_arr[$key] = $_GET[$key];
}
print_r($get_arr);
I would do it that way, this way you make sure that it will only return TRUE or FALSE
if (!isset($_GET['type']) || empty($_GET['type'])) {
// Display error
} else {
$type = $_GET['type'];
$case = $_GET['case'];
}
Or you can do it that way as well
$type = (isset($_GET['type'])===false)?'':trim($_GET['type']);
$case = (isset($_GET['case'])===false)?'':trim($_GET['case']);
$_GET is table, so you can easy use foreach function
For example
foreach ($_GET as $key => $value) {
... = $value;
}
If you would like to create variables with $key names use variable variables
PHP Manual Variable Variables
You can do it through extract()
extract($_GET, EXTR_PREFIX_ALL, 'g');
so that
$_GET['val'] becomes $g_val
Note the third parameter: g it prepends g_ to the keys.
This (untested) class should help you:
class MyGet {
public static $myValues = array();
public static function setMyValues($keywords, $where) {
MyGet::$myValues = array();
for ($index = 0; $index < count($keywords); $index++) {
if ((!(isset($where[$keywords[$index]]))) || (empty($where[$keywords[$index]]))) {
MyGet::$myValues = array();
return false;
}
MyGet::$myValues[$keywords[$index]] = $where[$keywords[$index]];
}
}
}
You can use it like this:
if (MyGet::setMyValues(array(0 => "type", 1 => "case"), $_GET)) {
//the values are initialized
} else {
//the values are not initialized
}
I want to pass one argument to a function, rather than multiple arguments, that tend to grow unexpectedly. So I figure an array will get the job done. Here's what I've drafted so far...
<?php
function fun_stuff($var){
// I want to parse the array in the function, and use
}
$my = array();
$my['recordID'] = 5;
$my['name'] = 'John Smith';
$my['email'] = 'john#someemail.com';
echo fun_stuff($my);
?>
I haven't quite grasped the concept of parsing an array. And this is a good way for me to learn. I generally pass the same variables, but on occasion a record does not have an email address, so I do need to make a condition for missing keys.
Am I doing this right so far? Can I pass an array as an argument to a function?
And if so, how do I parse and search for existing keys?
Hopefully this isn't too far off topic...but you sounded like you were just trying to avoid multiple parameters when some can be NULL. So, I would recommend that you use an object instead of an array for clarity...that way, there is no confusion as to what properties should exist. If you're using PHP 5, you can also strongly type the parameter so nothing else can get in. So:
class Record {
public $Id;
public $Name;
public $Email
}
function fun_stuff( Record $record ) {
// you will now have better intellisense if you use an IDE
// and other develoers will be able to see intended parameters
// clearly, while an array would require them to know what's
// intended to be there.
if( !empty($record->Email) ) {
// do whatever.
}
}
Yes you are on the right track. The approach I take is put required paramters as the first parameters and all optional parameters in the last argument which is an array.
For example:
function fun_stuff($required1, $required2, $var = array()) {
// parse optional arguments
$recordId = (key_exists('recordID', $var) ? $var['recordId'] : 'default value');
$name = (key_exists('name', $var) ? $var['name'] : 'default value');
$email = (key_exists('email', $var) ? $var['email'] : 'default value');
}
Then you can call your function like so:
fun_stuff('val 1', 'val 2', array(
'recordId' => 1,
'name' => 'John',
'email' => 'john#stackoverflow.com'
));
This is a bad design practice, but that's not the question here. You can "parse" array's like so...
if( array_key_exists( 'email', $var ))
{
// use email field
}
If you need to, you can loop through all elements like so...
foreach( $var as $key => $value )
{
echo '$var[\''.$key.'\'] = '.$value;
}
I'm not recommend you to use array for this.
You can define optional arguments with default values:
//$name and $email are optional here
function fun($record_id, $name='', $email='')
{
if (empty($name)) print '$name is empty';
}
//Usage:
fun(5, 'Robert');
fun(5);
fun(5, 'Robert', 'robert#gmail');
fun(3,'','robert#gmail');
If you will use array, IDE will not be able to show autocomplete suggestions, it means more typos, and you have to remember all keys of this array forever or look at code of the function each time.
I'm not really sure what you want to achieve, but I suspect something like this:
$aPersons = array();
$aPersons[] = array('name' => 'name1', 'age' => 1);
$aPersons[] = array('name' => 'name2', 'age' => 2);
array_map('parsePerson', $aPersons);
function parsePerson($aPerson) {
echo $aPerson['name'];
echo $aPerson['age'];
}
The problem with your current array is that it only has one dimension.
You can simple do echo $my['name'];. There are easier ways to parse arrays though.
foreach($aPersons as $aPerson) {
echo $aPerson['name'];
echo $aPerson['age'];
}
$iLength = sizeof($aPersons);
for($i = 0; $i <= $iLength; $i++) {
echo $aPersons[$i]['name'];
echo $aPersons[$i]['age'];
}
To parse and view, there is the signficant print_r function which gives out the array details.
When calling a function you need the return syntax at the end that will parse out anything you call in the return.
You obviously can pass array to the function. Inside it read the variable as you were assigning values before it. If you assign:
$my['key'] = 'value';
In you function use:
echo $var['key'];
Why you don't use a foreach to walk in array?
function fun_stuff($var){
foreach($var as $key => $item){
echo '[', $key, "] => ", $item, "\n";
}
}
$my = array();
$my['recordID'] = 5;
$my['name'] = 'John Smith';
$my['email'] = 'john#someemail.com';
fun_stuff($my);
Yes, this is correct (though your question is a bit broad). You're already referencing the array values via indexes when you set up the $my variable. You can do the same thing within your function (with the $var variable).
I recommend taking a look at all of PHP's built-in array functions: http://php.net/manual/en/ref.array.php
Try this:
function fun_stuff($var){
// I want to parse the array in the function, and use
$fun_string = "";
if( is_array( $var ) {
if( array_key_exists( "name", $var ) )
$fun_string .= "For " . $var["name"];
else $fun_string .= "A nameless one ";
if( array_key_exists( "email", $var ) )
$fun_string .= " (email: " . $var["email"] . ")";
else $fun_string .= " without a known e-mail address";
if( array_key_exists( "recordID", $var ) )
$fun_string .= " has record ID of " . $var["recordID"];
else $fun_string .= " has no record ID set";
$fun_string .= "\n";
}
return $fun_string;
}
Yes You can! Just pass the array and inside the function just use a foreach loop to parse it!
function myFunction($array)
{
foreach($array as $value)
{
echo $value;
}
}
or If you want to have full control over the pair key/value:
function myFunction($array)
{
foreach($array as $key=>$value)
{
echo "key:".$array[$key]."value:".$values;
}
}
I can access anywhere inside the multi-dimensional an array via reference method. And I can change the its value. For example:
$conf = array(
'type' => 'mysql',
'conf' => array(
'name' => 'mydatabase',
'user' => 'root',
'pass' => '12345',
'host' => array(
'127.0.0.1',
'88.67.45.123',
'129.34.123.55'
),
'port' => '3306'
)
);
$value = & $this->getFromArray('type.conf.host');
$value = '-- changed ---';
// result
$conf = array(
'type' => 'mysql',
'conf' => array(
'name' => 'mydatabase',
'user' => 'root',
'pass' => '12345',
'host' => '-- changed ---'
'port' => '3306'
)
);
BUT, I can't destroy the that section:
// normally success
unset($conf['type']['conf']['host']);
// fail via reference
$value = & $this->getFromArray('type.conf.host');
unset($value);
Is there a solution?
Ok, better answer I think. In order to unset , you should get a reference to the container array, then unset the element within the array;
i.e.
$value = & $this->getFromArray('type.conf');
unset $value['host'];
References are not like hard-links. If you unset a reference, this will not unset the origin value.
<?php
$a = 5;
xdebug_debug_zval('a'); // a: (refcount=1, is_ref=0), int 5
$b = &$a;
xdebug_debug_zval('a'); // a: (refcount=2, is_ref=1), int 5
xdebug_debug_zval('b'); // b: (refcount=2, is_ref=1), int 5
unset($b);
xdebug_debug_zval('a'); // a: (refcount=1, is_ref=0), int 5
Why not write a little Config class which abstracts the data (array)? Since objects are always passed by reference you won't need to handle this at your own.
class Config
{
// ...
}
$config = new Config(array(
'db' => array(
'name' => 'mydatabase',
'user' => 'root',
'pass' => '12345',
)
));
$config->get('db.user');
$config->set('db.user', 'newuser');
$config->unset('db.user');
//...
Here is my function for unsetting nested keys
public function unsetKey(string $dotSeparatedKey)
{
$keys = explode('.', $dotSeparatedKey);
$pointer = &$this->data;
$current = false; // just to make code sniffer happy
// we traverse all but the last key
while (($current = array_shift($keys)) && (count($keys) > 0)) {
// if some key is missing all the subkeys will be already unset
if (!array_key_exists($current, $pointer)) {
// is already unset somewhere along the way
return;
}
// set pointer to new, deeper level
// called for all but last key
$pointer = &$pointer[$current];
}
// handles empty input string
if ($current) {
// we finally unset what we wanted
unset($pointer[$current]);
}
}
Creating some functions for my framework, think they halp you.
1. Function set value in array using reference for navigation
- if reference ending by name/key, this name/key will be equal setting value
- if reference ending by delimiter, last name/key will be array with setting value
function array_reference_set($input_arr=array(),$reference='',$delimiter='->',$set_var=''){
switch ($reference){
case (is_string($reference)):
$reference = array_reverse(explode($delimiter, $reference),true);
break;
case (!is_array($reference)):
return $input_arr;
}
$key = array_pop($reference);
if (count($reference)<1){
if($key!=''){
$input_arr[$key] = $set_var;
}elseif (!is_array($input_arr) && $key==''){
$input_arr = array($set_var);
}elseif ($key==''){
$input_arr[] = $set_var;
}
}else{
if (!is_array($input_arr)){
$input_arr = array($key=>array());
}
if (isset($input_arr[$key])){
$input_arr[$key] = $this->array_reference_set($input_arr[$key],$reference,$delimiter,$set_var);
}else{
$input_arr[$key] = $this->array_reference_set(array(),$reference,$delimiter,$set_var);
}
}
return $input_arr;
}
$arr = array_reference_set(array(),'a->b->c','->','test');
//equal
$arr = array('a'=>array('b'=>array('c'=>'test')));//or
$arr['a']['b']['c'] = 'test';
$arr = array_reference_set(array(),'a->b->c->','->','test');
//equal
$arr = array('a'=>array('b'=>array('c'=>array('test'))));//or
$arr['a']['b']['c'][] = 'test';
2. Function set unset value from array using reference
- if reference ending is delimiter, then will be unset varible with name/key befor delimiter
- one moment of using this function: you need update array by returned result of function (in the end of code example)
function array_reference_unset($input_arr=array(),$reference='',$delimiter='->'){
switch ($reference){
case (is_string($reference)):
$reference = array_reverse(explode($delimiter, $reference),true);
break;
case (!is_array($reference)):
return $input_arr;
}
$key = array_pop($reference);
if (count($reference)<1 && is_string($key)){
if ($key!=''){
unset($input_arr[$key]);
}else{
return false;
}
}else{
if (isset($input_arr[$key])){
$ret = $this->array_reference_unset($input_arr[$key],$reference,$delimiter);
if ($ret!==false){
$input_arr[$key] = $ret;
}else{
unset ($input_arr[$key]);
}
}
}
return $input_arr;
}
$arr = array('a'=>array('b'=>array('c'=>'test')));// test subject
$arr = array_reference_unset($arr,'a->b->c','->');//and
$arr = array_reference_unset($arr,'a->b->c->','->');
//equal
unset($arr['a']['b']['c']);
p.s. sorry for my pure English