I'm reading a PHP book where the author says that symbols should be avoided except when it's valid to use them. Very informative stuff, if he could only elaborate or give code examples but he doesn't.
Can someone from the experienced PHP bunch give me an example of what these symbols are, and when it makes sense to use them or not. I'm looking for a code example that I can wrap my head around since I don't quite get it in plain English. Code is more plain English to me.
Edit:
He says you can define a string as a 'symbol' which will make this string global and constant, but it's good for security since it's constant and can't be changed by mistake or intentionally. I'm looking for concrete examples of when those symbols are used.
I assume you mean Constants?
One big reason against using those in PHP is the fact that if a constant was not defined, PHP will interpret it as a string value and throw only a notice, not a fatal error as it - in my humble opinion - should.
define("NUMBER_OF_PAGES", 1);
echo NUMBER_OF_PAGES; // outputs "1"
echo NUMBR_OF_PAGES; // outputs literally "NUMBER_OF_PAGES" and throws
// Notice: Use of undefined constant NUMBR_OF_PAGES -
// assumed 'NUMBR_OF_PAGES' in xyz
// constant() is a workaround but it takes away a lot
// of a constant's elegance IMO:
echo constant("NUMBER_OF_PAGES"); // Returns "1"
echo constant("NUMBR_OF_PAGES"); // Returns null
this makes the use of constants somewhat error-prone, especially in older applications that trigger dozens or hundreds of E_NOTICE messages by using less-than-perfect coding practices (but nothing anywhere near as bad as using an undefined constant) and make it hard to detect errors.
The cleaner your application, the less of a problem this becomes, because dealing with (and noticing) E_NOTICE level messages becomes possible. Still, an undefined constant should be a fatal error in my mind, and I regard this a design error.
IDEs with lookup functions can ease the problem somewhat, and help avoid typos.
There is also the function constant() that will return NULL if given a non-existent constant name. (See the code above.)
Concrete usage examples:
WordPress list of constants
Joomla constants
Constants are often used to define the current path and / or URL:
define("APP_WEBROOT", "http://www.mydomain.com/appname");
define("APP_WEBROOT_PATH", "/var/www/mydomain/appname");
or to define global settings:
define("MAX_PAGES", 100);
define("MAX_ITEMS_PER_PAGE", 250);
Related
Is there any way to easily fix this issue or do I really need to rewrite all the legacy code?
PHP Fatal error: Call-time pass-by-reference has been removed in ... on line 30
This happens everywhere as variables are passed into functions as references throughout the code.
You should be denoting the call by reference in the function definition, not the actual call. Since PHP started showing the deprecation errors in version 5.3, I would say it would be a good idea to rewrite the code.
From the documentation:
There is no reference sign on a function call - only on function definitions. Function definitions alone are enough to correctly pass the argument by reference. As of PHP 5.3.0, you will get a warning saying that "call-time pass-by-reference" is deprecated when you use & in foo(&$a);.
For example, instead of using:
// Wrong way!
myFunc(&$arg); # Deprecated pass-by-reference argument
function myFunc($arg) { }
Use:
// Right way!
myFunc($var); # pass-by-value argument
function myFunc(&$arg) { }
For anyone who, like me, reads this because they need to update a giant legacy project to 5.6: as the answers here point out, there is no quick fix: you really do need to find each occurrence of the problem manually, and fix it.
The most convenient way I found to find all problematic lines in a project (short of using a full-blown static code analyzer, which is very accurate but I don't know any that take you to the correct position in the editor right away) was using Visual Studio Code, which has a nice PHP linter built in, and its search feature which allows searching by Regex. (Of course, you can use any IDE/Code editor for this that does PHP linting and Regex searches.)
Using this regex:
^(?!.*function).*(\&\$)
it is possible to search project-wide for the occurrence of &$ only in lines that are not a function definition.
This still turns up a lot of false positives, but it does make the job easier.
VSCode's search results browser makes walking through and finding the offending lines super easy: you just click through each result, and look out for those that the linter underlines red. Those you need to fix.
PHP and references are somewhat unintuitive. If used appropriately references in the right places can provide large performance improvements or avoid very ugly workarounds and unusual code.
The following will produce an error:
function f(&$v){$v = true;}
f(&$v);
function f($v){$v = true;}
f(&$v);
None of these have to fail as they could follow the rules below but have no doubt been removed or disabled to prevent a lot of legacy confusion.
If they did work, both involve a redundant conversion to reference and the second also involves a redundant conversion back to a scoped contained variable.
The second one used to be possible allowing a reference to be passed to code that wasn't intended to work with references. This is extremely ugly for maintainability.
This will do nothing:
function f($v){$v = true;}
$r = &$v;
f($r);
More specifically, it turns the reference back into a normal variable as you have not asked for a reference.
This will work:
function f(&$v){$v = true;}
f($v);
This sees that you are passing a non-reference but want a reference so turns it into a reference.
What this means is that you can't pass a reference to a function where a reference is not explicitly asked for making it one of the few areas where PHP is strict on passing types or in this case more of a meta type.
If you need more dynamic behaviour this will work:
function f(&$v){$v = true;}
$v = array(false,false,false);
$r = &$v[1];
f($r);
Here it sees that you want a reference and already have a reference so leaves it alone. It may also chain the reference but I doubt this.
The PHP's documentation describes define() function with the constant name as a string. So it should be in quotes:
define('ANY_CONSTANT',1);
However I saw tons of examples with no quotes like this:
define(ANY_CONSTANT,1);
I also tested both ways in online PHP tester and both worked correctly.
Can anyone explain a little bit: is there any difference between those two methods? Is any of those better? In which circumstances?
Short answer: Yes you should.
If you don't, PHP will look for a constant with that name, not find one, and assume it's a string and use it as such. This will generate a notice in the error log, if you set PHP to report notices.
Using
define(ANY_CONSTANT,1);
will cause a warning:
PHP Notice: Use of undefined constant ANY_CONSTANT - assumed
'ANY_CONSTANT'
So you definitely need to use quotes.
(As always) It depends on the context:
define(ANY_CONSTANT,1);
is legal, since passing a literal (such as 'ANY_CONSTANT') is not mandatory - you could also pass a constant (such as PHP_OS aswell). You have to make sure not mixing up literals with constants.
Why it "works" for you is a dangerous approach, since in one case you might have simply forgot the quotation, but in another case you'd definitly wanted to use a constant. Hence the notices PHP will throw.
First off I know this question has gone around more than once here:
Why should I fix E_NOTICE errors?
Why should I fix E_NOTICE errors? Pros and cons
But the more that I fix all E_NOTICEs (as people say you should) the more I notice that:
I am micro-optimising
I am actually making more code and making my code harder to mantain and slower
Take an example:
Say your using the MongoDB PHP driver and you have a MongoDate object in a class var named ts within a class that represents a single row in a collection in your database. Now you acces this var like: $obj->ts->sec but PHP throws a fit (E_NOTICE) because ts in this case is not defined as an object in itself because this particular row does not have a ts field. So you think this is OK, this is desired behaviour, if it's not set return null and I will take care of it myself outside of the interpreters own robotic workings (since you wrap this in a date() function that just returns 1970 if the var is null or a none-object).
But now to fix that E_NOTICE as another developer really wants me to since having ANY E_NOTICEs is terribad and it makes the code slower to not do it according to the errors. So I make a new function in the $obj class called getTs and I give it 3 lines, literally to do nothing but check if the ts var is a MongoDate object and return it if it is...
WHY? Can't PHP do this perfectly fine for me within its much faster interpreter than having to do it within the runtime of the app itself? I mean every where I am having to add useless bumpth to my code, pretty much empty functions to detect variables that I actually just handle with PHPs own ability to return null or checking their instanceof when I really need to (when it is vital to the operation and behaviour of the said function) and don't get me started on the isset()s I have added about 300 lines of isset()s, it's getting out of hand. I have of course got to make this getTs functions because you can't do:
class obj{
public $ts = new MongoDate();
}
I would either have to store the ts within the __constructor (which I am not too happy about either, I am using a lot of magics as it is) or use a function to detect if it's set (which I do now).
I mean I understand why I should fix:
Undefined vars
Assigning properties of unset vars (null vars)
constant errors etc
But if you have tested your code and you know it is safe and will only work the way you desire what is the point in fixing all of the undefined index or none-object errors? Isn't adding a bunch of isset()s and 2 lines functions to your code actually micro-optimisation?
I have noticed after making half my site E_NOTICE compliant that actually it uses more CPU, memory and time now...so really what's the point of dealing with every E_NOTICE error and not just the ones that ARE errors?
Thanks for your thoughts,
You do certainly do get better performance by using isset(). I did some benchmarks, not too long ago, and just hiding errors came out to be about 10x slower.
http://garrettbluma.com/2011/11/14/php-isset-performance/
That said, performance usually isn't a critical factor in PHP. What does, personally drive me crazy is silent errors.
When the interpreter chooses to not flag something as an error (which could lead to instability) is a huge problem. PHP in particular has a tendency to
warn about things that should error (e.g. failure to connect to database) and
issue notices about things that ought to warn (e.g. attempting to access a member of a null object).
Perhaps I'm just overly opinionated about this kind of stuff but I've been bitten before by these silent errors. I recommend always including E_NOTICE in error reporting.
Whether or not you should fix them is certainly debatable, and will just depend on the return in your situation; eg, it's more important if the code will have a longer life-span, more devs, etc.
In general, assuming that your functions will be used (and mis-used) by someone else is the best practice, so you should do isset/!empty/is_object checks to account for this. Often, your code will find it's way into uses and situations you never intended it for.
As far as performance, Every time any kind of error is thrown--E_NOTICE included--the interpreter spins up the error handler, builds a stack trace, and formats the error. The point is that, whether or not you have them reporting, errors always slow execution; therefore, 2-3 function calls to avoid an E_NOTICE will still improve your performance.
Edit:
Alternatives for the above example
I wouldn't necessarily create extra objects to avoid the errors; you can gracefully avoid them without. Here are a couple of options:
1) Function that handles missing ts:
SpecialClass class {
funciton getTs () {
return !empty($this->ts) ? $ts->sec : false;
}
}
2) Deal with missing ts in template/procedure:
if (!empty($obj->ts->sec)) {
//do something
}
I particularly like empty() because you can use it to replace of (isset($var) && ($var or 0 != $var //etc)), saving multiple calls/comparisons and empty never throws notices for the target var or attribute. It will throw an error if you're calling it on a proptery/member of a non-existent variable.
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.
So I'm working on cleanup of a horrible codebase, and I'm slowly moving to full error reporting.
It's an arduous process, with hundreds of notices along the lines of:
Notice: Undefined index: incoming in /path/to/code/somescript.php on line 18
due to uses of variables assuming undefined variables will just process as false, like:
if($_SESSION['incoming']){
// do something
}
The goal is to be able to know when a incorrectly undefined variable introduced, the ability to use strict error/notice checking, as the first stage in a refactoring process that -will- eventually include rewriting of the spots of code that rely on standard input arrays in this way. There are two ways that I know of to replace a variable that may or may not be defined
in a way that suppresses notices if it isn't yet defined.
It is rather clean to just replace instances of a variable like $_REQUEST['incoming'] that are only looking for truthy values with
#$_REQUEST['incoming'].
It is quite dirty to replace instances of a variable like $_REQUEST['incoming'] with the "standard" test, which is
(isset($_REQUEST['incoming'])? $_REQUEST['incoming'] : null)
And you're adding a ternary/inline if, which is problematic because you can actually nest parens differently in complex code and totaly change the behavior.
So.... ...is there any unacceptable aspect to use of the # error suppression symbol compared to using (isset($something)? $something : null) ?
Edit: To be as clear as possible, I'm not comparing "rewriting the code to be good" to "#", that's a stage later in this process due to the added complexity of real refactoring. I'm only comparing the two ways (there may be others) that I know of to replace $undefined_variable with a non-notice-throwing version, for now.
Another option, which seems to work well with lame code that uses "superglobals" all over the place, is to wrap the globals in dedicated array objects, with more or less sensible [] behaviour:
class _myArray implements ArrayAccess, Countable, IteratorAggregate
{
function __construct($a) {
$this->a = $a;
}
// do your SPL homework here: offsetExists, offsetSet etc
function offsetGet($k) {
return isset($this->a[$k]) ? $this->a[$k] : null;
// and maybe log it or whatever
}
}
and then
$_REQUEST = new _myArray($_REQUEST);
This way you get back control over "$REQUEST" and friends, and can watch how the rest of code uses them.
You need to decide on your own if you rate the # usage acceptable or not. This is hard to rate from a third party, as one needs to know the code for that.
However, it already looks like that you don't want any error suppression to have the code more accessible for you as the programmer who needs to work with it.
You can create a specification of it in the re-factoring of the code-base you're referring to and then apply it to the code-base.
It's your decision, use the language as a tool.
You can disable the error suppression operator as well by using an own callback function for errors and warnings or by using the scream extension or via xdebug's xdebug.scream setting.
You answered you question yourself. It suppress error, does not debug it.
In my opinion you should be using the isset() method to check your variables properly.
Suppressing the error does not make it go away, it just stops it from being displayed because it essentially says "set error_reporting(0) for this line", and if I remember correctly it would be slower than checking isset() too.
And if you don't like the ternary operator then you should use the full if else statement.
It might make your code longer but it is more readable.
I would never suppress errors on a development server, but I would naturally suppress errors on a live server. If you're developing on a live server, well, you shouldn't. That means to me that the # symbol is always unacceptable. There is no reason to suppress an error in development. You should see all errors including notices.
# also slows things down a bit, but I'm not sure if isset() is faster or slower.
If it is a pain to you to write isset() so many times, I'd just write a function like
function request($arg, $default = null) {
return isset($_REQUEST[$arg]) ? trim($_REQUEST[$arg]) : $default;
}
And just use request('var') instead.
Most so-called "PHP programmers" do not understand the whole idea of assigning variables at all.
Just because of lack of any programming education or background.
Well, it isn't going a big deal with usual php script, coded with considerable efforts and consists of some HTML/Mysql spaghetti and very few variables.
Another matter is somewhat bigger code, when writing going to be relatively easy but debugging turns up a nightmare. And you are learn to value EVERY bloody error message as you come to understanding that error messages are your FRIENDS, not some irritating and disturbing things, which better to be gagged off.
So, upon this understanding you're learn to leave no intentional errors in your code.
And define all your variables as well.
And thus make error messages your friends, telling you that something gone wrong, lelping to hunt down some hard-spotting error which caused by uninitialized variable.
Another funny consequence of lack of education is that 9 out of 10 "PHP programmers" cannot distinguish error suppression from turning displaying errors off and use former in place of latter.
I've actually discovered another caveat of the # beyond the ones mentioned here that I'll have to consider, which is that when dealing with functions, or object method calls, the # could prevent an error even through the error kills the script, as per here:
http://us3.php.net/manual/en/language.operators.errorcontrol.php
Which is a pretty powerful argument of a thing to avoid in the rare situation where an attempt to suppress a variable notice suppressed a function undefined error instead (and perhaps that potential to spill over into more serious errors is another unvoiced reason that people dislike #?).