[PHP] 初心者的なクラスのメモ書き

を参考にしてクラスの学習

用語の羅列

  • メンバ(変数、関数)
  • プロパティ
  • メソッド
  • var
  • new
  • クラス
  • オブジェクト
  • インスタンス
  • コンストラクタ
  • クラスの継承
  • 基底クラス
  • 派生クラス
  • オーバーライド
  • stdClass

アクセス制限修飾子

public, protected, private の3つがアクセス制限修飾子。

メンバが参照できる範囲(可視性)を指定する。

  • public : アクセス自由。
  • protected : 同クラスのメソッドからだけでなく派生クラスのメソッドからもアクセスできる。
  • private : 同クラスのメソッドのみアクセス可能。同クラスでもメソッドからしかアクセスできないし、派生クラスなどの同クラス以外のものからは一切アクセスできない。
  • var : PHP4でメンバ定数定義時に使用。publicと同じ。PHP5では使わないほうが良い。

とりあえずごくごく簡単に

<?php

class CLASS_TEST{

  public  $foo = "public_property \n"  ;
  private $baa = "private_property \n" ;

  public /*アクセス修飾子を省略した場合もpublic扱い*/
    function CLASS_TEST($text) { /*コンストラクタ*/
    echo $text ;
    echo $this->foo ;
    echo $this->baa ;
    echo $this->hoge($this->baa) ;
    echo $this->baz( $this->baa) ;
  }
  function hoge( $property ){
    echo "hoge_method prints $property" ;
  }
  private function baz( $property ){
    echo "buz_method prints $property" ;
  }
}

$instance = new CLASS_TEST("//start// \n") ;

echo "//test//\n";
echo $instance->foo ;
$instance->hoge("HOGEHOGE");

$instance->baz("テスト"); //この時点で処理はストップ
echo $instance->baa; //これも実行されればFatal Error

?>

結果

//start//
public_property
private_property
hoge_method prints private_property
buz_method prints private_property
//test//
public_property
hoge_method prints HOGEHOGE

Fatal error:  Call to private method CLASS_TEST::baz() from context '' in
ファイルパス on line 30

プロパティ名が数字の場合どうやってアクセスするか?

$obj = json_decode( json_encode(
  array( 100, 200, 300, 400, "TEST" => "テスト" )
));

print_r( $obj );

echo $obj->{0}, "\r\n";
echo $obj->{'1'}, "\r\n";
echo $obj->{1+1}, "\r\n";
/*
 - 下の書き方だと 以下のエラーがでる
 - Parse error: syntax error, unexpected T_LNUMBER, expecting T_STRING or T_VARIABLE or '{' or '$'
 */
// echo $obj->3;

結果

stdClass Object
(
    [0] => 100
    [1] => 200
    [2] => 300
    [3] => 400
    [TEST] => テスト
)
100
200
300

可変変数を使用したメソッドコールの分岐

class MethodCall {

  private static function hello( $name ) {
    echo "Hello, {$name} \r\n";
  }

  private static function bye( $name ) {
    echo "Good bye, {$name} \r\n";
  }

  public static function test1( $arg ) {
    self::$arg("テスト1");
  }

  public static function test2( $arr ) {
    self::$arr[0]("テスト2");
  }

}

MethodCall::test1( "hello" );
MethodCall::test2( array("bye") );

結果

Hello, テスト1
Good bye, テスト2

クラスの継承

<?php
class BASE
{
  private $private = "base_pribate \n";

  function BASE($text){
    echo $text;
  }
  public function func1(){
    echo $this->private;
  }
  function hello(){
    echo "[BASE] Hello! \n" ;
  }
}
class EXTEND extends BASE
{
  function EXTEND($text = NULL ){
    echo $text;
  }
  function func2(){
    echo $this->private;
  }
  function func3($text){
    echo $this->BASE($text);
  }
  function hello(){
    echo "[EXTEND] Hello! \n" ;
  }
}

BASE::hello();
EXTEND::hello();
$class = new EXTEND;
$class->hello();
$class->func1();
$class->func2();
$class->func3("基底クラスのコンストラクタはコールできる?\n");
$class->EXTEND('自身のコンストラクタは呼べる?');
EXTEND::EXTEND();
?>

結果

[BASE] Hello!
[EXTEND] Hello!
[EXTEND] Hello!
base_pribate

Notice: Undefined property: EXTEND::$private
in *** on line 22

基底クラスのコンストラクタはコールできる?
自身のコンストラクタは呼べる?

Fatal error: Non-static method EXTEND::EXTEND()
cannot be called statically in *** on line 40

コンストラクタは静的に呼び出すことはできないので最後はFatal error。静的に呼べないというのはオカシイか。。コンストラクタは常に非スタティックな関数、だってインスタンスが生成されたときにそのインスタンスで実行されるのにクラスの持つスタティック関数を実行するわけにはいかないよね。

<?php
class BASE
{
  private $base_private = "base_private!! \n";

