【2019年07月21日】

どんな言語にも配列が存在する。
ご存知の通り、複数の値を格納できる変数である。

配列には通常の配列と連想配列(ハッシュ)と呼ばれるものがあり、大抵の言語では別物である。
通常の配列はキーに連番数値、ハッシュはキーに文字列を使用する。

そんな中でPHPの配列はどんなことになっているかを考えたことはないだろうか?
PHPの配列はよく考えてみると特徴的な挙動をする。

まずPHPの配列は、値の代入方法がいろいろとある。
下記の3つの配列は全て同じ結果の配列となる。

<?php

$ar1[] = 'aa';
$ar1[] = 'bb';
$ar1[] = 'cc';
var_dump($ar1);

$ar2[0] = 'aa';
$ar2[1] = 'bb';
$ar2[2] = 'cc';
var_dump($ar2);

$ar3 = array('aa', 'bb', 'cc');
var_dump($ar3);

?>
array(3) {
  [0]=>
  string(2) "aa"
  [1]=>
  string(2) "bb"
  [2]=>
  string(2) "cc"
}
array(3) {
  [0]=>
  string(2) "aa"
  [1]=>
  string(2) "bb"
  [2]=>
  string(2) "cc"
}
array(3) {
  [0]=>
  string(2) "aa"
  [1]=>
  string(2) "bb"
  [2]=>
  string(2) "cc"
}

3つとも、連番の添え字を持つ配列となった。

次に連想配列。
下の二つの連想配列は同じものとなる。

<?php

$ar1['name'] = '山田太郎';
$ar1['age'] = 30;
$ar1['tall'] = 170;
var_dump($ar1);

$ar2 = array('name' => '山田太郎', 'age' => 30, 'tall' => 170);
var_dump($ar2);

?>

それでは連想配列として文字列の数字を指定したらどうなるか。

<?php

$ar['0'] ='aa';
$ar['1'] ='bb';
$ar['2'] ='cc';
var_dump($ar);

?>
array(3) {
  ["name"]=>
  string(8) "山田太郎"
  ["age"]=>
  int(30)
  ["tall"]=>
  int(170)
}
array(3) {
  ["name"]=>
  string(8) "山田太郎"
  ["age"]=>
  int(30)
  ["tall"]=>
  int(170)
}
array(3) {
  [0]=>
  string(2) "aa"
  [1]=>
  string(2) "bb"
  [2]=>
  string(2) "cc"
}

結果、数値のキーとなってしまう。
それでは数値のキーと、同じ数字の文字列のキーの要素を同時に作った場合はどうなるか。

<?php

$ar[0] = 'aa';
$ar[1] = 'bb';
$ar[2] = 'cc';
$ar['0'] = 'dd';
$ar['1'] = 'ee';
$ar['2'] = 'ff';
var_dump($ar);

?>
array(3) {
  [0]=>
  string(2) "dd"
  [1]=>
  string(2) "ee"
  [2]=>
  string(2) "ff"
}

こんな結果となった。
数値のキーと数字文字列のキーは同じものとして扱われ、上書きされているのが分かる。

もうお分かりかと思うが、PHPにおいては配列は内部的には全て連想配列なのである。
0,1,2という連番キーの配列は、実は'0'、'1'、'2'という文字列の連想配列なのです。
なので以下のように飛び番号のキーも当然可能です。

<?php

$ar[0] = 'aa';
$ar[3] = 'bb';
$ar[10] = 'cc';
var_dump($ar);


?>
array(3) {
  [0]=>
  string(2) "aa"
  [3]=>
  string(2) "bb"
  [10]=>
  string(2) "cc"
}

また、連番の途中の要素を削除しても連番が詰められる事はない。

<?php

$ar[0] = 'aa';
$ar[1] = 'bb';
$ar[2] = 'cc';
unset($ar[1]);
var_dump($ar);

?>
array(2) {
  [0]=>
  string(2) "aa"
  [2]=>
  string(2) "cc"
}

これらの動きはPHPの配列は全て連想配列であることを考えるとごく自然な動きであると分かる。
つまり数値キーは配列の何番目の要素であるかを表すわけではなく、あくまで文字列の一つとしてのキーなのである。

データベースのクエリ結果についても同様の事が言える。
MySQLのテーブルよりデータを取得し、結果を配列に格納してみる。

<?php

mysql_connect('localhost', 'root', 'pass');
mysql_select_db('user_admin');
$result = mysql_query('SELECT user_id, user_nm FROM t_user');
$ar = array();
while ($row = mysql_fetch_array($result)) {
    $ar[] = $row;
}
var_dump($ar);

?>
array(2) {
  [0]=>
  array(4) {
    [0]=>
    string(6) "000001"
    ["user_id"]=>
    string(6) "000001"
    [1]=>
    string(12) "山田太郎"
    ["user_nm"]=>
    string(12) "山田太郎"
  }
  [1]=>
  array(4) {
    [0]=>
    string(6) "000002"
    ["user_id"]=>
    string(6) "000002"
    [1]=>
    string(12) "佐藤花子"
    ["user_nm"]=>
    string(12) "佐藤花子"
  }
}

SQLのクエリ結果は行×列の2次元配列になり、
各行は列名がキーとなった配列として取得できるが、
列番号の連番インデックスよりの取得も出来る。
しかしこれは単純に同じ値を列番号と列名の二つのキーで別々の要素として存在するだけで、
例えばforeachで全要素をループ処理し、HTMLのテーブルタグを作成したりするとおかしなことになる。

<?php

mysql_connect('localhost', 'root', 'pass');
mysql_select_db('user_admin');
$result = mysql_query('SELECT user_id, user_nm FROM t_user');
$html = '"1">';
while ($row = mysql_fetch_array($result)) {
    $html .= '';
    foreach ($row as $col) {
        $html .= '';
    }
    $html .= '';
}
$html .= '
' . $col . '
'
; print mb_convert_encoding($html, 'SJIS', 'UTF-8'); ?>

000001000001山田太郎山田太郎
000002000002佐藤花子佐藤花子

列が2列ずつになってしまっている。
mysql_fetch_arrayという関数の戻り値である配列は行単位のデータであるが、
格納されている値は列番号と列名の両方を含むためにこのようなことになる。
そのため、mysql_fetch_assocやmysql_fetch_rowという関数が用意されており、
結果を列名の配列のみ、または列番号の配列のみで取得することも出来るようになっているわけである。

PHPの配列は中になんでも格納でき、融通が利くため、かなり強力なのだが、
その特性を理解していないと思わぬ動きをすることがあるので注意してもらいたい。