Qiita に投稿した内容ですが、次のコードは php 5.3 と 5.4 で結果が異なります。
$str = "hoge"; var_dump($str[0][0]);
5.4 の場合
string(1) "h"
5.3 の場合
Fatal error: Cannot use string offset as an array
もっと調べた
php 5.3 では $str[0]
のような表現は内部的に一時的に 文字列オフセット? というものになるようです。
result->str_offset.str = container; // container => $str PZVAL_LOCK(container); result->str_offset.offset = Z_LVAL_P(dim); // Z_LVAL_P(dim) => 0 result->var.ptr_ptr = NULL; result->var.ptr = NULL;
- container は元となる変数(
$str
とか)、dim がオフセットの値です([0]
とか)
そして 文字列オフセット に対して更に [0]
のようなオフセット指定でアクセスすることは出来ません。
container = _get_zval_ptr_ptr_var(&opline->op1, EX(Ts), &free_op1 TSRMLS_CC); if (OP1_TYPE == IS_VAR && !container) { zend_error_noreturn(E_ERROR, "Cannot use string offset as an array"); }
static zend_always_inline zval **_get_zval_ptr_ptr_var(const znode *node, const temp_variable *Ts, zend_free_op *should_free TSRMLS_DC) { zval** ptr_ptr = T(node->u.var).var.ptr_ptr; if (EXPECTED(ptr_ptr != NULL)) { PZVAL_UNLOCK(*ptr_ptr, should_free); } else { /* string offset */ PZVAL_UNLOCK(T(node->u.var).str_offset.str, should_free); } return ptr_ptr; }
- 文字列オフセットに更にオフセット指定でアクセスすると
_get_zval_ptr_ptr_var
が NULL を返します
ただし、一旦 php の変数に入れてやれば 文字列オフセット が文字列に変換されるので、エラーにはなりません。
static zend_always_inline zval *_get_zval_ptr_var(const znode *node, const temp_variable *Ts, zend_free_op *should_free TSRMLS_DC) { zval *ptr = T(node->u.var).var.ptr; if (EXPECTED(ptr != NULL)) { PZVAL_UNLOCK(ptr, should_free); return ptr; } else { return _get_zval_ptr_var_string_offset(node, Ts, should_free TSRMLS_CC); } }
_get_zval_ptr_var_string_offset
が zval* を返します
$v = "hoge"; $c = $v[0]; var_dump($c[0]);
php 5.4 では
php 5.4 では文字列にオフセット指定でアクセスした時点で 長さ 1 の文字列 になっているので、このエラーは発生しません。
ALLOC_ZVAL(ptr); INIT_PZVAL(ptr); Z_TYPE_P(ptr) = IS_STRING; if (Z_LVAL_P(dim) < 0 || Z_STRLEN_P(container) <= Z_LVAL_P(dim)) { : Z_STRVAL_P(ptr) = (char*)emalloc(2); Z_STRVAL_P(ptr)[0] = Z_STRVAL_P(container)[Z_LVAL_P(dim)]; // $buf[0] = $str[0]; Z_STRVAL_P(ptr)[1] = 0; // $buf[0] = '\0'; Z_STRLEN_P(ptr) = 1; // $buf.length = 1; }
いくらでも [0]
を繋げられます。
<?php $str = "hoge"; var_dump($str[0][0][0][0][0][0][0][0][0][0][0]); // string(1) "h"
ただし、読み込みのときだけです。書き込もうとすると同じエラーになります。
<?php $str = "hoge"; $str[0][0] = 'a'; // Fatal error: Cannot use string offset as an array
最後に
エラーメッセージでググった感じ、元は PHP 4 から 5 のときに Fatal error になるようになったようです。
恐らく、php 5.4 で配列のデフィファレンスの辺りの変更の副作用で Fatal error にならなくなったのだと思います。