  protected function func_protected(){
    echo "--now in func_protected()--\n";
    $this->func_private();
  }
  private function func_private(){
    echo "--now in func_private()--\n";
    echo $this->base_private;
  }
}
class EXTEND extends BASE
{
  public function func1(){
    $this->func_protected();
  }
  public function func2(){
    $this->func_private();
  }
}

$class = new EXTEND;
$class->func1();
$class->func2();  
?>

結果

--now in func_protected()--
--now in func_private()--
base_private!!

Fatal error:  Call to private method
BASE::func_private() from context 'EXTEND' in *** on line 21

基底クラスのprivate methodは、派生クラスで定義された関数から直接コールできない。

基底クラスのprivate propertyは、継承した基底クラスのmethodを使えばアクセスできる。基底クラスのメソッドだけが基底クラスのprivateプロパティにアクセスできる。

<?php
class BASE
{
  public $pub = "base_pub \n";
  protected $pro = "base_pro \n";
  private $pri = "base_pri \n";

  function base_properties(){
    echo $this->pub.$this->pro.$this->pri;
  }
}
class EXTEND extends BASE
{
  public $pub = "ex_pub \n";
  protected $pro = "ex_pro \n";
  private $pri = "ex_pri \n";

  function extend_properties(){
    echo $this->pub.$this->pro.$this->pri;
  }
}

$class = new EXTEND();
$class->base_properties();
$class->extend_properties();
?>

結果

ex_pub
ex_pro
base_pri
ex_pub
ex_pro
ex_pri

base_pri が出力されるのに注目。。

<?php
class BASE
{
  private $var = "base";

  function func1(){
    $this->func3();
  }
  function func2(){
    self::func3();
  }
  function func3(){
    echo $this->var;
  }
}
class EXTEND extends BASE
{
  private $var = "extend";

  function func1(){
    parent::func1();
  }
  function func2(){
    parent::func2();
  }
  function func3(){
    echo "残念。ここは基底クラスではない \n";
  }
}
$extend = new EXTEND;
$extend->func1();
$extend->func2();
?>

結果

残念。ここは基底クラスではない
base

オーバーライドした基底クラスのメソッドにアクセスするには parent:: を使う。

$this は自身の参照が入っている。 継承した場合、基底クラスにある $this の参照はどうなるかというと派生クラスのインスタンス への参照になる。 だから $extend->func1() では 基底クラスのプロパティにアクセスはできない。 self:: を使えばクラス自身への参照を使えるので基底クラスのメソッドにアクセスできる。 self:: の変わりに BASE:: でも基底クラスのメソッドにアクセスできる。

というか…$this, parent::, self:: の違いが…混乱するな。。$this はインスタンス自身の参照でインスタンスプロパティのアクセスに使用できる。parent::, self:: はそれぞれ基底クラス、クラス自身orインスタンス自身への参照でありメソッドにアクセスできる。可視性がprotected 以上の基底クラスのプロパティはオーバーライドするとアクセスはできない。内部的にはref_count がゼロになるのでインスタンスを生成した時点で、基底クラスのprivate以外のプロパティは消去されてなくなっている。以下を参照

XDebugでzvalの解析をすると

<?php
class Z
{
  public $z_pub = "ZPUB";
  protected $z_pro = "ZPRO";
  private $z_pri = "ZPRI";
}
$z = new Z;
xdebug_debug_zval('z');

結果

z:
(refcount=1, is_ref=0),
object(Z)[1]
  public 'z_pub' => (refcount=2, is_ref=0),string 'ZPUB' (length=4)
  protected 'z_pro' => (refcount=2, is_ref=0),string 'ZPRO' (length=4)
  private 'z_pri' => (refcount=2, is_ref=0),string 'ZPRI' (length=4)
<?php
class Z
{
  public $pub = "Zpub";
  protected $pro_pro = "Zpro";
  protected $pro_pub = "Zpro";
  private $pri_pri = "Zpri";
  private $pri_pro = "Zpri";
  private $pri_pub = "Zpri";
}
class A extends Z
{
  public $pub = "Apub";
  protected $pro_pro = "Apro";
  public $pro_pub = "Apub";
  private $pri_pri = "Apri";
  protected $pri_pro = "Apro";
  public $pri_pub = "Apub";
}
$a = new A;
xdebug_debug_zval('a');

結果

a:
(refcount=1, is_ref=0),
object(A)[1]
  public 'pub' => (refcount=2, is_ref=0),string 'Apub' (length=4)
  protected 'pro_pro' => (refcount=2, is_ref=0),string 'Apro' (length=4)
  public 'pro_pub' => (refcount=2, is_ref=0),string 'Apub' (length=4)
  private 'pri_pri' => (refcount=2, is_ref=0),string 'Apri' (length=4)
  protected 'pri_pro' => (refcount=2, is_ref=0),string 'Apro' (length=4)
  public 'pri_pub' => (refcount=2, is_ref=0),string 'Apub' (length=4)
  private 'pri_pri' => (refcount=3, is_ref=0),string 'Zpri' (length=4)
  private 'pri_pro' => (refcount=3, is_ref=0),string 'Zpri' (length=4)
  private 'pri_pub' => (refcount=3, is_ref=0),string 'Zpri' (length=4)

