What does a variable set to "&NULL" means in PHP? - php

I'm working on a tree structure in PHP. When looping through the nodes, an exception is sometime thrown because some node that should not be null is null, more precisely it's set to "&NULL":
array(13) {
// ...
// some data...
// ...
["segments"]=>
NULL
["leaf"]=>
bool(false)
["children"]=>
&NULL
}
Since it's not inside quotes, I assume it's some kind of special value, but what does it mean?

It just means that it is a reference to a value NULL
$a = array();
$n = null;
$a[1] =& $n;
var_dump($a); // array(1) { [1]=> &NULL }
If you change $n = null; to $n = 1; - then you'll get &int(1)

It's a reference to a value that is null. "&" is the reference symbol.

Related

Unexpected behaviour in foreach using ternary operator and value by reference

Why the following code has the different output in PHP 5.5.* and PHP 7.*:
<?php
$foo = ['bar'=>[['item1'=>'value1']]];
foreach ($foo['bar'] ?: [] as $k => &$arr1) {
$arr1['item1'] = 'value2';
}
var_dump($foo);
In PHP 5 value of $foo['bar'][0]['item1'] will be modified with the value2, but in PHP 7 it will not.
From the documentation (emphasis mine): Please note that the ternary operator is an expression, and that it doesn't evaluate to a variable, but to the result of an expression. This is important to know if you want to return a variable by reference. The statement return $var == 42 ? $a : $b; in a return-by-reference function will therefore not work and a warning is issued.
With PHP 5, note how the innermost &array(1) is a reference:
array(1) {
["bar"]=>
array(1) {
[0]=>
&array(1) {
["item1"]=>
string(6) "value2"
}
}
}
But with PHP 7, it is not:
array(1) {
["bar"]=>
array(1) {
[0]=>
array(1) {
["item1"]=>
string(6) "value1"
}
}
}
If you remove ?: [], so the expression you're looping through is simply $foo['bar'], then the code behaves the same with both PHP 5 and 7, i.e. the innermost &array(1) is a reference.
TLDR: I can't say why this is, or where the difference is explained, but it seems clear that PHP 7 handles the conditional operator ?: differently than does PHP 5.

What is the effect of a double negating bitwise operator (~~) - also called "double tilde" - in PHP?

Refactoring legacy code I have found various occurrences of the following construct:
((bool) ~~$field->req ? ' required' : '')
According to the manual the Tilde (~) in PHP is the bitwise not, which simple shifts all bits in a variable to their opposite.
If I shift all bits in a variable to their opposite and then shift it back, the variable should be exactly the same as it was before right? So why would somebody do this? Am I missing something?
It should be !! (it converts the value to a boolean) but it is not needed at all. I guess the original coder mistaken ~ for ! then they added (bool) in front of it to achieve the desired result (because, as you noticed in the question, ~~ is a no-op).
The ternary operator (?:) forces the evaluation of its first argument as boolean.
The boolean value of $field->req is the same as of !! $field->req and (bool) ~~$field->req (and (bool)$field->req btw).
I would remove the (bool) ~~ part completely to get smaller and cleaner code.
Edit by questioner: The only effect of ~~ in PHP is to cut of decimals from a float value.
See the following results:
$a = 2.123;
$b = -2.123;
$c = new stdClass();
$d = ["a",2,"c"];
$e = "lord";
$f = -3;
$g = false;
$h = null;
$j = -2.99;
$k = 2.99;
var_dump(~~$a);
var_dump(~~$b);
// var_dump(~~$c); // error
// var_dump(~~$d); // error
var_dump(~~$e);
var_dump(~~$f);
// var_dump(~~$g); // error
// var_dump(~~$h); // error
var_dump(~~$j);
var_dump(~~$k);
var_dump(!!$a);
var_dump(!!$b);
var_dump(!!$c);
var_dump(!!$d);
var_dump(!!$e);
var_dump(!!$f);
var_dump(!!$g);
var_dump(!!$h);
var_dump(!!$j);
var_dump(!!$k);
int(2) int(-2) string(4) "lord" int(-3) int(-2) int(2)
bool(true) bool(true) bool(true) bool(true) bool(true) bool(true)
bool(false) bool(false) bool(true) bool(true)

PHP: Why Does array Syntax on a Zero Length String Cast the String as an Array?

