PHP入門 オブジェクト指向 オブジェクトの操作

オブジェクトの代入や比較、反復処理について解説しています。

オブジェクトの代入

変数の代入は値渡しが既定ですが、オブジェクト変数だけは例外的に参照渡しが既定です。

<?php
require_once 'Person.php';

$p1 = new Person('太郎','山田');
$p2 = $p1; //オブジェクトを代入
$p2->firstName = '花子';
$p2->lastName = '工藤';
print_r($p1);//結果:Person Object ( [firstName] => 花子 [lastName] => 工藤 ) 
print_r($p2);//結果:Person Object ( [firstName] => 花子 [lastName] => 工藤 ) 

オブジェクトが参照渡しされた結果、変数$p2への変更がもともとの変数$p1にも反映されています。

構文:clone命令

clone オブジェクト変数
$p2 = clone $p1;
実行結果
実行結果

オブジェクトの比較

オブジェクトは「==」「===」演算子で比較できます。それぞれのルールは次の通りです。

オブジェクトの比較演算子

演算子等しいとみなす条件
==同じクラスのインスタンスであること、同じプロパティと値を持つこと
===同じクラスの同じインスタンスを参照すること
<?php
require_once 'Person.php';

$p1 = new Person('太郎','山田');
//オブジェクト変数を参照渡した場合
$p2 = $p1; //オブジェクトを代入

var_dump($p1 == $p2); //結果:bool(true)
var_dump($p1 === $p2); //結果:bool(true)

$p3 = clone $p1;
var_dump($p1 == $p3); //結果:bool(true)
var_dump($p1 === $p3); //結果:bool(false)

オブジェクト変数は参照渡しが既定です。このため「=」演算子で代入された変数$p2は$p1とは同一のインスタンスとみなされ、オブジェクト同士の比較でも「==」「===」演算子はともにtrueを返します。一方、clone命令で値渡しした変数$p3と、代入元の$p1とを比較するとどうでしょう。この場合「==」演算子は両者等しいとみなしますが、「===」演算子は両者を違うものと判定します。プロパティ値は等しいものの、$p1、$p3が指し示しているインスタンスが別物であるからです。

オブジェクトの反復処理

PHPでは、オブジェクトの内容をforeach命令で反復処理するための手段を提供しています。オブジェクトをforeach命令で処理した場合、その文脈でアクセス権限があるすべてのプロパティを処理します。

MyClass.php

<?php
class MyClass {
    // それぞれのアクセス権限でプロパティを定義
    public string $pub ='public'; 
    protected string $pro ='protected'; 
    private string $pri = 'private';

    // プロパティをリスト表示するための showProperty メソッド 
    public function showProperty(): void {
        foreach ($this as $key => $value) {
            print "{$key} {$value} <br />";
        }
    }
}
<?php
require_once 'MyClass.php';

$cls = new MyClass();
//MyClassオブジェクトのプロパティをリスト表示
foreach($cls as $key => $value){
    print "{$key}:{$value}<br />";
}
print '<hr />';
//MyClassオブジェクトのプロパティをリスト表示(showPropertyメソッド経由)
$cls->showProperty();
実行結果
実行結果

基本的な考え方としては、オブジェクトはforeach命令にかけると「プロパティ名=>値」で構成される連想配列のように処理されるということです。ただし、その場合にもアクセス修飾子が加味されます。

たとえば➊ではクラス(オブジェクト)の外部からアクセスしているので、public修飾子を持ったプロパティにしかアクセスできません。しかし、➋ではクラス内部のshowPropertyメソッド経由でオブジェクトにアクセスしているので、publicプロパティはもちろん、protectedプロパティやprivateプロパティも取得できています。

オブジェクトを配列にキャストすることで、オブジェクトのプロパティに対して連想配列のようにアクセスすることもできます。

$ary = (array)#cls;
print $ary['pub'];

逆も可能です。連想配列をオブジェクトにキャストした場合、連想配列のそれぞれのキーにはプロパティにアクセスする要領でアクセスできるようになります。

反復処理のカスタイマイズ

オブジェクトに含まれるプロパティをそのまま処理したいケースは、それほど多くないと思います。そこで、PHPではオブジェクトの反復処理をカスタイマイズするための手段を提供しています。

例:カスタムの反復処理を実装したFriendListクラス例です。FriendListクラスはPersonクラスのリストを管理し、foreach命令でそのリストを取り出せるような仕組みを提供しします。

FriendList.php

<?php
class FriendList implements IteratorAggregate {
    //ダミープロパティを定義
    public string $version = '1.0.0';
    public string $name ='友人リスト';
    //Personオブジェクトのリストを格納するためのprivate変数
    private array $list = [];

    //反復処理の対象を定義
    public function getIterator(): Traversable {
        return new ArrayIterator($this->list);
    }
    //Personオブジェクトを追加するためのaddメソッド
    public function add(Person $p): void {
        $this->list[] = $p;
    }

}

