I found what appears to be an abusable bug in the PHP language. When creating variable variables a seemingly illegal variable can be declared and then handled as long as you keep accessing it as a variable variable.
Example:
${'0'} = 1; //I am an illegal variable called $0
${'0'}++; //I can be accessed and manipulated.
var_dump(${'0'}); //Output: int(2)
This behavior appears rather odd. It is briefly mentioned in the official documentation for SimpleXml as a way to create variable names that contain hyphens but my example shows it can be abused pretty badly nonetheless.
I would like to know how come this behavior is possible and is tolerated when the code is parsed?
Internally PHP stores variables (zend_array* symbol_table) in a same data structure that is used for arrays. This allows you to have variable names with the same constraints as array keys.
Eg. Zend API function zend_set_local_var sets a value to symbol table using zend_hash_update, which is a function used to manipulate PHP array types as well.
We can't however allow every character in variable names in PHP source code. That's because lexical analysis must distinguish labels from other tokens. Variable variables offer a workaround for this.
It's not a bug nor a way to abuse anything. That being said, the documentation states that all labels, including variables, must have a regex form [a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*, so i wouldn't rely on having arbitrary characters in variable names.
What ${'0'} = 1; actually does, it sets value 1 to the current scope's symbol table at key 0.
You can get that particular table with get_defined_vars function.
Looking at the source code, we'll see that the function just copies the current symbol table and returns it to caller.
PHP extract function (src) actually checks that keys have a valid label format (by calling php_valid_var_name), so you can't generate variables with funky names using that.
Anyway, even though it's possible to create variables of any name using variable variable syntax (even a variable with no name ${''}), i think it's bad practice. Even worse if a library expects or enforces you to do so. Saying it's a workaround might be a bit overstatement actually. Maybe it should be considered as an implementation detail and an undocumented feature.
#Nadav I don't have full idea about that but have a little bit idea,
Remember that curly braces({}) literally mean "evaluate what's inside the curly braces" so, you can squeeze the variable creation like you did ${'0'} = 1
reffered it from this http://php.net/manual/en/language.variables.php#73373
PHP stores variable in the array form, if you want to check then use $GLOBALS(an array) is the super global variable which has all the reference of the variables present in the script, it stores variable name as key and value as variable value, suppose below case:
<?php
${'0'} = 1; // here php stores it as 0th index in the array
${'1'} = 2; // here php stores it as 1th index in the array
$b = "I am b"; // here php stores it as bth index in the array
echo "<pre>";
print_r($GLOBALS);
output:
Array
(
[_GET] => Array
(
)
[_POST] => Array
(
)
[_COOKIE] => Array
(
)
[_FILES] => Array
(
)
[GLOBALS] => Array
*RECURSION*
[0] => 1
[1] => 2
[b] => I am b
)
Related
I am learning PHP from w3schools' PHP Tutorial.
While learning PHP I came across the concept of predefined global variables i.e. Superglobals.
In a curiosity to understand "Superglobals" more deeply I wrote the following code and executed it in a browser on my local machine(i.e.localhost) :
<!DOCTYPE html>
<html>
<body>
<?php
echo "<pre>";
print_r($GLOBALS);
echo "</pre>";
?>
</body>
</html>
I got following output in a browser :
Array
(
[_GET] => Array
(
)
[_POST] => Array
(
)
[_COOKIE] => Array
(
[toWorkNormally] => 1
)
[_FILES] => Array
(
)
[GLOBALS] => Array
*RECURSION*
)
The above output has created many doubts in my mind as follows :
As per my knowledge in PHP there are nine types of
superglobals (predefined PHP global variables) viz. $GLOBALS,
$_SERVER, $_REQUEST, $_POST, $_GET, $_FILES, $_ENV, $_COOKIE and
$_SESSION then my doubt is what does the array elements from the predefined global array
$GLOBALS viz. [_GET], [_POST], [_COOKIE], [_FILES] mean as they have
their own independent existence as superglobals?
What is meant by [toWorkNormally] => 1 from above array output?
What does mean by RECURSION in element [GLOBALS] and how to print
those elements?
As the purpose of $GLOBALS array is to store variables declared by user globally then how this array has been pre-populated with some other values as I haven't declared any global variable in my code?
Note : I'm using "Microsoft Windows 10 Home Single Language" operating system on my machine. It's a 64-bit Operating System. I'm using latest edition of XAMPP with PHP 7.0.13 and HTTP Apache web server v.2.4.23 for running the program locally. Also, please note that I have not defined any other variable as global or local in my code.
From my knowledge of PHP and doing some research as well as testing this on multiple OS' with various version of PHP I found the following.
Question 1 & 3:
Yes you are correct with regards to the 9 superglobals, but a very important thing to keep in mind is that $GLOBALS -- References all variables available in global scope.
An interesting sidenote, notice that $GLOBALS is the only superglobal that doesn't start with an underscore.
Because of the fact that $GLOBALS contains references to all the other superglobals including itself, when we print_r($GLOBALS) it will also include the other superglobals in the output. Because $GLOBALS references itself as well we get the RECURSION you asked about in your 3rd point. You can think of it as a infinite dimensional array containing $GLOBALS. Almost the same idea as an infinte loop.
[GLOBALS] => Array
(
[GLOBALS] => Array
(
[GLOBALS] => Array
(
...
)
)
)
Instead the script sees this and stop executing and just prints RECURSION. Now I have tested it on 3 different environments and each time the order in which the superglobals are printed changed, but as soon as it hits $GLOBALS it stops and prints RECURSION.
Question 2:
I could not find any info on $_COOKIE[toWorkNormally] => 1. I am assuming this is set somewhere else. I didn't see it in any of my tests.
Question 4:
This is neither correct nor incorrect. The purpose of $GLOBALS is not to store all variables created by the user globally. It merely references all variables available in global scope including, the superglobals. That is why you are seeing all the other superglobals in the output. But a lot of developers assume that the user defined global variables are stored in $GLOBALS.
Description in the PHP.net manual
An associative array containing references to all variables which are
currently defined in the global scope of the script. The variable
names are the keys of the array.
To view all the superglobals you will have to print_r() each one of them individually.
To check all user defined global variables you can use array_keys($GLOBALS) all the items which are not superglobals will most probably be user defined global variables.
EDIT in response to users comments
In response to your 1st comment, No they are not different. The superglobals not printed are still part of the array but execution/output stops because it hits the RECURSION when it gets to $GLOBALS. The superglobals are printed in a random order and which ever comes after the $GLOBALS will not be seen as it detects a RECURSION at $GLOBALS and stops the output.
You can check all the superglobals/global variables by using print_r(array_keys($GLOBALS)); With an exception of $_SESSION because a session has not been started yet. print_r($_SESSION) will give you an undefined variable $_SESSION Notice. You will be able to see $_SESSION when you put session_start(); just before you print it.
Link to What References Are in PHP
References in PHP are a means to access the same variable content by different names.
Note that in PHP, variable name and variable content are different, so the same content can have different names
The PHP manual says the following about the $GLOBALS variable:
An associative array containing references to all variables which are currently defined in the global scope of the script. The variable names are the keys of the array.
This describes exactly what the variable does. It is simply a reference to existing variables.
The RECURSION you are talking about is the $GLOBALS variable referring to itself. Since we don't want PHP to endlessly output the same output and crashing your server in the process, there is a built-in failsafe that shows you the RECURSION alert if this is the case.
I would like to add that $GLOBALS is a superglobal, or preset global, variable. This means that it is available in all scopes throughout your script.
Resources
The PHP manual about $GLOBALS
$GLOBALS is the global of all super global and user defined variables. So for example if you have declared variable $a = 10; in your $GLOBALS array you have key=>value pair where key is a and value is 10.If you want to get something from $GLOBALS you just need to write it as array key.
example
$a = 25;
echo $GLOBALS['a'];
In the example above output will be the value of $a so 25;
In your example toWorkNormally=>1 it`s mean that you have set cookie with name toWorkNormally and with value 1 or true
Also when you submit form with get or post method in the $GLOBALS['_GET'] or $GLOBALS['_POST'] there you can find your form data as you can get them from super global $_GET or $_POST
1. As per my knowledge in PHP there are nine types of superglobals (predefined PHP global variables) viz. $GLOBALS, $_SERVER, $_REQUEST, $_POST, $_GET, $_FILES, $_ENV, $_COOKIE and $_SESSION then my doubt is what does the array elements from the predefined global array $GLOBALS viz. [_GET], [_POST], [_COOKIE], [_FILES] mean as they have their own independent existence as superglobals?
From PHP's doc:
References all variables available in global scope
This means you can access a superglobal directly or from $GLOBALS, yes, you have two ways of accessing them.
2. What is meant by [toWorkNormally] => 1 from above array output?
It's inside $_COOKIE so there's a cookie named toWorkNormally with the value of 1. More info on cookies
3. What does mean by RECURSION in element [GLOBALS] and how to print those elements?
Recursion means it refereces itself, if it was printed then it would show the contents of $GLOBALS again nested inside GLOBALS, that would cause infinite loop. To avoid that PHP just printed *RECURSION* instead.
4. As the purpose of $GLOBALS array is to store variables declared by user globally then how this array has been pre-populated with some other values as I haven't declared any global variable in my code?
From PHP's doc:
Several predefined variables in PHP are "superglobals", which means they are available in all scopes throughout a script. There is no need to do global $variable; to access them within functions or methods.
So in other words, $GLOBALS will show you those predefined variables from PHP and also the values you set manually.
This question already has answers here:
Reference Guide: What does this symbol mean in PHP? (PHP Syntax)
(24 answers)
Closed 6 years ago.
I frequently see $var->another_var, or $somevar=>yet_another, or even $third_var->another=>$fourth_var in various snippets of code.
Is there some super amazing info-graphic somewhere that clearly explains the various usages and what they mean, specifically within a PHP context?
(In my case, using Drupal, which uses LOTS of arrays, but probably useful in lots of other CMSs / frameworks.)
EDIT: I have since been informed about a catch-all page that has a very useful, encyclopedic list of various symbols and syntaxes. However, I believe one section NOT covered there is the mix-and-match combo of $var->element=>$anothervar.
Single arrow - T_OBJECT_OPERATOR
->
This is used for access to an object property and the value associated with that property.
$object->property='value'
I have a dog and his name is Captain
$dog->name='Captain';
Now I have access to properties of my dog. The property that we have set is name
$dogName=$dog->name;
echo $dogName;
Will output: Captain
I can also add other properties and their associated value to my object.
$dog->weight='57lbs';
Now my object has two properties associated with it, name and weight.
Double arrow - T_DOUBLE_ARROW
=>
As is stated in the documentation an array is just a map of comma separated keys and the values associated with the key. The double arrow is essentially an assignment operator that assigns, or associates, the value to a key.
$array = array("key" => "value");
Again using the dog example.
$dog = array("name" => "Captain", "weight" => "57lbs");
And we can access values in my dog array by the respective keys.
$fatDog = $dog["weight"];
echo $fatDog;
Will output: 57lbs
Combinations of single and double arrow
$object->property=>$value;
This combines object/property with key/values. If we break it down into it's constituents it can make things much more clear.
We know that $object->property will yield the value associated with the property. Lets start by associating that with a variable:
$valueAssociatedWithProperty = $object->property;
Using substitution into the original gives:
$valueAssociatedWithProperty => $value;
We have seen that before it is just the key/value of an array! Lets apply this to the dog example and see what comes out:
$dog->name="Captain";
$description="He is crazy";
$array = array($dog->name => $description);
// $array = array("Captain" => "He is crazy");
$whatIsCaptain = $array["Captain"];
echo $whatIsCaptain;
He is crazy
I hope this helps.
Also look HERE for all the references you could ever hope for!
$var->another_var is "property another_var of object referenced by $var".
$somevar=>yet_another is used in array definitions, like this: $arr = array($somevar => yet_another). It would define an associative property with key equal to the value of variable $somevar, and value equal to the constant yet_another.
$third_var->another=>$fourth_var can be rewritten so it becomes more clear:
array( /*key=*/ ($third_var->another) => /*value=*/ $fourth_var )`
I have the following array and i want to access the value of a specified element with twig.
numbers => Array ([01234567] => Array ( [0] => 9876543210 [1] => 8765432109 [2] => 0000000000))
I know there is only one entry in numbers, so I want to access the array with the key 01234567 directly.
Even tough numbers|keys[0] does return the correct key, I can't use it like numbers[numbers|keys[0]] to get the array. I also tried the attribute(array, item) function, but i didn't got it to work.
Is it possible to access it directly or do I need to use loops?
You have found a probably undocumented "feature" of Twig. If you check the source code, twig tries to determine if the given key is numeric, or not. It does this check with the ctype_digit function, which checks if a variable contains only numeric characters.
The example in your question contains an array key, which meets this conditions: it contains only numbers. Unfortunately, it also starts with a zero, which is removed when the string is converted into an integer.
I'm not exactly sure that this is intended behavior, so you may try to report this example as a bug.
For the current twig implementation, because everything except the loop construct uses the getAttribute function, you have no other choice but to use a for loop.
I know that prepending a '&' to your PHP variable sets up a reference to the original variable instead of copying its value like so:
$original = 'apples';
$secondary = &$original;
$original = 'oranges';
echo $secondary; // 'oranges'
If it works this way, why not just use the original variable then?
Passing by reference is useful and necessary when passing a variable as a parameter to a function, expecting that variable to be modified without a copy being created in memory. Many of PHP's native array_*() functions operate on array references, for example.
This function, for example, receives an array reference and appends an element onto the original array. If this was done without the & reference, a new array copy would be created in scope of the function. It would then have to be returned and reassigned to be used.
function add_to_an_array(&$array)
{
// Append a value to the array
$array[] = 'another value';
}
$array = array('one', 'two', 'three');
add_to_an_array($array);
print_r($array);
Array
(
[0] => one
[1] => two
[2] => three
[3] => another value
)
$original = 'apples';
function foo($word) {
$word = 'oranges';
}
foo($original);
echo $original; // apples, because only local $word was changed, not $original.
foo(&$original);
echo $original; // oranges, because $original and $word are the same
Pass by reference is really a cop out and goes against good encapsulation. If you need to manipulate a variable in that way, it probably should belong to a class as a member variable and then does not need to be passed to the function. Good OO design would usually make member variables immutable with a "final" keyword, but PHP doesn't have this. It's not intuitive that passing a variable to a function might change it's value which is why it should be avoided in most cases.
Also going to a more full OO design prevents you have having method signatures that are long and complex with many optional parameters that are difficult to re-factor.
A more interesting use of the is when it's used in the formal argument to a function
foo($a);
...
function foo (&$a) {
....
}
this allows you to modify a in the function.
There are many uses for references.
You can pass a reference to a variable to a function so you can change the value inside the function
You can use references to create linked lists
etc...
Just keep in mind that they're there, and you'll surely find an application for them when the time comes and you face a problem that can be solved with references.
Check out the following article for other ideas and uses of references:
http://www.elated.com/articles/php-references/
DISCLAIMER:
Please read carefully as this is NOT a question about storing arrays in constants or simple eval() or serialize() techniques. This IS a question primarily about how constants work in PHP and why the constant() function is not working to convert a constant name into a constant value. Thanks.
BACKGROUND:
For various reasons, I started out with a flat config file for a homebrewed LAMP(PHP) CMS (in private development). Looking back this may have been misguided, and I have transitioned my variable storage into a DB table. However, the bulk of the code still depends on the CONSTs, so I use eval("define(A, B...);") to load the DB values A and B into constants. This part works fine. But it gets a bit more complicated.
PROBLEM:
The problem I'm having now is with constants in arrays (NB. NOT arrays in constants). I have a big, GLOBAL array called defaults that contains config settings in the format shown below.
Initially, I declare: <?php define('THIS_IS_A_CONSTANT', 'abcdefg'); ?> (And THIS WORKS...)
Next, I define $GLOBALS['defaults'] as the following nested array:
Array
(
'var_name' => Array
(
'display' => THIS_IS_A_CONSTANT,
'value' => 12,
'type' => 'int',
'params' => Array ( ... )
),
...
Lots more variables...
...
)
To prevent the client (who has file system access but no direct DB access but can change certain values, including most constants, via the CMS's administrative backend) from mucking up this array structure, I serialize the array structure and store that string in the DB. On each page request, I first define all the constants (stored in the DB) using the eval(define(A,B...)), then I unserialize the array above (which was serialized and stored in the DB). However, no matter what I try I cannot get the values at $GLOBALS['defaults']['var_name']['display'] to be recognized as the values that the constants contain. Instead, the constant name shows up and NOT the constant value (in other words, my output contains THIS_IS_A_CONSTANT instead of 'abcdefg'). Very frustrating, right?
I've tried something like the following (where $arr contains the unserialized array that I fetch from the DB):
foreach ($arr as $item => $contents) {
$display = isset($contents['display']) ? $contents['display'] : 1;
$value = constant("$display");
// This doesn't work, though it seems like it should
$contents['display'] = $value;
// Neither does this, no matter how much I juggle the quotation marks and backslashes
eval("\$contents['display'] = constant(\"$value\");");
// or this ...
eval("\$contents['display'] = $value;");
// or this ...
eval("\$contents['display'] = \$value;");
// or a number of other things...
}
$GLOBALS['defaults'] = $arr;
QUESTIONS:
Has anyone dealt with this kind of situation before? Can anyone advise me how to force my constants to be recognized as CONSTANTS and not strings. Do I need to serialize my array differently? Or maybe process the unserialized array differently (after retrieving it from the DB)? Is these some combination of eval() and constant() that will allow me to do this? Why are the constants within my array behaving badly while the constants I define normally are working without problem? Any help at all would be greatly appreciated, as I've been puzzling over this for a few days now and haven't come to any solutions.
All the best, Dakota.
Although eval does have its uses, this is not one of those cases. You have no idea what is being submitted to the eval at run time - and by the sounds of things you are storing something which you are then treating as code in a user-data storage location.
If the constant was defined before the array was declared then the serialized version would contain the value instead of the label. I can only assume that the value may differ depending on the context at run-time.
I would suggest that a better solution would be to roll your own macro language in PHP, e.g. something like:
<?php
global $defs;
$defs=array(
'THIS_IS_A_CONSTANT' => 'abcdefg',
'SO_IS_THIS' => 23
);
// for inline constants
foreach ($defs as $label =>$val) {
define($label, $key);
}
replacer($defs,true);
function replacer($in, $init=false;)
{
static $defs;
static $vals;
if ($init) {
$defs=array_keys($in);
$vals=array_values($in);
return count($in);
}
return str_replace($defs, $vals, $in);
// you might want to use preg_replace() with a pattern based on $defs for a neater solution
}
function fix_var(&$in)
{
if (is_array($in)) {
foreach ($in as $key=>$dummy) {
fix_var($in[$key]);
}
} else {
$in=replacer($in);
}
}
?>
C.
First, why are you evaling? From what you are saying you want the value of $GLOBALS['defaults']['var_name']['display'] to be the value of the constant THIS_IS_A_CONSTANT. You have de-serialized the string from your database and shoved in in $GLOBALS['defaults'] and the string stored the value as the constant name, not the value of the constant.
Have you tried:
<?php
define('THIS_IS_A_CONSTANT', 'abcdefg');
$value = constant($GLOBALS['defaults']['var_name']['display']);
//$value should now be abcdefg not THIS_IS_A_CONSTANT
$GLOBALS['defaults']['var_name']['display'] = $value;
?>
If "$GLOBALS['defaults']['var_name']['display']" does contain the string THIS_IS_A_CONSTANT then all you should need to do is pass that string to the constant funct.
Am I missing something?