How can I include a variable inside a callback function? - php

I'm trying to get counts of array values greater than n.
I'm using array_reduce() like so:
$arr = range(1,10);
echo array_reduce($arr, function ($a, $b) { return ($b > 5) ? ++$a : $a; });
This prints the number of elements in the array greater than the hard-coded 5 just fine.
But how can I make 5 a variable like $n?
I've tried introducing a third argument like this:
array_reduce($arr, function ($a, $b, $n) { return ($b > $n) ? ++$a : $a; });
// ^ ^
And even
array_reduce($arr, function ($a, $b, $n) { return ($b > $n) ? ++$a : $a; }, $n);
// ^ ^ ^
None of these work. Can you tell me how I can include a variable here?

The syntax to capture parent values can be found in the function .. use documentation under "Example #3 Inheriting variables from the parent scope".
.. Inheriting variables from the parent scope [requires the 'use' form and] is not the same as using global variables .. The parent scope of a closure is the function in which the closure was declared (not necessarily the function it was called from).
Converting the original code, with the assistance of use, is then:
$n = 5;
array_reduce($arr, function ($a, $b) use ($n) {
return ($b > $n) ? ++$a : $a;
});
Where $n is "used" from the outer lexical scope.
NOTE: In the above example, a copy of the value is supplied and the variable itself is not bound. See the documentation about using a reference-to-a-variable (eg. &$n) to be able and re-assign to variables in the parent context.

Related

Evaluate concatonated operator with numbers

I'm struggling with a small piece of code that doesn't want to evaluate itself :
$t = 5;
$s = "<=";
$r = 6;
var_dump($t.$s.$r);
Here the var_dump return "5<=6" which make sense but I just want it to tell me if 5 is inferior or equal to 6 with a boolean.
I wanted to know if there was an other way to get this boolean beside using an eval() or a switch throught all the possible operator
Thanks in advance.
If you want a safe and flexible solution, this allows you to define a method which is executed depending on the operator matching the key in an array, it only works with two operands, but the last one in the examples # just multiplies the first value by 4 and returns the value...
$operators = [ "<=" => function ($a, $b) { return $a <= $b;},
"<" => function ($a, $b) { return $a < $b;},
">=" => function ($a, $b) { return $a >= $b;},
">" => function ($a, $b) { return $a > $b;},
"#" => function ($a) { return $a * 4; }];
$t = 5;
$s = "<=";
$r = 6;
var_dump($operators[$s]($t,$r));
$s = "<";
var_dump($operators[$s]($t,$r));
$s = ">=";
var_dump($operators[$s]($t,$r));
$s = ">";
var_dump($operators[$s]($t,$r));
$s = "#";
var_dump($operators[$s]($t,$r));
gives...
/home/nigel/workspace2/Test/t1.php:14:
bool(true)
/home/nigel/workspace2/Test/t1.php:17:
bool(true)
/home/nigel/workspace2/Test/t1.php:20:
bool(false)
/home/nigel/workspace2/Test/t1.php:23:
bool(false)
/home/nigel/workspace2/Test/t1.php:26:
int(20)
It's a bit convoluted, but also extensible and safe.
while it is generally not a good idea to have code usch as this (evaluating code that is sstored as plaintext), there is a function for exactly this: eval().
eval() does what you expect PHP to do naturally: evaluate valid code stored in a string.
eval("var_dump(".$t.$s.$r.");"); will do the job - however, since any code inside those variables is executed without question, it can be a security risk, or at least introduce some hard-to-debug errors.
(the extra quoting and the ; are needed to make the code inside eval actually valid PHP code)

PHP array multisort (version 5/7 differences)

