[PHP-dev 1509] Re: 新潟アクセス修飾子ってのを作りました

rti super.rti @ gmail.com
2010年 5月 23日 (日) 11:12:53 JST


komura さん。

始めまして。rtiです。
レスありがとうございます。

>一つ疑問に思ったのでお聞きしたいのですが、「クラス外から読めるが、
>書けない属性」を作成したい場合、__get() を利用すれば同様の機能が
>実装できるように思います。

そうか、php 5.1からそれでいけるんですね、、
気が付かなかったというのが正直なところです。
不勉強ですいません。


と、いうわけで、
あえていうなら、(niigata表記が一般化すれば)ぱっとみ分かりやすくなるぐらいですかね。

外部に公開するが読めるけど書けないメンバ変数(niigata)と、
外部に公開しない、完全にクラス内で閉じた状態のメンバ変数(private)の違いを、
言語レベルで保証するので、ソースを見ただけで一目で分かると思います。


Human の例で書くとしたら、名前は読み込みのみ公開してもいいけど、
年齢、住所、電話番号はクラスの中だけの非公開にしたい時です。
#実際には、クラス内で引き回したいワーク領域などが該当すると思われます。

class Human
{
    private $Age ,$Name, $Address, $Tel;

    public function __get( $key )
    {
    	//名前以外を返してはいけない。
        if ( $key != 'Name' ) return NULL;

        return isset( $this->$key ) ? $this->$key : NULL;
    }
};

↓

class Human
{
    // 名前は外部に読み込みのみ公開する。
    niigata $Name;
    //年齢、住所、電話番号は非公開にする。
    private $Age , $Address, $Tel;
};



ただ、、
非公開にしたいメンバ変数には
_ をつけるようなコーディング規約と そういう __getを作れば回避できそうですね。。。
class Human
{
    private $_Age ,$Name, $_Address, $_Tel;

    public function __get( $key )
    {
    	//_で始まるメンバ変数を公開してはいけない。
        if ( $key[0] == '_' ) return NULL;

        //それ以外はすべて公開
        return isset( $this->$key ) ? $this->$key : NULL;
    }
};

さらに、これだと、 __get を共通の基底オブジェクトにして 外にくくりだせそうですしね。。。
うーん、、、存在意義がどんどんなくなってきましたね。

そうなると、残ったのはパフォーマンスだけですか。
なんてこったw


せっかくなので、最後に残ったパフォーマンスを比較してみました。

