忍者ブログ

VB.NET-TIPS などプログラミングについて

VB.NETのTIPS(小技集)を中心に、Javascript、PHP その他のプログラミングについて少し役に立つ情報を発信します。いわゆる個人的な忘備録ですが、みなさんのお役に立てれば幸いです。

PHP クラス(Class)のプロパティの使い方について(__get(), __set())


今回はPHPのクラス(Class)の プロパティ について説明したいと思います。

クラスの プロパティ はそのクラスの内部で必要な属性を保存しておくための変数として使用します。 プロパティ の宣言は private 及び public で行います。 public で宣言したプロパティは、外部から直接アクセスができますが private で宣言したプロパティは、外部から直接アクセスができないため、値を設定したり、取得する関数を別に用意します。

また、宣言されていないプロパティをアクセスした時に呼ばれる関数(マジックメソッド)が在りますので、 それを使った対処方法を以下に順次記します。




■簡単な プロパティ の宣言例

テスト用クラスとして test を宣言しその中に1個のプロパティ $prop1 を宣言します。
このクラスを生成し、プロパティを var_dump 関数で直接アクセスします。

<?php
// テストクラス
class test {
    public $prop1;
}
// クラス生成
$obj = new test();
// プロパティへのアクセス
var_dump($obj->prop1);
?>

これを実行するとブラウザに以下の様に表示されます。クラス生成後「$prop1」には何も設定されていので「NULL」と表示されます。

NULL



■存在しない プロパティ へのアクセス例

以下の例は上の例と似ていますが、宣言されていないプロパティ $prop2 へのアクセスを行います。

<?php
// テストクラス
class test {
    public $prop1;
}
// クラス生成
$obj = new test();
// 宣言されていないプロパティへのアクセス
var_dump($obj->prop2);
?>

これを実行するとブラウザに以下の様に表示されます。

Notice: Undefined property: test::$prop2 in xxxxx on line 9
NULL

(xxxxx:この部分は実際のソースの位置なので伏字にしました)
PHPのエラーメッセージが Notice となっていて、その後に NULL が表示されているので、どうも $prop2 が存在している様な感じです。
そこでソースの最後に、クラスのインスタンスオブジェクトそのものを表示してみます。

<?php
// テストクラス
class test {
    public $prop1;
}
// クラス生成
$obj = new test();
// 宣言されていないプロパティへのアクセス
var_dump($obj->prop2);
?>

これを実行するとブラウザに以下の様に表示されます。クラスのインスタンス内には $prop2 は存在しない様です。 Notice の後の NULL はプロパティが存在しないので、仮に NULL が返された様です。

Notice: Undefined property: test::$prop2 in xxxxx on line 9

object(test)#1 (1) {
  ["prop1"]=>
  NULL
}



■存在しない プロパティ へのアクセス例その2(設定)

今度は存在しない プロパティ へ直接上書き設定を行っています。

<?php
// テストクラス
class test {
    public $prop1;
}
// クラス生成
$obj = new test();
// 宣言されていないプロパティへのアクセス(強制設定)
$obj->prop2 = "2";
$obj->prop3 = "3";
// クラスインスタンスの表示
echo "<pre>";
var_dump($obj);
echo "</pre>";
?>

これを実行するとブラウザに以下の様に表示されます。宣言されていないプロパティへの設定は強制的に作成される様です。
これは仕様の様なので仕方無いのですが、微妙な感じです。 この微妙さを解消する為にPHPには宣言されていないプロパティをアクセスした時に呼ばれるメソッドをクラス内に宣言するころで 自分で対処できる様です。

object(test)#1 (3) {
  ["prop1"]=>
  NULL
  ["prop2"]=>
  string(1) "2"
  ["prop3"]=>
  string(1) "3"
}



マジックメソッド での存在しない プロパティ へのアクセス制御(__get(), __set())

PHPには宣言されていないプロパティをアクセスした時に呼ばれるメソッド名が決まっていまして、 プロパティ値の取得・設定用にそれぞれ __get()__set() を宣言します。 以下にその例を示します。

<?php
class Test {
    public $prop1;
 
