I ran across some interesting code today. I tried to find out if this is a feature of PHP or if I am missing something, but was unable to find anything on Google. Probably because I don't know the name of it.
Code
if($logo = \Repositories\Logo::getLogoData($id)){
$logo_href = $logo->link;
}
The variable $logo is not being set anywhere else. It seems like the expression in this if statement is checking to see if the that class method is returning anything and simultaneously setting the variable $logo to be used in the statement.
Is this true? If so, what in the world is this called!?!
You can make an assignment like that in a conditional. What happened logically is that the value is assigned to $logo and then $logo is evaluated as to whether it is truthy. If it is truthy, the code in the conditional executes.
You will oftentimes see this sort of assignment/evaluation in the case of looping through database result sets, but generally I would suggest that it should be avoided outside such a common use case for sake of clarity in reading g code.
Yes, this is a feature. It's like:
$a=$b=5;
But in this case, imagine the bool result of if as var $a.
However, IDE's are used to complain about solutions like this because of == vs. = as a very common possible bug source.
Related
When I learned PHP I was taught to make my code error free, but to still hide errors in production code to ensure a clean user experience.
I've recently been involved in some projects where the original writer took the approach of leaving in errors and warnings and even utilizing them to achieve something, rather than write code without it.
For example, the code would look like this:
$numm = 0;
while($numm < 10){
$var = "something,".$var;
$numm++;
}
This code will throw a non-fatal Noticethe first time through the loop, because $var doesn't exist for the first concatenation.
There are tons of other examples where they either ignore errors, or even utilize them (to end loops, etc.) but then hide them from the user.
To me, this seems like bad practice, but I could just be OCD.
A Notice is a bug waiting to happen. I routinely run development with error_reporting(E_ALL); set. I want to find the bugs before they are a problem, and not simply ignore the problems, potential, or not.
Set a requirement of isset($var) in the while loop.
One thing that I have always found annoying was doing things like:
$var = isset($var) ? "something,".$var : "something,";
This one liner will prevent the error but not ideal way of doing it when you consider the number of possible uses. Imagine an associative array that returns that doesn't always have all it's key/values you would expect set.
One approach that i take to nearly all my apps is creating and using the following function:
function rtnVal(&$val, $default = null){
return isset($val) ? $val : $default;
}
so in this case, all I have to do is this:
$var = "something,".rtnVal($var);
Easy ain't it? In case you didn't know, defining
function rtnVal(&$var) { ... }
instead of:
function rtnVal($var) { ... }
(notice the & symbol) means that $var is a 'placeholder' (passed by reference) and not actually passed. So when you use it, it doesn't have to be previously set.
There is one limitation to this though and that's working with Objects, they don't like being passed by reference this way so for those, I have yet to find a better solution.
This may not be an important issue but it bugs me anytime(quite often) I come across it, so I need to put my mind to rest. Please bear with me.
I am more used to seeing and using comparison operations like this:
if($some_var ==NULL){}
But Zend documentation(and only in zend do I notice this) always reverses the operands:
if(NULL ==$some_var){}
I'm not a computer scientist, so I would like to know if the order of these operands matter. Is there a difference or reason why zend documentation chooses the reverse style?
The result is the same. The advantage of this approach is what happens if the programmer accidentally types one equals instead of two:
if ($some_var = NULL) // this will perform an assignment instead of a comparison
if (NULL = $some_var) // this will give you a parse error
so it helps prevent those kinds of coding mistakes.
I prefer to use the Zend style for one reason. If you accidentally type = instead of == or === you will get an error. In the variable-first format, you will silently assign that value to your variable.
Ok, first of all, i suspect this is going to be closed.
Right, i have a question relating to using function calls inside statements as opposed to assigning to a variable first.
For example:
(code is in php, but question applies generally. Also, code is overly simplified)
if (myAmazingFunction() === true) {
// do something amazing
}
instead of
$amazingresult = myAmazingFuncton();
if ($amazingResult === true) {
// do something amazing
}
The question is:
Is there any performance, or other underlying pros or cons to each approach
Stylistically, is any of the approaches considered better than the other
In most languages, there will be no performance difference. In the first case, the compiler will allocate storage for the result of the function call before checking whether it is true. In the second case you're simply making this explicit.
If you are debugging, sometimes the second form is easier, as you can set a breakpoint on the second line and check the value returned by the function before the comparison is made - but then you see the result of the function by the path the executing code takes anyway in the example you've given. You can also re-use the value without rerunning the function, as Zac says in his comment.
Stylistically, this is going to be largely subjective. The only thing I'd say here is that if your variable name makes the purpose of the function output clear, then you might be adding something to the ability for others to understand your code easily.
#DavidM's answer is correct. However, I'd just like to add that stylistically, I think it depends on the name of the function and its context.
Example:
if ($food->tastesGood()) {
echo 'Mmmm!';
}
// vs.
$foodTastesGood = $food->tastesGood();
if ($foodTastesGood) {
echo 'Mmmm!';
}
In this case, it's very clear that the return value of the method tastesGood() is going to be a boolean from both the name of the method and its context. Using a temporary variable adds nothing to your code except making it redundant and less-readable at a glance. In addition, if the variable is not defined right before its used, then you have to go find the definition to understand the condition. In these cases, I would say use of a variable is worse.
Another example:
if ($dishes->wash() !== FALSE) {
echo 'Sparkly!';
}
// vs.
$dishesAreClean = $dishes->wash() !== FALSE;
if ($dishesAreClean) {
echo 'Sparkly!';
}
In this case, we can't really infer the return type of the wash() method from its name, and indeed, it would seem that it returns nothing on success and FALSE on errors. Checking if the dishes are clean then requires us to make sure that there were no errors, but the first case doesn't make for particularly readable or self-documenting code. The second case, however, adds very explicit information about what's going on by way of the temporary variable. In these cases, I would say use of a variable is better.
Is there any performance, or other underlying pros or cons to each approach
Performance-wise, assigning an extra variable that you will use only in your if condition will use extra memory, and one useless line of code. So it will use more memory. Will it be noticeable? Probably not.
Stylistically, is any of the approaches considered bad
Using the method in your if statement is perfectly valid, and I think it's a better approach, since you can read the code and see exactly what value is being tested in the if condition. No need to look for the variable and search where it was affected.
I have inherited some code in which the previous coder wrote this:
if (!not_null($page)) {
die('<b>Error!</b><br><b>Unable to determine the page link!<br><br>');
}
Is there any advantage to this, as opposed to just saying:
if (is_null($page))
It seems unnecessarily confusing to me.
not_null must be a local function.
It is not a standard PHP function.
Without knowing what not_null does, it is impossible to say. Since not_null is a user-defined function, it could be doing anything
If, after checking in to things, you find that not_null is doing a simple comparison, you could refactor the code in the name of readability:
if (is_null($page))
die('<p><strong>Error!</strong><br>Unable to determine the page link!</p>')
OR
if ($page === null)
die('<p><strong>Error!</strong>Unable to determine the page link!</p>')
P.S. - don't use <b> -- use <strong> instead. Also, when you need multiple line breaks, use a <p> instead of multiple <br>. Your sample code has an unclosed <b> tag on the error message.
Documentation
http://php.net/manual/en/function.is-null.php
There is no not_null function in PHP I believe.
What should you use?
Use === null or !== null instead of making a call to function explicitly.
It actually seems worse to do so because one can simply write in the if statement:
if($page==null) which evaluates quicker than using this function. You can also try if(!$page) which will evaluate quickly as well.
I am a C++ programmer starting with PHP. I find that I lose most of the debugging time (and my selfesteem!) due to undefined variables. From what I know, the only way to deal with them is to watch the output at execution time.
Are other strategies to notice these faults earlier (something like with C++ that a single compile gives you all the clues you need)?
This is a common complaint with PHP. Here are some ideas:
Use a code analysis tool. Many IDEs such as NetBeans will help also.
Just run the code. PHP doesn't have an expensive compilation step like C++ does.
Use unit testing. Common side effects include: better code.
Set error_reporting(-1), or the equivalent in your ini file.
Get xdebug. It's not preventative, but stack traces help with squishing bugs.
isset(), === null (identity operator), and guard clauses are your friends.
Loose and dynamic typing are a feature of the language. Just because PHP isn't strict about typing doesn't mean you can't be. If it really bugs you and you have a choice, you could try Python instead—it's a bit stricter with typing.
Log your E_NOTICE messages to a text file. You can then process logs with automated scripts to indicate files and lines where these are raised.
No. In PHP, you can only know a variable doesn't exist when you try to access it.
Consider:
if ($data = file('my_file.txt')) {
if (count($data) >= 0)
$line = reset($data);
}
var_dump($line);
You have to restructure your code so that all the code paths leads to the variable defined, e.g.:
$line = "default value";
if ($data = file('my_file.txt')) {
if (count($data) >= 0)
$line = reset($data);
}
var_dump($line);
If there isn't any default value that makes sense, this is still better than isset because you'll warned if you have a typo in the variable name in the final if:
$line = null;
if ($data = file('my_file.txt')) {
if (count($data) >= 0)
$line = reset($data);
}
if ($line !== null) { /* ... */ }
Of course, you can use isset1 to check, at a given point, if a variable exists. However, if your code relies on that, it's probably poorly structured. My point is that, contrary to e.g. C/Java, you cannot, at compile time, determine if an access to a variable is valid. This is made worse by the nonexistence of block scope in PHP.
1 Strictly speaking, isset won't tell you whether a variable is set, it tell if it's set and is not null. Otherwise, you'll need get_defined_vars.
From what I know the only way to deal with them is to watch the output at execution time.
Not really: To prevent these notices from popping up, you just need to make sure you initialize variables before accessing them the first time. We (sadly IMO) don't have variable declaration in PHP, but initializing them in the beginning of your code block is just as well:
$my_var = value;
Using phpDocumentor syntax, you can also kind of declare them to be of a certain a type, at least in a way that many IDEs are able to do code lookup with:
/** #desc optional description of what the variable does
#var int */
$my_var = 0;
Also, you can (and sometimes need to) use isset() / empty() / array_key_exists() conditions before trying to access a variable.
I agree this sucks big time sometimes, but it's necessary. There should be no notices in finished production code - they eat up performance even if displaying them is turned off, plus they are very useful to find out typos one may have made when using a variable. (But you already know that.)
Just watch not to do operations that requires the variable value when using it the first time, like the concatenate operator, .=.
If you are a C++ programmer you must be used to declare all variables. Do something similar to this in PHP by zeroing variables or creating empty array if you want to use them.
Pay attention to user input, and be sure you have registered globals off and check inputs from $_GET and $_POST by isset().
You can also try to code classes against structural code, and have every variable created at the beginning of a class declaration with the correct privacy policy.
You can also separate the application logic from the view, by preparing all variables that have to be outputted first, and when it goes to display it, you will be know which variables you prepared.
During development stages use
error_reporting(E_ALL);
which will show every error that has caused, all NOTICE errors, etc.
Keep an eye on your error_log as well. That will show you errors.
Use an error reporting system, example:
http://php.net/manual/en/function.set-error-handler.php
class ErrorReporter
{
public function catch($errno, $errstr, $errfile, $errline)
{
if($errno == E_USER_NOTICE && !defined('DEBUG'))
{
// Catch all output buffer and clear states, redirect or include error page.
}
}
}
set_error_handler(array(new ErrorReporter,'catch'));
A few other tips is always use isset for variables that you may / may not have set because of a if statement let’s say.
Always use if(isset($_POST['key'])) or even better just use if(!empty($_POST['key'])) as this checks if the key exists and if the value is not empty.
Make sure you know your comparison operators as well. Languages like C# use == to check a Boolean state whereas in PHP to check data-types you have to use === and use == to check value states, and single = to assign a value!
Unless I'm missing something, then why is no one suggesting to structure your page properly? I've never really had an ongoing problem with undefined variable errors.
An idea on structuring your page
Define all your variables at the top, assign default values if necessary, and then use those variables from there. That's how I write web pages and I never run into undefined variable problems.
Don't get in the habit of defining variables only when you need them. This quickly creates spaghetti code and can be very difficult to manage.
No one likes spaghetti code
If you show us some of your code we might be able to offer suggestions on how you can better structure it to resolve these sorts of errors. You might be getting confused coming from a C background; the flow may work differently to web pages.
Good practice is to define all variable before use, i.e., set a default value:
$variable = default_value;
This will solve most problems. As suggested before, use Xdebug or built-in debugging tools in editors like NetBeans.
If you want to hide the error of an undefined variable, then use #. Example: #$var
I believe that various of the Code Coverage tools that are available for PHP will highlight this.
Personally, I try and set variables, even if it's with an empty string, array, Boolean, etc. Then I use a function such as isset() before using them. For example:
$page_found = false;
if ($page_found==false) {
// Do page not found stuff here
}
if (isset($_POST['field'])) {
$value = $_POST['field'];
$sql = "UPDATE table SET field = '$value'";
}
And so on. And before some smart-ass says it: I know that query's unsafe. It was just an example of using isset().
I really didn't find a direct answer already here. The actual solution I found to this problem is to use PHP Code Sniffer along with this awesome extension called PHP Code Sniffer Variable Analysis.
Also the regular PHP linter (php -l) is available inside PHP Code Sniffer, so I'm thinking about customizing my configuration for regular PHP linting, detecting unused/uninitialized variables and validating my own code style, all in one step.
My very minimal PHPCS configuration:
<?xml version="1.0"?>
<ruleset name="MyConfig">
<description>Minimal PHP Syntax check</description>
<rule ref="Generic.PHP.Syntax" />
<rule ref="VariableAnalysis" />
</ruleset>