メンバのアクセス制限

クラスとオブジェクト

アクセス制限とは?

 状態:-  閲覧数:4,113  投稿日:2010-05-07  更新日:2013-04-18  
・メンバ定義時に「アクセス制限修飾子」を指定することで、そのメンバが参照できる範囲(可視性)を限定すること

・アクセス制限修飾子 … 3種類         
種類 意味
public 何処からでも(オブジェクト経由で)アクセス  可能
protected 同じクラスと派生クラス内のメソッドか  らのみアクセス可能
private 同じクラス内のメソッドからのみアクセス可  能
▼同名プロパティの可視性の優先順位と派生クラスでのアクセス制限修飾子設定時の注意点

派生クラスでメンバ関数(メソッド)をオーバーライドした場合、「基底クラスメソッド」「派生クラスメソッド」「オブジェクト経由」いずれでアクセスする場合も、コールされるのはオーバーライドされた方のメソッドとなる。一方、メンバ変数(プロパティ)を派生クラスで再定義し、基底クラスのメソッドがそのプロパティを参照しようとした場合、参照可能な可視性であれば、候補は両方のプロパティとなる。この場合、どちらを参照するかについては、基底クラスで指定した可視性により決定される。         
基底クラスでの可視性 参照されるプロパティ
protected public 派生クラスのプロパティ
private 基底クラスのプロパティ
つまり、常に同じクラス内のプロパティを参照したい場合は「プライベート」で宣言し、再定義された派生クラスを参照する場合には「プロテクテッド」以上の可視性を設定するということ。



▼クラス継承時の、可視性設定における注意点

派生クラスで基底クラスと同名のプロパティを再定義する場合も、メソッドをオーバーライドする場合も、派生クラスの可視性は基底クラスで設定したものと同じか、それよりも“視界が広い”ものに設定しなくてはならない。例えば基底クラスで「プロテクテッド」宣言したメンバを派生クラスでは「プライベート」で宣言する事は出来ない。この場合は必ず「プロテクテッド」か「パブリック」に設定する。         
基底クラスでの可視性 派生クラスで有効な可視性
「public」 「public」
「protected」 「public」、  「protected」
「private」 「public」、  「protected」、「private」
▼アクセス制限修飾子(「public」、「protected」、「private」)設定例

