php ternary shorthand for use in template - php

I'm building my first application with Kohana, and using a basic templating system within that. In my templates I want to echo variables for the various contents of the page, but only if each variable is set, and I want to keep the code in the templates as short as possible, so something like this:
<?=$foo?>
works fine if the variable is set, but if it's not I get a notice. So I thought a ternary operator would do the trick nicely:
<?=$foo?:''?>
according to the PHP manual, from 5.3 it's ok to leave out the middle part and the above should output nothing if the variable isn't set, but I still get an error notice."Notice: Undefined variable: foo in /"
I can get the desired result with a slight alteration to suppress the notice:
<?=#$foo?:''?>
but I know that's generally not beset practice and would like a better solution if possible, while still keeping the code to a minimum in the template files.
the following works, but it's not as concise (mainly because my actual variables can be quite long):
<?=isset($foo)?$foo:'';?>
am I missing something or doing something wrong?

The ternary operation is not meant to replace checking with isset() - it needs it's variable defined or else you get a notice.
Template engines usually offer a way to use a default value instead, but they also do not use pure PHP code. You you are out of luck here: Either suppress the notice, or use the longer code variant. Or ensure that every variable is set, which enables you to consider any notice an error.

To avoid notices for undefined variables, you can create custom function that takes first parameter by reference
function tplvar(&$value, $default = '') {
return ($value !== null) ? $value : $default;
}
<?=tplvar($foo, 'bar');?>
Uninitialized variables passed by reference will be seen as nulls.

Related

Check if a variable is set in PHP

After 10 years of PHP programming, just now I found out that isset() is not good at all, according to PHP isset() manual, isset() returns false when variable 'is set' and has a null value.
Well, I was working with an array and I fixed this by array_key_exists(), just after about 2 hours of reading and testing codes.
but what to use for variables?
isset($var) || is_null($var)
This makes NOTICE when $var is not set... empty() is completely in another world too. As I said, is_null() makes NOTICE on not set variables and returns true!!... Well, this one is out, too.
A quick guide on is_null and empty can be found here, I used to reference it quite a bit.
As you noted, is_null is a complainer. The only way to test this without throwing a NOTICE is to check if the variable is set in the global vars array. You can do this by testing as such:
// Is Set (to anything)
if(!(isset($var) || array_key_exists('var',get_defined_vars()))){ /* ... */ }
// Set and Null
if(!(is_null($var) || array_key_exists('var',get_defined_vars())))) { /* ... */ }
// Etc
The problem here is that you are using get_defined_vars and this has significant performance impacts, both in wall-time and memory usage, if over-used, and I would suggest it should just be used for debugging.
If you are interested in strictly global variables, you can use the $GLOBALS supervar. However, this will not give you access to variables inside the scope of a function or method: https://stackoverflow.com/a/418162/1301994
If you are trying to avoid NOTICES to increase performance, this is not worth it, as the overhead for check is greater than the slight performance gains.
I guess it really matters what your goals are. If you are working in an OOP fashion, then you should be able to tell fairly clearly if a variable is set or not in the scope of the method. Otherwise, it would be best to not code in such a way that undefined and NULL behave differently. If this is not possible, consider utilizing other flags to help you get around the use of get_undefined_vars.
Check below for a little more on the topic:
Is var set to null same as undefined and how to check differences

php acceptable to generate errors when using the or command to assign variables?

In PHP I have the following code:
$my_var = $_GET['my_var'] or $my_var = 'empty';
Which gives a me a default value to fall back on if $_GET['my_var'] is not set and that works just fine. Although this does work as I want it to, it still generates an undefined variable warning in the error log.. is that acceptable or should I strive to never have anything go into the error log? If so can I suppress this error?
In the past I've used such functions as isset() or empty() but I like the closeness of this code to python's try.
A better way to set $my_var could be:
$my_var = isset($_GET['my_var']) ? $_GET['my_var'] : "empty";
You definitely don't want to pollute your error logs with unnecessary messages. It makes it hard to find errors you actually want to debug, and is probably making the interpreter work hard than it needs to.

Defining constants with $GLOBALS