   	public function __get($name) {
        echo "__get($name)プロパティエラー
"; } public function __set($name, $value) { echo "__set($name, $value)プロパティエラー
"; } } // クラス生成 $obj = new test(); // 宣言されていないプロパティへのアクセス(強制設定) $w = $obj->prop2; $obj->prop2 = "2"; ?>

これを実行するとブラウザに以下の様に表示されます。

__get(prop2)プロパティエラー
__set(prop2, 2)プロパティエラー

この例では、宣言されていないプロパティへのアクセスでエラーを表示しているだけですので、 これを少し修正して、プロパティを内部に連想配列として退避することで、プロパティの動きを作ります。 以下にその修正版を示します。

取得用の __get() では退避配列にデータが存在すればその値を返し、無ければ NULL を返しています。 また設定用の __set() では退避配列にデータを退避するだけです。

<?php
class test {
    public $prop1;
    // 宣言していないプロパティ退避用配列
    private $arrProp = array();
    
    public function __get($name) {
        // 退避配列にデータが存在するか?
        if (isset($this->arrProp[$name])) {
            // データを返す
            return $this->arrProp[$name];
        }
        else {
            return NULL;
        }
    }

    public function __set($name, $value) {
        // 退避配列にデータを設定
        $this->arrProp[$name] = $value;
    }
}
echo "<pre>";
// クラス生成
$obj = new test();
// 宣言されていないプロパティへのアクセス(取得)
var_dump($obj->prop2)."
"; // 宣言されていないプロパティへのアクセス(設定) $obj->prop2 = "2"; // 宣言されていないプロパティへのアクセス(再取得) var_dump($obj->prop2)."
"; var_dump($obj)."
"; echo "</pre>"; ?>

これを実行するとブラウザに以下の様に表示されます。 配列「$arrProp」にプロパティ「prop2」が格納されています。
今回 __get() で配列にデータが無い場合には NULL を返していますが、 Exception でエラーを返した方が良い場合があると思います。

NULL
string(1) "2"
object(test)#1 (2) {
  ["prop1"]=>
  NULL
  ["arrProp":"test":private]=>
  array(1) {
    ["prop2"]=>
    string(1) "2"
  }
}


マジックメソッド での存在しない プロパティ へのアクセス制御その2(__isset(), __unset())

マジックメソッド である __get()__set() を宣言することで 宣言されていないプロパティをアクセスできる様になりました。
但し、以下の様にそのプロパティの存在チェックを isset() 関数で行うと思った様になりません。
<?php
class test {
    public $prop1;
    // 宣言していないプロパティ退避用配列
    private $arrProp = array();
    
    public function __get($name) {
        // 退避配列にデータが存在するか?
        if (isset($this->arrProp[$name])) {
            // データを返す
            return $this->arrProp[$name];
        }
        else {
            return NULL;
        }
    }

    public function __set($name, $value) {
        // 退避配列にデータを設定
        $this->arrProp[$name] = $value;
    }
}
// クラス生成
$obj = new test();
// プロパティ存在確認
if (isset($obj->prop2) == true) {
    echo "プロパティ存在OK"."
"; } else { echo "プロパティ存在NG"."
"; } // プロパティ設定 $obj->prop2 = "2"; // プロパティ存在確認 if (isset($obj->prop2) == true) { echo "プロパティ存在OK"."
"; } else { echo "プロパティ存在NG"."
"; } ?>
これを実行するとブラウザに以下の様に表示されます。
プロパティ存在NG
プロパティ存在NG
これは、よくよく考えてみれば、__set() でプロパティ値として退避しているのは 「$arrProp」なのでこの中の存在をチェックしなければ、意味が無いことが分かります。

尚、マジックメソッド として宣言できるものの中に __isset()__unset() があるのですが これらはクラスのプロパティに対して isset()unset() を行う時に呼ばれるメソッドです。
よって、これらを宣言し、クラス内の「$arrProp」を処理する様に修正します。
<?php
class test {
    public $prop1;
    // 宣言していないプロパティ退避用配列
    private $arrProp = array();
    
    public function __get($name) {
        // 退避配列にデータが存在するか?
        if (isset($this->arrProp[$name])) {
            // データを返す
            return $this->arrProp[$name];
        }
        else {
            return NULL;
        }
    }

    public function __set($name, $value) {
        // 退避配列にデータを設定
        $this->arrProp[$name] = $value;
    }

    public function __isset($name) {
        // 退避配列内の存在チェック
        return isset($this->arrProp[$name]);
    }

    public function __unset($name) {
        if (isset($this->arrProp[$name]) == true) {
            // 退避配列内のunset
            unset($this->arrProp[$name]);
        }
    }
}
// クラス生成
$obj = new test();
// プロパティ存在確認
if (isset($obj->prop2) == true) {
    echo "プロパティ存在OK"."
"; } else { echo "プロパティ存在NG"."
"; } // プロパティ設定 $obj->prop2 = "2"; // プロパティ存在確認 if (isset($obj->prop2) == true) { echo "プロパティ存在OK"."
"; } else { echo "プロパティ存在NG"."
"; } ?>
これを実行するとブラウザに以下の様に表示されます。
プロパティ存在NG
プロパティ存在OK

これでクラス内で宣言していないプロパティを外部から任意の名前でアクセスができるクラスを作成できました。 このクラスを基底クラスとして、拡張したクラスを作成していけると思います。

ただし、任意の時点でプロパティが作成できることは、あちらこちらでその処理を行うと、 後からソースを見た時に理解に時間が掛かるかもしれません。 この辺りは、プロパティの仕様をしっかりしないと、クラス内に個別にプロパティを宣言した方が良かったことになりかねませんので。












PR

コメント

コメントを書く