PHP legacy code mysteries: Case-sensitivity in variable names - php

I'm trying to make the client's legacy PHP application to work for PHP 5.3. I see this kind of code a lot:
$SETTINGS[cart][picsize] = "100";
$settings[user][time] = 123;
1) Are variables case-insensitive in PHP? Is $SETTINGS same as $settings?
2) I'm getting 'undefined constant' for the cart, picsize, etc. and they don't seem to be defined anywhere in the codebase. Did some older versions of PHP allow referencing array indices without quotes, or what is going on here?

Variables are case sensitive, functions and classes are not.
It could have been that your PHP configuration had a certain level of error reporting turned off and you never saw the error. It might have worked for you because those undefined constants will get turned into strings. (credit: #watcher). You should always reference associate array indexes with quotes, unless they are not a string. Strings should always be quoted, unless you have defined a constant. You do not have to quote integers, double or bool.
$SETTINGS["cart"]["picsize"] = 100;
$settings[user][time] = 123;
Those two statements are completely different and are in no way related as far as variables are concerned. Regardless if there only difference is upper case and lower case. They are still not the same.

Related

PHP 7.2 warning for unquoted string in arrays inconsistency

This gives the classical warning (that in next version will be an error):
$var[$otherVar[someIndex]] = $myValue;
This does not:
$var["$otherVar[someIndex]"] = $myValue;
Both works the same.
Shouldn't PHP 7.2 warn in both cases?
In case it does not, what is the justification to allow the second one, with the new rules PHP new versions tries to impose?
String interpolation doesn't require that the key be quoted, because the key is already a string. It won't be interpreted as a constant (which is the point of the message you get from your first example), and there's no chance that you meant to get $otherVar[someIndex()], because that wouldn't even work as intended in this context. So the problems with barewords simply don't apply.
You will get a message if you tried to say $var["{$otherVar[someIndex]}"]. Variables interpolated that way work more like you expect, and someIndex will again be interpreted as a constant.

Proper usage of constants in PHP

So I have recently learned about constants in PHP. fortunately there is not really anything to learn on them, they appear to be just an entity which stores value of something, just like a variable.
The only different that I can see (Correct me if I am wrong) is that a constant cannot be assigned a new value (that is it?). Which makes sense asthetically for example if you are making a config file with configurations that should not be changed.
But surely it brings other benefits/usage than just using a variable anyway?
In what situations should I be choosing to use constants?
If you find yourself setting a variable for convenience and never changing it during a script, the chances are you should be using a constant instead. Constants are like variables except that once they are defined they cannot be undefined or changed - they are constant as the name suggests. In many languages, constants are faster than variables and so are recommended, but this is not the case as much in PHP - although they are perhaps a small amount faster, the primary advantage to using constants is the fact that they do not have a dollar sign at the front, and so are visibly different from variables. Furthermore, constants are automatically global across your entire script, unlike variables.
To set a constant, use the define() function - it takes two parameters, with the first being the name of the constant to set, and the second being the value to which you wish to set it. For example, this following line of code sets the variable CURRENT_TIME to be the return value of the time() function, then prints it out:
define("CURRENT_TIME", time());
print CURRENT_TIME;
Note that it is not $CURRENT_TIME or Current_Time - constants, like variables, are case sensitive, but unlike variables they do not start with a dollar sign. You can change this behaviour by passing true as a third parameter to define(), which makes the constant case-insensitive:
define("CURRENT_TIME", time(), true);
print Current_TiMe;
There are two helpful functions available for working with constants, and these are defined() and constant(). The defined() function is basically the constant equivalent of isset(), as it returns true if the constant string you pass to it has been defined. For example:
define("CURRENT_TIME", time(), true);
if (defined("CURRENT_time")) {
/// etc }
Note that you should pass the constant name into defined() inside quotes.
A key characteristic of constants is that they behave like functions and classes, in the sense that they need to be defined when you try to use them, otherwise PHP will produce an error; and that they're global and unconcerned by scope. This makes them ideal for flags in function parameters, where the value is relatively unimportant, yet requires unique identification. Just look at how PHP itself uses constants:
json_encode($foo, JSON_UNESCAPED_UNICODE)
pathinfo($bar, PATHINFO_FILENAME)
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION)
What would the alternatives be?
$pdo->setAttribute('errmode', 'exception')
Strings cannot be checked for existence the same way constants can, typos are easier and require more manual error handling inside the function, and strings cannot be introspected the same way you can introspect PDO for its constants, and hence discoverability of available options is reduced.
$pdo->setAttribute(1, 2)
Magic numbers are obviously even worse, with even less meaning and the same lack of enforcement and discoverability.
In these cases, constants are ideal. They provide an enforced, readable identifier for a meaning; the actual value the constant holds is relatively unimportant.
Constants are for when someone else might read and try to install your code, but doesn't know what bits they should change to add their own details in. It's especially useful if you don't have an installation script, as the new admin will need to set their own details.
Example:
You need to install a new script on your website that uses a database of some kind. You'd open up the main file, and edit the constants that you need to add in your database connection details. Because they're constants, you know they shouldn't change - And they shouldn't, otherwise your database wouldn't work! If you use variables, the values could be accidentally changed in the script. It's also clearer to know which parts you have to change - and as they're usually at the start of a document or in a separate file easy to identify as well.

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.

PHP: Using spaces in associative array indices

Is this bad practice/can cause problems?
$_SESSION['stuff to keep']
As opposed to calling str_replace() on the indices.
This is bad practice, but not because of the space.
// file foo.php
$_SESSION['stuff to keep'] = 42;
// file bar.php
if ($_SESSION['stufft o keep'] == 42) frobnicate();
Here, your code is silently misbehaving, and the bug can take a while to be found. Good practice is to use a PHP-enforced name, such as a class constant:
$_SESSION[Stuff::TO_KEEP] = 42;
if($_SESSION[Stuff::TOO_KEEP] == 42)
// error: no constant TOO_KEEP in class Stuff
You may then define that constant to any constant you find interesting or readable, such as "stuff to keep" (with spaces). Of course, extract() and casting to object won't work anymore, but you shouldn't be doing that anyway with your session.
Allowing user-entered text into session keys is, of course, a blatant security fault.
You can do that, it'll work -- and even if I don't generally do it when I set the keys of my arrays "by hand", it sometimes happens when I get the keys from a file (for instance), and I've never had any problem with this.
Maybe this could cause some kind of a problem if you are using the extract functions, though. If it creates variables with spaces in their names (don't know if it will) it'll be difficult (but not impossible) to access your variables.
It won't cause a problem, but array keys are usually considered like variable names so should be chosen with the same considerations
Seems like adding unnecessary whitespace in my opinion... I don't usually use spaces. If you do, though, make sure you quote the array keys.

PHP: getting a "use of undefined constant COOKIE_LOGIN" how do I fix this?

I haven't made any changes to the code affecting the COOKIE's and now I get the following:
Use of undefined constant COOKIE_LOGIN - assumed 'COOKIE_LOGIN'
//Destroy Cookie
if (isset($_COOKIE[COOKIE_LOGIN]) && !empty($_COOKIE[COOKIE_LOGIN]))
setcookie(COOKIE_LOGIN,$objUserSerialized,time() - 86400 );
I'm not sure what I need to do to actually change this since I do not know what chnaged to begin with and so cannot track the problem.
Thanks.
You need to surround the array key by quotes:
if (isset($_COOKIE['COOKIE_LOGIN']) && !empty($_COOKIE['COOKIE_LOGIN']))
setcookie('COOKIE_LOGIN',$objUserSerialized,time() - 86400 );
PHP converts unknown literals to strings and throws a warning. Your php.ini probably had the error reporting level to not display warnings but someone may have updated it or something. In either case, it is bad practice to take advantange of PHP's behavior in this case.
For more information, check out the php documentation:
Always use quotes around a string literal array index. For example, $foo['bar'] is correct, while $foo[bar] is not.
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.
You can't say $_COOKIE[COOKIE_LOGIN] without error unless COOKIE_LOGIN is an actual constant that you have defined, e.g.:
define("COOKIE_LOGIN", 5);
Some people have habits where they will write code like:
$ary[example] = 5;
echo $ary[example];
Assuming that "example" (as a string) will be the array key. PHP has in the past excused this behavior, if you disable error reporting. It's wrong, though. You should be using $_COOKIE["COOKIE_LOGIN"] unless you have explicitly defined COOKIE_LOGIN as a constant.

Categories