I got a array like this:
$json = '{"Categorie1":[{"created":"2017-07-17 08:53:00","catid":"54"},{"created":"2017-05-23 10:15:00","catid":"54"},{"created":"2017-05-09 05:49:23","catid":"54"}],"Categorie2":[{"created":"2017-03-21 08:58:37","catid":"59"},{"created":"2016-12-23 12:48:00","catid":"59"},{"created":"2016-12-08 09:57:10","catid":"59"}],"Categorie3":[],"Categorie4":[{"created":"2017-08-02 07:15:07","catid":"70"},{"created":"2017-08-01 08:03:00","catid":"70"},{"created":"2017-07-31 09:25:00","catid":"70"}],"Categorie5":[{"created":"2017-07-26 14:09:00","catid":"74"},{"created":"2017-06-29 14:03:00","catid":"74"},{"created":"2017-06-28 06:35:35","catid":"74"}]}';
And I wrote a sorting function. Basically it checks which block (categorie) got the newest entry and brings that block to top, sort the blocks):
$array = json_decode($json, true);
function custom($a, $b) {
foreach($a as $k => $v) {
if (isset($b[$k])) {
return (strtotime($a[$k]["created"]) <= strtotime($b[$k]["created"]));
}
}
}
uasort($array, "custom");
When I print this with PHP 5 its perfect: Categorie4 is the first block". But with PHP 7 it doesn't.
print("<pre>");
print_r($array); // PHP 5 is as expected, php 7 is not
I know there where changes, but I can't figure out how to change my code.
Can you guys helping me change the code? The result should show categorie4 as first cat...
The callback function used by the user-defined array sorting functions must return an integer value that is <0 if $a < $b, 0 when $a == $b or >0 when $a > $b. Yours return a boolean that is converted to 1 or 0 and that doesn't reflect the correct order of $a and $b.
It's not clear from the question how should be sorted the empty entries (Categorie3) and I think their place is at the end.
Try this code for PHP 5:
uasort($array, function (array $a, array $b) {
if (empty($a)) { return +1; } // empty arrays go to the end of the list
if (empty($b)) { return -1; }
return strcmp($a[0]['created'], $b[0]['created']);
});
The date&time values of created use a format that can be sorted directly as strings, there is no need to convert them to timestamps.
In PHP 7 you can use the new <=> comparison operator that, in theory, should run faster than the strcmp() function while it produces the same result.
uasort($array, function (array $a, array $b) {
if (empty($a)) { return +1; } // empty arrays go to the end of the list
if (empty($b)) { return -1; }
return $b[0]['created'] <=> $a[0]['created'];
});

PHP "use" over additional parameters for closures?

In what situation is using the use keyword with a closure more beneficial then just passing along additional parameters to the closure?
Example #1:
$myClosure = function($var1, $var2) use ($var3){
//Some code
}
Example #2:
$myClosure = function($var1, $var2, $var3){
//Some code
}
Like all things it probably depends, but I don't see any functional difference between the two. Can anyone suggest a situation or example where one example would be preferred over the other?
I don't see any functional difference between the two.
The arguments are provided by the caller of the function. If you are the caller and can provide all the necessary arguments, then there is basically no difference*.
However, you might be passing the function somewhere else, so you are not caller and do not control which arguments are passed. This is the situation that closures solve: You can make values available without calling the function.
See also In PHP 5.3.0, what is the function "use" identifier? .
*: The variables that are bound through use are defined at definition time. So if the value of the variable changes, there will be a difference:
$a = 1;
$b = 2;
$c = 3;
$f1 = function($a, $b) use ($c) {
echo $a, $b, $c;
};
$f2 = function($a, $b, $c) {
echo $a, $b, $c;
};
$c = 42;
$f1($a, $b); // 1, 2, 3
$f2($a, $b, $c); // 1, 2, 42
Say you're using a closure with pre-defined parameters, like preg_replace_callback for example, and you want to use a variable from other places in your code, you'd use the use keyword
$myvar = "hello";
$text = "";
$text = preg_replace_callback('/regex/', function($matches) use ($myvar) {
return $myvar . " " . $matches[1] . "!";
}, $text);
It's a great way to use a variable within a closure with pre-defined parameters, which do not have access to variables outside the closure

