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)
Related
Is there any nice way to use (potentially) undefined Variables (like from external input) as optional Function parameters?
<?php
$a = 1;
function foo($a, $b=2){
//do stuff
echo $a, $b;
}
foo($a, $b); //notice $b is undefined, optional value does not get used.
//output: 1
//this is even worse as other erros are also suppressed
#foo($a, $b); //output: 1
//this also does not work since $b is now explicitly declared as "null" and therefore the default value does not get used
$b ??= null;
foo($a,$b); //output: 1
//very,very ugly hack, but working:
$r = new ReflectionFunction('foo');
$b = $r->getParameters()[1]->getDefaultValue(); //still would have to check if $b is already set
foo($a,$b); //output: 12
the only semi-useful method I can think of so far is to not defining the default value as parameter but inside the actual function and using "null" as intermediary like this:
<?php
function bar ($c, $d=null){
$d ??= 4;
echo $c,$d;
}
$c = 3
$d ??= null;
bar($c,$d); //output: 34
But using this I still have to check the parameter twice: Once if it is set before calling the function and once if it is null inside the function.
Is there any other nice solution?
Ideally you wouldn't pass $b in this scenario. I don't remember ever running into a situation where I didn't know if a variable existed and passed it to a function anyway:
foo($a);
But to do it you would need to determine how to call the function:
isset($b) ? foo($a, $b) : foo($a);
This is kind of hackish, but if you needed a reference anyway it will be created:
function foo($a, &$b){
$b = $b ?? 4;
var_dump($b);
}
$a = 1;
foo($a, $b);
I would do something like this if this was actually a requirement.
Just testing with sum of the supplied values just for showing an example.
<?php
$x = 1;
//Would generate notices but no error about $y and t
//Therefore I'm using # to suppress these
#$sum = foo($x,$y,4,3,t);
echo 'Sum = ' . $sum;
function foo(... $arr) {
return array_sum($arr);
}
Would output...
Sum = 8
...based on the array given (unknown nr of arguments with ... $arr)
array (size=5)
0 => int 1
1 => null
2 => int 4
3 => int 3
4 => string 't' (length=1)
array_sum() only sums up 1,4 and 3 here = 8.
Even if above actually works I would not recommend it, because then whatever data can be sent to your function foo() without you having any control over it. When it comes to user input of any kind you should always validate as much as you can in your code before using the actual data from the user.
This is the code I used to have to check if $A doesn't match $B
if($A!=$B) {
$set = array();
echo $val= str_replace('\\/', '/', json_encode($set));
//echo print_r($_SERVER);
exit;
}
Now I need the opposite of this condition: ($A need to match one of these $B,$C or $D)
A simple shortcut to seeing if a value matches one of multiple values you can put the values to be compared against ($B, $C, and $D) into an array and then use in_array() to see if the original value ($A) matches any of them.
if (in_array($A, [$B, $C, $D])) {
// ...
}
If you don't want it to match any of $B, $C, or $D just use !:
if (!in_array($A, [$B, $C, $D])) {
// ...
}
You can use array_search
$B = 'B';
$C = 'C';
$D = 'D';
//match B
$A = 'B';
$options = [$B, $C, $D];
if (false !== ($index = array_search($A, $options ))) {
echo "Match: {$index} '{$options[$index]}'";
}
Output
Match: 0 'B'
Sandbox
The nice thing here is you can set the $index and use that to tell which one matched later.
Note you have to use false !== because array search returns the index where the match happened at, so it can happen on the first array element which is index 0. As we know PHP can treat 0 as false (in this case the condition would fail when it should pass). However, when we use the strict type check PHP also compares the type and INT 0 is not BOOL false (which passes the condition).
for reference.
http://php.net/manual/en/function.array-search.php
Another probably the most efficient way is to use isset, and use keys instead of values:
$options = [$B=>1,$C=>1,$D=>1]; //values don't matter
if(!isset($options[$A])){
//....
}
I am talking about the second "return -1;" on the 12th line of the code. This gets reached only if two sets of numbers are exactly the same, like when comparing '192.167.11' to '192.167.11'. I will also add that using range(0,2) would be a better option for this piece of code (range(0,3) produces errors if two elements happen to be the same; I did not change that as this is the original code example from PHP Array Exercise #21 from w3resource.com).
<?php
function sort_subnets($x, $y){
$x_arr = explode('.', $x);
$y_arr = explode('.', $y);
foreach (range(0, 3) as $i) {
if ($x_arr[$i] < $y_arr[$i]) {
return -1;
} elseif ($x_arr[$i] > $y_arr[$i]) {
return 1;
}
}
return -1;
}
$subnet_list =
array('192.169.12',
'192.167.11',
'192.169.14',
'192.168.13',
'192.167.12',
'122.169.15',
'192.167.16'
);
usort($subnet_list, 'sort_subnets');
print_r($subnet_list);
?>
Returning "-1" would move the second element (the same as the first in the current $x and $y pair) towards the higher index of the array (down the array). Why not return "0" and keep everything as is if the two elements are exactly the same? Is there any reason for returning the "-1" maybe based on how the usort() works (or any other factor of this)?
Thanks.
EDIT:
I think that this is Insertion Sort (array size 6-15 elements; normally it would be Quicksort).
If the two elements are the same, there's no difference between swapping the order and keeping the order the same. So it doesn't make a difference what it returns in that case.
You're right that 0 is more appropriate. This would be more important if usort were "stable". But the documentation says
Note:
If two members compare as equal, their relative order in the sorted array is undefined.
To illustrate the excellent point of #Don'tPanic:
<?php
function sort_subnets($x, $y){
$x_arr = explode('.', $x);
$y_arr = explode('.', $y);
return $x_arr <=> $y_arr;
}
$subnet_list =
array('192.169.12',
'192.167.11',
'192.169.14',
'192.168.13',
'192.167.12',
'122.169.15',
'192.167.16'
);
usort($subnet_list, 'sort_subnets');
print_r($subnet_list);
See live code
Note the use of the "spaceship" operator, namely <=> which offers a conciseness that spares one from having to write code like the following in a function:
if ($a == $b) {
return 0;
}
return ($a < $b) ? -1 : 1;
Lastly, note that the user-defined callback for usort() makes use of ternary logic because sometimes as in the case of sorting bivalent logic is insufficient. Yet, usort() itself utilizes two-part logic, returning TRUE on success and FALSE on failure.
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Coalesce function for PHP?
I'm not sure what this is normally called, but I hope the title communicates well enough. What I have is a handful of variables some of which might be null.
I want to do:
$a = $b || $c || $d;
Where $a ends up being = to the first non-null variable.
To my knowledge, PHP doesn't support this in the same way JavaScript does.
You can, however do something like this:
$a = $b ? $b : ($c ? $c : $d);
A more general solution:
function fallthrough($arr) {
//$arr should be an array of possible values. The first non-null value is returned
do $a = array_shift($arr);
while($a === null && $arr);
return $a;
}
<?php
$a = 0;
$b = false;
$c = true; //should become this
$d = '1';
$e = $a ?: $b ?: $c ?: $d;
var_dump($e);
//bool(true)
//should be '1' if order is different
$e = $a ?: $b ?: $d ?: $c;
var_dump($e);
//string(1) "1"
... however ?: is kinda new, you will confuse your colleagues / fellow coders.
I don't think that's possible. I think you'd have to use some other, more laborious, way. I.e. make an array of the variables, iterate through it until you find a non-null value and break the loop, like so:
$vars = array("b" => $b, "c" => $c, "d" => $d);
foreach($vars as $var) {
if($var != null) {
$a = $var;
break;
}
}
Well, like some other answers here say, you can use the shorthand way of writing this, but writing readable code is important too. The above code is pretty readable.
So we got this function in PHP
strcmp(string $1,string $2) // returns -1,0, or 1;
We Do not however, have an intcmp(); So i created one:
function intcmp($a,$b) {
if((int)$a == (int)$b)return 0;
if((int)$a > (int)$b)return 1;
if((int)$a < (int)$b)return -1;
}
This just feels dirty. What do you all think?
this is part of a class to sort Javascripts by an ordering value passed in.
class JS
{
// array('order'=>0,'path'=>'/js/somefile.js','attr'=>array());
public $javascripts = array();
...
public function __toString()
{
uasort($this->javascripts,array($this,'sortScripts'));
return $this->render();
}
private function sortScripts($a,$b)
{
if((int)$a['order'] == (int)$b['order']) return 0;
if((int)$a['order'] > (int)$b['order']) return 1;
if((int)$a['order'] < (int)$b['order']) return -1;
}
....
}
Sort your data with:
function sortScripts($a, $b)
{
return $a['order'] - $b['order'];
}
Use $b-$a if you want the reversed order.
If the numbers in question exceed PHP's integer range, return ($a < $b) ? -1 : (($a > $b) ? 1 : 0) is more robust.
Purely as some additional information, there has been an accepted RFC for this (https://wiki.php.net/rfc/combined-comparison-operator).
So, the comparison function would be along the lines of ...
<?php
$data = [...];
usort($data, function($left, $right){ return $left <=> $right; });
?>
A few really nice feature here is that the comparison is done in exactly the same way as all other comparisons. So type juggling will happen as expected.
As yet, there is no magic __forCompare() like method to allow an object to expose a comparison value. The current proposal (a different RFC) is to have each object be injected into every other object during the comparison so that it does the comparison - something which just seems odd to me - potential opportunity for recursion and stack overflow ... ! I would have thought either injecting the type of object for comparison (allowing an object the ability to represent appropriate values depending upon the type of comparison) or a blind request for a value that the object can serve up for comparison, would have been a safer solution.
Not yet integrated into PHP-NG (PHP 7 at the moment), but hopefully will be soon.
why reinventing the wheel?
http://php.net/manual/en/function.strnatcmp.php
echo strnatcmp(1, 2) . PHP_EOL; // -1
echo strnatcmp(10, 2) . PHP_EOL; // 1
echo strnatcmp(10.5, 2) . PHP_EOL; // 1 - work with float numbers
echo strnatcmp(1, -2) . PHP_EOL; // 1 - work with negative numbers
Test it here:
https://3v4l.org/pSANR
You could use
function intcmp($a,$b)
{
return ($a-$b) ? ($a-$b)/abs($a-$b) : 0;
}
Although I don't see the point in using this function at all
Does it have to be +1 and -1? If not, just return (int) $a - (int) $b. I don't like the divide that someone else recommended, and there's no need to check for all three cases. If it's not greater and not equal, it must be less than.
return (int) $a > (int) $b ? 1 : (int) $a == (int) $b ? 0 : -1;
At a glance, yes it feels dirty. Except there must be a good reason you wrote that instead of just using the actual ==, >, and < operators. What was the motivation for creating this function?
If it were me, I'd probably just do something like:
$x = $a==$b ? 0 : ($a>$b ? 1 : ($a<$b ? -1 : null));
I realize this is just as ugly, and the : null; - not sure if PHP requires it or if I could have just done :; but I don't like it and that code should never execute anyway... I think I'd be a lot less confused about this if I knew the original requirements!
For strings
usort($points, function ($a, $b) use ($orderFlag, $key1, $key2) {
return strcmp($a[$key1][$key2], $b[$key1][$key2]) * $orderFlag;
});
orderFlag => 1 (ascending): -1 (descending)
For numbers
usort($points, function ($a, $b) use ($orderFlag, $key1, $key2) {
return ($a[$key1][$key2] - $b[$key1][$key2]) * $orderFlag;
});
orderFlag => 1 (ascending): -1 (descending)