PHPで分割代入していくだけの投稿

この記事は PHP Advent Calendar 2019 の1日目の記事です。


# 分割代入とは

配列の中身を複数の変数に一回で代入できる書き方

# `list()` と `[]`

`list()`を使うか、PHP7.1以降では`[]`を使ってもOK

この投稿では基本的に`[]`の書き方で試していきます。

```php
$colors = ['red', 'yellow', 'blue'];

[$x, $y, $z] = $colors;
var_dump($x, $y, $z); // "red", "yellow", "blue"

list($a, $b, $c) = $colors;
var_dump($a, $b, $c); // "red", "yellow", "blue"
```

### swapした例

```php
$a = 'apple';
$b = 'banana';

[$a, $b] = [$b, $a];
var_dump($a, $b); // "banana" "apple"

list($a, $b) = [$b, $a];
var_dump($a, $b); // "apple" "banana"

// 関数を用意する例
// 参考：https://www.php.net/manual/en/migration71.new-features.php
function swap(&$a, &$b): void {
    [$a, $b] = [$b, $a];
}
swap($a, $b);
var_dump($a, $b); // "banana" "apple"
```

# keyを指定してできる

※`list()`でキーを指定するやり方はPHP7.1から

```php
$animals = ['dog' => 'イヌ', 'cat' => 'ネコ'];

['dog' => $dog1, 'cat' => $cat1] = $animals;
var_dump($dog1, $cat1); // "イヌ" "ネコ"

list('dog' => $dog2, 'cat' => $cat2) = $animals;
var_dump($dog2, $cat2); // "イヌ" "ネコ"
```

# 注意点

一回はまった部分なのですが、配列をソートなどした場合に、インデックスを振り直さないと思い通りに動いてくれない時がありました

```php
// 下2つの例は同じ意味で代入される
[$a, $b] = ['aaa', 'bbb'];
var_dump($a, $b); // "aaa", "bbb"
[$c, $d] = [0 => 'ccc', 1 => 'ddd'];
var_dump($c, $d); // "ccc", "ddd"

// 配列をソートしたがインデックスが変わってないなどの場合に、このような配列になったとすると
[$z, $y] = [1 => 'zzz', 0 => 'yyy'];
// $zに "yyy"、$yに "zzz" が代入された状態になってしまう！
var_dump($z, $y); // "yyy", "zzz"

// やり方はいろいろあると思いますが、
// array_values などで インデックスが振り直された状態にするとうまくいきました。
[$z, $y] = array_values([1 => 'zzz', 0 => 'yyy']);
var_dump($z, $y); // "zzz", "yyy"
```

# 代入する変数が少ない場合

```php
[$a, $b] = [10, 20, 30, 40, 50];
var_dump($a, $b); // 10 , 20

[, $a, , , $b] = [10, 20, 30, 40, 50];
var_dump($a, $b); // 20 , 50

['blue' => $blue] = ['red' => '赤', 'blue' => '青'];
var_dump($blue); // 青
```

# 配列のキーが存在しない場合

```php
[$a, $b] = [10];
var_dump($a, $b); // 10, NULL

[5 => $c] = [10]; // Notice: Undefined offset: 5

['a' => $aaa] = ['b' => 'bbb']; // Notice: Undefined index: a
```

# ネストできる

```php
[[$a, $b], [$c, $d]] = [[1, 2], [3, 4]];
var_dump($a, $b, $c, $d); // 1, 2, 3, 4
```

```php
$config = ['db' => [
    'connection' => 'mysql',
    'host' => 'localhost',
    'database' => 'sample',
    'user' => 'root',
    'pass' => 'root',
]];

['db' => [
    'connection' => $connection,
    'host' => $host,
    'database' => $database,
    'user' => $user,
    'pass' => $pass,
]] = $config;

var_dump($connection, $host, $database, $user, $pass); // "mysql" "localhost" "sample" "root" "root"
```

# foreach

```php
$foods = [
    ['apple', 'tomato', 'cherry'],
    ['banana', 'orange', 'paprika'],
];
foreach($foods as [$food1, $food2, $food3]) {
    var_dump($food1, $food2, $food3);
}

$foods = [
    [
        ['apple', 'cherry'],
        ['tomato'],
    ],
    [
        ['banana', 'orange'],
        ['paprika'],
    ]
];
foreach($foods as [[$fruit1, $fruit2], [$vegetable1]]) {
    var_dump($fruit1, $fruit2, $vegetable1);
}
```