In PHP, you can use array syntax to access string indexes. The following program
<?php
$foo = "Hello";
echo $foo[0],"\n";
?>
echos out
H
However, if you access the first character of a zero length string
<?php
$bar = "";
$bar[0] = "test";
var_dump($bar);
?>
PHP turns your string into an array. The above code produces
array(1) {
[0] =>
string(4) "test"
}
i.e. my zero length string was cast to an array. Similar "accessing an undefined index of a string" examples don't produce this casting behavior.
$bar = " ";
$bar[1] = "test";
var_dump($bar);
Produces the string t. i.e. $bar remains a string, and is not converted into an array.
I get these sorts of unintuitive edge cases are inevitable when the language needs to infer and/or automatically cast variable for you, but does anyone know what's going on behind the scenes here?
i.e. What is happening at the C/C++ level in PHP to make this happen. Why does my variable get turned into an array.
PHP 5.6, if that matters.
On the C level the variable is converted to an array when assignment is done by the using [] operator. Of course when it is a string, has a length of 0 and is not a unset type of call( eg. unset($test[0])).
case IS_STRING: {
zval tmp;
if (type != BP_VAR_UNSET && Z_STRLEN_P(container)==0) {
goto convert_to_array;
}
https://github.com/php/php-src/blob/PHP-5.6.0/Zend/zend_execute.c#L1156
Same conversion happens for boolean false values.
case IS_BOOL:
if (type != BP_VAR_UNSET && Z_LVAL_P(container)==0) {
goto convert_to_array;
}
Confirmed by using a test:
<?php
$bar = false;
$bar[0] = "test";
var_dump($bar);
Outputs:
array(1) { [0]=> string(4) "test" }
When using true:
<?php
$bar = true;
$bar[0] = "test";
var_dump($bar);
Outputs:
WARNING Cannot use a scalar value as an array on line number 3
bool(true)
https://github.com/php/php-src/blob/PHP-5.6.0/Zend/zend_execute.c#L1249
When the value is a type of bool and has a value of true the following code is executed:
case IS_BOOL:
if (type != BP_VAR_UNSET && Z_LVAL_P(container)==0) {
goto convert_to_array;
}
/* break missing intentionally */
default:
if (type == BP_VAR_UNSET) {
zend_error(E_WARNING, "Cannot unset offset in a non-array variable");
result->var.ptr_ptr = &EG(uninitialized_zval_ptr);
PZVAL_LOCK(EG(uninitialized_zval_ptr));
} else { // Gets here when boolean value equals true.
zend_error(E_WARNING, "Cannot use a scalar value as an array");
result->var.ptr_ptr = &EG(error_zval_ptr);
PZVAL_LOCK(EG(error_zval_ptr));
}
break;
PHP version 5.6 uses ZEND version 2.6.0
I suspect "" is being treated as unset and then being converted to an array. Generally "" != null != unset, however, php is a little whatty when it comes to that.
php > $a="test"; $a[0] = "yourmom"; var_dump( $a );
string(4) "yest"
php > $a=""; $a[0] = "yourmom"; var_dump( $a );
array(1) {
[0]=>
string(7) "yourmom"
}
php > var_dump((bool) "" == null);
bool(true)
php > var_dump((bool) $f == null);
PHP Notice: Undefined variable: f in php shell code on line 1
PHP Stack trace:
PHP 1. {main}() php shell code:0
Notice: Undefined variable: f in php shell code on line 1
Call Stack:
470.6157 225848 1. {main}() php shell code:0
bool(true)
I tried to find where this would be happening in the PHP source. I have limited experience with PHP internals, and with C in general, so someone please correct me if I'm wrong.
I think this is happening in zend_fetch_dimension_address:
if (EXPECTED(Z_TYPE_P(container) == IS_STRING)) {
if (type != BP_VAR_UNSET && UNEXPECTED(Z_STRLEN_P(container) == 0)) {
zval_ptr_dtor_nogc(container);
convert_to_array:
ZVAL_NEW_ARR(container);
zend_hash_init(Z_ARRVAL_P(container), 8, NULL, ZVAL_PTR_DTOR, 0);
goto fetch_from_array;
}
It looks like if the container is a zero length string, it converts it to an array before it does anything with it.
The reason it is changed to an array when you use array syntax on an empty string is because index 0 is undefined, and doesn't have a type at that point. Here is a kind of case study.
<?php
$foo = "Hello"; // $foo[0] is a string "H"
echo $foo[0],"\n"; // H
$foo[0] = "same?"; // $foo[0] is still a string, "s" note that only the s is kept.
echo $foo,"\n"; // sello
echo $foo[0],"\n"; // s
$foo[1] = "b"; // $foo[1] is a string "b"
echo $foo,"\n"; // sbllo
$bar = ""; // nothing defined at position 0
$bar[0] = "t"; // array syntax creates an array with a string as the first index
var_dump($bar); // array(1) { [0] => string(1) "t" }

in_array comparing two different types and yet returning true

I have a real mystery on my hands..
Behold the following lines of code..
if (in_array($_SESSION['enemies'][$i], $clones[$j]->defendAgainst)) {
..
}
where $_SESSION['enemies'][$i] is an object of something like:
object(skinhead)#4 (16)
{
["weapon"]=> object(bowieknife)#5 (2)
{ ["name":protected]=> NULL ["damage":protected]=> NULL }
["name"]=> string(8) "skinhead"
["health"]=>string(3) "100"
["strength"]=> string(2) "10"
["luck"]=> string(1) "2"
["money"]=>string(1) "0"
["exp"]=> string(1) "0"
["rank"]=> string(2) "20"
["points"]=> string(1)"0"
["location_id"]=> NULL
["comboAttack"]=> int(2)
["attackValue"]=> int(15)
["attackType"]=> NULL
["attackMessage"]=> string(198) "Enemy #1 pulls off a 2-hit combo.Enemy #1 slashes at you with a bowie knife.You defend.You lose 8 health.Enemy #1 slashes at you with a bowie knife."
["target1"]=> NULL ["target2"]=> NULL }
and $clones[$j]->defendAgainst is an array of ints
Now in_array should evaluate to false, since it is searching for an object in an array of ints. But instead it returns true!!!! How can this be?????
In order for php to compare an object to an int, it will cast the object to an int, and then do the comparison.
$new = (int) $someObject;
var_dump($new); // int 1
var_dump($new == 1); // true, obviously.
in_array() uses == by default for comparisons.
...and my magic crystal ball tells me your array of ints contains an integer with value 1.
That is the expected output, you need to add a third value as TRUE to make it also compare the types, as can be seen in the PHP manual for in_array():
bool in_array ( mixed $needle , array $haystack [, bool $strict = FALSE ] )
The third value is default to FALSE, but you can change that with a simple:
if (in_array($_SESSION['enemies'][$i], $clones[$j]->defendAgainst, TRUE))
EDIT: I think I know how you can find the problem yourself. I just found this question. Try changing the in_array() to the form of the foreach() of the first answer, but change the return TRUE; like this to see what it brings:
foreach ($clones[$j]->defendAgainst as &$member) {
if ($member == $_SESSION['enemies'][$i]) {
var_dump($_SESSION['enemies'][$i]);
var_dump($member);
}
}

Differences between $var = null and unset($var) [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
What's better at freeing memory with PHP: unset() or $var = null
As far as the garbage collection goes, is 1 better than the other in any circumstances?
EDIT:
Particularly if $var is a very large variable with a lot of levels of recursion and other objects (so to do with recursive cleanup of large objects)
EDIT:
Removed this:
I can think of only 1 thing, and that's that isset($var) will respond differently in either case.
Because apparently I was mistaken... They both react the same.
unset($var);
// You will get Undefined variable Notice.
if ($var) {}
$var = null;
// it's ok.
if ($var) {}
Addition of GC of php.
PHP's Garbage Collection is based on the refcount of the zval, if refcount becomes 0, then the zval can be freed.
So if $a = $b = array(/*a very large array*/);, unset only $a or only $b won't free the memory of the large array.
unset($a); or $a = null or assign another value to $a will all let the refcount decrease by 1, but the memory will be freed only when the refcount decrease to 0.
unset does not force immediate memory freeing but leaves it for the gc. $var = null; however forces immediate memory release.
See example:
// $a = NULL; (better I think)
$a = 5;
$b = & $a;
$a = NULL;
print "b $b "; // b
print(! isset($b)); // 1
?>
It is also worthy to note that in the case of an array unset destroys the variable completely. i.e.:
<?php
$ar = array(1,2,3,4);
var_dump($ar);
echo "<br />";
unset($ar[2]);
var_dump($ar);
echo "<br />";
$ar[1] = null;
var_dump($ar);
?>
Returns the output:
array(4) { [0]=> int(1) [1]=> int(2) [2]=> int(3) [3]=> int(4) }
array(3) { [0]=> int(1) [1]=> int(2) [3]=> int(4) }
array(3) { [0]=> int(1) [1]=> NULL [3]=> int(4) }
$a = 5;
$b = &$a;
unset($b); //just say $b should not pointer to any var
print $a; // 5
$a = 5;
$b = &$a;
$b = null;
print $a; // nothing, because $a = null

Categories