2012年2月29日水曜日

PHP でプライベート変数やメソッドをクラスの外から操作する

何のために操作するのか
※本エントリはPHP 5.3.2以上で正常に動きます。

何のためかというと、例えばテストのときに使います。

プログラムの構造を変更するコトを、リフレクションといいます。
リフレクションとは、

プログラムの実行過程でプログラム自身の構造を読み取ったり書き換えたりする技術のことである。
リフレクション (情報工学) | Wikipediaより。

PHPのリフレクションまわりのManualはコチラです。

このエントリの目次
1.このエントリで使うサンプルクラス
2.プライベート変数をクラスの外から操作する
3.プライベートメソッドをクラスの外から操作する
4.最後に

1.このエントリで使うサンプルクラス
<php

class HideyoshiToyotomi {
    //建前
    //(書いてるだけで本エントリでは特に触れません)
    public $statedReason = '信長さまー!';
    //本音
    private $_realIntention = '信長うぜー!';

    //表向きは信長に心酔して仕えているように見えます
    //(書いてるだけで本エントリでは特に触れません)
    public function serveNobunaga() {
        echo '信長様のためなら命も惜しくありませぬ!';
    }   

    //外からは見えませんが、
    //内には天下取りの夢を秘めています
    private function _getTenka(){
        echo 'よっしゃ俺が天下取ったるで!';
    }   
}
HideyoshiToyotomi.php

秀吉の枷という小説を読んでいるので、秀吉クラスを例にしてみました。
(信長に仕えていた頃は「豊臣」ではないですが…豊臣の方が分かりやすいと思ったので。)

以下、秀吉クラスの概要です。

外から見えるのは、
    public $statedReason = '信長さまー!';
という建前と、
    public function serveNobunaga() {
        echo '信長様のためなら命も惜しくありませぬ!';
    } 
という信長への尽力です。

でも外から見えないところには
    private $_realIntention = '信長うぜー!';
という本音と
    private function _getTenka(){
        echo 'よっしゃ俺が天下取ったるで!';
    }
という天下取りの行動が隠されています!

こんなサンプルでよいのか。

2.プライベート変数をクラスの外から操作する
<どうでもいい>
本能寺の変が起こり、秀吉は弔い合戦として光秀を討つことを決めます。
秀吉の本音はいかに…?
</どうでもいい>

private変数である $_realIntention にアクセスできるようにして、秀吉の本音を暴いてみます。

サンプルコードです。
<?php
include './HideyoshiToyotomi.php';

$hideyoshiToyotomi = new HideyoshiToyotomi();
$reflectionClass = new ReflectionClass($hideyoshiToyotomi);

//変数名を文字列で指定します
$realIntention = $reflectionClass
    ->getProperty('_realIntention');
//private変数にアクセスできるようにします。
$realIntention->setAccessible(true);

//getValueメソッドで
//$_realIntention の値を外から取得します
echo $realIntention
    ->getValue($hideyoshiToyotomi) . "\n";

15行目の echo で、$_realIntention = 秀吉の本音 の「信長うぜー!」が外から見えました。

<どうでもいい>
さて、ここで秀吉は黒田官兵衛の言葉で、考えが変わります。
</どうでもいい>

では次に、private変数である $_realIntention = 秀吉の本音について、外から値をセットして変えてみます。

以下がそのコードです。
上記サンプルコードの最後に追加します。
//setValueメソッドで外から値をセットする
$realIntention->setValue(
    $hideyoshiToyotomi,
    '俺が天下人じゃー!'
);
echo $realIntention
    ->getValue($hideyoshiToyotomi) . "\n";

private変数である$_realIntention の値を外から変更し、「俺が天下人じゃー!」という本音になりました!

3.プライベートメソッドをクラスの外から操作する
<どうでもいい>
秀吉は光秀を討つこときっかけに、天下取りの行動をとり始めます。
</どうでもいい>

privateメソッドである_getTenka を外から見えるようにしてみます。

以下がサンプルコードです。
<?php
include './HideyoshiToyotomi.php';

$hideyoshiToyotomi = new HideyoshiToyotomi();

$reflectionClass = new ReflectionClass($hideyoshiToyotomi);

//メソッド名を文字列で指定します
$getTenka = $reflectionClass
    ->getMethod('_getTenka');
//privateメソッドにアクセスできるようにします。
$getTenka->setAccessible(true);
//invokeメソッドにて、_getTenkaメソッドを実行します
$getTenka->invoke($hideyoshiToyotomi);

privateで隠蔽されていた_getTenka メソッドが実行され「よっしゃ俺が天下取ったるで!」と表示されました。

4.最後に
テストで重宝するので書きました。
他にもPHPUnit の モックオブジェクトという便利なものがあるのですが、それはまたの機会に…。

無駄な情報が多くて例が分かりにくかったかもしれません。
ドラゴンボールとかの方がまだ分かりやすかったかしら、と思ってます。

でもCarクラスなどの味気ないサンプルもあれかなあ…と思ったので、秀吉クラスを例としました。

以上です。

0 件のコメント:

コメントを投稿


Related Posts Plugin for WordPress, Blogger...