```php
$todos = [
    [
        'id' => 1, 
        'is_done' => true, 
        'todo' => '買い物',
    ],
    [
        'id' => 2, 
        'is_done' => false, 
        'todo' => '映画',
    ],
];
foreach($todos as ['id' => $id, 'is_done' => $isDone, 'todo' => $todo]) {
    var_dump($id, $isDone, $todo);
}

$todos = [
    [
        'id' => 1, 
        'is_done' => true, 
        'todo' => [
            'shopping' => 'コンビニ',
        ],
    ],
    [
        'id' => 2, 
        'is_done' => true, 
        'todo' => [
            'shopping' => '百均',
        ],
    ],
];
foreach($todos as ['todo' => ['shopping' => $shopping]]) {
    var_dump($shopping);
}
```

# 配列の形でも代入できる

```php
$todos = [
    [
        'id' => 1, 
        'is_done' => true, 
        'todo' => '買い物',
    ],
    [
        'id' => 2, 
        'is_done' => false, 
        'todo' => '映画',
    ],
];

$ids = [];
foreach($todos as $todo) {
    ['id' => $ids[]] = $todo;
}
var_dump($ids); // [1, 2]
```

# クラスでも分割代入する

クラスで分割代入しようとすると、このようにエラーになるのですが、

```php
class A {
    public $hoge = 'hoge';
}

['hoge' => $hoge] = new A; // PHP Fatal error:  Uncaught Error: Cannot use object of type A as array
var_dump($hoge);
```

[ArrayAccess](https://www.php.net/manual/ja/class.arrayaccess.php)インターフェースを実装することで、クラスでも分割代入することができました。

参考：[ArrayAccess,IteratorAggregateインタフェースで配列操作可能なオブジェクトを作成する - qiita](https://qiita.com/metheglin/items/87e25bbdf37fbe0cd6e2)

※↓実装の内容はLaravelのCollectionクラスをパクりました。

```php
<?php

class A implements ArrayAccess
{
    private $items = [];

    public function __construct(array $values = []) {
        $this->items = $values;
    }

    public function offsetExists($offset)
    {
        return array_key_exists($offset, $this->items);
    }

    public function offsetGet($offset)
    {
        return $this->items[$offset];
    }

    public function offsetSet($offset, $value)
    {
        if (is_null($offset)) {
            $this->items[] = $value;
        } else {
            $this->items[$offset] = $value;
        }
    }

    public function offsetUnset($offset)
    {
        unset($this->items[$offset]);
    }
}

// ArrayAccessインターフェースを実装すると、クラスを配列のように扱うことができる
$A1 = new A;
$A1[0] = 'hoge';
var_dump($A1[0]); // "hoge"

// 分割代入もできました！
$A2 = new A(['a', 'b', 'c']);
[$a, $b, $c] = $A2;
var_dump($a, $b, $c); // "a" "b" "c"

$A3 = new A(['dog' => '犬', 'cat' => '猫', 'mouse' => 'ネズミ']);
['dog' => $dog, 'cat' => $cat, 'mouse' => $mouse] = $A3;
var_dump($dog, $cat, $mouse); // "犬" "猫" "ネズミ"
```

---

参考

- [ここが変わった！ PHP7.1で知っておきたい新機能まとめ - WPJ](https://www.webprofessional.jp/whats-new-and-exciting-in-php-7-1/)
- [PHP: list - Manual](https://www.php.net/manual/ja/function.list.php)
- [PHP: Migrating from PHP 7.0.x to PHP 7.1.x - Manual](https://www.php.net/manual/en/migration71.php)
    - [PHP: New features - Manual](https://www.php.net/manual/en/migration71.new-features.php)
- [The list function &amp; practical uses of array destructuring in PHP — Sebastian De Deyne](https://sebastiandedeyne.com/the-list-function-and-practical-uses-of-array-destructuring-in-php/)
- [A Re-Introduction To Destructuring Assignment — Smashing Magazine](https://www.smashingmagazine.com/2019/09/reintroduction-destructuring-assignment/)

---

最後まで見ていただいてありがとうございました。m(_ _)m