Is there anything problematic in simply setting $GLOBALS to be an empty array? I want to reverse the effect of register_globals where it is turned on and one does not have access to the .ini file, but rather than iterate over each of the relevant super globals and unset where necessary, skipping such elements as $GLOBALS['_POST'], $GLOBALS['_GET'], etc. as is usually done, I wonder if it's OK to just remove them all.
Are there any problems that may arise from this? I don't ever plan to reference the $GLOBALS array as any variables that are to be scope-independent will either be set in the relevant super global ($_GET, $_POST, etc.) or will be stored as properties of a relevant registry class.
For information, the FAQ at http://www.php.net/manual/en/faq.misc.php#faq.misc.registerglobals has the following to emulate register_globals = 0:
<?php
// Emulate register_globals off
function unregister_GLOBALS()
{
if (!ini_get('register_globals')) {
return;
}
// Might want to change this perhaps to a nicer error
if (isset($_REQUEST['GLOBALS']) || isset($_FILES['GLOBALS'])) {
die('GLOBALS overwrite attempt detected');
}
// Variables that shouldn't be unset
$noUnset = array('GLOBALS', '_GET',
'_POST', '_COOKIE',
'_REQUEST', '_SERVER',
'_ENV', '_FILES');
$input = array_merge($_GET, $_POST,
$_COOKIE, $_SERVER,
$_ENV, $_FILES,
isset($_SESSION) && is_array($_SESSION) ? $_SESSION : array());
foreach ($input as $k => $v) {
if (!in_array($k, $noUnset) && isset($GLOBALS[$k])) {
unset($GLOBALS[$k]);
}
}
}
unregister_GLOBALS();
?>
Presumably performing unset($GLOBALS[$k]); does the same as performing $GLOBALS = array(); except that the latter removes everything and involves only one line of code.
The question then is: is it bad to unset $GLOBALS['_GET'], $GLOBALS['_PUT'], etc. ("the variables that shouldn't be unset" as the example states -- is this really the case?)?
Update:
I've answered this myself below. Silly me for not trying earlier.
PHP Manual says
$GLOBALS — References all variables available in global scope
If the word 'Refernences' is used in the typical PHP way, setting $GLOBALS=array() won't help you, you would need:
foreach ($GLOBALS as $k=>$v) unset($$k);
Edit: This could be expanded to
foreach ($GLOBALS as $k=>$v)
if (substr($k,0,1)!='_')
unset($$k);
just for safety! (Didn't try out)
Edit 2:
As from Karoly's comment, if you do this in a class or function, you need
foreach ($GLOBALS as $k=>$v) {
global $$k;
unset($$k);
}
Edit 3 was editied awy
.. because it contained fat fingers - unexpected result came from a typo!
I've answered part of this myself. I was stupid for not trying earlier:
$var = 'Hello, world!';
unset($GLOBALS['var']);
echo $var; // Notice: Undefined variable: var
$var = 'Hello, world!';
$GLOBALS = array();
echo $var; // Hello, world!
So simply setting $GLOBALS as an empty array won't unset the global variables. They need to be explicitly unset.
Update:
Using unset($GLOBALS['_GET']); is the same as using unset($_GET) which is definitely not what I want. Question answered.
Related
This question already has answers here:
PHP, an odd variable scope?
(6 answers)
Closed 11 months ago.
I have this piece of code:
$exists = false;
foreach ($outsiderTests as $key => $outsiderTest) {
$textExists = null;
foreach ($tests as $test) {
if ($outsiderTest->getName() == $test->getName()) {
$exists = true;
$existingTest = $test;
break;
} else
$exists = false;
}
var_dump($existingTest, $test);
}
As you can see, I want to see if there is an equivalent to an outsiderTest in $tests array. I thought I would have to save the existing equivalent $test on another variable as it would be gone after the foreach ends, but it does not.
The value of $existingTest and $test is the same when I dump them. This is cool, and makes me able to get rid of the mentioned $existingTest variable, but makes me wonder if I am understanding PHP's loop functionality.
Doesn't the $test variable only exist inside the foreach scope? Does PHP temporarily save the value of the last index the execution has run through?
PHP's variable scope is explained here: https://www.php.net/manual/en/language.variables.scope.php
Effectively you have 2 scopes:
The global scope
The local function scope
So a loop variable will be accessible out of it's scope and will contain the last value it had. This is why you got this behaviour.
If you have a loop calling a function then you have multiple options:
Declare the external variable with the global keyword inside the function.
Access globals with the $GLOBALS variable.
Pass the globals you need to your anonymous function with the use () syntax.
I got the following function:
public function stopWatcherSession($sessionID) {
if(array_key_exists($sessionID, $_SESSION[self::WATCHER_SESSION_KEY])) {
foreach ($_SESSION[self::WATCHER_SESSION_KEY][$sessionID]['names'] as $v) {
$ptype = $this->paramTypeFromName($v);
unset($_SESSION[self::SESSION_KEY][$ptype][$v]); //does not work, sets entry to null
}
unset($_SESSION[self::WATCHER_SESSION_KEY][$sessionID]); //does not work, sets entry to null
}
}
As the comments say, the array entry does not get unset, the array_key_exists()-function still returns true, and if you var_dump($_SESSION) you can see, that $_SESSION[self::WATCHER_SESSION_KEY][$sessionID] is null.
How can I unset the variable, so that also the key in the array gets removed?
Things i tried, that did not work:
// v1 (tried also for `$_SESSION[self::WATCHER_SESSION_KEY][$sessionID]` )
$tmp = $_SESSION[self::SESSION_KEY][$ptype];
unset($tmp[$v]);
$_SESSION[self::SESSION_KEY][$ptype] = $tmp;
//v2
unset($_SESSION[self::WATCHER_SESSION_KEY][$sessionID]);
session_write_close();
session_start();
//v3 => v1 & v2 combined
$tmp = $_SESSION[self::SESSION_KEY][$ptype];
unset($tmp[$v]);
$_SESSION[self::SESSION_KEY][$ptype] = $tmp;
session_write_close();
session_start();
I could add a crappy hack all over the code to check whether it's null, but then »empty« values must be changed to something else (like a predefined const, but thats a nasty workaround and leads to confusion for other devs!)
Anyone got some ideas?
Got it working:
unset($GLOBALS['_SESSION'][self::WATCHER_SESSION_KEY][$sessionID]); does the job.
http://php.net/manual/en/function.unset.php says:
The behavior of unset() inside of a function can vary depending on what type of variable you are attempting to destroy. […] To unset() a global variable inside of a function, then use the $GLOBALS array to do so
Seems like a classical »PHP behaviour is undefined in some cases«-example.
I'm trying to debug a PHP application, and as a section of the debug process, I passed print_r($GLOBALS) through the AJAX request to my browser. However, I'd prefer to see it in native JSON form because it comes out better in the browser. I'm trying to use the following snippet of code:
json_encode($GLOBALS);
but I've found it returns bool(false). The JSON documentation says "Returns a JSON encoded string on success or FALSE on failure." But what about $GLOBALS makes it fail? Is it the recursive $GLOBALS['GLOBALS']?
I was thinking as an alternative to loop over $GLOBALS and put that in an array, but that seems quite pointless when the point of json_encode is to encode an array.
Upon testing this myself, it appears json_encode() can't handle recursion such as what's provided in $GLOBALS['GLOBALS']... etc.
So a trick(?) you can do is:
json_encode(array_slice($GLOBALS, 1));
Which will skip past $GLOBALS['GLOBALS'] and continue encoding the rest of the array.
*EDIT: $GLOBALS['GLOBALS'] appears first for me when printing this array, but a better way is to find where $GLOBALS['GLOBALS'] appears and skip that element entirely.
I propose a way where the position of GLOBALS is not important:
json_encode(array_intersect_key($GLOBALS,array_flip(array("_GET", "_POST", "_FILES", "_COOKIE"))));
or a better way:
$new_array = $GLOBALS;
$index = array_search('GLOBALS',array_keys($new_array));
json_encode(array_splice($new_array, $index, $index-1));
The fact that $GLOBALS contains reference to itself results in an infinite recursion that json_encode can't handle because it exceeds the maximum depth of 512 and thus will return false by default.
The solution would be creating a copy of $GLOBALS without self-referencing, the function below references superglobals _GET _POST _SERVER .. only, assuming you don't use $_ in your variables naming conventions:
function getEncodableGlobals()
{
$g = [];
foreach ($GLOBALS as $key => &$val)
{
if ($key[0] == '_')
{
$g[$key] = &$val;
}
}
return $g;
}
Notice that $g doesn't hold copies but only references to variables, just like $GLOBALS does. If you wish to include all the variables in the global scope simply change the condition to exclude the troublesome reference:
...
if ($key !== 'GLOBALS')
...
Now you can safely encode it:
json_encode(
getEncodableGlobals()
);
You could also do it the array_filter way and copy the variables.
$encodableGlobals = array_filter($GLOBALS, function($key) {
return $key !== 'GLOBALS';
}, ARRAY_FILTER_USE_KEY);
json_encode can't handle recursion such as what's provided in $GLOBALS['GLOBALS']... etc.
JSON.stringify is same!
you will retrieve error :
So in php you can create a new variable, example $globals which holds an array of $GLOBALS.
And, unset the recursive $globals['GLOBALS'].
$globals = $GLOBALS;
unset($globals['GLOBALS']);
echo json_encode($globals);
And... Congrats!! Now you can json_encode $GLOBALS variable!
My PHP script processes some POST variables.
Instead of manually naming each variable
$name = $_POST['name'];
$email = $_POST['account'];
I'd like my script to grab all the variable names from the $_POST array and automatically create variables with those names, e.g. (not code, just illustrating the principle)
foreach ($_POST as $name => $value) {
$[$name] = $value;
}
Is something like that possible?
You can use the extract function for this. But there is a risk, because you cannot know what data is posted, and it will create or overwrite variables in the scope in which you call it, possibly leading to unexpected behaviour.
You can partially counter this, using one of the flags for extract, for instance:
extract($_POST, EXTR_SKIP);
Anyway, make sure to read the two warnings (red block) on the documentation page of this function. And of course, the same warning applies when you do this using your own foreach loop, so answers suggesting that are no more secure.
There is extract function in php:
extract($_POST);
This is a very bad idea because it allows a user to create any variable in your PHP script (within the scope that it this code is used). Take for example if you have a $debugging flag:
$debugging = false;
foreach ($_POST as $name => $value) {
$$name = $value;
}
// some time later, we do a query and output the SQL if debugging
if($debugging){
echo $sql;
}
What if a malicious user submitted an input called debugging with a value of 1? Your debugging flag would be changed and the user could see sensitive debug data.
Try this (which is a bad practice):
foreach ($_POST as $name => $value) {
$$name = $value;
}
You can do this with variable variables as follows:
foreach ($_POST as $name => $value) {
$$name = $value;
}
You can also use the following format if you want to muck about with the variable names some more:
foreach ($_POST as $name => $value) {
${$name.'_1'} = $value;
}
There are comments here saying don't use variable variables - mainly because they are hard as heck to troubleshoot, make it damn hard for others to read your code and will (for the most part) create more headaches than they solve.
I would like to set a session variable with something akin to:
$key = '_SESSION[element]';
$$key = 'value';
This does indeed set $_SESSION['element'] equal to value, but it also seems to clear the rest of my $_SESSION variable, resulting in the $_SESSION array only containing the new key/value pair.
How can I write into the session using variable variables without nuking it?
Edit: if this can't be done, so be it, we'll probably have to restructure and do things the "right" way. I just wanted to know if there was an easy fix
#Mala, I think eval will help you.
Check the code below. It may help you for what you want.
session_start();
$_SESSION['user1'] = "User 1";
$_SESSION['user2'] = "User 2";
$key = "_SESSION['user3']";
eval("\$$key = 'User 3';");
foreach ($_SESSION as $key=>$value){
echo $key." => ".$value."<br/>";
unset($_SESSION[$key]);
}
session_destroy();
If you still have any trouble, Let me know. Thank you
From PHP Documentation:
Please note that variable variables cannot be used with PHP's
Superglobal arrays within functions or class methods. The variable
$this is also a special variable that cannot be referenced
dynamically.
How you ended up with a situation like this, is really questionable. You're probably doing something wrong.
EDIT
This little trick should give you what you want:
$key = '_SESSION[element]';
$key = str_replace(array('_SESSION[', ']'), '', $key);
$_SESSION[$key] = 'value';
var_dump($_SESSION);
This will basically produce the same results as xdazz's answer
Isn't this way better?
$key = 'element';
$_SESSION[$key] = 'value';