<pre>
<?php
 class BASE{
   #PHP4式(PHP5では「E_STRICT」注意を発する)
   var       $var4       = "publicメンバ変数\$var4";
   #PHP5式
   public    $public_var    = "publicメンバ変数\$public_var";
   protected $protected_var = "protectedメンバ変数\$protected_var";
   private   $private_var   = "privateメンバ変数\$private_var";
   function BASE(){//②基底クラスコンストラクタ呼び出される。基底クラスコンストラクタからは全メンバにアクセスできる
     echo "****** ②BASEconstructor(基底コンストラクタ) よりアクセス。基底クラスコンストラクタからは全メンバにアクセス可能******\n";
     echo '$this->var4               : ', $this->var4, "\n";
     echo '$this->public_var         : ', $this->public_var, "\n";
     echo '$this->protected_var      : ', $this->protected_var, "\n";
     echo '$this->private_var        : ', $this->private_var, "\n";
     echo '$this->public_method()    : ', $this->public_method(), "\n";
     echo '$this->protected_method() : ', $this->protected_method(), "\n";
     echo '$this->private_method()   : ', $this->private_method(), "\n\n";
   }
   public function public_method(){
     return "public_method()";
   }
   protected function protected_method(){
     return "protected_method()";
   }
   private function private_method(){
     return "private_method()";
   }
   private function private_method_test(){
     return "private_method_test()";
   }
 }
 
 class SUB extends BASE{
   function SUB(){//⑤派生クラスコンストラクタが呼び出される
     echo "\n****** ⑤SUBconstructor(派生コンストラクタ) よりアクセス ******\n";
     echo '$this->var4                  : ', $this->var4, "\n";
     echo '$this->public_var            : ', $this->public_var, "\n";
     echo '$this->protected_var         : ', $this->protected_var, "\n";
     echo '$this->private_var           : ', $this->private_var, "×\n";
     echo '$this->public_method()       : ', $this->public_method(), "\n";
     echo '$this->protected_method()    : ', $this->protected_method(), "\n";
     echo '$this->private_method()      : '/*, $this->private_method()*/,"×【Fatal error:  Call to private method BASE::private_method() from context '(派生クラス名)】'\n";
     echo '$this->private_method_test() : ', $this->private_method_test(),"\n\n";
   }
   #プライベートメソッドをオーバーライド
   private function private_method_test(){
     return "オーバーライドした派生クラスのprivate_method_test()";
   }
 }
 
 class AIR{
   #プライベートなコンストラクタは・・・
   private function AIR(){
     echo "****** AIR constructor ******";
   }
 }
 
 
 
 #①「BASE」オブジェクト生成。コンストラクタ(メンバ関数)自動実行
 $base = new BASE;
 
 #③base(基底)クラスオブジェクト経由でアクセス
 echo "****** ③base(基底)クラスオブジェクト経由でアクセス。「public(またはvar)」宣言したもののみ、アクセス可能  ******\n";
 echo '$base->var4               : ', $base->var4, "\n";
 echo '$base->public_var         : ', $base->public_var, "\n";
 echo '$base->protected_var      : '/*, $base->protected_var,*/ ."×【Fatal error:  Cannot access protected property BASE::\$protected_var】\n";
 echo '$base->private_var        : '/*, $base->private_var*/, "×【Fatal error:  Cannot access private property BASE::\$private_var】\n";
 echo '$base->public_method()    : ', $base->public_method(), "\n";
 echo '$base->protected_method() : '/*, $base->protected_method()*/, "×【Fatal error:  Call to protected method BASE::protected_method() from context ''】\n";
 echo '$base->private_method()   : '/*, $base->private_method()*/, "×【Fatal error:  Call to private method BASE::private_method() from context ''】\n";
 
 
 #④「SUB」(「BASE」の派生クラス)オブジェクトを生成。コンストラクタ(メンバ関数)自動実行
 $sub = new SUB;
 
 #⑥sub(派生)クラスオブジェクト経由でアクセス
 echo "****** ⑥sub(派生)クラスオブジェクト経由でアクセス。「public(またはvar)」宣言したもののみ、アクセス可能  ******\n";
 echo '$sub->var4               : ', $sub->var4, "\n";
 echo '$sub->public_var         : ', $sub->public_var, "\n";
 echo '$sub->protected_var      : '/*, $sub->protected_var*/, "×【Fatal error:  Cannot access protected property SUB::$protected_var 】\n";
 echo '$sub->private_var        : '/*, $sub->private_var*/, "×【Fatal error:  Using $this when not in object contex 】\n";
 echo '$sub->public_method()    : ', $sub->public_method(), "\n";
 echo '$sub->protected_method() : '/*, $sub->protected_method()*/, "×【Fatal error:  Call to protected method BASE::protected_method() from context 】\n";
 echo '$sub->private_method()   : '/*, $sub->private_method()*/, "×【Fatal error:  Call to private method BASE::private_method() from context 】\n";
 echo '$sub->private_method_test() : '/*, $sub->private_method_test()*/,"×【Fatal error:  Call to private method SUB::private_method_test() from context '' 】\n\n";
 
 #⑦「AIR」オブジェクト生成。コンストラクタを自動実行しようとするが、Fatal error:  Call to private AIR::AIR() from invalid context in
 new AIR;
?>
</pre>


▼結果

/demo/access_limit2.html




▼修正前/オブジェクトのメンバが常に参照可能な例

メンバ -オブジェクトの「メンバ変数(プロパティ)」と「メンバ関数(メソッド)」のこと。

サンプルの「BOXER」クラスは、活動する(「fight()」メソッド)ごとにスタミナ(「プロパティ$stamina」)が減少していき、当初スタミナの6割を切ると試合終了という設定。「get_stamina()」メソッドでスタミナ表示。また、「BOXER」クラスを継承した「MIDDLEWIIGHT_BOXER」クラスを定義。