How to use a pointer in a PHP function? [duplicate]

This question already has answers here:
Are there pointers in php?
(9 answers)
Closed 7 years ago.
In C we can use pointer for a function parameter:
test( *text);
function test( *txt){
//codes goes here
}
Is it possible in PHP?
Variable names in PHP start with $ so $entryId is the name of a variable.
$this is a special variable in Object Oriented programming in PHP, which is reference to current object.
-> is used to access an object member (like properties or methods) in PHP, like the syntax in C++.
So your code means this: place the value of variable $entryId into the entryId field (or property) of this object.
The & operator in PHP, means pass reference. Here is an example:
$b=2;
$a=$b;
$a=3;
print $a;
print $b;
// output is 32
$b=2;
$a=&$b; // note the & operator
$a=3;
print $a;
print $b;
// output is 33
In the above code, because we used & operator, a reference to where $b is pointing is stored in $a. So $a is actually a reference to $b.
There is a good explanation of pointers on this page
There are references, but that's not the same as pointers.
php.net has multiple pages explaining What References Are, What References Do and What References Are Not.
There too, it is mentioned multiple times that
References are not pointers.
They provide a way to assign $a to $b so that if you reassign $b, $a changes as well:
$a = 'a';
$b = &$a; // Reference
$b = 'b'; // Now $a == 'b'
This can be used for function arguments as well:
function myFunc(&$b)
{
$b = 'b';
}
$a = 'a';
myFunc($a); // Now $a == 'b'
Before PHP 5.3.0 is was also possible to do a thing called "call-time pass-by-reference", where you would have a "normal" function declaration and use the & operator in the call instead:
function myFunc($b)
{
$b = 'b';
}
$a = 'a';
myFunc(&$a); // As of PHP 5.3.0 produces a Fatal Error, saying:
// Call-time pass-by-reference has been removed; If you would like to pass argument by reference, modify the declaration of myFunc().
But beware! Assigning another reference will not update the original reference:
$a = 'a';
$b = 'b';
$c = &$a;
$c = &$b; // $a == 'a'
[ Demo ]
A trap resulting from that exists with the global keyword.
$a = 'a';
$b = 'b';
function myFunc()
{
global $a, $b;
$a = &$b;
var_dump($a);
}
myFunc(); // 'b'
var_dump($a); // 'a'
That is because global $a effectively means $a = &$GLOBALS['a'], so assigning it a new reference will not update $GLOBALS.
Of course you can prevent that by using $GLOBALS directly.
But if you're using globals at all, you should probably rethink your design anyway.
With references, there is now also a difference between setting a variable = NULL and using unset().
= NULL follows references, unset() does not:
$a = 'a';
$b = &$a;
unset($b); // $a == 'a'
$a = 'a';
$b = &$a;
$b = NULL; // $a == NULL
[ Demo ]
Bottom line:
References allow for things that wouldn't be possible without them, but sometimes do not behave the way one would expect them to, because of how PHP was built.

PHP. How does user defined compare function work?

Example:
<?php
function cmp($a, $b)
{
if ($a == $b) {
return 0;
}
return ($a < $b) ? -1 : 1;
}
$a = array(3, 2, 5, 6, 1);
usort($a, "cmp");
foreach ($a as $key => $value) {
echo "$key: $value\n";
}
?>
Result
0: 1
1: 2
2: 3
3: 5
4: 6
in the user defined compare function 'cmd', it has two arguments, $a & $b.
What do the first and second argument represent? Are they array[x] and array[x+1] ?
They are just two of the elements from the array being sorted, not necessarily neighbours. usort performs some sort algorithm (see http://en.wikipedia.org/wiki/Sort_algorithm#Comparison_of_algorithms), which involves comparing various pairs of elements to determine their order. It uses your callback function to determine this.

Categories