LoginSignup
31
23

More than 1 year has passed since last update.

PHP 自作のコレクション(リスト)クラスを実装する

Last updated at Posted at 2021-02-10

LaravelCollectionクラスのような、foreach可能なオブジェクトを作ります。
実装の幅が広がるので覚えておいて損はないです。

環境

  • PHP 8.0.1

前提知識: IteratorAggregate

コレクションクラス(イテレート可能なオブジェクト)を作成するためにはPHPが用意している IteratorAggregate インターフェースを契約する必要があります。
https://www.php.net/manual/ja/class.iteratoraggregate.php

IteratorAggregate extends Traversable {
    abstract public getIterator(): Traversable
}

Let's try!

  • Task.php (入れ物に入れる物)
  • TaskList.php (入れ物)
  • main.php (お試し実行用のファイル)

3つのファイルを作ります。

Task.php

何でもいいのですが、例としてTaskクラスを作ります。

Task.php
<?php declare(strict_types=1);

final class Task
{
    public function __construct(private int $id)
    {
    }

    public function name(): string
    {
        return 'task' . $this->id;
    }
}

TaskList.php

Taskクラスを格納するTaskListコレクションクラスを実装します。

TaskList.php
<?php declare(strict_types=1);

final class TaskList implements IteratorAggregate
{
    public function __construct(private array $attributes = [])
    {
    }

    /**
     * @return Task[]|ArrayIterator
     */
    #[ReturnTypeWillChange]
    public function getIterator(): array|ArrayIterator
    {
        return new ArrayIterator($this->attributes);
    }
}

コレクションとなるクラスは getIterator() メソッドを実装していればokです。
@return Task[] としておくとPhpStormで補完が効くようになります。

#[ReturnTypeWillChange] はPHP8.1で追加される属性です。
(PHP8.0 では戻り値の型が不一致の場合に非推奨の通知が表示されます。)

main.php

main.php
<?php declare(strict_types=1);

require('./Task.php');
require('./TaskList.php');

$taskList = new TaskList([
    new Task(1),
    new Task(2),
    new Task(3),
    new Task(4),
    new Task(5),
]);

foreach ($taskList as $task) {
    echo $task->name() . PHP_EOL;
}

main.php を実行してみます。

$ php main.php
task1
task2
task3
task4
task5

TaskList クラスをforeachで回してTaskクラスのインスタンスを取得してname()メソッドを実行できました。

補足: add(), count()

add(), count() を追加するとより便利に使えます。
Countableインターフェースを契約してcount()メソッドを実装するとより良いです。

<?php declare(strict_types=1);

final class TaskList implements IteratorAggregate, Countable
{
    public function __construct(private array $attributes = [])
    {
    }

    /**
     * @param Task $task
     */
    public function add(Task $task): void
    {
        $this->attributes[] = $task;
    }

    /**
     * @return int
     */
    public function count(): int
    {
        return count($this->attributes);
    }

    /**
     * @return Task[]|ArrayIterator
     */
    #[ReturnTypeWillChange]
    public function getIterator(): array|ArrayIterator
    {
        return new ArrayIterator($this->attributes);
    }
}

add(Task $task) メソッドでは型宣言しているので、配列追加時にTaskのインスタンスを安心して追加できます。

main.php
<?php declare(strict_types=1);

require('./Task.php');
require('./TaskList.php');

$taskList = new TaskList();
$taskList->add(new Task(1));
$taskList->add(new Task(2));
$taskList->add(new Task(3));
echo $taskList->count() . PHP_EOL;
echo count($taskList) . PHP_EOL;

Countableインターフェースを実装したクラスはcount($taskList)count()関数を使用できます。

$ php main.php
3
3

参考

31
23
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
31
23