Simple question, but tough answer? I have the following anonymous function inside a class method:
$unnest_array = function($nested, $key) {
$unnested = array();
foreach ($nested as $value) {
$unnested[] = (object) $value[$key];
}
return $unnested;
};
In the same class method I have this array, where I save anonymous functions. I.e. I create a new anonymous function using the inline create_function() and I would want to use the already defined anonymous function $unnest_array(). Is it possible?
$this->_funcs = array(
'directors' => array(
'func' => create_function('$directors', 'return $unnest_array($directors, "director");'),
'args' => array('directors')
)
);
At the moment I am getting "Undefined variable: unnest_array". Help?
Why are you using create_function in the first place? Closures replace create_function entirely, leaving it essentially obsolete in all versions of PHP after 5.3. It seems like you're trying to partially apply $unnest_array by fixing the second argument as "director".
Unless I've misunderstood you, you should be able to achieve the same result by using a closure/anonymous function (untested):
$this->_funcs = array(
'directors' => array(
'func' => function($directors) use ($unnest_array)
{
return $unnest_array($directors, "director");
},
'args' => array('directors')
)
);
The use ($unnest_array) clause is necessary to access local variables in the parent scope of the closure.
Related
I have a class within which I want to
Set an array
Loop through the array
Invoke anonymous functions set in #1 as I loop through the array in #2
EDIT: The code below is working when tested out of my application, but in my CodeIgniter 3 Controller, I keep getting an error: Method name must be a string
My simplified code for the purposes of example (CodeIgniter 3):
<?php
class MyClass {
// Setting my array
public function my_arr ($options = array())
{
$arr = array(
'1' => array(
'a' => 'z',
'b' => function($i) {
return 'c' . $i;
},
),
'2' => array(
'a' => 'y',
'b' => function($i) {
return 'd' . $i;
},
),
);
return $arr;
/**
*
* EDIT: Later in my code I found that there was some kind
* of serialization/encoding attempt like:
* json_decode(json_encode($arr));
*/
}
// Doing My Loop
public function do_loop()
{
$my_arr = $this->my_arr();
$i = 0;
foreach($my_arr as $key => $value){
$anonymous_function = $value['b'];
echo $anonymous_function($i) . '<br>'; // Keep getting `Method name must be a string`
$i++;
}
}
}
(new MyClass())->do_loop();
Posting back here in hopes that it helps.
My issue:
Later on in my codebase, I was trying to encode/serialize my method's
output.
After a bit of reading, I found out that json_encode() &
serialize() don't support serialization of closures / anonymous functions.
So, the Method name must be a string error happend because my attempts to encode/serialize - effectively - stripped the anonymous functions out & thus referenced a null function.
How I resolved the issue:
First I started with Opis Closure library to seralize the closure (I didn't end up going with this, but including it because I think it's a pretty cool library)
After the above, I just realized that I should refactor so that I wasn't trying serialize closures at all.
This question already has answers here:
Is there a function to extract a 'column' from an array in PHP?
(15 answers)
Closed 10 months ago.
Let's assume I have the following multidimensional array (retrieved from MySQL or a service):
array(
array(
[id] => xxx,
[name] => blah
),
array(
[id] => yyy,
[name] => blahblah
),
array(
[id] => zzz,
[name] => blahblahblah
),
)
Can we get an array of ids in one "built-in" php function call? or one line of code?
I am aware of the traditional looping and getting the value but I don't need this:
foreach($users as $user) {
$ids[] = $user['id'];
}
print_r($ids);
Maybe some array_map() and call_user_func_array() can do the magic.
Since PHP 5.5, you can use array_column:
$ids = array_column($users, 'id');
This is the preferred option on any modern project. However, if you must support PHP<5.5, the following alternatives exist:
Since PHP 5.3, you can use array_map with an anonymous function, like this:
$ids = array_map(function ($ar) {return $ar['id'];}, $users);
Before (Technically PHP 4.0.6+), you must create an anonymous function with create_function instead:
$ids = array_map(create_function('$ar', 'return $ar["id"];'), $users);
PHP 5.5+
Starting PHP5.5+ you have array_column() available to you, which makes all of the below obsolete.
PHP 5.3+
$ids = array_map(function ($ar) {return $ar['id'];}, $users);
Solution by #phihag will work flawlessly in PHP starting from PHP 5.3.0, if you
need support before that, you will need to copy that wp_list_pluck.
PHP < 5.3
Wordpress 3.1+
In Wordpress there is a function called wp_list_pluck
If you're using Wordpress that solves your problem.
PHP < 5.3
If you're not using Wordpress, since the code is open source you can copy paste the code in your project (and rename the function to something you prefer, like array_pick). View source here
If id is the first key in the array, this'll do:
$ids = array_map('current', $users);
You should not necessarily rely on this though. :)
You can also use array_reduce() if you prefer a more functional approach
For instance:
$userNames = array_reduce($users, function ($carry, $user) {
array_push($carry, $user['name']);
return $carry;
}, []);
Or if you like to be fancy,
$userNames = [];
array_map(function ($user) use (&$userNames){
$userNames[]=$user['name'];
}, $users);
This and all the methods above do loop behind the scenes though ;)
I'm using an external class (Zebra_cURL) to execute multiple HTTP GET requests. It worked in the following way:
$items = array(
0=>array('url' => 'url0'),
1=>array('url' => 'url1'),
2=>array('url' => 'url2'),
3=>array('url' => 'url3'),
);
$curl = new Zebra_cURL();
$curl->get(array_column($urls,'url'),'scan_item',$moreimfo);
function scan_item($result,$moreimfo){
$items[$key]['size'] = strlen($result->body);
}
So my callback should fill up my $items array with more info for each url (in my case - size of the page). So there is a missing $key variable.
This class supports extra parameters in the callbacks ($moreimfo in my case). BUT as I understand the data passing to each callback will be always the same.
$result object containing the original url info ($result->info['url']). So I COULD use it to find needed array element. However this looks too inefficient in case the size of an array will be big enough.
I think that I should find how to pass an array member key information for EACH callback execution. Is it possible without modifying the original class?
If you use the url as key in the $items array the solution could be something like
<?php
$items = [
'url0'=>array('url' => 'url0'),
'url1'=>array('url' => 'url1'),
'url2'=>array('url' => 'url2'),
'url3'=>array('url' => 'url3'),
];
$curl = new Zebra_cURL();
$curl->get(
array_keys($items),
function($result) use (&$items) {
$key = $result->info['url'];
$items[$key]['size'] = strlen($result->body);
}
);
using an anymous function that "Imports" the $items array via reference.
While it doesn't solve the original problem of passing a reference to the according array element to the callback, the following should be very fast (as noted in the comments, PHP Arrays are implemented using a hashtable).
$items = array(
0=>array('url' => 'url0'),
1=>array('url' => 'url1'),
2=>array('url' => 'url2'),
3=>array('url' => 'url3'),
);
$lookup=array();
foreach($lookup as $k=>$v) {
$lookup[$v['url']]=$k;
}
$curl = new Zebra_cURL();
$curl->get(array_column($urls,'url'),'scan_item',$moreimfo);
function scan_item($result,$moreimfo){
global $lookup,$items;
$items[$lookup[$result->info['url']]]['size'] = strlen($result->body);
}
Probably you may consider using an OOP-approach, with the callback as a method, then the global-izing of the arrays shouldn't be necessary if you use $this->anyMember
In the past few years I've used this formula to read parameters in my methods inside my php classes:
$params = func_get_args();
if(is_array($params[0])){
foreach($params[0] as $key => $value){
${$key} = $value;
}
}
And it works fine, as if I pass something like this:
$class->foo(array('bar' => 'hello', 'planet' => 'world'));
I will have in my foo method the variables bar and planet with their relative values.
But what I'm asking is: Is there any better way to do it? Something that maybe I can encapsulate in another method for example?
UPDATE
So, taking in consideration rizier123 comment, and after a chat with a friend of mine, I nailed down what I think is the better way pass parameters to function. As I know that I will always pass just one parameter to the function, which is always going to be an array, there's no need to call the func_get_args() function, but I better to expect an array all the time and by default I set an empty array, like in the following example:
class MyClass{
public function MyMethod(array $options = array()){
extract($options);
}
}
$my = new MyClass();
$my->MyMethod(array('name' => 'john', 'surname' => 'doe'));
// Now MyMethod has two internal vars called $name and $surname
Yes you can use extract() to convert your arrays to variables, like this:
extract($params[0]);
There is a new feature from PHP 5.6, it's called Variadic functions
http://php.net/manual/en/functions.arguments.php#functions.variable-arg-list
function foo(...$arguments) {
foreach ($arguments as $arg) {
var_dump($arg);
}
}
foo('1', 2, true, new DateTime('now'));
You can do this with PHP built-in function extract().
Use it this way:
$var_array = array("color" => "blue",
"size" => "medium",
"shape" => "sphere");
extract($var_array);
When you run echo $color, $size, $shape; it outputs:
blue, medium, sphere
In the following example, is it possible to access the 'str' value from within the anonymous function?
$foo = array(
'str' => 'THIS IS A STRING',
'fn' => function () {
// is it possible from within here to access 'str'?
}
);
if $foo is defined in the global namespace you should be able to access it via $GLOBALS['foo']['str'] (or make it available via the global $foo; construct). If it isn't (local var, parameter, member variable, …), you have to pass it (as reference!) to the anonymous function:
$foo = array(
'str' => 'THIS IS A STRING',
'fn' => function () use(&$foo) {
echo $foo['str'];
}
);
I have found a different way to do this without using global variables:
<?php
$arr = array("a" => 12,
"b" => function($num)
{
$c = $num * 2;
return $c;
});
echo $arr["b"]($arr["a"]);
?>
Note the weird syntax of ending the array index call with method parentheses. By passing $arr["a"] as a parameter you can have access to that value (I imagine you could pass by reference as well).If you were not to pass anything into the anonymous function, you would call it like this:
$arr["b"]();
If you don't include the method parentheses it won't work.