How can I have PHP avoid lazy evaluation? - php

I have an interesting question about the way PHP evaluates boolean expressions. When you have, for example,
$expression = $expression1 and $expression2;
or
if ($expression1 and $expression2)
PHP first checks if $expression1 evaluates to true. If this is not the case, then $expression2 is simply skipped to avoid unnecessary calculations. In a script I am writing, I have:
if ($validator->valid("title") and $validator->valid("text"))
I need to have the second statement ($validator->valid("text")) evaluated even if the first one evaluates to false. I would like to ask you whether there is some easy way to force PHP to always evaluate both statements. Thank you!

$isValidTitle = $validator->valid("title");
$isValidText = $validator->valid("text");
if($isValidTitle && $isValidText)
{
...
}
Will that suit?

This is known as short circuit evaluation, and to avoid it you need to do this, using a single &:
if($validator->valid("title") & $validator->valid("text")) {
}
Note that this is not using logical operators but actually bitwise operators:
They're operators that act on the binary representations of numbers. They do not take logical values (i.e., "true" or "false") as arguments without first converting them to the numbers 1 and 0 respectively. Nor do they return logical values, but numbers. Sure, you can later treat those numbers as though they were logical values (in which case 0 is cast to "false" and anything else is cast to "true"), but that's a consequence of PHP's type casting rules, and nothing to do with the behavior of the operators.
As such, there is some debate as to whether it is good practice to use this side effect to circumvent short-circuit evaluation. I would personally at least put a comment that the & is intentional, but if you want to be as pure as possible you should evaluate whether they are valid first and then do the if.

try to evaluate each term separately:
$term1 = $validator->valid("title");
$term2 = $validator->valid("text");
if($term1 && $term2) {
//things to do
}

This might not be the best implementation, but you could always do:
$a=$validator->valid("title");
$b=$validator->valid("text");
if($a && $b) {...}

You can define a function like:
function logical_and($x,$y) {return ($x && $y);}
Since PHP uses call-by-value, this works.

Alternatively, if you can modify the class $validator instantiates, you could make the valid method accept a string or an array. If it's an array, it runs the code that already exists on each item and only returns TRUE if all items are "valid".

Related

Check if variable has value and isn't empty