<pre>
<?php
   class BOXER{
       var $first_stamina;
       var $stamina;
       var $voice = "...";
       var $judge = true;
       function BOXER($paramstamina){//④「コンストラクタBOXER」呼び出される。「引数$paramstamina(=10)」を受け取る
           $this->first_stamina = $this->stamina = $paramstamina;//⑤「引数$paramstamina(=10)」を「メンバ変数$stamina」へ格納。「メンバ変数$stamina(=10)」を「メンバ変数$first_stamina」へ格納
           echo "当初スタミナ {$this->stamina} パワー\n";//⑥
       }
       function fight(){//⑨「メンバ関数fight」呼び出される
           echo "{$this->stamina} パワー\n";//⑩
           $this->judgef() and $this->stamina *= 0.9;//⑪論理積。左辺及び右辺の条件式が共に真の場合のみ、それぞれの式が実行される。「関数judgef」実行。⑭メンバ変数×0.9
$this->stamina < $this->first_stamina * 0.6
and $this->judgef(true);//⑮論理積。「現在のスタミナが当初スタミナの6割より少ない」and「メンバ関数judgefが真」
var_dump($this->stamina < $this->first_stamina * 0.6
and $this->judgef(true));//デバッグ。この式自体は、常にFALSEとなるが、左辺が真の場合、右辺の処理先judgef(true)で、メンバ変数にfalseが格納される
           if($this->judgef()){//⑯
               $this->soundeffect();//⑰
               return true;
           }else{
               echo "試合終了※当初スタミナ(10)の6割を切ったため\n";
               return false;
           }
       }
       function soundeffect(){//⑱
           echo $this->voice, "\n";//⑲派生クラスのメンバ変数が呼ばれる
       }
       function judgef($paramjudge = false){//⑫⑰
           if($paramjudge)
               $this->judge = false;
           return $this->judge;//⑬⑱
}
       function get_stamina(){
           return $this->stamina;
       }
   }
   class MIDDLEWEIGHT_BOXER extends BOXER{
       var $voice = "ファイッ!";//⑳
       function MIDDLEWEIGHT_BOXER($stamina){//②「コンストラクタMIDDLEWEIGHT_BOXER」呼び出される。「引数$stamina(=10)」を受け取る
           $this->BOXER($stamina);//③「派生クラスコンストラクタMIDDLEWEIGHT_BOXER」内で、「基底クラスコンストラクタBOXER」実行。その際、「引数$stamina(=10)」を渡す
       }
   }
   
   #オブジェクト生成
   $middleweight_boxer1 = new MIDDLEWEIGHT_BOXER(10);//①MIDDLEWEIGHT_BOXERクラスの「インスタンス$middleweight_boxer1」生成。コンストラクタ実行。その際、引数(10)を渡す。
   
   #middleweight_boxer1の設定(試合終了となるまで活動)
   while($middleweight_boxer1->fight())//⑦trueの間は(falseとなるまで)、
       continue;//⑧基底クラスの「メンバ関数fight」実行

   #スタミナ測定
   echo "\n試合終了時スタミナ ", $middleweight_boxer1->get_stamina(), "パワー\n";
   
   #スタミナ操作(不正操作=メンバ変数に簡単にアクセス・操作できてしまう例)
   $middleweight_boxer1->stamina = 200;
   
   #スタミナ測定
   echo "\n不正操作後の現在のスタミナ ", $middleweight_boxer1->get_stamina(), "パワー\n";
?>
</pre>


▼結果

/demo/access_limit1.html




アクセス制限修飾子を付ける時のポイント - 必要以上に可視性を広めないこと

基本「プライベート」、必要な箇所のみ「プロテクテッド」か「パブリック」へ変更。

プロパティ(メンバ変数)選択肢は「プライベート」か「プロテクテッド」のみ 外部から直接参照するような使い方は不可。


まず、「BOXER」クラスのプロパティを全て「プライベート」に設定。ただ「プロパティ$voice」は「MIDDLEWEIGHT_BOXER」クラスで再定義し、それを参照させたいので「プロテクテッド」に変更。これに伴い、「MIDDLEWEIGHT_BOXER」クラス側の「プロパティ$voice」も「プロテクテッド」に変更。また、オブジェクト経由でコールする「fight()」メソッドと「get_stamina()」メソッド、あと「MIDDLEWEIGHT_BOXER」クラスのコンストラクタの可視性は「パブリック」に設定。「BOXER」クラスのコンストラクタは、「BOXER」クラスがオブジェクトを生成することを前提とせず、その派生クラスがオブジェクトを生成するという仕様なので、「プロテクテッド」に設定。その他のメソッドはそのまま「プライベート」に。


▼「オブジェクトのメンバが常に参照可能な例」にアクセス制限修飾子を追記した例

