Help us understand the problem. What is going on with this article?

【PHP】継承とprotectedと参照パターン

More than 1 year has passed since last update.

普通の継承 子→親参照

ex1.php
class ParentClass1
{
    protected $parentName = "parent\n";
}

class ChildClass1 extends \ParentClass1
{
    public function GetParentName()
    {
        echo $this->parentName;
    }
}

$child = new \ChildClass1;
$child->GetParentName(); // parent

// 参照というよりは、子クラスに $parentName が(継承されて)定義されているだけの状態
parent

静的な参照 parentとselfの違い

ex2_1.php
class ParentClass2
{
    protected static $parentName = "parent\n";
}

class ChildClass2 extends \ParentClass2
{
    public static function GetParentName ()
    {
        // parent::$parentName と self::$parentName は同一の存在

        echo 'test1:' . parent::$parentName;
        echo 'test2:' . self::$parentName; // 定義を継承している = 自身のプロパティとして parentName を持っている

        self::$parentName = "child\n"; // 代入
        echo 'test3:' . parent::$parentName;
        echo 'test4:' . self::$parentName; // 定義を継承している = 自身のプロパティとして parentName を持っている
    }
}

\ChildClass2::GetParentName(); // test1:parent test2:parent test3:child test4:child
test1:parent
test2:parent

test3:child
test4:child
ex2_2.php
class ParentClass3
{
    protected static $parentName = "parent\n";
}

class ChildClass3 extends \ParentClass3
{
    protected static $parentName = "child\n"; // ex2_1.php との違い

    public static function GetParentName ()
    {
        // 子クラスに同名プロパティを定義したとき、
        // parent::$parentNameとself::$parentNameは別物になる

        echo 'test1:' . parent::$parentName;
        echo 'test2:' . self::$parentName; // 自身のプロパティとして parentName を持っている

        self::$parentName = "child2\n"; // 代入
        echo 'test3:' . parent::$parentName;
        echo 'test4:' . self::$parentName; // 自身のプロパティとして parentName を持っている
    }
}

\ChildClass3::GetParentName(); // test1:parent test2:child test3:parent test4:child2
test1:parent
test2:child

test3:parent
test4:child2

普通の継承:親から子のプロパティを参照するということ(前置き)

ex3_1.php
class ParentClass4
{
    protected $name = "parent\n";

    protected function parentFunction()
    {
        echo $this->name;
    }
}

class ChildClass4 extends \ParentClass4
{
    protected $name = "child\n";

    public function CallParentFunction()
    {
        echo $this->parentFunction(); // child
    }
}

$child = new \ChildClass4;
$child->CallParentFunction(); // child

// ex1.phpと同じ
// 参照というよりは、子クラスに parentFunction() が(継承されて)定義されているだけの状態

child

静的な参照 self が指すものと遅延静的束縛

selfはその持ち主のメソッドやプロパティを、
staticは子の持つメソッドやプロパティを参照可能にする

ex3_2.php
class ParentClass5
{
    protected static $name = "parent\n";

    protected static function parentFunction()
    {
        echo self::$name;
        echo static::$name; // 遅延静的束縛
    }
}

class ChildClass5 extends \ParentClass5
{
    protected static $name = "child\n";

    public static function CallParentFunction()
    {
        // parent::parentFunction と self::parentFunction の状況は
        // ex2_1.php, ex2_2.php を参照
        echo self::parentFunction(); // parent child
    }
}

\ChildClass5::CallParentFunction(); // parent child
parent
child
ex3_3.php
class ParentClass6
{
    protected static $name = "parent\n";

    protected static function parentFunction()
    {
        echo self::$name;
        echo static::$name; // 遅延静的束縛
    }
}

class ChildClass6 extends \ParentClass6
{
    protected static $name = "child\n";

    protected static function parentFunction() // ex3_2.php との違い
    {
        echo self::$name;
        echo static::$name;
    }

    public static function CallParentFunction()
    {
        // parent::parentFunction と self::parentFunction の状況は
        // ex2_1.php, ex2_2.php を参照
        echo self::parentFunction(); // child child
    }
}

\ChildClass6::CallParentFunction(); // child child
child
child

親が子クラスの protected を利用できる ということ

相互参照できるとは書かれていても、使いどころも少ないのであまり詳細は見かけないかも
# Factory パターンとかで使えなくもなさそうだけど、他にやりようはあるし…

よくある誤解

# PHPだからね

ex4_0.php
class ParentClass7
{
    public function GetChildName()
    {
        echo $this->name;
    }
}

class ChildClass7 extends \ParentClass7
{
    protected $name = "child\n";
}

$parent = new \ParentClass7;
$parent->GetChildName();    // ""

$parent->name = "parent_name";
var_dump($parent); // object(ParentClass)#1 (1) { ["name"]=> string(11) "parent_name" } 親クラスに public の新たなプロパティが追加された
$parent->GetChildName(); // parent_name 動くようになるが、子クラスの定義は関係ない
object(ParentClass7)#4 (1) {
  ["name"]=>
  string(11) "parent_name"
}
parent_name

インスタンスで見ると直接的な関係が無くても extends があれば protected が読め、なければ読めない

ex4_1_1.php
class ParentClass8
{
    public function GetChildName()
    {
        $child = new \ChildClass8; // この ChildClass のインスタンス($child)と自身($parent)に直接的な関係がなさそうでも、
        echo $child->name; // 定義上親子であれば子の protected は参照できる
    }

    public function CallChildFunction()
    {
        \ChildClass8::childFunction(); // インスタンスじゃなくても。
    }
}

class ChildClass8 extends \ParentClass8
{
    protected $name = "child\n";

    protected static function childFunction ()
    {
        echo "child_function\n";
    }
}

$parent = new \ParentClass8;
$parent->GetChildName(); // child
$parent->CallChildFunction(); // child_function
child
child_function
ex4_1_2.php
class ParentClass9
{
    public function GetChildName($child)
    {
        echo $child->name;    // 定義上親子であれば子のprotectedは参照できる
    }

    public function CallChildFunction($child)
    {
        $child->childFunction();
    }
}

class ChildClass9 extends \ParentClass9
{
    protected $name = "child\n";

    protected function childFunction()
    {
        echo "child_function\n";
    }
}

$parent = new \ParentClass9;
$child = new \ChildClass9;
$parent->GetChildName($child); // child この ChildClass のインスタンス($child)と自身($parent)に直接的な関係がなさそうでも
$parent->CallChildFunction($child); // child_function
child
child_function

extends がなければもちろん読めない

ex4_2.php
class ParentClass10
{
    public static function GetChildName()
    {
        $child = new \ChildClass10;
        echo $child->name;
    }

    public static function CallChildFunction()
    {
        \ChildClass10::childFunction();
    }
}

class ChildClass10 /* extends \ParentClass10 */
{
    protected $name = "child\n";

    protected static function childFunction ()
    {
        echo "child_function\n";
    }
}

\ParentClass10::GetChildName(); // PHP Fatal error:  Uncaught Error: Cannot access protected property \ChildClass10::$name
PHP Fatal error:  Uncaught Error: Cannot access protected property ChildClass10::$name

余談(継承とかじゃないけど)

同じクラスの private は、インスタンスが分かれていても読むことができる

ex5.php
class Test
{
    private $name = "";

    public function __construct($name = "")
    {
        $this->name = $name;
    }

    public function GetName($instance)
    {
        echo $instance->name;
    }
}

$instanceA = new \Test("A");
$instanceB = new \Test("B");

$instanceA->GetName($instanceB); // B
B

他の言語でも大体同じなので試してみてね

Why do not you register as a user and use Qiita more conveniently?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away