I want to check if a numeric variable has a value (including '0') and is not empty. Empty meaning EMPTY (''), not '0'.
Is this really the best I can do with PHP?
if (isset($variable) && $variable !== '') { ... }
I'd like to do this with one check without writing a function for it...
What you are trying to check is string length, not "empty". This can easily be done using strlen().
if (isset($variable) && strlen($variable) > 0) {
// Do something
}
If you want to exclude whitespace as invalid, you can add a trim() in there as well (generally recommended).
if (isset($variable) && strlen(trim($variable)) > 0 } {
// ...
}
The best thing you could do, is making your own custom function. The point is to pass the variables by reference to not trigger a warning, when you pass an undefined variable. As posted as comment, I'd use something along the line isset($variable) AND !empty($variable) AND !is_numeric($variable) AND $variable !== false to cover all cases.
Your custom function could look like this (improved version):
function is_blank(&$variable) {
return (bool) !(isset($variable) AND (!empty($variable) OR is_numeric($variable) OR $variable === false));
}
https://3v4l.org/ZcCDu
Yes, your way is the best (most efficient) way to:
insure the variable has been set (so you don't get an warning checking a variable that's not been set)
it's not the empty string ''
But, could be '0', 0,false, null, or [] which all count as empty in php, but you wish to consider as non-empty as indicated by your OP
your !== will ensure only exactly the string '' is compared (no casting/conversion)
The use of strlen works as well, but if you look at the opcode generated you'll see direct comparison is more 3 times computationally more efficient (assuming all operations are equally weighted, even more efficient if operations like DO_FCALL take significantly more cycles to execute than a basic IS_NOT_IDENTICAL check)
The !== ''version bytecode:
IS_NOT_IDENTICAL ~1 !0, ''
The strlen() > 0 version bytecode:
SEND_VAR !0
DO_FCALL 1 $1 'strlen'
IS_SMALLER ~2 $1, 0
(The answer has been edited. Consult the additionals further down under "ternary operations").
Why go through the trouble of using all that?
Just use an "not empty" if(!empty($var)){...}
However, if you're using this with a GET array, then yes; it would be best to use an isset() and empty() on a conditional statement.
I want to check if a variable has a value (including '0') and is not empty
That to me interprets as:
Check if a value has a value and is not empty (as you wrote) and stands to contain a 0 (zero).
Therefore:
if(!empty($var) && $var='0'){...}
I'd like to do this with one check without writing a function for it...
Use a ternary operator then.
However "without a function"... right well you can't. You still need "some type of function".
About that "ternary operator" I mentioned above. You can reference what are called "nested ternary operations" in both these Q&A's on Stack:
How to concatenate multiple ternary operator in PHP?
nested php ternary trouble: ternary output != if - else
That way you won't need a custom function.
Sidenote: I am by far not taking away or trying to take away from (Charlotte's) accepted answer (which should remain as accepted). This is just an additional method of achieving your (ultimate) goal.

Why do some people put the value before variable in if statement?

For example, what's different from $variable === true?
<?php
if (true === $variable) {
//
}
if (1 === intval($variable)) {
//
}
They are equivalent.
Some programmers prefer this "Yoda style" in order to avoid the risk of accidentally writing:
if ($variable = true) {
// ...
}
, which is equivalent to
$variable = true;
// ...
, when they meant to write
if ($variable === true) {
// ...
}
(whereas if (true = $variable) would generate an obvious error rather than a potentially-subtle bug).
Short answer
Some people do it in order to avoid mistakenly using the assignment operator (=) when they really meant to use a comparison operator (== or ===).
Long answer
In PHP there are 3 operators that can be mistaken for eachother:
= is the assignment operator
== is the "equal to" operator
=== is the "identical to" operator
The first operator is only used for assigning a value to a variable. The second and third are only used for comparing two values, or expressions, against eachother.
In PHP it is possible to assign a value to a variable inside control structures (if, for, while, etc.). This may cause problems if you are not careful. For example, if you want to compare $a against a boolean value you can do it like this:
if ($a == false) // or
if ($a === false)
If you are not careful, however, it may end up like this:
if ($a = false)
... which is perfectly legal code, but it is an assignment and will eventually cause problems. The reason it will cause problems is because the expression, $a=false, will be evaluated and the code will keep running as if nothing is wrong, when in fact it was not intended.
The reason some people switch around the operands, when comparing a value against a literal (fixed value, like true, false, 7, 5.2, and so on), is because it is not possible to assign a value to a literal. For example:
if (false = $a)
... will cause an error, telling the programmer that they made a mistake, which they can then fix.
I guess this is just the way of thinking in general. When you really think about a deep if statement, you think if that statement is true, not the other point of view. It's not wrong, but in my head naming it inverse, would annoy me and make me lose concentration about the statement. So I would say it's just the way people think :D.
There is no difference between ($var === true) and (true === $var). They are equivalent.
See http://php.net/manual/en/types.comparisons.php for a complete table of comparisons. You'll see that all equivalent comparisons have the same result.
Also, some people do prefer to see what's the result which is been evaluated before the statement.
Some statements might be longer and harder to read.
Ex:
if (false == (new DataParser()->makeAComplexDataParser((new SomeTransformer()->doTransformation((new someClass()->getMethodOfLongParameters($param1, $param2, (new Date()->format('Y-m-d')))))))) ) {
// do stuff
}
So it's better to think "is it false this complex expression?" instead of thinking
"this looooooooonger complex expression is....hmmmm....false?"

Assignment statement with AND operator

Can any one explain me following construct.
I do googling for this about 2 hours but can't understand.
public function __construct($load_complex = true)
{
$load_complex and $this->complex = $this->getComplex();
}
See: http://www.php.net/manual/en/language.operators.logical.php
PHP uses intelligent expression evaluation. If any of AND's operands evaluates to false, then there is no reason to evaluate other, because result will be false.
So if $load_complex is false there is no need to evaluate $this->complex = $this->getComplex();
This is some kind of workaround, but I do not suggest to use it, because it makes your code hard to read.
Specifically to your example $this->complex = $this->getComplex() if and only if $load_complex is set to true.
LIVE DEMO
NOTE: If any one of OPERAND result becomes 'false' in short
circuit AND evaluation means, the part of statement will be
OMITTED because there is no need to evaluate it.
Dont code like below line because, you may get probably logical
error while you are putting Expression instead of assigning values
to the variable on LEFT HAND SIDE...
$load_complex and $this->complex = $this->getComplex();
I have modified below with conditinal statement for your needs...
if($load_complex and $this->complex) {
$this->getComplex();
}

Processing of php 'if x && y'

I was wondering how php processes if statements.
If i were to have something such as:
if (isset($_GET['foo']) && $_GET['foo'] != $bar)
If foo isn't set, would it then drop out of the if straight away (as it is an 'and' statement so it can't succeed anyway) or would it also check the second part, rather pointlessly?
What you're describing is known as "short-circuit evaluation".
Most languages work this way, including PHP, so they will evaluate an expression until they are certain of the result, and then stop, so the remainder of the expression would not be evaluated.
As you say, this is the most efficient approach.
However, it can potentially throw a spanner in the works for inexperienced programmers, who nay try something like this:
if(doFirstProcess() && doSecondProcess() {
print "both processes succeeded";
}
In this case, the programmer is expecting both functions to be called, but if the first one returns false, then the second one will not be executed, as the program already knows enough to be certain of the final result of the expression, so it short-circuits the remainder of the expression.
There are a few languages which don't do short-circuit evaluation. VB6 was one example (back in the day). I don't know about VB.Net, but since it's evolved from VB6, I would suspect it would be similar. But aside from that, all other languages that I've worked with have used short-circuit evaluation, including PHP.
There is a section in the PHP manual about this here: http://www.php.net/manual/en/language.operators.logical.php
And you can read more on short circuit evalution here: http://en.wikipedia.org/wiki/Short-circuit_evaluation
Hope that helps.
It's known as short-circuit:
&& and and operators is executed from left to side
If left side is considered false, no reasons to check right side, so it's omitted, false returned
|| and or operators is executed from left to side too
If left side is considered true, no reasons to check right side, so it's omitted, true returned
Manual example:
// foo() will never get called as those operators are short-circuit
$a = (false && foo());
$b = (true || foo());
$c = (false and foo());
$d = (true or foo());
it will leave the if statement after the first expression evaluates to false because this statement can never be true if the first one is false and they are combinded via AND
you can check this very easily. If it wouldn't be like I said, you would get a notice that $_GET['foo'] is not defined
If the first part is false, it stops the if.
Certain operators, most notably && and || are so-called short-circuit operators, meaning that if the result of the operation is clear from the first operand (false or true, respectively), the second operand does not get evaluated.
Edit: Additionally, operands are guaranteed to be evaluated in order, this is not always true of other operators.
Will go out after the first statement.
you can test it:
will print 1:
if(1==1 && $a=1 == 1){
}
print $a;
Will not print a thing:
if(1==2 && $a=1 == 1){
}
print $a;
&& does short-circuit (i.e. returns false as soon as one condition fails).
If it doesn't, then having the isset would be pointless — it exists to prevent errors when trying to compare an undefined value to a string.
If the first check if (isset($_GET['foo']) returns false, the second part will not even be looked into anymore.

Question with setting variables in if statements

Here is the sample:
if(($test = array('key'=>true)) && $test['key']){
// works
}
if($test = array('key'=>true) && $test['key']){
// does not work
}
Why is the parenthesis required? My understanding is that it computes the first conditional then the second no matter what.
And is it "safe" to do an assignment like this?
It's a matter of operator precedence in the language. In your second statement, you're essentially writing this to be evaluated:
$test = array('key'=>true) && $test['key'];
Just about any language is going to take that to mean this:
$test = (array('key'=>true) && $test['key']);
It's assigning to $test the value of the evaluation of the logical && between the two other values. So $test will be either true or false when evaluated.
i don't think the parens are required in PHP. they are in JS.
depends what you mean by "safe". it works. but some would argue that this is bad style and makes for less understandable and less maintainable code. otoh, K&R positively recommended it. it doesn't worry me and sometimes makes for tidier code.
It's because it's forcing the interpreter to perform the assignment before it attempts to evaluate any of conditions within the if.
Without the parenthesis, you're simply assigning the results of array('key'=>true) && $test['key'] to $test.
It is safe to use, but in this case the parenthesis are required for disambiguation. The operator precedence rules of PHP mean that the second line will be executed as:
if ($test = (array('key' => true) && $test['key'])) { .. }
So test will not be an array but a bool.
The practice of doing assignment in if statements itself is not really encouraging readability though, so you probably want to avoid doing this too much.
In PHP, the assignment operator = has lower precedence that most other operators. (For more information, see the documentation). Furthermore, the result of an expression using the assignment operator is the value of the assignment. With this in mind, consider the expressions you posted:
First example:
($test = array('key'=>true)) && $test['key']
The first part of this expression, ($test = array('key'=>true)) evaluates to array('key'=>true) (by the rule above), and the second part evaluates to true since the key was just set. Thus the whole expression evaluates to true.
Second example:
$test = array('key'=>true) && $test['key'] By the rules of operator precedence, $test is going to get assigned the value of the expression array('key'=>true) && $test['key']. The first half of this is true, but $test['key'] hasn't been set yet, so true && false is false, so $test takes the value false, which is also the result of the if condition.

Categories