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.
Related
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.
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.
Stupid question - I'm surprised this one has bitten me. Why do undefined constants in PHP evaluate to true?
Test case:
<?php
if(WHATEVER_THIS_ISNT_DEFINED)
echo 'Huh?';
?>
The above example prints 'Huh?'
Thanks so much for your help! :)
Try defined('WHATEVER_THIS_ISNT_DEFINED')
When PHP encounters a constant that is not defined, it throws an E_NOTICE, and uses the constant name you've tried to use as a string. That's why your snippet prints Huh!, because a non-empty string (which is not "0") will evaluate to true.
From the manual:
If you use an undefined constant, PHP
assumes that you mean the name of the
constant itself, just as if you called
it as a string (CONSTANT vs
"CONSTANT"). An error of level
E_NOTICE will be issued when this
happens.
If you set your error reporting level to report E_NOTICEs, which is a good practice during development, you will also see the notice thrown.
PHP Constant Syntax
defined()
Casting to Boolean
error_reporting
error_reporting() function
From the manual:
If you use an undefined constant, PHP assumes that you mean the name of the constant itself, just as if you called it as a string (CONSTANT vs "CONSTANT").
Basically, if WHATEVER_THIS_ISNT_DEFINED isn't defined, PHP interprets it as "WHATEVER_THIS_ISNT_DEFINED". Non-empty strings evaluate to true, so your expression will always pass (unless WHATEVER_THIS_ISNT_DEFINED is defined and set to a falsey value.)
This is, frankly, stupid behaviour. It was implemented, I believe, to allow things like $foo[bar] to work when the programmer should have used $foo['bar']. It's illogical behaviour like this that makes people think PHP isn't a real programming language.
The way to test whether a constant is defined is with defined.
Undefined constants are treated as strings by PHP: docs. Taking that fact, think it through in English language:
If "WHATEVER_THIS_ISNT_DEFINED", then do something.
... it is logical that it is "true" - you aren't comparing anything to anything else.
That is why, when doing if statements, it is best practice to include a specific evaluation. If you're checking for false, put it in the code: if (something === false) vs if (something). If you're checking to see if it is set, use isset, and so on.
Also, this highlights the importance of developing with notices and warnings enabled. Your server will throw a notice for this issue:
Notice: Use of undefined constant
MY_CONST - assumed 'MY_CONST' in
some_script.php on line 5
Turn on notices and warnings to develop, turn them off for production. Can only help!
Try defined(). If it's not defined then the constant assumes it's simply text.
Note that constant name must always be quoted when defined.
e.g.
define('MY_CONST','blah') - correct
define(MY_CONST,'blah') - incorrect
also
<?php
if (DEBUG) {
// echo some sensitive data.
}
?>
and saw this warning:
"Use of undefined constant DEBUG - assumed 'DEBUG'"
A clearer workaround is to use
<?php
if (defined('DEBUG')) {
// echo some sensitive data.
}
?>
See http://php.net/manual/en/language.constants.php
It's not just constants, it is a much broader issue with PHP's parsing engine. (You ought to see warnings in your logs.)
In PHP, "bare words" that it doesn't recognize are generally treated as strings that happen to be missing their quotes, and strings with a non-zero length tend to evaluate to true.
Try this:
$x = thisisatest ;
$y = "thisisatest";
if($x == $y){
echo("They are the same");
}
You should see "They are the same".
Old question, but in addition to defined() you can also use strict type checking using ===
<?php
if(WHATEVER_THIS_ISNT_DEFINED === true) // Or whatever type/value you are trying to check
echo 'Huh?';
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>
Is it okay to use array without single or double quotion like $array[key]? I thought it is bad because PHP look for constant first if I don't use single or double quotation. One of my colleagues told me that it does not matter.
What do you guys think?
It is not considered as OK -- even if it will work in most cases.
Basically, when PHP sees this :
echo $array[key];
It will search for a constant, defined with define, called key -- and, if there is none, if will take the 'key' value.
But, if there is something like this earlier in your code :
define('key', 'glop');
It will not take
echo $array['key'];
anymore ; instead, it'll use the value of the key constant -- and your code will be the same as :
echo $array['glop'];
In the end, not putting quotes arround the key's name is bad for at least two reasons :
There is a risk that it will not do what you expect -- which is very bad
It might, today...
But what about next week / month / year ?
Maybe, one day, you'll define a constant with the wrong name ;-)
It's not good for performance :
it has to search for a constant, before using 'key'
And, as said in a comment, it generates notices (even if you disable error_reporting and display_errors, the notices/warnings/errors are still generated, even if discarded later)
So : you should not listen to that guy on this point : he is wrong : it does matter.
And if you need some "proof" that's "better" than what people can tell you on stackoverflow, you can point him to this section of the manual, as a reference : Why is $foo[bar] wrong?
This is not okay and to add to what others have said, it will trigger an error in most cases:
8 Notice Use of undefined constant key - assumed 'key' in file: 'index.php' on line 46
See the section in the PHP Manual for "Why is $foo[bar] wrong?" under "Array do's and don'ts" on this page: http://php.net/manual/en/language.types.array.php
This is wrong and will auto-define a constant:
$var = $array[bar];
This usage however is correct:
$var = "string $array[bar] ...";
For compatibility with PHP2 this old syntax is still allowed in string context. Quoting the key would lead to a parse error, unless you also use { curly braces } around it.
From the PHP Manual - Why is $foo[bar] wrong?
Always use quotes around a string literal array index. For example, $foo['bar'] is correct, while $foo[bar] is not. But why? It is common to encounter this kind of syntax in old scripts:
<?php
$foo[bar] = 'enemy';
echo $foo[bar];
// etc
?>
This is wrong, but it works. The reason is that this code has an undefined constant (bar) rather than a string ('bar' - notice the quotes). PHP may in future define constants which, unfortunately for such code, have the same name. It works because PHP automatically converts a bare string (an unquoted string which does not correspond to any known symbol) into a string which contains the bare string. For instance, if there is no defined constant named bar, then PHP will substitute in the string 'bar' and use that.
There is some more examples in the manual for you to check out.
Unless the key actually is a constant, there is no reason for you not to be putting quotes around the key.
The way PHP works is it looks for the constant value of what you've put, but it takes the string representation of it if the constant cannot be found.
If someone were to edit your code down the road and add a constant with that key name, it would just cause more headaches.
It's bad practice to not quote key values, for a number of reasons:
Potential collisions with meaningful symbol names, such as define'd constants.
Some keys can't be expressed without quoting (for instance, the key "]").
Bad habits can bite you later on (namely in regards to #1 and #2).
Performance - searching for define's takes time.
If you're wanting to avoid typing quotes around names that are just standard elements of a thing you're passing around a lot, perhaps you might want to use objects instead, which take a object->property syntax instead of an $array["element"] syntax.