开发者

(bool)true var does not get converted to array on array assignment, but (bool)false does. Why?

Using PHP... an example. This produces a warning - as expected - and $myVar stays as bool(true).

$myVar = true;
$myVar[] = 'Hello';  // Warning: Cannot use a scalar value as an array

But this开发者_运维技巧 next example 'works', $myVar is converted into an array with a single element 'Hello'.

$myVar = false;
$myVar[] = 'Hello';  // Converted into an array

Results in:

array(1) {
  [0]=>
  string(5) "Hello"
}

Yet both bool(true) and bool(false) are both scalar. So why the difference? What rule in PHP governs this behaviour? Or is it 'just the way it is'?!

I initially thought it might be to do with type casting rules, but both bool(true) and bool(false) behave the same in this respect.

Thanks.


So, even though I don't know why PHP does that, I looked at some Zend code and can at least tell you where you can find out how exactly PHP does it.

So, the important code is in zend_fetch_dimension_address.

So, let's cover the above cases:

If it IS_ARRAY - everything obvious.

If it IS_OBJECT throw error unless it has ArrayAccess.

If it IS_STRING throw an error, unless the strings length is zero.

If it IS_NULL create a new array.

If it IS_BOOL throw an error, unless it is false.

Otherwise, throw an error.

So, this confirms your and my tests:

Error if object, non-empty string, true and other scalars, i.e. long and double. No error if array, empty string, null and false. So basically it does an automatic cast on most (but not all) "falsy" values.


PHP is not a strongly typed language. You are assigning an array to a variable that contains a false value, not necessary a boolean false value.

Under the PHP covers, it must see $myVar as having an value that evaluates as empty and therefore allows the array assignment.

Again, if you look at PHP as a dynamic scripting language, this is not all that unexpected.


If you want to know why it is like this, then checkout php from here I think, compile it and do some step-by-step debugging with gdb... unless someone is good enough to find the piece of code responsible for this. Then look at the comments (if there are some) around the code responsible for this. As mentioned in the comments below, another way to find out would be to search the code for the error message. Let's do this!

    [greg@liche php-src-5.3]$ grep -rn --exclude-dir=".svn" "Cannot use a scalar value as an array" .
./tests/lang/bug29893.phpt:10:Warning: Cannot use a scalar value as an array in %sbug29893.php on line %d
./tests/lang/engine_assignExecutionOrder_002.phpt:12:// Warning: Cannot use a scalar value as an array in %s on line %d
./tests/lang/engine_assignExecutionOrder_002.phpt:94:Warning: Cannot use a scalar value as an array in %s on line %d
./Zend/zend_execute.c:1015:                             zend_error(E_WARNING, "Cannot use a scalar value as an array");
./Zend/tests/indexing_001.phpt:51:Warning: Cannot use a scalar value as an array in %s on line %d
./Zend/tests/indexing_001.phpt:54:Warning: Cannot use a scalar value as an array in %s on line %d
./Zend/tests/indexing_001.phpt:57:Warning: Cannot use a scalar value as an array in %s on line %d
./Zend/tests/indexing_001.phpt:77:Warning: Cannot use a scalar value as an array in %s on line %d
./Zend/tests/indexing_001.phpt:96:Warning: Cannot use a scalar value as an array in %s on line %d
./Zend/tests/indexing_001.phpt:99:Warning: Cannot use a scalar value as an array in %s on line %d
./Zend/tests/indexing_001.phpt:102:Warning: Cannot use a scalar value as an array in %s on line %d
./Zend/tests/indexing_001.phpt:119:Warning: Cannot use a scalar value as an array in %s on line %d
./Zend/tests/indexing_001.phpt:137:Warning: Cannot use a scalar value as an array in %s on line %d
./Zend/tests/indexing_001.phpt:140:Warning: Cannot use a scalar value as an array in %s on line %d
./Zend/tests/indexing_001.phpt:143:Warning: Cannot use a scalar value as an array in %s on line %d
./Zend/tests/indexing_001.phpt:160:Warning: Cannot use a scalar value as an array in %s on line %d
./Zend/tests/indexing_001.phpt:179:Warning: Cannot use a scalar value as an array in %s on line %d
./Zend/tests/indexing_001.phpt:182:Warning: Cannot use a scalar value as an array in %s on line %d
./Zend/tests/indexing_001.phpt:185:Warning: Cannot use a scalar value as an array in %s on line %d
./Zend/tests/indexing_001.phpt:202:Warning: Cannot use a scalar value as an array in %s on line %d

looks like it is in zend_execute.c, here is what I found :

 case IS_BOOL:
1223    if (type != BP_VAR_UNSET && Z_LVAL_P(container)==0) {
1224    goto convert_to_array;
1225    }
1226    /* break missing intentionally */
1227    
1228    default:
1229    if (type == BP_VAR_UNSET) {
1230    zend_error(E_WARNING, "Cannot unset offset in a non-array variable");
1231    AI_SET_PTR(result, &EG(uninitialized_zval));
1232    PZVAL_LOCK(&EG(uninitialized_zval));
1233    } else {
1234    zend_error(E_WARNING, "Cannot use a scalar value as an array");
1235    result->var.ptr_ptr = &EG(error_zval_ptr);
1236    PZVAL_LOCK(EG(error_zval_ptr));
1237    }
1238    break; 

I think the ZLVAL_P(container)==0 condition is responsible for this difference... lval means left value, the value that is being assigned... and I think it evaluates to 0.


Whilst PHP is loosely typed, types do still have some importance, hence === or !== for example.

The reason Nikic gets an error if it is reset to zero is it is actually now an integer, rather than a boolean.

To demonstrate, you could do the following and get the same error by casting.

$a = (int) false;
$a[] = 'goat'; #throws warning, thinks it's a 0

$a = (bool) 0;
$a[] = 'goat'; #works, thinks it's a false

I know thats just a pedantic example, however! (bool) true still evaluates to a 1.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