条件
VMWare Server 1.0.9 / Linux debian 2.6.26-2-686
ホストマシン windows XP / CPU:Q6600 2.4GHz
php 5.3.2 + niigata(http://rtilabs.net/files/2010_05_20/php-5.3.2.niigata.tar.gz)
コンパイルオプションなし ./configure ; make のみ。
測定方法: timeコマンド による測定を 4回実行し、もっとも real の数字が小さかったものを採用。


<?php
//__get の比較 __get1.php
class Human
{
   private $Age ,$Name, $Address, $Tel;

   public function
            __construct($name)
   {
       $this->Name = $name;
       //略
   }
	

    public function __get( $key )
    {
        return isset( $this->$key ) ? $this->$key : NULL;
    }
};

$humanObject = new Human("rti");
for ($i = 0 ; $i < 1000000 ; $i++)
{
	$myname = $humanObject->Name;
}
?>

rti @ debian:~/lab/php-5.3.2$ time ./sapi/cli/php ../__get1.php

real    0m0.993s
user    0m0.884s
sys     0m0.012s


<?php
//__get + '_' の比較 __get2.php
class Human
{
    private $_Age ,$Name, $_Address, $_Tel;

   public function
            __construct($name)
   {
       $this->Name = $name;
       //略
   }
	

    public function __get( $key )
    {
    	//_で始まるメンバ変数を公開してはいけない。
        if ( $key[0] == '_' ) return NULL;

        return isset( $this->$key ) ? $this->$key : NULL;
    }
};

$humanObject = new Human("rti");
for ($i = 0 ; $i < 1000000 ; $i++)
{
	$myname = $humanObject->Name;
}
?>

rti @ debian:~/lab/php-5.3.2$ time ./sapi/cli/php ../__get2.php

real    0m1.191s
user    0m1.172s
sys     0m0.008s




<?php
//新潟 __get3.php
class Human
{
    niigata $Name;
    private $Age , $Address, $Tel;

   public function
            __construct($name)
   {
       $this->Name = $name;
       //略
   }
};

$humanObject = new Human("rti");
for ($i = 0 ; $i < 1000000 ; $i++)
{
	$myname = $humanObject->Name;
}
?>

rti @ debian:~/lab/php-5.3.2$ time ./sapi/cli/php ../__get3.php

real    0m0.215s
user    0m0.188s
sys     0m0.008s


<?php
//public __get4.php
class Human
{
    public $Name,$Age , $Address, $Tel;

   public function
            __construct($name)
   {
       $this->Name = $name;
       //略
   }
};

$humanObject = new Human("rti");
for ($i = 0 ; $i < 1000000 ; $i++)
{
	$myname = $humanObject->Name;
}
?>

rti @ debian:~/lab/php-5.3.2$ time ./sapi/cli/php ../__get4.php

real    0m0.195s
user    0m0.164s
sys     0m0.012s


まとめ
__getで取得(__get1.php)    0m0.993s
__get + '_'(__get2.php)    0m1.191s
新潟       (__get3.php)    0m0.215s
public     (__get4.php)    0m0.195s

5倍程度の差しかないですね。。。

2010年5月22日12:00  <php-dev-request @ php.gr.jp>:
> PHP-dev メーリングリストへの投稿は以下のアドレスに送ってください.
>        php-dev @ php.gr.jp
>
> Webブラウザを使って入退会するには以下のURLにどうぞ.
>        http://ml.php.gr.jp/mailman/listinfo/php-dev
> メールを使う場合,件名(Subject:)または本文に help と書いて以下の
> アドレスに送信してください.
>        php-dev-request @ php.gr.jp
>
> メーリングリストの管理者への連絡は,以下のアドレスにお願いします.
>        php-dev-owner @ php.gr.jp
>
> 返信する場合,件名を書き直して内容がわかるようにしてください.
> そのままだと,以下のようになってしまいます. "Re: PHP-dev まとめ読み,
> XX 巻 XX 号"
>
>
> 本日の話題:
>
>   1. [PHP-dev 1507] 新潟アクセス修飾子ってのを作りました (rti)
>
>
> ----------------------------------------------------------------------
>
> Message: 1
> Date: Sat, 22 May 2010 06:20:44 +0900
> From: rti <super.rti @ gmail.com>
> Subject: [PHP-dev 1507] 新潟アクセス修飾子ってのを作りました
> To: php-dev @ php.gr.jp
> Message-ID:
>        <AANLkTikNfQ9EUWVbwRi8wf72f6kDMyRi6lsCxCm7umr_ @ mail.gmail.com>
> Content-Type: text/plain; charset=ISO-2022-JP
>
> 始めまして。rtiと申します。
> php 5.3.2を改造して、クラスの外から読めるけど書けないアクセス修飾子を実装しました。
> http://d.hatena.ne.jp/rti7743/20100520/1274309263
>
>
> これを利用すると、 getter を書く手間がなくなります。
> また、 $obj->getVal() ではなく、 $obj->Val; 形式でアクセスできます。
> それでいて、オブジェクトのカプセル化を一切破壊しません。
>
> こんな感じで指定すると、、、
>
> class Human{
>   niigata $Age;        //これです。クラス外から読めるけど書けない。
>
>   public function
>            __construct($age,$name)
>   {
>       $this->Age = $age;
>       //略
>   }
> };
>
> //コレできても、
> echo $HumanObject->Age;
>
> //これはできない
> $HumanObject->Age = 100;
> // Fatal error: Cannot access niigata property Human::$Age in
> // /home/rti/lab/php-5.3.2/a.php on line 13
>
>
> getter がたくさんあるクラスで利用すると効果は抜群です。
>
>
> //非新潟のコード
> //無意味なgetterの連続
> class Human{
>   private $Age,$Name,$Address,$Tel;
>
>   public function
>            __construct($age,$name)
>   {
>       $this->Age = $age;
>       //略
>   }
>   public function getAge(){
>       return $this->Age;
>   }
>   public function getName(){
>       return $this->Name;
>   }
>   public function getAddress(){
>       return $this->Address;
>   }
>   public function getTel(){
>       return $this->Tel;
>   }
>
> };
>
> ↓新潟ライズ
>
>
> //新潟なコード
> class Human{
>   niigata $Age,$Name,$Address,$Tel; //これ
>
>   public function
>            __construct($age,$name)
>   {
>       $this->Age = $age;
>       //略
>   }
> };
>
>
> 実装は簡単に言うと zend_object_handlers.c の zend_verify_property_access に引数
> writable を追加して、
> 読み込み時は、 public のように振舞って、書き込み時は private のように振舞っています。
>
> static int zend_verify_property_access(zend_property_info
> *property_info, zend_class_entry *ce , int writable TSRMLS_DC) /* {{{
> */
> {
>        switch (property_info->flags & ZEND_ACC_PPP_MASK) {
>                case ZEND_ACC_PUBLIC:
>                        return 1;
>                case ZEND_ACC_PROTECTED:
>                        return zend_check_protected(property_info->ce, EG(scope));
>                case ZEND_ACC_NIIGATA:
>                    if (!writable)
>                        {
>                                return 1;
>                        }
>                case ZEND_ACC_PRIVATE:
>                        if ((ce==EG(scope) || property_info->ce == EG(scope)) && EG(scope)) {
>                                return 1;
>                        } else {
>                                return 0;
>                        }
>                        break;
>        }
>        return 0;
> }
>
>
> ------------------------------
>
> _______________________________________________
> PHP-dev mailing list
> PHP-dev @ php.gr.jp
> http://ml.php.gr.jp/mailman/listinfo/php-dev
>
> 以上: PHP-dev まとめ読み, 65 巻, 1 号
> *************************************
>


PHP-dev メーリングリストの案内