Is it possible, in any way, to pass comparison operators as variables to a function? I am looking at producing some convenience functions, for example (and I know this won't work):
function isAnd($var, $value, $operator = '==')
{
if(isset($var) && $var $operator $value)
return true;
}
if(isAnd(1, 1, '===')) echo 'worked';
Thanks in advance.
You can also use version_compare() function, as you can pass operator which will be used for comparison as third argument.
How about this one?
function num_cond ($var1, $op, $var2) {
switch ($op) {
case "=": return $var1 == $var2;
case "!=": return $var1 != $var2;
case ">=": return $var1 >= $var2;
case "<=": return $var1 <= $var2;
case ">": return $var1 > $var2;
case "<": return $var1 < $var2;
default: return true;
}
}
Test:
$ops = array( "=", "!=", ">=", "<=", ">", "<" );
$v1 = 1; $v2 = 5;
foreach ($ops as $op) {
if (num_cond($v1, $op, $v2)) echo "True ($v1 $op $v2)\n"; else echo "False ($v1 $op $v2)\n";
}
How about a small class:
class compare
{
function is($op1,$op2,$c)
{
$meth = array('===' => 'type_equal', '<' => 'less_than');
if($method = $meth[$c]) {
return $this->$method($op1,$op2);
}
return null; // or throw excp.
}
function type_equal($op1,$op2)
{
return $op1 === $op2;
}
function less_than($op1,$op2)
{
return $op1 < $op2;
}
}
The top answer recommends a small class, but I like a trait.
trait DynamicComparisons{
private $operatorToMethodTranslation = [
'==' => 'equal',
'===' => 'totallyEqual',
'!=' => 'notEqual',
'>' => 'greaterThan',
'<' => 'lessThan',
];
protected function is($value_a, $operation, $value_b){
if($method = $this->operatorToMethodTranslation[$operation]){
return $this->$method($value_a, $value_b);
}
throw new \Exception('Unknown Dynamic Operator.');
}
private function equal($value_a, $value_b){
return $value_a == $value_b;
}
private function totallyEqual($value_a, $value_b){
return $value_a === $value_b;
}
private function notEqual($value_a, $value_b){
return $value_a != $value_b;
}
private function greaterThan($value_a, $value_b){
return $value_a > $value_b;
}
private function lessThan($value_a, $value_b){
return $value_a < $value_b;
}
private function greaterThanOrEqual($value_a, $value_b){
return $value_a >= $value_b;
}
private function lessThanOrEqual($value_a, $value_b){
return $value_a <= $value_b;
}
}
The bigger problem is that this function is pretty pointless. Let's replace that with a real (hypothetically working) example:
function isAnd($var, $value, $operator = '==') {
return isset($var) && $var $operator $value;
}
isAnd($foo, 1, '===');
In this example $foo is not set. You'll get an error because you're trying to pass a non-existent variable ($foo) to a function (isAnd). So, you will need to test $foo for isset before calling isAnd:
isset($foo) && isAnd($foo, 1, '===');
So, any variable that ever enters the isAnd function is definitely set. You don't need to test for it inside the function. So the whole exercise is pretty pointless.
What may be confusing is that isset() and empty() don't have this limitation, i.e. you can pass a non-existent variable to them without error. The thing is though, these are not normal functions, they're special language constructs (that happen to look like functions; blame PHP). Unfortunately you can not make these kinds of constructs, parameters for your functions always need to exist.
You should just get used to writing isset($foo) && $foo === 1. With properly structured code, you can reduce this to a minimum by always declaring all variables you're going to use, which is good practice anyway.
For the dynamic operator... you'll need some form of if ... else somewhere to decide which operator to use anyway. Instead of setting the operator variable and then evaluating it, isn't it easier to do the evaluation right there?
If you absolutely insist you can use eval.
if(isset($var) && eval("return \$var $operator \$value"))
return true;
But I wouldn't recommend it.
Here is a simple solution which should work for almost all the operators
Eg.
$b = 10;
$c = '+';
$p = $a . $c. $b; // Forming a String equation
$p = eval('return '.$p.';'); // Evaluating the Equation
echo $p;
Output:
15
Another example with comparison operator:
$b = 10;
$c = '==';
$p = $a . $c. $b;
$p = eval('return '.$p.';');
echo $p;
Output:
false
Hope this helps.
As Michael Krelin suggests you could use eval - but that potentially enables a lot of code injection attacks.
You can't substitute a variable for an operator - but you can substitute a variable for a function:
function is_equal($a, $b) {
return $a==$b;
}
function is_same($a, $b) {
return $a===$b;
}
function is_greater_than($a, $b)
....
$compare='is_equal';
if ($compare($a, $b)) {
....
C.
As far as I know it is not possible and since there is no reference about callback on operators in PHP documentation,
http://www.php.net/manual/en/language.operators.php
instead of using eval, I would redefine each operators in global functions and use php callbacks
How do I implement a callback in PHP?
$a = 4;
eval('$condition=($a == 4)?true:false;');
if($condition){ echo "Yes"; }else{ echo "No"; }
No, it's impossible.
You can use conditional operators instead, but it will be much,much better if you redesign your application to make such a dynamic comparison unnecessary.
Related
I'm try to solve a task which uses new functions php7 uniform variable syntax nested () support foo()() (https://wiki.php.net/rfc/uniform_variable_syntax).
I need write function test for this code:
$sum = function($a, $b) { return $a + $b; };
test(6)(2)(3)($sum); // 11
test(3)(1)($sum); // 4
test(3)(3)('pow'); // 27
I don't found any explanation for this feature. Where can I find how to use it? I see that I must return function name in function test, but how to pass argument?
Thanks all for help. It's something like this:
<?php
function test($a) {
echo '<br/>';
$arr[] = $a;
return $mf = function($b) use(&$mf, &$a, &$arr) {
if(gettype($b) == 'object') {
echo(array_reduce($arr, $b));
} elseif (gettype($b) == 'string') {
if($b == 'pow') {
echo array_reduce($arr, function ($carry, $a) {
return !empty($carry) ? pow($carry, $a) : $a;
});
}
} elseif (gettype($b) == 'integer') {
$arr[] = $b;
}
return $mf;
};
}
$sum = function($a, $b) { return $a + $b; };
test(6)(2)(3)($sum); // 11
test(3)(1)($sum); // 4
test(3)(3)('pow'); // 27
This is more about nested recursive functions, or currying, than that rfc. That rfc just enabled the syntax that supported it.
This uses recursion until you pass a callable:
function test($var) {
$values = [$var];
$function = function($callback) use (&$values, &$function) {
if (is_callable($callback)) {
return array_reduce(array_slice($values, 1), $callback, $values[0]);
}
$values[] = $callback;
return $function;
};
return $function;
}
Because your functions expect two parameters but your nesting could have unlimited parameters, it's best to use an array and array reduce.
However, since multiplication functions like pow won't work with a null initial value, you can specify the initial value as the first passed parameter from the array.
Consider the following code. It features ensure_default() helper that assigns default value to a variable IF that variable is set to false:
function resource_intensive_lookup() {
echo 'Running '.__FUNCTION__.'<br>';
return 'default_value';
}
function ensure_default(&$var, $default_value)
{
if (! $var) {
$var = $default_value;
}
}
$var = 'some_value';
ensure_default($var, resource_intensive_lookup());
print_r($var);
The problem is that even if $var is set to non-false value, resoursive_intensive_lookup() will still be called. I could prevent that by passing just the function name as a parameter and using call_user_func() within the ensure_default(), but such approach does not scale very well if codebase has more functions that are similar to ensure_default().
I have also tried to using a closure which prevents resource_intensive_lookup() from called needlessly, but unfortunatelly sets $var to Closure Object ( ) instead of the default_value when initially $var starts out being set to false:
$closure = function () {
return resource_intensive_lookup();
};
function resource_intensive_lookup() {
echo 'Running '.__FUNCTION__.'<br>';
return 'default_value';
}
function ensure_default(&$var, $default_value)
{
if (! $var) {
$var = $default_value;
}
}
$var = false;
ensure_default($var, $closure);
print_r($var);
The last example in your question is almost good. You need to change:
$var = $default_value;
to:
$var = $default_value();
EDIT:
If you don't want't to change ensure_default, the only solution I can think of is creating your own proxy that will call the callable if it will be returned by ensure_default and call that proxy instead of the original ensure_default:
function ensure_default_with_callback(&$var, $default_value) {
ensure_default($var, $default_value);
if (is_callable($var)) {
$var = $var();
}
}
ensure_default_with_callback($var, $closure);
// you can also call it without closure
ensure_default_with_callback($var, "not a closure");
If you can pass your initialisation as an anonymous function, you can then call that function only when it's needed...
function ensure_default(&$var, $default_value)
{
if (! $var) {
$var = $default_value();
}
}
$var = 'some_value'; // Values used for testing
$var = null;
ensure_default($var, function () {
echo 'Running '.__FUNCTION__.'<br>';
return 'default_value';
});
print_r($var);
Edit:
If you want ensure_default() to be able to cope with a function or a literal value, then you can check if $default_value is callable...
function ensure_default(&$var, $default_value)
{
if (! $var) {
$var = (is_callable($default_value))?$default_value():$default_value;
}
}
Which allows...
$var1 = 'some_value'; // Values used for testing
$var1 = null;
ensure_default($var1, 'default_value1');
print_r($var);
to set the default to a string (or in fact most other values).
Why not $var = 'some_value' || resource_intensive_lookup()? I suppose the 'some_value' would be a variable in the real world, but I think the snippet captures the idea.
How do we conditionally chain methods in PHP? For example, this works fine:
$a->foo()->bar->baz->qux();
However, depending on a condition, I'd like to chain some methods but not others. Basically, shorten the following code:
if ($cond === true) {
$a->foo()->baz();
} else {
$a->foo()->bar();
}
Ideally something like the following would work:
$a->foo()
->bar()
($cond === true) ? ->baz() : ->qux()
->more();
Additionally, how would we conditionally chain a method (or not) depending on a condition? For example:
$a->foo()
->bar()
if($cond === true) ->baz()
->more();
The Self-Explanatory Mock-Snippet below (which you may Quick-Test Here) shows how you could do that
<?php
class Test{
protected $prop1;
protected $prop2;
protected $prop3;
protected $prop4;
public function __construct() {
}
public function setProp1($prop1) {
$this->prop1 = $prop1;
return $this;
}
public function setProp2($prop2) {
$this->prop2 = $prop2;
return $this;
}
public function setProp3($prop3) {
$this->prop3 = $prop3;
return $this;
}
public function setProp4($prop4) {
$this->prop3 = $prop4;
return $this;
}
}
$a = 2;
$b = 7;
$cond = ($a > $b);
$cond2 = ($b > 50);
$test = new Test;
$test->setProp1(2)->{($cond === true) ? 'setProp4' : 'setProp3'}(11);
$test->setProp3(3)->{($cond2 === false) ? 'setProp2' : 'setProp4'}(6);
var_dump($test);
//YIELDS::
object(Test)[1]
protected 'prop1' => int 2
protected 'prop2' => int 6
protected 'prop3' => int 3
protected 'prop4' => null
What you're looking for is variable methods (see example #2). They allow you to do something like this:
class a {
function foo() { echo '1'; return $this; }
function bar() { echo '2'; return $this; }
function baz() { echo '3'; return $this; }
}
$a = new a();
$cond = true;
$a->foo()->{($cond === true) ? 'baz' : 'bar'}();
// Prints 13
$cond = false;
$a->foo()->{($cond === true) ? 'baz' : 'bar'}();
// Prints 12
Here's a way that lets you set up requirements for each of the function calls. Note that this is just as hard to maintain as the previous solution, if not harder. You'll probably want to use some sort of configuration and the ReflectionClass's getMethods function, too.
class a {
function foo() { echo '1'; return $this; }
function bar() { echo '2'; return $this; }
function baz() { echo '3'; return $this; }
}
function evaluateFunctionRequirements($object, $functionRequirements, $condition) {
foreach ($functionRequirements as $function=>$requirements) {
foreach ($requirements as $requiredVariableName=>$requiredValue) {
if (${$requiredVariableName} !== $requiredValue) {
continue 2;
}
}
$object->{$function}();
}
}
$a = new a();
$functionRequirements = array('foo'=>array(), 'bar'=>array(), 'baz'=>array('condition'=>true));
$condition = true;
evaluateFunctionRequirements($a, $functionRequirements, $condition);
// Prints 123
$condition = false;
evaluateFunctionRequirements($a, $functionRequirements, $condition);
// Prints 12
Notes: This has the added even harder to maintain of requiring the functions in order for the $functionRequirements array. Additionally, this rudimentary example has only one possible condition var passed, update to another setup for getting more $requiredVariableName vars with func_get_args. You'll also want to verify that the methods passed in via $functionRequirements are is_callable() safe.
Try this by assigning the chaining to variable
$a = $a->foo();
if ($cond === true) {
$a = $a->baz();
} else {
$a = $a->bar();
}
$a->more();
Another way to solve this is to create a method when (or name it whatever makes sense to you):
public function when($condition, $callback)
{
if ($condition) {
return $callback($this) ?: $this;
}
return $this;
}
Of course, you can extend it to accept additional arguments if you need to pass them to your methods foo, bar, etc...
And the usage with chaining would be:
$a->when($cond === true, function ($a) {
return $a->foo();
})->when($cond !== true, function ($a) {
return $a->bar();
}
)->baz(); // a regular chaining method without condition
Is it possible to pass an operator to a function? Like this:
function operation($a, $b, $operator = +) {
return $a ($operator) $b;
}
I know I could do this by passing $operator as a string and use switch { case '+':... }. But I was just curious.
It's not possible to overload operators in php, but there is a workaround. You could e.g. pass the functions add, sub, mul and etc.
function add($a, $b) { return $a+$b; }
function sub($a, $b) { return $a-$b; }
function mul($a, $b) { return $a*$b; }
And then you function would be something like:
function operation($a, $b, $operator = add) {
return $operator($a, $b);
}
This can be done using eval function as
function calculate($a,$b,$operator)
{
eval("echo $a $operator $b ;");
}
calculate(5,6,"*");
Thanks.
Try, You cannot able to pass the operators in functions, YOU CAN USE FUNCTION NAME LIKE ADDITION, SUBTRACTION, MULTIPLICATION ... etc,
function operation($a, $b, $operator ='ADDITION') {
$operator($a, $b);
}
function ADDITION($a, $b){
return $a + $b;
}
I know you specifically mention not using a switch statement here, but I think it's important to show how this could be set up using switch statements as in my opinion it is the easiest, safest, and most convenient way to do this.
function calculate($a, $b, $operator) {
switch($operator) {
case "+":
return $a+$b;
case "-":
return $a-$b;
case "*":
return $a*$b;
case "/":
return $a/$b;
default:
//handle unrecognized operators
}
return false;
}
I am using a uasort function like this:
uasort($entity_list, 'sortArray');
function sortArray($a, $b) {
if($a['fixed_column_name'] == $b['fixed_column_name']) {
return 0;
}
return ($a['fixed_column_name'] < $b['fixed_column_name']) ? -1 : 1;
}
I would like to pass a parameter to the sortArray function, like this:
uasort($entity_list, 'sortArray($arg)');
function sortArray($a, $b, $arg) {
$larg = $arg;
if($a[$larg] == $b[$larg]) {
return 0;
}
return ($a[$larg] < $b[$larg]) ? -1 : 1;
}
If you're using PHP 5.3+ (and you really should at this point in time), you can use closures:
uasort($entity_list, function ($a, $b) use ($arg) {
if ($a[$arg] == $b[$arg]) {
return 0;
}
return ($a[$arg] < $b[$arg]) ? -1 : 1;
});
Otherwise, you'll have to work around doing the same thing using global variables (oh noes!) or a class.
You could always use a class:
$sorter = new Sorter($arg);
usort($entity_list, array($sorter, "sort")); //will use $sorter->sort as callback
class Sorter {
function __construct($arg) {
$this->arg = $arg;
}
function sort($a, $b) {
// sort using $a, $b and $this->arg
}
}
Closures are nicer though ^^
Take a look at Example #4 using a closure on the usort() page of the PHP manual. The same technique can be used with all the basic sorts, including uasort()