I want to use a global variable setup where they are all declared, initialized and use friendly syntax in PHP so I came up with this idea:
<?
error_reporting(E_ERROR | E_WARNING | E_PARSE | E_NOTICE);
$GLOBALS['debugger'] = 1; // set $GLOBALS['debugger'] to 1
DEFINE('DEBUGGER','$GLOBALS["debugger"]'); // friendly access to it globally
echo "1:" . DEBUGGER . ":<br>";
echo "2:" . ${DEBUGGER}. ":<br>";
echo "3:" . $GLOBALS['debugger'] . ":<br>";
if (DEBUGGER==1) {echo "DEBUG SET";}
?>
generates the following:
1:$GLOBALS["debugger"]:
Notice: Undefined variable: $GLOBALS["debugger"] in /home/tra50118/public_html/php/test.php on line 8
2::
3:1:
How can there be an error with 2: when clearly $GLOBALS["debugger"] IS defined? And then not generate a similar notice with the test at line 10?
I think what I am trying to do is to force PHP to interpret a string ($GLOBALS["debugger"]) as a variable at run time i.e. a constant variable variable
Disclaimer: I agree with the comments, globals are generally a bad idea.
That said, there's a few questions here that are worth answering, and the concept of indirection is useful, so here goes.
${'$GLOBALS["debugger"]'} is undefined. You don't include the leading '$' when using indirection. So, the correct version would be define('DEBUGGER', 'GLOBALS["debugger"]').
But, this doesn't work either. You can only access one level down via indirection. So you can access the array $GLOBALS, but you can't access keys in that array. Hence, you might use :
define('DEBUGGER', 'debugger');
${DEBUGGER};
This isn't useful, practically. You may as well just use $debugger directly, as it's been defined as a global and will be available everywhere. You may need to define global $debugger; at the start of functions however.
The reason your if statement is not causing notices is because you defined DEBUGGER to be a string. Since you aren't trying to use indirection in that line at all, it ends up reading as:
if ("$GLOBALS['debugger']"==1) {echo "DEBUG SET";}
This is clearly never true, though it is entirely valid PHP code.
I think you may have your constants crossed a bit.
DEFINE('DEBUGGER','$GLOBALS["debugger"]'); sets the constant DEBUGGER to the string $GLOBALS["debugger"].
Note that this is neither the value nor the reference, just a string.
Which causes these results:
1: Output the string $GLOBALS["debugger"]
2: Output the value of the variable named $GLOBALS["debugger"]. Note that this is the variable named "$GLOBALS["debugger"]", not the value of the key "debugger" in the array $GLOBALS. Thus a warning occurs, since that variable is undefined.
3: Output the actual value of $GLOBALS["debugger"]
Hopefully that all makes sense.
OK, thanks to all who answered. I think I get it now, I am new to PHP having come form a C++ background and was treating the define like the C++ #define and assuming it just did a string replace in the precompile/run phase.
In precis, I just wanted to use something like
DEBUGGER = 1;
instead of
$GLOBALS['debugger'] = 1;
for a whole lot of legitimate reasons; not the least of which is preventing simple typos stuffing you up. Alas, it appears this is not doable in PHP.
Thanks for the help, appreciated.
You can not use "variable variables" with any of the superglobal arrays, of which $GLOBALS is one, if you intend to do so inside an array or method. To get the behavior you would have to use $$, but this will not work as I mentioned.
Constants in php are already global, so I don't know what this would buy you from your example, or what you are going for.
Your last comparison "works" because you are setting the constant to a string, and it is possible with PHP's typecasting to compare a string to an integer. Of course it evaluates to false, which might be surprising to you, since you expected it to actually work.

isset needs to be used on all variables in if statements?

If I use an if statement like,
<?php if(strlen($variable) >=3) { ... } ?>
and the variable isn't actually set, this would bring me an error in PHP 5(3.6). But if I use isset() and strlen(), it would not. However, in PHP 4(4.9), when I use only strlen(), it comes through completely fine, without any errors.
Is this something that has changed since version 4.4.9? If not, is there a way I can bring back the old fashioned way of checking variables in php.ini? I find it really strange that you'd have to run both of these functions on a variable, as it really makes the code more messy than necessary. Especially if you're working with ternary operators...
To be exact, I'm working on a GET variable, and I'm getting these kind of errors:
Undefined index: x in file.php on line x
Although I agree with #deceze's comment, the reason you are seeing these messages, is not a difference between php 4 and php 5 but a difference in your error reporting settings. Now you are showing E_NOTICE and before you were not.

PHP and undefined variables strategy

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>

Categories