2016-05-26 1 views
3

В PHP вы можете использовать синтаксис массива для доступа к строковым индексам. Следующая программаPHP: Почему синтаксис массива на строке нулевой длины? Строка как массив?

<?php 
$foo = "Hello"; 
echo $foo[0],"\n"; 
?> 

отголоски из

H 

Однако, если доступ к первому символу нулевой длины строки в

<?php 
$bar = ""; 
$bar[0] = "test"; 
var_dump($bar); 
?> 

PHP превращает строку в массив. Вышеприведенный код производит

array(1) { 
    [0] => 
    string(4) "test" 
} 

То есть моя строка нулевой длины была отлита к массиву. Аналогичные примеры «доступа к неопределенному индексу строки» не приводят к такому поведению.

$bar = " "; 
$bar[1] = "test"; 
var_dump($bar); 

Производит строку t. то есть $bar остается строкой и не преобразуется в массив.

Я получаю такие неинтуитивные кромки, которые неизбежны, когда язык должен вызывать и/или автоматически использовать переменную для вас, но кто-нибудь знает, что происходит за кулисами здесь?

Т.е. что происходит на уровне C/C++ в PHP, чтобы это произошло. Почему моя переменная превращается в массив.

PHP 5.6, если это имеет значение.

+2

Нет вы создали массив, выполнив '$ bar [0] =" test ";' – RiggsFolly

+0

@RiggsFolly, если вы используете '$ bar =" ";' пробел, '$ bar [0] =" test ";' будет показывать ' T'. Почему массив не создается в этом случае? –

+2

@RiggsFolly Нет Я этого не сделал, я создал строку, когда я сказал '$ bar =" "'. PHP передал эту строку в виде массива, когда я попытался получить доступ к индексу в строке ('0'), которой не было. Попробуйте сценарий сценария '$ bar =" "' (одна строка символов) –

ответ

12

На уровне C переменная преобразуется в массив, когда назначение выполняется с помощью оператора []. Конечно, когда это строка, имеет длину 0 и не является отмененным типом вызова (например, 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

То же преобразование происходит для логических ложных значений.

case IS_BOOL: 
      if (type != BP_VAR_UNSET && Z_LVAL_P(container)==0) { 
       goto convert_to_array; 
      } 

Подтверждено с помощью теста: При использовании истинно

array(1) { [0]=> string(4) "test" } 

::

<?php 
$bar = true; 
$bar[0] = "test"; 
var_dump($bar); 

Выходы:

<?php 
$bar = false; 
$bar[0] = "test"; 
var_dump($bar); 

Выходы

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

Если значение является тип BOOL и имеет значение истинного следующего кода выполняется:

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 версии 5.6 использует ZEND версию 2.6.0

4

Я подозреваю, что «" обрабатывается как неустановленный, а затем преобразовывается в массив. Обычно «"! = Null! = Unset, однако php немного зависит от этого.

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) 
+1

+1 Полезная информация, а не мой вопрос;), но если вы проверите тип на пустой строке, она по-прежнему будет отображаться как строка, поэтому я не думаю, что это так же просто, как рассматривать ее как неустановленную. –

+0

См. Последний пример, который я только что обновил. Кроме того, проверка типа и использование его как другого типа, несомненно, приведет к другому поведению. Если вы проверите тип $ f, он будет равен нулю, даже если он никогда не был установлен. –

+0

не уверен, что показывает последний пример - плюс вы используете '==' not '===', что означает, что вы добавляете поведение принуждения типа '==' в микс. –

0

Причина, по которой изменяются в массив, когда вы используете синтаксис массива на пустой строке, потому что индекс-не определен, и не имеет типа в этой точке. Вот пример тематического исследования.

<?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" } 
+0

Спасибо за ваш ответ, однако * и не имеет типа в этой точке * неверно. Если вы ссылаетесь на строку, у нее есть тип: '$ bar =" "; echo gettype ($ bar)," \ n ";' Если вы имеете в виду массив, вы правы, что index undefined, однако '$ bar =" a "; $ bar [2] =" b "; var_dump ($ bar);' does ** not ** cast $ bar как массив, а '2' не определен. –

+0

Я имею в виду элемент в индексе 0, если строка, как я уже сказал, «потому что индекс 0 не определен и не имеет типа в этой точке». В вашем примере вы просто указали '$ bar =" a "' устанавливает строку как строку, которая внутренне хранится как массив байтов ([ref] (http://php.net/manual/en/language.types .string.php # language.types.string.substr)). Из-за этого я бы * предполагал *, что до тех пор, пока переменная не будет иметь фактическое строковое значение (т.е. байты, присвоенные индексам), использование синтаксиса массива позволит вам хранить элементы, не ограниченные байтами, становясь нормальным массивом. – shamsup

1

Я попытался найти, где это будет происходить в источнике PHP. У меня ограниченный опыт работы с внутренними компонентами PHP и с C в целом, поэтому кто-то, пожалуйста, исправьте меня, если я ошибаюсь.

Я думаю, что это происходит в 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; 
     } 

Похоже, если контейнер строка нулевой длины, он преобразует его в массив, прежде чем он делает что-нибудь с ним.

+0

Учет того же ответа –

Смежные вопросы