Undefined Constant Is a String?
From the I-noticed-this-one-day-while-looking-at-a-co-worker’s-code department comes a tale about the use of undefined constants in PHP—and relying on this twisted “feature” to make an application function properly.
So, there I was, looking over the shoulder of a colleague, trying to help him debug an application, when I saw something that struck me as quite odd.
“Where is that constant defined?” I asked, pointing to the screen.
“What constant?”
It was plain as day to me. “That constant.”
“Oh, that’s not a constant. It’s a string.”
“No, it’s not,” I retorted. “If it’s a string, why are there no quotation marks. You should be getting an error about an undefined constant.”
“No, it’s a feature of PHP. You don’t have to use the quotation marks.”
And then it struck me: If this is a feature, it’s most likely the dumbest feature I’ve ever seen. Perhaps Jamie of Yet Another Web Development Blog has something there…
Nevertheless, after digging around some, I found this in the PHP 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. See also the manual entry on why
$foo[bar]is wrong (unless you firstdefine()bar as a constant).
Hmm. So, if $foo[bar] is wrong, remind me again why this is even a feature? In my opinion, it should return, at the least, an E_WARNING error. Instead, the error level is E_NOTICE, but let’s examine what happens when the millions of people running distribution versions of PHP use this “feature.”
Most distributions use the php.ini-dist file instead of the php.ini-recommended file. This means that error_reporting is set to E_ALL & ~E_NOTICE and notices are not generated or displayed, so users will never know about their undefined constants. My colleague was using the PHP distribution for Debian, Debian’s default php.ini settings, and, thus, was never aware of the notices generated by the use of undefined constants. What’s worse is that the code my colleague was using was not his own—it was third-party, open source software!
Here’s why it’s bad to rely on this behavior. In the follow snippet of code, I expect that foo will always be the string “foo.” The problem is that I’m really using an undefined constant.
$var = foo;
my_func($var);
When I pass $var to my_func(), the function may do something that expects the value to be the string “foo,” but what if the core developers decide to add a constant with the name foo and assign it a value. Now, $var is no longer the string “foo.” It’s whatever the language has defined it to be. That’s an unlikely scenario, but what if another programmer in my team decides to define the constant foo (this is much more likely)? Now, the code mysteriously breaks, and it’s difficult to find the problem (especially because E_NOTICE is turned off). Even worse: what if I’m using third-party code that relies on this behavior, and I’ve integrated it into my application that has some global constants by the same names set?!
So, the lessons learned from this are:
- Always develop applications using an error level of
E_ALLorE_STRICT; when you encounter notices, fix them because they could indicate problems with your logic that may not immediately present themselves - If you want a string value, use quotation marks (single or double), otherwise, treat your “string” as a constant because that’s what it is
- Don’t rely on the use of undefined constants—they aren’t
NULLor empty; instead, they are actually the string value of the constant name
Note that this behavior of constants continues to exist even in the latest versions of PHP, including 5.2 and 6.0.0-dev. Why? I don’t know.
10 Comments
>Note that this behavior of constants continues to exist even in the latest versions of PHP,
>including 5.2 and 6.0.0-dev. Why? I don’t know.
There are 0 reasons to change it except for if you're to break tons of poorly written apps (even if they deserve it).
Slap your coworker with a E_ALL|E_STRICT bat and don't waste so much ink to explain something that exists since years ;-)
The fact that you ask if it is really a feature makes me wonder ... bah :)
Unquoted strings were a feature of PHP before PHP 3.0,
quoting strings was optional if there was no whitespace within the string.
With the introduction of constants in PHP 3 this has become a backward
compatibility burden, but even with the warning added in at least 4.0
(or was it already in 3.0?) there is still lots of code out there that
relies on this.
But on the other hand this one was the easiest to fix and the least likely to break of the NOTICE level warnings that i have encountered in making legacy code E_NOTICE free, there are way more serious things reported by it like use of uninitialized variables etc.
So always coding with E_NOTICE on is a must for any serious PHP developer IMHO, and just the fact that you inherited legacy code
to maintain is no excuse to have it set to off ... but that might just be me ...
You will see this a lot in legacy code that many developers have worked on. The current codeset that i am working on has a ton of $foo[bar] and throws many errors about undefined constants. Time to cleanup and use proper methods.
This problem has bit me before too.
The worst is when people do conditional checks against undefined constants...
if(SUPER_CONSTANT){
do this
}
It will always pass if the constant isn't defined. Now I always develop with notices turned on.
Am I showing my age when I was really confused that you were confused with the contants parsed as strings when not defined?
Old post, but for those coming across this page who are also wondering "why?", the feature was inspired by Perl. Barewords in Perl become strings, unless you use the "strict" module. The feature, both in Perl and PHP, is not officially deprecated but is considered harmful.
Reference:
Wall, Larry; "Programming Perl", 3rd Ed; p860
Joseph Crawford, i went around this using
if (constant('SUPER_CONSTANT')) {
#won't be executed if constant is not defined
#as constant() function returns FALSE
}
I'm not sure the comparison to Perl is valid...
PHP:
php -r 'error_reporting(E_ALL);if(SOMETHING) { echo "hello\n"; }'
Notice: Use of undefined constant SOMETHING - assumed 'SOMETHING' in Command line code on line 1
hello
Perl:
perl -e 'use strict;if(SOMETHING) { echo "hello\n"; }'
String found where operator expected at -e line 1, near "echo "hello\n""
(Do you need to predeclare echo?)
syntax error at -e line 1, near "echo "hello\n""
Bareword "SOMETHING" not allowed while "strict subs" in use at -e line 1.
Execution of -e aborted due to compilation errors.
- Greg
Sorry should have said:
Perl:
perl -e 'use strict;if(SOMETHING) { print "hello\n"; }'
Bareword "SOMETHING" not allowed while "strict subs" in use at -e line 1.
Execution of -e aborted due to compilation errors.
- Greg