久しぶりPHPに触れたので配列をおさらいしてみた

数年ぶりに PHP に触れる機会があったのですが、PHP の（独特な）配列について忘れていたので、振り返りがてらここにまとめてみたいと思います。

同じように久しぶりに PHP に触れる方や、初めて触れる方の参考になれば幸いです。

## 環境

**PHP 8.2.11** の環境で動かしたコードを掲載しています。

## PHP の配列
PHP の[リファレンス](https://www.php.net/manual/ja/language.types.array.php)には、配列について次のように書かれてありました。

> PHP の配列は、実際には順番付けられたマップです。マップは型の一種で、 値をキーに関連付けます。 この型は、さまざまな使い道にあわせて最適化されます。 配列としてだけでなく、リスト (ベクター)、 ハッシュテーブル (マップの実装の一つ)、辞書、コレクション、スタック、 キュー等として使用することが可能です。 PHP の配列には他の PHP 配列を値として保持することができるため、 非常に簡単にツリー構造を表現することが可能です。

ここにあります通り、PHPの配列は様々な形態で利用できるというのが1つの特徴なのかなと思います。

#### ・キーと値を指定する場合

上述の通り、PHPの配列の実体は「順番付けられたマップ」なので、各要素は「キーと値」の組み合わせで構成され、下記のように「キー => 値」のように記述します。 キーは**整数**か**文字列**となります。

```php
$languages = ['p' => 'PHP', 'j' => 'Java', 'r' => 'Ruby'];
print_r($languages);

Array
(
    [p] => PHP
    [j] => Java
    [r] => Ruby
)
```

```php
$languages = [1 => 'PHP', 10 => 'Java', 100 => 'Ruby'];
print_r($languages);

Array
(
    [1] => PHP
    [10] => Java
    [100] => Ruby
)
```

#### ・キーを省略した場合

キーは省略可能です。
その場合は、`0, 1, ...` という整数の**キー**が割り振られています。
※添え字に見えますが、実体としては**キー**です。

```php
$languages = ['PHP', 'Java', 'Ruby'];
print_r($languages);

Array
(
    [0] => PHP
    [1] => Java
    [2] => Ruby
)
```

#### ・一部だけキーを省略する場合
一部の要素だけキーを省略することも可能です。
下記の例では、`TypeScript` と `Swift` についてキーを省略していますが、それぞれ、`0`, `1` の整数のキーが割り振られています。

```php
$languages = ['p' => 'PHP', 'j' => 'Java', 'TypeScript', 'r' => 'Ruby', 'Swift'];
print_r($languages);

Array
(
    [p] => PHP
    [j] => Java
    [0] => TypeScript
    [r] => Ruby
    [1] => Swift
)
```

もし、`TypeScript` を `10 => 'TypeScript` とした場合は、`Swift` のキーは `1` ではなく `11` が割り振られています。

```php
$languages = ['p' => 'PHP', 'j' => 'Java', 10 => 'TypeScript', 'r' => 'Ruby', 'Swift'];
print_r($languages);

Array
(
    [p] => PHP
    [j] => Java
    [10] => TypeScript
    [r] => Ruby
    [11] => Swift
)
```

#### ・1つの配列内で、各要素の型が異なる場合

値は任意の型を指定可能で、1つの配列内で、例えば、数値と文字列を格納できます。

```php
$values = ['PHP', 'Java', 'Ruby', 100];
print_r($values);

Array
(
    [0] => PHP
    [1] => Java
    [2] => Ruby
    [3] => 100
)
```

要素が配列、ということもできます。

```php
$languages = ['p' => 'PHP', 'j' => 'Java', 'r' => 'Ruby',  [1, 10, 100]];
print_r($languages);

Array
(
    [p] => PHP
    [j] => Java
    [r] => Ruby
    [0] => Array
        (
            [0] => 1
            [1] => 10
            [2] => 100
        )

)
```

#### ・キーは重複できない

1つの配列の中でキーは重複できないため、重複した場合は（最初の）値が上書きされます。
下記のような場合、キー `j` が重複しており、最初の値 `Java` は、`JavaScript` で上書きされます。

```php
$languages = ['j' => 'Java', 'j' => 'JavaScript'];
print_r($languages);

Array
(
    [j] => JavaScript
)
```

これを踏まえて、下記のような場合を見てみます。
整数キー `10` と 文字列キー `'10'` は、整数と文字列で異なるため、`PHP` と `Java` を持つ要素数2の配列となりそうですが、実際は、`Java` だけを持つ要素数1の配列となります。
PHP では、文字列キーが（10進数として妥当な場合は）整数キーにキャストされるというルールがあるようで、`10` と `'10'` が**同一である**と見なされて、このような挙動となります。

PHP で配列を扱う上で、ここはハマりどころかも知れません。

```php
$languages = [10 => 'PHP', '10' => 'Java'];
print_r($languages);

# この結果になりそうだが
Array
(
    [10] => PHP
    ['10'] => Java
)

# 実際の結果はこちら
Array
(
    [10] => Java
)
```

## 要素の操作

配列の各要素は `配列名[キー]` で参照できます。

```php
$languages = ['p' => 'PHP', 'j' => 'Java', 'r' => 'Ruby'];
echo $languages['p'];

PHP
```

`配列名[（存在する）キー] = 値` で値の上書きができます。

```php
$languages = ['p' => 'PHP', 'j' => 'Java', 'r' => 'Ruby'];
$languages['j'] = 'JavaScript';
print_r($languages);

Array
(
    [p] => PHP
    [j] => JavaScript
    [r] => Ruby
)
```

`配列名[（存在しない）キー] = 値` で追加ができます。
配列の末尾に追加されています。

```php
$languages = ['p' => 'PHP', 'j' => 'Java', 'r' => 'Ruby'];
$languages['t'] = 'TypeScript';
print_r($languages);

Array
(
    [p] => PHP
    [j] => Java
    [r] => Ruby
    [t] => TypeScript
)
```

`配列名[] = 値` でも追加ができます。
配列の末尾に追加されて、整数キー `0` が割り振られています。

```php
$languages = ['p' => 'PHP', 'j' => 'Java', 'r' => 'Ruby'];
$languages[] = 'TypeScript';
print_r($languages);

Array
(
    [p] => PHP
    [j] => Java
    [r] => Ruby
    [0] => TypeScript
)
```

配列内に整数キーの要素がある場合は、それに続く整数キーが割り振られています。

```php
$languages = ['p' => 'PHP', 10 => 'Java', 'r' => 'Ruby'];
$languages[] = 'TypeScript';
print_r($languages);

Array
(
    [p] => PHP
    [10] => Java
    [r] => Ruby
    [11] => TypeScript
)
```

`unset(配列名[キー])` で削除ができます。
※**注意：unset() 以外の関数でも削除できます。あくまで一例として紹介しています。**

```php
$languages = ['p' => 'PHP', 'j' => 'Java', 'r' => 'Ruby'];
unset($languages['j']);
print_r($languages);

Array
(
    [p] => PHP
    [r] => Ruby
)
```

`unset()` を使って削除した場合、キーが詰められることはないようです。

```php
$languages = ['PHP', 'Java', 'Ruby'];
unset($languages[1]);
print_r($languages);

Array
(
    [0] => PHP
    [2] => Ruby # [1] ではなく [2] のまま
)
```

## ループ処理

**foreach** 文を使えばループ処理（一括処理）が可能で、2つの書式があります。

```php
$languages = ['p' => 'PHP', 'j' => 'Java', 'r' => 'Ruby'];
foreach ($languages as $language) {
  echo $language . PHP_EOL;
}

PHP
Java
Ruby
```

```php
$languages = ['p' => 'PHP', 'j' => 'Java', 'r' => 'Ruby'];
foreach ($languages as $key => $value) {
  echo  $key . ':' . $value . PHP_EOL;
}

p:PHP
j:Java
r:Ruby
```

## さいごに

昔を思い出しつつ、PHPの配列について振り返ってみました。

PHP には配列操作系の関数がたくさん用意されているので、要素の削除で `unset()` を紹介しましたが、例えば、**先頭要素の削除**であれば `array_shift()` でも実現できます。

削除に限らずですが、この記事で記載した方法がすべてという事ではありませんので、興味を持たれた方は、是非色々な方法を調べてみてください。