[PHP] 複数のキーが配列に存在／必須を網羅しているか確認する

> 受け取った配列が、必須のキーを網羅しているか確認したい。

[「php 複数キー 配列 確認」で Qiita 記事をググって](https://www.google.com/search?q=site:qiita.com+php+%E8%A4%87%E6%95%B0%E3%82%AD%E3%83%BC+%E9%85%8D%E5%88%97+%E7%A2%BA%E8%AA%8D)も、タイトルからわかるピンポイントの記事がなかったので、自分のググラビリティとして。

## TL; DR

> 必須キーの配列を用意し、それを `array_flip()` でキーと値をフリップ（反転）したのち、`array_diff_key()` でキーの差で比べる。

```php:
if (array_diff_key(array_flip($keys_required), $data)) {
    echo '網羅していません';
}
```

- 必須キー以外は認めない場合は TS; DR の末尾参照

## TS; DR

- [array-diff-key](https://www.php.net/manual/ja/function.array-diff-key.php) @ PHP 公式マニュアル
- [array-flip](https://www.php.net/manual/ja/function.array-flip.php) @ PHP 公式マニュアル
- [How to check if multiple array keys exists](https://stackoverflow.com/questions/13169588/how-to-check-if-multiple-array-keys-exists/27109097) @ StackOverflow

```php
<?php
// 必須のキーの配列
$keys_required = [
    'a',
    'b',
    'c',
];

// 受け取った配列
$data = [
    'a' => 'hoge',
    'b' => 'fuga',
    'c' => 'piyo',
    'e' => 'mogera',
];
echo array_diff_key(array_flip($keys_required), $data) ? '必須キーを網羅していません' : '必須キーを網羅しています', PHP_EOL;
// 結果: 必須キーを網羅しています
```

### 色々なパターン

```php:必須キー
// 必須キー
$keys_required = [
    'a',
    'b',
    'c',
];
```
```php:必須キーが足りないパターン
$data = [
    'a' => 'hoge',
    'b' => 'fuga',
];
echo array_diff_key(array_flip($keys_required), $data) ? 'NG' : 'OK', PHP_EOL;
//結果: NG
```
```php:必須キーとピッタリ同じ
$data = [
    'a' => 'hoge',
    'b' => 'fuga',
    'c' => 'piyo',
];
echo array_diff_key(array_flip($keys_required), $data) ? 'NG' : 'OK', PHP_EOL;
// 結果: OK
```
```php:必須キーとピッタリだが順番が違う
$data = [
    'c' => 'piyo',
    'b' => 'fuga',
    'a' => 'hoge',
];
echo array_diff_key(array_flip($keys_required), $data) ? 'NG' : 'OK', PHP_EOL;
// 結果: OK
```
```php:必須キーとピッタリだが値が"null"
$data = [
    'a' => 'hoge',
    'b' => 'fuga',
    'c' => null,
];
echo array_diff_key(array_flip($keys_required), $data) ? 'NG' : 'OK', PHP_EOL;
// 結果: OK
```
```php:必須キーは網羅しているが余計なキーも含む
$data = [
    'a' => 'hoge',
    'b' => 'fuga',
    'c' => 'piyo',
    'd' => 'mogera',
];
echo array_diff_key(array_flip($keys_required), $data) ? 'NG' : 'OK', PHP_EOL;
// 結果: OK
```
```php:必須キーは網羅していないが、要素数は同じ
$data = [
    'a' => 'hoge',
    'b' => 'fuga',
    'e' => 'mogera',
];
echo array_diff_key(array_flip($keys_required), $data) ? 'NG' : 'OK', PHP_EOL;
// 結果: NG
```
```php:必須キーは網羅しているが、値が"null"
$data = [
    'a' => 'hoge',
    'b' => 'fuga',
    'c' => null,   // null であってもキーがあればおk
    'd' => 'piyo',
];
echo array_diff_key(array_flip($keys_required), $data) ? 'NG' : 'OK', PHP_EOL;
// 結果: OK
```

- [オンラインで動作を見る](https://paiza.io/projects/YSDw6L2mrwJ9RrsZhonJMw) @ paiza.IO

### 必須キー以外は認めない

> `array_diff_key($a, $b)` と `array_diff_key($b, $a)` の差を `array_merge()` する。

```php:必須のキーの配列
<?php

$keys_required = [
    'a',
    'b',
    'c',
];

// 受け取った配列
$data = [
    'a' => 'hoge',
    'b' => 'fuga',
    'c' => null,
];

$a = array_flip($keys_required);
$b = $data;
if (array_merge(array_diff_key($a, $b), array_diff_key($b, $a))) {
    echo '網羅していません';
}
```

```php:必須キーが完全一致
$data = [
    'a' => 'hoge',
    'b' => 'fuga',
    'c' => null,
];
$keys_required_flipped =array_flip($keys_required);
echo array_merge(array_diff_key($keys_required_flipped, $data), array_diff_key($data, $keys_required_flipped)) ? 'NG' : 'OK', PHP_EOL;
// 結果: OK
```
```php:必須キーが足りない
$data = [
    'a' => 'hoge',
    'b' => 'fuga',
];
$keys_required_flipped =array_flip($keys_required);
echo array_merge(array_diff_key($keys_required_flipped, $data), array_diff_key($data, $keys_required_flipped)) ? 'NG' : 'OK', PHP_EOL;
// 結果: NG
```
```php:必須キーは網羅しているが要素数が多い
$data = [
    'a' => 'hoge',
    'b' => 'fuga',
    'c' => null,   // null
    'd' => 'piyo',
];
$keys_required_flipped =array_flip($keys_required);
echo array_merge(array_diff_key($keys_required_flipped, $data), array_diff_key($data, $keys_required_flipped)) ? 'NG' : 'OK', PHP_EOL;
// 結果: NG
```

- [オンラインで動作を見る](https://paiza.io/projects/7EdD030YyR8tHIUWaUew1w) @ paiza.IO