stdClass

stdClass は定義済みのクラス。 オブジェクトではない型の値がオブジェクトに変換されるときは、 定義済みクラス stdClass のインスタンスに変換される。

<?php
$arr = array(1,array(2,3));
$obj = (object) $arr ;
var_dump($obj);
?>

結果

object(stdClass)#1 (2) {
  [0]=>
  int(1)
  [1]=>
  array(2) {
    [0]=>
    int(2)
    [1]=>
    int(3)
  }
}

オブジェクトと参照の関係

PHP5以降のオブジェクトは参照渡し、というのはウソらしい。 だから参照渡しするなら & を使わなければならない。 詳しくは以下のサイトを読んで理解すべし

クラス定数

class CLS1{
  const CONSTANT = "クラス定数" ;
  function CLS1(){
    echo self::CONSTANT . "(self)" ;
  }
}
class CLS2{
  public $var = CLS1::CONSTANT ;
}
function foo($text){
  echo $text ."/". CLS1::CONSTANT ;
}
echo CLS1::CONSTANT ;
echo "\n--1--\n";

$class = new CLS2 ;
echo $class->var ;
echo "\n--2--\n";

foo(CLS1::CONSTANT);
echo "\n--3--\n";

new CLS1 ;
?>

結果

クラス定数
--1--
クラス定数
--2--
クラス定数/クラス定数
--3--
クラス定数(self)

クラスに属するというだけの定数で、扱い方は他の定数とほとんど差がない。

スタティックメンバ

  • アクセス制限修飾子のように メンバ定義時に static $var; static function func(){} というように使う。
  • スタティックプロパティにアクセスするには ::演算子 を使うしかない。 stdClass::$var ;

    <?php
    class CLS
    {
    public $pub = "public!!";
    static $static = "static!!";
    
    function CLS(){
    echo "**in CLS()**\n";
    echo self::$static, "\n\n";
    }
    public function pub_func(){
    echo "**in pub_func()**\n\n\n";
    self::sta_func();
    }
    static function sta_func(){
    echo "**in sta_func()**\n";
    echo self::$static;
    echo $this->pub;
    }
    }
    echo "**in global**\n";
    echo CLS::$static, "\n\n\n";
    
    $class = new CLS;
    echo $class->static, "\n";
    
    $class->pub_func();
    ?>
    

結果

**global**
static!!

**in CLS()**
static!!
Notice:  Undefined property:  CLS::$static
in *** on line 25

**in pub_func()**

**in sta_func()**
static!!
Fatal error:  Using $this when not in object context
in *** on line 18
  • staticメンバにはインスタンスを生成しなくてもアクセスできる
  • staticメンバへのアクセスは基本的に ::演算子を使う
  • インスタンスからstaticプロパティにアクセス( $instance->property )しようとするとNotice: Undefined property。つまりインスタンスからstaticプロパティにはアクセス不能…というとちょっと変か。インスタンスを生成してもスタティックプロパティはインスタンスに付いてこないよ、と。
  • staticメソッドはinstanceメンバにアクセスできない。つまり $this は関数定義に使えない。

    <?php
    class CLS
    {
    public $pub;
    
    static function sta_func($memo){
    echo "[$memo] this is static method.\n";
    }
    static function sta_func2(){
    self::sta_func("sta_func2");
    }
    }
    CLS::sta_func("class");
    
    $class = new CLS;
    $arg = "instance";
    $class->sta_func( $arg );
    CLS::sta_func2();
    CLS::$pub;
    ?>
    

結果

[class] this is static method.
[instance] this is static method.
[sta_func2] this is static method.

Fatal error:  Access to undeclared static property:
CLS::$pub in *** on line 19

static method は ::演算子だけではなく、$instance->method(); という様にコールすることができる…が、静的なのでいかなるインスタンスから呼び出しても ::演算子を使った場合とまったく同じ動作をするだけなのでインスタンスから呼び出す意味はない…よね?

instanceプロパティには ::演算子ではアクセス不能でFatal errorとなる…じゃなかった::演算子を使うとスタティックプロパティを見に行ってしまいFatal Errorとなる。

<?php
class CLS
{
  static $sta = 0 ;
  static function sta_increment(){
    self::$sta++;
  }
  function CLS(){
    echo self::$sta, "\n";
  }
}
CLS::sta_increment();
$class1 = new CLS;
CLS::sta_increment();
$class2 = new CLS;
$class2->sta_increment();
echo CLS::$sta;
?>

結果

1
2
3

この動作はスタティック変数の動作と同じですな。

さてこれは何だろうね

  1. CLASS::test
  2. CLASS::$test
  3. CLASS::test()
  4. CLASS::$test()

上から順に、クラス定数、スタティックプロパティ、スタティックメソッド、可変変数によるスタティックメソッドコール。ただしstatic宣言しなくてもメンバにアクセスしないメソッド($thisを使わないメソッド)はstaticメソッドと等しいので3番目は『メンバにアクセスしないインスタンスメソッド』でもオッケーなのかな?

その2に続く。。

Share
関連記事