カスタムの反復処理を実装する手順は次のとおりです。

➊IteratorAggregateインターフェースを実装する

IteratorAggregateインターフェースは、PHP標準で提供するインターフェースの1つで、カスタムの反復処理を提供するためのメソッドを取り決めています。カスタムの反復処理を実装するには、IteratorAggregateインターフェースを実装した上で、そのgetIteratorメソッドをオーバーライドする必要があります。

➋getIteratorメソッドをオーバーライドする

getIteratorメソッドは、反復処理に使用するイテレーター(反復のための基本機能を提供するオブジェクト)を返すためのメソッドです。ここでは、getIteratorメソッドがArrayIteratorオブジェクトを返すように、コードを実装します。ArrayIteratorクラスもPHPが標準で提供するクラスの1つで、配列にイテレーターとしての機能を持たせるための機能を提供します。ここでは、private配列$listの内容をArrayIteratorコンストラクターに引き渡すことで、$listの内容を読み込むためのイテレーターを返しているわけです。

構文:ArrayIteratorクラス(コンストラクター)

new ArrayIterator(mixed $array)
$array基になる配列

これでFriendListクラスをforeach命令で処理すると、配列$listの内容が順番に処理されるようになります。

<?php
require_once 'Person.php';
require_once 'FriendList.php';

$list = new FriendList();
$list->add(new Person('太郎','山田'));
$list->add(new Person('花子','工藤'));
$list->add(new Person('たけし','佐藤'));

//FriendListオブジェクトの内容を順に処理し、そのPerson::showメソッドを実行
foreach ($list as $value){
    print $value->show();
}
実行結果
実行結果

反復処理によってプライベート変数$listの内容が取り出せています。

素数を求めるイテレーター

実践的な例として、PrimeIterator(素数)クラスを作成してみます。PrimeIteratorクラスはmaxプロパティを持ち、インスタンスを列挙することでmaxを上限とする素数を出力できるものとします。


<?php
class PrimeIterator implements Iterator {
    private int $key;
    private int $current;
    private int $max;
    // キー
    // 現在
    // 上限值
    public function __construct (int $max) {
        $this->rewind();
        $this->max = $max;
    }

    // 次の要素に移動
    public function next(): void {

        $this->key+= 1; while(true) {
            $this->current++;
            if ($this->isPrime ($this->current)) {
                return;
            }
        }
    }

    // 現在の要素のキーを取得
    public function key(): mixed { 
        return $this->key;
    }
    // 現在の要素を取得
    public function current() : mixed { 
        return $this->current;
    }
    // 最初の要素に移動
    public function rewind(): void {
        $this->key= 0;
        $this->current = 2;
    }
    // 現在の位置が有効かを判定する
    public function valid(): bool {
        return $this->current() <= $this->max;
    }
    //引数$valueが素数かどうかを判定
    private function isPrime (int $value) : bool { 
        $prime = true;
        for($i = 2; $i <= floor(sqrt($value));$i++){
            if($value % $i === 0){
                $prime = false;
                break;
            }
        }
        return $prime;
    }
}

// 100以下の素数を列挙
$pr = new PrimeIterator(100);//➏
foreach ($pr as $p) {
    print "{$p}<br />";
}

今度は、列挙可能な値が配列などで管理されているわけではないので、イテレーターそのものを自作する必要があります。これにIteratorインターフェースを実装します。Iteratorインターフェースで実装すべきメソッドは以下の表のとおりです。

Iteratorインターフェースのメソッド

メソッド概要
current()現在の要素を取得
key()現在の要素のキーを取得
next()次の要素に移動
rewind()最初の要素に移動
valid()現在の要素位置が有効かを判定

PrimeIteratorクラスでは、現在取得済みの素数をcurrentプロパティで、インデックス番号をkeyプロパティで、それぞれ管理しています。nextメソッドでは、currentの値を順にカウントアップし、次の素数が見つかったところで、これを現在値とします。validメソッドは、foreachループでの終了条件を表します。この例であれば、currentプロパティがmaxプロパティを超えた場合にfalseを返します。以上のようなPrimeIteratorクラスをインスタンス化し、foreachループにかけたのが➏です。確かに、指定した値(100)を上限とする素数が列挙されることをが確認できます。

PHPでは、Iteratorインターフェースを実装したイテレーターが標準でたくさん用意されています。

クラス名概要
ArrayIterator配列をもとにしたイテレーター
AppendIterator複数のイテレーターを順に走査
DirectoryIterator指定のフォルダ配下を走査するためのイテレーター
Infinitelterator他のイテレーターを無限に反復
LimitIterator他のイテレーターの指定範囲を反復
CallbackFilterIterator他のイテレーターをコールバック関数でフィルタリング
RegexIterator他のイテレーターを正規表現でフィルタリング