<pre>
<?php
   class BOXER{
       private $first_stamina;
       private $stamina;
       protected $voice = "...";
       private $judge = true;
       protected function BOXER($paramstamina){//④「コンストラクタBOXER」呼び出される。「引数$paramstamina(=10)」を受け取る
           $this->first_stamina = $this->stamina = $paramstamina;//⑤「引数$paramstamina(=10)」を「メンバ変数$stamina」へ格納。「メンバ変数$stamina(=10)」を「メンバ変数$first_stamina」へ格納
           echo "当初スタミナ {$this->stamina} パワー\n";//⑥
       }
       public function fight(){//⑨「メンバ関数fight」呼び出される
           echo "{$this->stamina} パワー\n";//⑩
           $this->judgef() and $this->stamina *= 0.9;//⑪論理積。左辺及び右辺の条件式が共に真の場合のみ、それぞれの式が実行される。「関数judgef」実行。⑭メンバ変数×0.9
$this->stamina < $this->first_stamina * 0.6
and $this->judgef(true);//⑮論理積。「現在のスタミナが当初スタミナの6割より少ない」and「メンバ関数judgefが真」
var_dump($this->stamina < $this->first_stamina * 0.6
and $this->judgef(true));//デバッグ。この式自体は、常にFALSEとなるが、左辺が真の場合、右辺の処理先judgef(true)で、メンバ変数にfalseが格納される
           if($this->judgef()){//⑯
               $this->soundeffect();//⑰
               return true;
           }else{
               echo "試合終了※当初スタミナ(10)の6割を切ったため\n";
               return false;
           }
       }
       private function soundeffect(){//⑱
           echo $this->voice, "\n";//⑲派生クラスのメンバ変数が呼ばれる
       }
       private function judgef($paramjudge = false){//⑫⑰
           if($paramjudge)
               $this->judge = false;
           return $this->judge;//⑬⑱
}
       public function get_stamina(){
           return $this->stamina;
       }
   }
   class MIDDLEWEIGHT_BOXER extends BOXER{
       protected $voice = "ファイッ!";//⑳
       public function MIDDLEWEIGHT_BOXER($stamina){//②「コンストラクタMIDDLEWEIGHT_BOXER」呼び出される。「引数$stamina(=10)」を受け取る
           $this->BOXER($stamina);//③「派生クラスコンストラクタMIDDLEWEIGHT_BOXER」内で、「基底クラスコンストラクタBOXER」実行。その際、「引数$stamina(=10)」を渡す
       }
   }
   
   #オブジェクト生成
   $middleweight_boxer1 = new MIDDLEWEIGHT_BOXER(10);//①MIDDLEWEIGHT_BOXERクラスの「インスタンス$middleweight_boxer1」生成。コンストラクタ実行。その際、引数(10)を渡す。
   
   #middleweight_boxer1の設定(試合終了となるまで活動)
   while($middleweight_boxer1->fight())//⑦trueの間は(falseとなるまで)、
       continue;//⑧基底クラスの「メンバ関数fight」実行

   #スタミナ測定
   echo "\n試合終了時スタミナ ", $middleweight_boxer1->get_stamina(), "パワー\n";
   
   #スタミナ操作失敗(不正操作失敗=メンバ変数にアクセスできない例)
   $middleweight_boxer1->stamina = 200;
   
   #スタミナ測定
   echo "\n不正操作(失敗)後の現在のスタミナ ", $middleweight_boxer1->get_stamina(), "パワー\n";
?>
</pre>


▼結果

/demo/access_limit3.html


オーバーライド

static修飾子

コメント投稿(ログインが必要)



週間人気ページランキング / 11-16 → 11-22
順位 ページタイトル抜粋 アクセス数
1 ブラウザを閉じたらセッションデータはどうなるの? | セッション 8
2 Parse error: syntax error, unexpected 'public' (T_PUBLIC) | Parse error(エラーメッセージ) 5
3 Fatal error: Access level to ▲::$△ must be protected (as in class ●) or weaker | Fatal error(エラーメッセージ) 4
3 Warning: strlen() expects parameter 1 to be string, array given in ○○.php on line △△ | Warning(エラーメッセージ) 4
4 PHPで定数を定義する方法は2種類ある / 配列定数の定義 3
4 インポートするデータを受信できませんでした。ファイル名が送信されていないか、ファイルサイズが PHP の設定で許可された最大値を超えています。FAQ 1.16 をご覧ください | エラーメッセージ 3
4 ブラウザを閉じたらセッションデータはどうなるの? | セッション 3
4 クラスの継承 | クラスとオブジェクト 3
5 curl で Cookie を使用する 2
5 例外処理 | 制御構造 2
5 Fatal error: Uncaught Error: Call to a member function modify() on string | Fatal error(エラーメッセージ) 2
5 関数定義内での「外部ファイル読込」 | 制御構造 2
5 set_error_handler | 例外処理(制御構造) 2
5 ( ! ) Fatal error: Uncaught PDOException: SQLSTATE[HY093]: Invalid parameter number: parameter was not defined | Fatal error(エラーメッセージ) 2
5 コード例 … 「例外処理」はネストすることができる 2
5 PHP用語 2
5 register_shutdown_function | 関数処理 関数 2
6 クロージャ | 関数 1
6 session.cookie_lifetime / session.use_cookies | セッション 1
6 セッション管理が必要な理由は、HTTPプロトコルには状態を保持する機能がないため | セッション 1
2024/11/23 1:01 更新